From f449f278dd3c70e479a035f50a9bb817a9b433ba Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 17:24:08 +0200 Subject: Adding upstream version 3.2.6. Signed-off-by: Daniel Baumann --- src/Makefile.am | 23 + src/Makefile.in | 8325 ++ src/config.h.in | 277 + src/contrib/Makefile.inc | 269 + src/contrib/asan.h | 37 + src/contrib/base32hex.c | 353 + src/contrib/base32hex.h | 104 + src/contrib/base64.c | 272 + src/contrib/base64.h | 103 + src/contrib/base64url.c | 287 + src/contrib/base64url.h | 103 + src/contrib/color.h | 31 + src/contrib/conn_pool.c | 243 + src/contrib/conn_pool.h | 100 + src/contrib/ctype.h | 193 + src/contrib/dnstap/convert.c | 142 + src/contrib/dnstap/convert.h | 60 + src/contrib/dnstap/dnstap.c | 41 + src/contrib/dnstap/dnstap.h | 47 + src/contrib/dnstap/dnstap.proto | 270 + src/contrib/dnstap/message.c | 130 + src/contrib/dnstap/message.h | 63 + src/contrib/dnstap/reader.c | 103 + src/contrib/dnstap/reader.h | 73 + src/contrib/dnstap/writer.c | 120 + src/contrib/dnstap/writer.h | 71 + src/contrib/files.c | 254 + src/contrib/files.h | 77 + src/contrib/getline.c | 60 + src/contrib/getline.h | 39 + src/contrib/json.c | 237 + src/contrib/json.h | 87 + src/contrib/libbpf/LICENSE | 1 + src/contrib/libbpf/bpf/bpf.c | 710 + src/contrib/libbpf/bpf/bpf.h | 184 + src/contrib/libbpf/bpf/bpf_core_read.h | 263 + src/contrib/libbpf/bpf/bpf_endian.h | 72 + src/contrib/libbpf/bpf/bpf_helper_defs.h | 2759 + src/contrib/libbpf/bpf/bpf_helpers.h | 47 + src/contrib/libbpf/bpf/bpf_prog_linfo.c | 246 + src/contrib/libbpf/bpf/bpf_tracing.h | 195 + src/contrib/libbpf/bpf/btf.c | 2884 + src/contrib/libbpf/bpf/btf.h | 311 + src/contrib/libbpf/bpf/btf_dump.c | 1386 + src/contrib/libbpf/bpf/hashmap.c | 229 + src/contrib/libbpf/bpf/hashmap.h | 178 + src/contrib/libbpf/bpf/libbpf.c | 6581 ++ src/contrib/libbpf/bpf/libbpf.h | 637 + src/contrib/libbpf/bpf/libbpf_errno.c | 63 + src/contrib/libbpf/bpf/libbpf_internal.h | 217 + src/contrib/libbpf/bpf/libbpf_probes.c | 323 + src/contrib/libbpf/bpf/libbpf_util.h | 47 + src/contrib/libbpf/bpf/netlink.c | 451 + src/contrib/libbpf/bpf/nlattr.c | 195 + src/contrib/libbpf/bpf/nlattr.h | 106 + src/contrib/libbpf/bpf/str_error.c | 18 + src/contrib/libbpf/bpf/str_error.h | 6 + src/contrib/libbpf/bpf/xsk.c | 797 + src/contrib/libbpf/bpf/xsk.h | 246 + src/contrib/libbpf/include/asm/barrier.h | 7 + src/contrib/libbpf/include/linux/compiler.h | 70 + src/contrib/libbpf/include/linux/err.h | 38 + src/contrib/libbpf/include/linux/filter.h | 118 + src/contrib/libbpf/include/linux/kernel.h | 44 + src/contrib/libbpf/include/linux/list.h | 82 + src/contrib/libbpf/include/linux/overflow.h | 90 + src/contrib/libbpf/include/linux/ring_buffer.h | 18 + src/contrib/libbpf/include/linux/types.h | 31 + src/contrib/libbpf/include/uapi/linux/bpf.h | 3692 + src/contrib/libbpf/include/uapi/linux/bpf_common.h | 57 + src/contrib/libbpf/include/uapi/linux/btf.h | 165 + src/contrib/libbpf/include/uapi/linux/if_link.h | 1033 + src/contrib/libbpf/include/uapi/linux/if_xdp.h | 108 + src/contrib/libbpf/include/uapi/linux/netlink.h | 252 + src/contrib/libngtcp2/LICENSE | 1 + src/contrib/libngtcp2/ngtcp2/crypto/gnutls.c | 644 + src/contrib/libngtcp2/ngtcp2/crypto/shared.c | 1418 + src/contrib/libngtcp2/ngtcp2/crypto/shared.h | 350 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c | 335 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.h | 221 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c | 117 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.h | 69 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c | 90 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.h | 91 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c | 692 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.h | 156 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.c | 1489 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.h | 149 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.c | 56 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.h | 108 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.c | 615 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.h | 421 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cid.c | 147 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cid.h | 175 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c | 13698 +++ src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.h | 1115 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.c | 291 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.h | 208 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.c | 895 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.h | 148 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.c | 154 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.h | 34 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.c | 167 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.h | 98 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.c | 79 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.h | 89 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.c | 819 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.h | 345 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.c | 822 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.h | 123 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_macro.h | 58 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.c | 336 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.h | 136 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.c | 113 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.h | 72 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_net.h | 136 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.c | 40 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.h | 140 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c | 46 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.h | 65 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.c | 77 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.h | 49 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c | 2527 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h | 1235 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.c | 160 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.h | 123 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.c | 230 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.h | 153 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pq.c | 164 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pq.h | 126 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.c | 172 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.h | 198 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.c | 1218 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.h | 161 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.c | 61 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.h | 80 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rcvry.h | 40 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c | 120 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h | 132 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c | 319 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.h | 197 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rst.c | 137 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rst.h | 85 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c | 1676 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.h | 467 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_str.c | 233 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_str.h | 94 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c | 698 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.h | 310 + .../libngtcp2/ngtcp2/lib/ngtcp2_unreachable.c | 71 + .../libngtcp2/ngtcp2/lib/ngtcp2_unreachable.h | 46 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.c | 243 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.h | 120 + src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_version.c | 39 + .../libngtcp2/ngtcp2/lib/ngtcp2_window_filter.c | 99 + .../libngtcp2/ngtcp2/lib/ngtcp2_window_filter.h | 65 + src/contrib/libngtcp2/ngtcp2/ngtcp2.h | 5906 + src/contrib/libngtcp2/ngtcp2/ngtcp2_crypto.h | 893 + .../libngtcp2/ngtcp2/ngtcp2_crypto_gnutls.h | 107 + src/contrib/libngtcp2/ngtcp2/version.h | 51 + src/contrib/licenses/0BSD | 12 + src/contrib/licenses/BSD-3-Clause | 21 + src/contrib/licenses/LGPL-2.0 | 339 + src/contrib/licenses/LGPL-2.1 | 503 + src/contrib/licenses/MIT | 19 + src/contrib/macros.h | 41 + src/contrib/mempattern.c | 122 + src/contrib/mempattern.h | 47 + src/contrib/musl/inet_ntop.c | 79 + src/contrib/musl/inet_ntop.h | 28 + src/contrib/net.c | 747 + src/contrib/net.h | 209 + src/contrib/openbsd/LICENSE | 2 + src/contrib/openbsd/siphash.c | 176 + src/contrib/openbsd/siphash.h | 83 + src/contrib/openbsd/strlcat.c | 48 + src/contrib/openbsd/strlcat.h | 31 + src/contrib/openbsd/strlcpy.c | 46 + src/contrib/openbsd/strlcpy.h | 29 + src/contrib/os.h | 37 + src/contrib/proxyv2/proxyv2.c | 281 + src/contrib/proxyv2/proxyv2.h | 29 + src/contrib/qp-trie/trie.c | 1451 + src/contrib/qp-trie/trie.h | 280 + src/contrib/semaphore.c | 80 + src/contrib/semaphore.h | 41 + src/contrib/sockaddr.c | 368 + src/contrib/sockaddr.h | 160 + src/contrib/spinlock.h | 133 + src/contrib/string.c | 247 + src/contrib/string.h | 104 + src/contrib/strtonum.h | 125 + src/contrib/time.c | 441 + src/contrib/time.h | 211 + src/contrib/toeplitz.h | 122 + src/contrib/tolower.h | 52 + src/contrib/trim.h | 36 + src/contrib/ucw/LICENSE | 1 + src/contrib/ucw/array-sort.h | 195 + src/contrib/ucw/binsearch.h | 50 + src/contrib/ucw/heap.c | 153 + src/contrib/ucw/heap.h | 46 + src/contrib/ucw/lists.c | 264 + src/contrib/ucw/lists.h | 74 + src/contrib/ucw/mempool.c | 323 + src/contrib/ucw/mempool.h | 124 + src/contrib/url-parser/LICENSE | 1 + src/contrib/url-parser/README.md | 14 + src/contrib/url-parser/url_parser.c | 635 + src/contrib/url-parser/url_parser.h | 64 + src/contrib/vpool/vpool.c | 243 + src/contrib/vpool/vpool.h | 60 + src/contrib/wire_ctx.h | 355 + src/knot/Makefile.inc | 240 + src/knot/catalog/catalog_db.c | 347 + src/knot/catalog/catalog_db.h | 187 + src/knot/catalog/catalog_update.c | 407 + src/knot/catalog/catalog_update.h | 171 + src/knot/catalog/generate.c | 346 + src/knot/catalog/generate.h | 56 + src/knot/catalog/interpret.c | 257 + src/knot/catalog/interpret.h | 52 + src/knot/common/evsched.c | 268 + src/knot/common/evsched.h | 154 + src/knot/common/fdset.c | 336 + src/knot/common/fdset.h | 382 + src/knot/common/log.c | 491 + src/knot/common/log.h | 187 + src/knot/common/process.c | 194 + src/knot/common/process.h | 60 + src/knot/common/stats.c | 309 + src/knot/common/stats.h | 53 + src/knot/common/systemd.c | 168 + src/knot/common/systemd.h | 105 + src/knot/common/unreachable.c | 148 + src/knot/common/unreachable.h | 87 + src/knot/conf/base.c | 1056 + src/knot/conf/base.h | 322 + src/knot/conf/conf.c | 1469 + src/knot/conf/conf.h | 939 + src/knot/conf/confdb.c | 951 + src/knot/conf/confdb.h | 230 + src/knot/conf/confio.c | 1612 + src/knot/conf/confio.h | 231 + src/knot/conf/migration.c | 81 + src/knot/conf/migration.h | 30 + src/knot/conf/module.c | 509 + src/knot/conf/module.h | 126 + src/knot/conf/schema.c | 530 + src/knot/conf/schema.h | 279 + src/knot/conf/tools.c | 1069 + src/knot/conf/tools.h | 147 + src/knot/ctl/commands.c | 2331 + src/knot/ctl/commands.h | 160 + src/knot/ctl/process.c | 128 + src/knot/ctl/process.h | 30 + src/knot/dnssec/context.c | 351 + src/knot/dnssec/context.h | 82 + src/knot/dnssec/ds_query.c | 288 + src/knot/dnssec/ds_query.h | 22 + src/knot/dnssec/kasp/kasp_db.c | 610 + src/knot/dnssec/kasp/kasp_db.h | 296 + src/knot/dnssec/kasp/kasp_zone.c | 447 + src/knot/dnssec/kasp/kasp_zone.h | 63 + src/knot/dnssec/kasp/keystate.c | 74 + src/knot/dnssec/kasp/keystate.h | 35 + src/knot/dnssec/kasp/keystore.c | 89 + src/knot/dnssec/kasp/keystore.h | 22 + src/knot/dnssec/kasp/policy.h | 135 + src/knot/dnssec/key-events.c | 863 + src/knot/dnssec/key-events.h | 69 + src/knot/dnssec/key_records.c | 300 + src/knot/dnssec/key_records.h | 54 + src/knot/dnssec/nsec-chain.c | 797 + src/knot/dnssec/nsec-chain.h | 174 + src/knot/dnssec/nsec3-chain.c | 733 + src/knot/dnssec/nsec3-chain.h | 69 + src/knot/dnssec/policy.c | 51 + src/knot/dnssec/policy.h | 26 + src/knot/dnssec/rrset-sign.c | 425 + src/knot/dnssec/rrset-sign.h | 123 + src/knot/dnssec/zone-events.c | 470 + src/knot/dnssec/zone-events.h | 134 + src/knot/dnssec/zone-keys.c | 767 + src/knot/dnssec/zone-keys.h | 213 + src/knot/dnssec/zone-nsec.c | 429 + src/knot/dnssec/zone-nsec.h | 163 + src/knot/dnssec/zone-sign.c | 1081 + src/knot/dnssec/zone-sign.h | 162 + src/knot/events/events.c | 564 + src/knot/events/events.h | 214 + src/knot/events/handlers.h | 49 + src/knot/events/handlers/backup.c | 71 + src/knot/events/handlers/dnssec.c | 116 + src/knot/events/handlers/ds_check.c | 49 + src/knot/events/handlers/ds_push.c | 277 + src/knot/events/handlers/expire.c | 46 + src/knot/events/handlers/flush.c | 33 + src/knot/events/handlers/freeze_thaw.c | 46 + src/knot/events/handlers/load.c | 406 + src/knot/events/handlers/notify.c | 212 + src/knot/events/handlers/refresh.c | 1391 + src/knot/events/handlers/update.c | 433 + src/knot/events/replan.c | 210 + src/knot/events/replan.h | 35 + src/knot/include/module.h | 602 + src/knot/journal/journal_basic.c | 92 + src/knot/journal/journal_basic.h | 118 + src/knot/journal/journal_metadata.c | 422 + src/knot/journal/journal_metadata.h | 187 + src/knot/journal/journal_read.c | 436 + src/knot/journal/journal_read.h | 158 + src/knot/journal/journal_write.c | 333 + src/knot/journal/journal_write.h | 121 + src/knot/journal/knot_lmdb.c | 770 + src/knot/journal/knot_lmdb.h | 446 + src/knot/journal/serialization.c | 501 + src/knot/journal/serialization.h | 169 + src/knot/modules/cookies/Makefile.inc | 13 + src/knot/modules/cookies/cookies.c | 308 + src/knot/modules/cookies/cookies.rst | 110 + src/knot/modules/dnsproxy/Makefile.inc | 13 + src/knot/modules/dnsproxy/dnsproxy.c | 191 + src/knot/modules/dnsproxy/dnsproxy.rst | 125 + src/knot/modules/dnstap/Makefile.inc | 15 + src/knot/modules/dnstap/dnstap.c | 338 + src/knot/modules/dnstap/dnstap.rst | 113 + src/knot/modules/geoip/Makefile.inc | 17 + src/knot/modules/geoip/geodb.c | 216 + src/knot/modules/geoip/geodb.h | 67 + src/knot/modules/geoip/geoip.c | 1061 + src/knot/modules/geoip/geoip.rst | 324 + src/knot/modules/noudp/Makefile.inc | 12 + src/knot/modules/noudp/noudp.c | 110 + src/knot/modules/noudp/noudp.rst | 68 + src/knot/modules/onlinesign/Makefile.inc | 15 + src/knot/modules/onlinesign/nsec_next.c | 113 + src/knot/modules/onlinesign/nsec_next.h | 29 + src/knot/modules/onlinesign/onlinesign.c | 736 + src/knot/modules/onlinesign/onlinesign.rst | 158 + src/knot/modules/probe/Makefile.inc | 12 + src/knot/modules/probe/probe.c | 190 + src/knot/modules/probe/probe.rst | 89 + src/knot/modules/queryacl/Makefile.inc | 12 + src/knot/modules/queryacl/queryacl.c | 93 + src/knot/modules/queryacl/queryacl.rst | 70 + src/knot/modules/rrl/Makefile.inc | 15 + src/knot/modules/rrl/functions.c | 554 + src/knot/modules/rrl/functions.h | 111 + src/knot/modules/rrl/rrl.c | 208 + src/knot/modules/rrl/rrl.rst | 133 + src/knot/modules/static_modules.h.in | 25 + src/knot/modules/stats/Makefile.inc | 13 + src/knot/modules/stats/stats.c | 676 + src/knot/modules/stats/stats.rst | 274 + src/knot/modules/synthrecord/Makefile.inc | 13 + src/knot/modules/synthrecord/synthrecord.c | 625 + src/knot/modules/synthrecord/synthrecord.rst | 170 + src/knot/modules/whoami/Makefile.inc | 12 + src/knot/modules/whoami/whoami.c | 114 + src/knot/modules/whoami/whoami.rst | 97 + src/knot/nameserver/axfr.c | 225 + src/knot/nameserver/axfr.h | 27 + src/knot/nameserver/chaos.c | 145 + src/knot/nameserver/chaos.h | 24 + src/knot/nameserver/internet.c | 728 + src/knot/nameserver/internet.h | 79 + src/knot/nameserver/ixfr.c | 332 + src/knot/nameserver/ixfr.h | 63 + src/knot/nameserver/log.h | 88 + src/knot/nameserver/notify.c | 92 + src/knot/nameserver/notify.h | 28 + src/knot/nameserver/nsec_proofs.c | 677 + src/knot/nameserver/nsec_proofs.h | 38 + src/knot/nameserver/process_query.c | 978 + src/knot/nameserver/process_query.h | 107 + src/knot/nameserver/query_module.c | 791 + src/knot/nameserver/query_module.h | 99 + src/knot/nameserver/tsig_ctx.c | 189 + src/knot/nameserver/tsig_ctx.h | 97 + src/knot/nameserver/update.c | 107 + src/knot/nameserver/update.h | 27 + src/knot/nameserver/xfr.c | 96 + src/knot/nameserver/xfr.h | 69 + src/knot/query/capture.c | 63 + src/knot/query/capture.h | 32 + src/knot/query/layer.h | 136 + src/knot/query/query.c | 85 + src/knot/query/query.h | 66 + src/knot/query/requestor.c | 378 + src/knot/query/requestor.h | 119 + src/knot/server/dthreads.c | 767 + src/knot/server/dthreads.h | 295 + src/knot/server/proxyv2.c | 69 + src/knot/server/proxyv2.h | 23 + src/knot/server/server.c | 1335 + src/knot/server/server.h | 203 + src/knot/server/tcp-handler.c | 380 + src/knot/server/tcp-handler.h | 43 + src/knot/server/udp-handler.c | 575 + src/knot/server/udp-handler.h | 43 + src/knot/server/xdp-handler.c | 506 + src/knot/server/xdp-handler.h | 67 + src/knot/updates/acl.c | 361 + src/knot/updates/acl.h | 83 + src/knot/updates/apply.c | 379 + src/knot/updates/apply.h | 101 + src/knot/updates/changesets.c | 628 + src/knot/updates/changesets.h | 290 + src/knot/updates/ddns.c | 701 + src/knot/updates/ddns.h | 47 + src/knot/updates/zone-update.c | 1098 + src/knot/updates/zone-update.h | 299 + src/knot/worker/pool.c | 254 + src/knot/worker/pool.h | 93 + src/knot/worker/queue.c | 67 + src/knot/worker/queue.h | 65 + src/knot/zone/adds_tree.c | 262 + src/knot/zone/adds_tree.h | 120 + src/knot/zone/adjust.c | 628 + src/knot/zone/adjust.h | 123 + src/knot/zone/backup.c | 461 + src/knot/zone/backup.h | 74 + src/knot/zone/backup_dir.c | 247 + src/knot/zone/backup_dir.h | 39 + src/knot/zone/contents.c | 609 + src/knot/zone/contents.h | 291 + src/knot/zone/digest.c | 305 + src/knot/zone/digest.h | 72 + src/knot/zone/measure.c | 133 + src/knot/zone/measure.h | 71 + src/knot/zone/node.c | 464 + src/knot/zone/node.h | 419 + src/knot/zone/semantic-check.c | 562 + src/knot/zone/semantic-check.h | 116 + src/knot/zone/serial.c | 78 + src/knot/zone/serial.h | 76 + src/knot/zone/timers.c | 228 + src/knot/zone/timers.h | 99 + src/knot/zone/zone-diff.c | 402 + src/knot/zone/zone-diff.h | 31 + src/knot/zone/zone-dump.c | 236 + src/knot/zone/zone-dump.h | 32 + src/knot/zone/zone-load.c | 173 + src/knot/zone/zone-load.h | 68 + src/knot/zone/zone-tree.c | 512 + src/knot/zone/zone-tree.h | 337 + src/knot/zone/zone.c | 792 + src/knot/zone/zone.h | 290 + src/knot/zone/zonedb-load.c | 643 + src/knot/zone/zonedb-load.h | 40 + src/knot/zone/zonedb.c | 188 + src/knot/zone/zonedb.h | 135 + src/knot/zone/zonefile.c | 371 + src/knot/zone/zonefile.h | 104 + src/knotd.pc.in | 9 + src/libdnssec.pc.in | 13 + src/libdnssec/Makefile.inc | 70 + src/libdnssec/binary.c | 163 + src/libdnssec/binary.h | 116 + src/libdnssec/crypto.c | 42 + src/libdnssec/crypto.h | 52 + src/libdnssec/digest.c | 105 + src/libdnssec/digest.h | 76 + src/libdnssec/dnssec.h | 36 + src/libdnssec/error.c | 87 + src/libdnssec/error.h | 106 + src/libdnssec/key.h | 309 + src/libdnssec/key/algorithm.c | 180 + src/libdnssec/key/algorithm.h | 30 + src/libdnssec/key/convert.c | 375 + src/libdnssec/key/convert.h | 44 + src/libdnssec/key/dnskey.c | 91 + src/libdnssec/key/dnskey.h | 46 + src/libdnssec/key/ds.c | 128 + src/libdnssec/key/internal.h | 35 + src/libdnssec/key/key.c | 439 + src/libdnssec/key/keytag.c | 88 + src/libdnssec/key/privkey.c | 140 + src/libdnssec/key/privkey.h | 35 + src/libdnssec/key/simple.c | 55 + src/libdnssec/keyid.c | 87 + src/libdnssec/keyid.h | 64 + src/libdnssec/keystore.h | 155 + src/libdnssec/keystore/internal.h | 50 + src/libdnssec/keystore/keystore.c | 186 + src/libdnssec/keystore/pkcs11.c | 397 + src/libdnssec/keystore/pkcs8.c | 497 + src/libdnssec/keytag.h | 44 + src/libdnssec/nsec.h | 155 + src/libdnssec/nsec/bitmap.c | 142 + src/libdnssec/nsec/hash.c | 125 + src/libdnssec/nsec/nsec.c | 130 + src/libdnssec/p11/p11.c | 113 + src/libdnssec/p11/p11.h | 41 + src/libdnssec/pem.c | 182 + src/libdnssec/pem.h | 73 + src/libdnssec/random.c | 53 + src/libdnssec/random.h | 79 + src/libdnssec/shared/bignum.c | 64 + src/libdnssec/shared/bignum.h | 41 + src/libdnssec/shared/binary_wire.h | 53 + src/libdnssec/shared/dname.c | 165 + src/libdnssec/shared/dname.h | 57 + src/libdnssec/shared/keyid_gnutls.c | 99 + src/libdnssec/shared/keyid_gnutls.h | 30 + src/libdnssec/shared/shared.h | 121 + src/libdnssec/sign.h | 114 + src/libdnssec/sign/der.c | 229 + src/libdnssec/sign/der.h | 56 + src/libdnssec/sign/sign.c | 433 + src/libdnssec/tsig.c | 242 + src/libdnssec/tsig.h | 155 + src/libdnssec/version.h | 25 + src/libdnssec/version.h.in | 25 + src/libknot.pc.in | 14 + src/libknot/Makefile.inc | 136 + src/libknot/attribute.h | 51 + src/libknot/codes.c | 129 + src/libknot/codes.h | 60 + src/libknot/consts.h | 163 + src/libknot/control/control.c | 568 + src/libknot/control/control.h | 161 + src/libknot/cookies.c | 170 + src/libknot/cookies.h | 103 + src/libknot/db/db.h | 92 + src/libknot/db/db_lmdb.c | 578 + src/libknot/db/db_lmdb.h | 73 + src/libknot/db/db_trie.c | 176 + src/libknot/db/db_trie.h | 40 + src/libknot/descriptor.c | 425 + src/libknot/descriptor.h | 307 + src/libknot/dname.c | 801 + src/libknot/dname.h | 354 + src/libknot/dynarray.h | 188 + src/libknot/endian.h | 51 + src/libknot/errcode.h | 272 + src/libknot/error.c | 239 + src/libknot/error.h | 50 + src/libknot/libknot.h | 72 + src/libknot/lookup.h | 89 + src/libknot/mm_ctx.h | 41 + src/libknot/packet/compr.h | 101 + src/libknot/packet/pkt.c | 837 + src/libknot/packet/pkt.h | 416 + src/libknot/packet/rrset-wire.c | 727 + src/libknot/packet/rrset-wire.h | 69 + src/libknot/packet/wire.h | 1045 + src/libknot/probe/data.c | 135 + src/libknot/probe/data.h | 132 + src/libknot/probe/probe.c | 228 + src/libknot/probe/probe.h | 116 + src/libknot/rdata.h | 101 + src/libknot/rdataset.c | 371 + src/libknot/rdataset.h | 218 + src/libknot/rrset-dump.c | 2292 + src/libknot/rrset-dump.h | 118 + src/libknot/rrset.c | 217 + src/libknot/rrset.h | 194 + src/libknot/rrtype/dnskey.h | 72 + src/libknot/rrtype/ds.h | 64 + src/libknot/rrtype/naptr.c | 47 + src/libknot/rrtype/naptr.h | 41 + src/libknot/rrtype/nsec.h | 50 + src/libknot/rrtype/nsec3.h | 98 + src/libknot/rrtype/nsec3param.h | 64 + src/libknot/rrtype/opt.c | 687 + src/libknot/rrtype/opt.h | 587 + src/libknot/rrtype/rdname.h | 98 + src/libknot/rrtype/rrsig.h | 100 + src/libknot/rrtype/soa.h | 94 + src/libknot/rrtype/svcb.h | 44 + src/libknot/rrtype/tsig.c | 409 + src/libknot/rrtype/tsig.h | 122 + src/libknot/rrtype/zonemd.h | 71 + src/libknot/tsig-op.c | 683 + src/libknot/tsig-op.h | 187 + src/libknot/tsig.c | 188 + src/libknot/tsig.h | 89 + src/libknot/version.h | 25 + src/libknot/version.h.in | 25 + src/libknot/wire.h | 154 + src/libknot/xdp.h | 35 + src/libknot/xdp/Makefile.am | 20 + src/libknot/xdp/Makefile.in | 545 + src/libknot/xdp/bpf-consts.h | 57 + src/libknot/xdp/bpf-kernel-obj.c | 941 + src/libknot/xdp/bpf-kernel-obj.h | 2 + src/libknot/xdp/bpf-kernel.c | 293 + src/libknot/xdp/bpf-user.c | 313 + src/libknot/xdp/bpf-user.h | 139 + src/libknot/xdp/eth.c | 312 + src/libknot/xdp/eth.h | 111 + src/libknot/xdp/msg.h | 62 + src/libknot/xdp/msg_init.h | 74 + src/libknot/xdp/protocols.h | 446 + src/libknot/xdp/quic.c | 1028 + src/libknot/xdp/quic.h | 134 + src/libknot/xdp/quic_conn.c | 506 + src/libknot/xdp/quic_conn.h | 314 + src/libknot/xdp/tcp.c | 729 + src/libknot/xdp/tcp.h | 227 + src/libknot/xdp/tcp_iobuf.c | 266 + src/libknot/xdp/tcp_iobuf.h | 120 + src/libknot/xdp/xdp.c | 568 + src/libknot/xdp/xdp.h | 189 + src/libknot/yparser/yparser.c | 176 + src/libknot/yparser/yparser.h | 149 + src/libknot/yparser/ypbody.c | 460 + src/libknot/yparser/ypformat.c | 121 + src/libknot/yparser/ypformat.h | 101 + src/libknot/yparser/ypschema.c | 598 + src/libknot/yparser/ypschema.h | 352 + src/libknot/yparser/yptrafo.c | 1099 + src/libknot/yparser/yptrafo.h | 311 + src/libzscanner.pc.in | 13 + src/libzscanner/Makefile.inc | 40 + src/libzscanner/error.c | 202 + src/libzscanner/error.h | 119 + src/libzscanner/functions.c | 980 + src/libzscanner/functions.h | 141 + src/libzscanner/scanner.c.g2 | 102263 ++++++++++++++++++ src/libzscanner/scanner.c.t0 | 9692 ++ src/libzscanner/scanner.h | 405 + src/libzscanner/scanner.rl | 564 + src/libzscanner/scanner_body.rl | 2326 + src/libzscanner/version.h | 25 + src/libzscanner/version.h.in | 25 + src/utils/Makefile.inc | 187 + src/utils/common/cert.c | 61 + src/utils/common/cert.h | 36 + src/utils/common/exec.c | 982 + src/utils/common/exec.h | 137 + src/utils/common/hex.c | 82 + src/utils/common/hex.h | 31 + src/utils/common/https.c | 525 + src/utils/common/https.h | 150 + src/utils/common/lookup.c | 295 + src/utils/common/lookup.h | 124 + src/utils/common/msg.c | 40 + src/utils/common/msg.h | 42 + src/utils/common/netio.c | 896 + src/utils/common/netio.h | 239 + src/utils/common/params.c | 343 + src/utils/common/params.h | 168 + src/utils/common/quic.c | 887 + src/utils/common/quic.h | 125 + src/utils/common/resolv.c | 211 + src/utils/common/resolv.h | 24 + src/utils/common/sign.c | 109 + src/utils/common/sign.h | 63 + src/utils/common/tls.c | 739 + src/utils/common/tls.h | 81 + src/utils/common/token.c | 115 + src/utils/common/token.h | 65 + src/utils/common/util_conf.c | 139 + src/utils/common/util_conf.h | 86 + src/utils/kcatalogprint/main.c | 178 + src/utils/kdig/kdig_exec.c | 1309 + src/utils/kdig/kdig_exec.h | 21 + src/utils/kdig/kdig_main.c | 45 + src/utils/kdig/kdig_params.c | 2740 + src/utils/kdig/kdig_params.h | 187 + src/utils/keymgr/bind_privkey.c | 411 + src/utils/keymgr/bind_privkey.h | 72 + src/utils/keymgr/functions.c | 1135 + src/utils/keymgr/functions.h | 62 + src/utils/keymgr/main.c | 405 + src/utils/keymgr/offline_ksk.c | 549 + src/utils/keymgr/offline_ksk.h | 35 + src/utils/khost/khost_main.c | 45 + src/utils/khost/khost_params.c | 365 + src/utils/khost/khost_params.h | 22 + src/utils/kjournalprint/main.c | 466 + src/utils/knotc/commands.c | 1340 + src/utils/knotc/commands.h | 74 + src/utils/knotc/interactive.c | 450 + src/utils/knotc/interactive.h | 26 + src/utils/knotc/main.c | 172 + src/utils/knotc/process.c | 291 + src/utils/knotc/process.h | 78 + src/utils/knotd/main.c | 635 + src/utils/knsec3hash/knsec3hash.c | 187 + src/utils/knsupdate/knsupdate_exec.c | 1053 + src/utils/knsupdate/knsupdate_exec.h | 25 + src/utils/knsupdate/knsupdate_interactive.c | 177 + src/utils/knsupdate/knsupdate_interactive.h | 26 + src/utils/knsupdate/knsupdate_main.c | 46 + src/utils/knsupdate/knsupdate_params.c | 304 + src/utils/knsupdate/knsupdate_params.h | 72 + src/utils/kxdpgun/ip_route.c | 358 + src/utils/kxdpgun/ip_route.h | 46 + src/utils/kxdpgun/load_queries.c | 159 + src/utils/kxdpgun/load_queries.h | 32 + src/utils/kxdpgun/main.c | 1302 + src/utils/kzonecheck/main.c | 181 + src/utils/kzonecheck/zone_check.c | 92 + src/utils/kzonecheck/zone_check.h | 23 + src/utils/kzonesign/main.c | 276 + 699 files changed, 335109 insertions(+) create mode 100644 src/Makefile.am create mode 100644 src/Makefile.in create mode 100644 src/config.h.in create mode 100644 src/contrib/Makefile.inc create mode 100644 src/contrib/asan.h create mode 100644 src/contrib/base32hex.c create mode 100644 src/contrib/base32hex.h create mode 100644 src/contrib/base64.c create mode 100644 src/contrib/base64.h create mode 100644 src/contrib/base64url.c create mode 100644 src/contrib/base64url.h create mode 100644 src/contrib/color.h create mode 100644 src/contrib/conn_pool.c create mode 100644 src/contrib/conn_pool.h create mode 100644 src/contrib/ctype.h create mode 100644 src/contrib/dnstap/convert.c create mode 100644 src/contrib/dnstap/convert.h create mode 100644 src/contrib/dnstap/dnstap.c create mode 100644 src/contrib/dnstap/dnstap.h create mode 100644 src/contrib/dnstap/dnstap.proto create mode 100644 src/contrib/dnstap/message.c create mode 100644 src/contrib/dnstap/message.h create mode 100644 src/contrib/dnstap/reader.c create mode 100644 src/contrib/dnstap/reader.h create mode 100644 src/contrib/dnstap/writer.c create mode 100644 src/contrib/dnstap/writer.h create mode 100644 src/contrib/files.c create mode 100644 src/contrib/files.h create mode 100644 src/contrib/getline.c create mode 100644 src/contrib/getline.h create mode 100644 src/contrib/json.c create mode 100644 src/contrib/json.h create mode 100644 src/contrib/libbpf/LICENSE create mode 100644 src/contrib/libbpf/bpf/bpf.c create mode 100644 src/contrib/libbpf/bpf/bpf.h create mode 100644 src/contrib/libbpf/bpf/bpf_core_read.h create mode 100644 src/contrib/libbpf/bpf/bpf_endian.h create mode 100644 src/contrib/libbpf/bpf/bpf_helper_defs.h create mode 100644 src/contrib/libbpf/bpf/bpf_helpers.h create mode 100644 src/contrib/libbpf/bpf/bpf_prog_linfo.c create mode 100644 src/contrib/libbpf/bpf/bpf_tracing.h create mode 100644 src/contrib/libbpf/bpf/btf.c create mode 100644 src/contrib/libbpf/bpf/btf.h create mode 100644 src/contrib/libbpf/bpf/btf_dump.c create mode 100644 src/contrib/libbpf/bpf/hashmap.c create mode 100644 src/contrib/libbpf/bpf/hashmap.h create mode 100644 src/contrib/libbpf/bpf/libbpf.c create mode 100644 src/contrib/libbpf/bpf/libbpf.h create mode 100644 src/contrib/libbpf/bpf/libbpf_errno.c create mode 100644 src/contrib/libbpf/bpf/libbpf_internal.h create mode 100644 src/contrib/libbpf/bpf/libbpf_probes.c create mode 100644 src/contrib/libbpf/bpf/libbpf_util.h create mode 100644 src/contrib/libbpf/bpf/netlink.c create mode 100644 src/contrib/libbpf/bpf/nlattr.c create mode 100644 src/contrib/libbpf/bpf/nlattr.h create mode 100644 src/contrib/libbpf/bpf/str_error.c create mode 100644 src/contrib/libbpf/bpf/str_error.h create mode 100644 src/contrib/libbpf/bpf/xsk.c create mode 100644 src/contrib/libbpf/bpf/xsk.h create mode 100644 src/contrib/libbpf/include/asm/barrier.h create mode 100644 src/contrib/libbpf/include/linux/compiler.h create mode 100644 src/contrib/libbpf/include/linux/err.h create mode 100644 src/contrib/libbpf/include/linux/filter.h create mode 100644 src/contrib/libbpf/include/linux/kernel.h create mode 100644 src/contrib/libbpf/include/linux/list.h create mode 100644 src/contrib/libbpf/include/linux/overflow.h create mode 100644 src/contrib/libbpf/include/linux/ring_buffer.h create mode 100644 src/contrib/libbpf/include/linux/types.h create mode 100644 src/contrib/libbpf/include/uapi/linux/bpf.h create mode 100644 src/contrib/libbpf/include/uapi/linux/bpf_common.h create mode 100644 src/contrib/libbpf/include/uapi/linux/btf.h create mode 100644 src/contrib/libbpf/include/uapi/linux/if_link.h create mode 100644 src/contrib/libbpf/include/uapi/linux/if_xdp.h create mode 100644 src/contrib/libbpf/include/uapi/linux/netlink.h create mode 100644 src/contrib/libngtcp2/LICENSE create mode 100644 src/contrib/libngtcp2/ngtcp2/crypto/gnutls.c create mode 100644 src/contrib/libngtcp2/ngtcp2/crypto/shared.c create mode 100644 src/contrib/libngtcp2/ngtcp2/crypto/shared.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cid.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cid.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_macro.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_net.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pq.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pq.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rcvry.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rst.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rst.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_str.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_str.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_unreachable.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_unreachable.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.h create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_version.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_window_filter.c create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_window_filter.h create mode 100644 src/contrib/libngtcp2/ngtcp2/ngtcp2.h create mode 100644 src/contrib/libngtcp2/ngtcp2/ngtcp2_crypto.h create mode 100644 src/contrib/libngtcp2/ngtcp2/ngtcp2_crypto_gnutls.h create mode 100644 src/contrib/libngtcp2/ngtcp2/version.h create mode 100644 src/contrib/licenses/0BSD create mode 100644 src/contrib/licenses/BSD-3-Clause create mode 100644 src/contrib/licenses/LGPL-2.0 create mode 100644 src/contrib/licenses/LGPL-2.1 create mode 100644 src/contrib/licenses/MIT create mode 100644 src/contrib/macros.h create mode 100644 src/contrib/mempattern.c create mode 100644 src/contrib/mempattern.h create mode 100644 src/contrib/musl/inet_ntop.c create mode 100644 src/contrib/musl/inet_ntop.h create mode 100644 src/contrib/net.c create mode 100644 src/contrib/net.h create mode 100644 src/contrib/openbsd/LICENSE create mode 100644 src/contrib/openbsd/siphash.c create mode 100644 src/contrib/openbsd/siphash.h create mode 100644 src/contrib/openbsd/strlcat.c create mode 100644 src/contrib/openbsd/strlcat.h create mode 100644 src/contrib/openbsd/strlcpy.c create mode 100644 src/contrib/openbsd/strlcpy.h create mode 100644 src/contrib/os.h create mode 100644 src/contrib/proxyv2/proxyv2.c create mode 100644 src/contrib/proxyv2/proxyv2.h create mode 100644 src/contrib/qp-trie/trie.c create mode 100644 src/contrib/qp-trie/trie.h create mode 100644 src/contrib/semaphore.c create mode 100644 src/contrib/semaphore.h create mode 100644 src/contrib/sockaddr.c create mode 100644 src/contrib/sockaddr.h create mode 100644 src/contrib/spinlock.h create mode 100644 src/contrib/string.c create mode 100644 src/contrib/string.h create mode 100644 src/contrib/strtonum.h create mode 100644 src/contrib/time.c create mode 100644 src/contrib/time.h create mode 100644 src/contrib/toeplitz.h create mode 100644 src/contrib/tolower.h create mode 100644 src/contrib/trim.h create mode 100644 src/contrib/ucw/LICENSE create mode 100644 src/contrib/ucw/array-sort.h create mode 100644 src/contrib/ucw/binsearch.h create mode 100644 src/contrib/ucw/heap.c create mode 100644 src/contrib/ucw/heap.h create mode 100644 src/contrib/ucw/lists.c create mode 100644 src/contrib/ucw/lists.h create mode 100644 src/contrib/ucw/mempool.c create mode 100644 src/contrib/ucw/mempool.h create mode 100644 src/contrib/url-parser/LICENSE create mode 100644 src/contrib/url-parser/README.md create mode 100644 src/contrib/url-parser/url_parser.c create mode 100644 src/contrib/url-parser/url_parser.h create mode 100644 src/contrib/vpool/vpool.c create mode 100644 src/contrib/vpool/vpool.h create mode 100644 src/contrib/wire_ctx.h create mode 100644 src/knot/Makefile.inc create mode 100644 src/knot/catalog/catalog_db.c create mode 100644 src/knot/catalog/catalog_db.h create mode 100644 src/knot/catalog/catalog_update.c create mode 100644 src/knot/catalog/catalog_update.h create mode 100644 src/knot/catalog/generate.c create mode 100644 src/knot/catalog/generate.h create mode 100644 src/knot/catalog/interpret.c create mode 100644 src/knot/catalog/interpret.h create mode 100644 src/knot/common/evsched.c create mode 100644 src/knot/common/evsched.h create mode 100644 src/knot/common/fdset.c create mode 100644 src/knot/common/fdset.h create mode 100644 src/knot/common/log.c create mode 100644 src/knot/common/log.h create mode 100644 src/knot/common/process.c create mode 100644 src/knot/common/process.h create mode 100644 src/knot/common/stats.c create mode 100644 src/knot/common/stats.h create mode 100644 src/knot/common/systemd.c create mode 100644 src/knot/common/systemd.h create mode 100644 src/knot/common/unreachable.c create mode 100644 src/knot/common/unreachable.h create mode 100644 src/knot/conf/base.c create mode 100644 src/knot/conf/base.h create mode 100644 src/knot/conf/conf.c create mode 100644 src/knot/conf/conf.h create mode 100644 src/knot/conf/confdb.c create mode 100644 src/knot/conf/confdb.h create mode 100644 src/knot/conf/confio.c create mode 100644 src/knot/conf/confio.h create mode 100644 src/knot/conf/migration.c create mode 100644 src/knot/conf/migration.h create mode 100644 src/knot/conf/module.c create mode 100644 src/knot/conf/module.h create mode 100644 src/knot/conf/schema.c create mode 100644 src/knot/conf/schema.h create mode 100644 src/knot/conf/tools.c create mode 100644 src/knot/conf/tools.h create mode 100644 src/knot/ctl/commands.c create mode 100644 src/knot/ctl/commands.h create mode 100644 src/knot/ctl/process.c create mode 100644 src/knot/ctl/process.h create mode 100644 src/knot/dnssec/context.c create mode 100644 src/knot/dnssec/context.h create mode 100644 src/knot/dnssec/ds_query.c create mode 100644 src/knot/dnssec/ds_query.h create mode 100644 src/knot/dnssec/kasp/kasp_db.c create mode 100644 src/knot/dnssec/kasp/kasp_db.h create mode 100644 src/knot/dnssec/kasp/kasp_zone.c create mode 100644 src/knot/dnssec/kasp/kasp_zone.h create mode 100644 src/knot/dnssec/kasp/keystate.c create mode 100644 src/knot/dnssec/kasp/keystate.h create mode 100644 src/knot/dnssec/kasp/keystore.c create mode 100644 src/knot/dnssec/kasp/keystore.h create mode 100644 src/knot/dnssec/kasp/policy.h create mode 100644 src/knot/dnssec/key-events.c create mode 100644 src/knot/dnssec/key-events.h create mode 100644 src/knot/dnssec/key_records.c create mode 100644 src/knot/dnssec/key_records.h create mode 100644 src/knot/dnssec/nsec-chain.c create mode 100644 src/knot/dnssec/nsec-chain.h create mode 100644 src/knot/dnssec/nsec3-chain.c create mode 100644 src/knot/dnssec/nsec3-chain.h create mode 100644 src/knot/dnssec/policy.c create mode 100644 src/knot/dnssec/policy.h create mode 100644 src/knot/dnssec/rrset-sign.c create mode 100644 src/knot/dnssec/rrset-sign.h create mode 100644 src/knot/dnssec/zone-events.c create mode 100644 src/knot/dnssec/zone-events.h create mode 100644 src/knot/dnssec/zone-keys.c create mode 100644 src/knot/dnssec/zone-keys.h create mode 100644 src/knot/dnssec/zone-nsec.c create mode 100644 src/knot/dnssec/zone-nsec.h create mode 100644 src/knot/dnssec/zone-sign.c create mode 100644 src/knot/dnssec/zone-sign.h create mode 100644 src/knot/events/events.c create mode 100644 src/knot/events/events.h create mode 100644 src/knot/events/handlers.h create mode 100644 src/knot/events/handlers/backup.c create mode 100644 src/knot/events/handlers/dnssec.c create mode 100644 src/knot/events/handlers/ds_check.c create mode 100644 src/knot/events/handlers/ds_push.c create mode 100644 src/knot/events/handlers/expire.c create mode 100644 src/knot/events/handlers/flush.c create mode 100644 src/knot/events/handlers/freeze_thaw.c create mode 100644 src/knot/events/handlers/load.c create mode 100644 src/knot/events/handlers/notify.c create mode 100644 src/knot/events/handlers/refresh.c create mode 100644 src/knot/events/handlers/update.c create mode 100644 src/knot/events/replan.c create mode 100644 src/knot/events/replan.h create mode 100644 src/knot/include/module.h create mode 100644 src/knot/journal/journal_basic.c create mode 100644 src/knot/journal/journal_basic.h create mode 100644 src/knot/journal/journal_metadata.c create mode 100644 src/knot/journal/journal_metadata.h create mode 100644 src/knot/journal/journal_read.c create mode 100644 src/knot/journal/journal_read.h create mode 100644 src/knot/journal/journal_write.c create mode 100644 src/knot/journal/journal_write.h create mode 100644 src/knot/journal/knot_lmdb.c create mode 100644 src/knot/journal/knot_lmdb.h create mode 100644 src/knot/journal/serialization.c create mode 100644 src/knot/journal/serialization.h create mode 100644 src/knot/modules/cookies/Makefile.inc create mode 100644 src/knot/modules/cookies/cookies.c create mode 100644 src/knot/modules/cookies/cookies.rst create mode 100644 src/knot/modules/dnsproxy/Makefile.inc create mode 100644 src/knot/modules/dnsproxy/dnsproxy.c create mode 100644 src/knot/modules/dnsproxy/dnsproxy.rst create mode 100644 src/knot/modules/dnstap/Makefile.inc create mode 100644 src/knot/modules/dnstap/dnstap.c create mode 100644 src/knot/modules/dnstap/dnstap.rst create mode 100644 src/knot/modules/geoip/Makefile.inc create mode 100644 src/knot/modules/geoip/geodb.c create mode 100644 src/knot/modules/geoip/geodb.h create mode 100644 src/knot/modules/geoip/geoip.c create mode 100644 src/knot/modules/geoip/geoip.rst create mode 100644 src/knot/modules/noudp/Makefile.inc create mode 100644 src/knot/modules/noudp/noudp.c create mode 100644 src/knot/modules/noudp/noudp.rst create mode 100644 src/knot/modules/onlinesign/Makefile.inc create mode 100644 src/knot/modules/onlinesign/nsec_next.c create mode 100644 src/knot/modules/onlinesign/nsec_next.h create mode 100644 src/knot/modules/onlinesign/onlinesign.c create mode 100644 src/knot/modules/onlinesign/onlinesign.rst create mode 100644 src/knot/modules/probe/Makefile.inc create mode 100644 src/knot/modules/probe/probe.c create mode 100644 src/knot/modules/probe/probe.rst create mode 100644 src/knot/modules/queryacl/Makefile.inc create mode 100644 src/knot/modules/queryacl/queryacl.c create mode 100644 src/knot/modules/queryacl/queryacl.rst create mode 100644 src/knot/modules/rrl/Makefile.inc create mode 100644 src/knot/modules/rrl/functions.c create mode 100644 src/knot/modules/rrl/functions.h create mode 100644 src/knot/modules/rrl/rrl.c create mode 100644 src/knot/modules/rrl/rrl.rst create mode 100644 src/knot/modules/static_modules.h.in create mode 100644 src/knot/modules/stats/Makefile.inc create mode 100644 src/knot/modules/stats/stats.c create mode 100644 src/knot/modules/stats/stats.rst create mode 100644 src/knot/modules/synthrecord/Makefile.inc create mode 100644 src/knot/modules/synthrecord/synthrecord.c create mode 100644 src/knot/modules/synthrecord/synthrecord.rst create mode 100644 src/knot/modules/whoami/Makefile.inc create mode 100644 src/knot/modules/whoami/whoami.c create mode 100644 src/knot/modules/whoami/whoami.rst create mode 100644 src/knot/nameserver/axfr.c create mode 100644 src/knot/nameserver/axfr.h create mode 100644 src/knot/nameserver/chaos.c create mode 100644 src/knot/nameserver/chaos.h create mode 100644 src/knot/nameserver/internet.c create mode 100644 src/knot/nameserver/internet.h create mode 100644 src/knot/nameserver/ixfr.c create mode 100644 src/knot/nameserver/ixfr.h create mode 100644 src/knot/nameserver/log.h create mode 100644 src/knot/nameserver/notify.c create mode 100644 src/knot/nameserver/notify.h create mode 100644 src/knot/nameserver/nsec_proofs.c create mode 100644 src/knot/nameserver/nsec_proofs.h create mode 100644 src/knot/nameserver/process_query.c create mode 100644 src/knot/nameserver/process_query.h create mode 100644 src/knot/nameserver/query_module.c create mode 100644 src/knot/nameserver/query_module.h create mode 100644 src/knot/nameserver/tsig_ctx.c create mode 100644 src/knot/nameserver/tsig_ctx.h create mode 100644 src/knot/nameserver/update.c create mode 100644 src/knot/nameserver/update.h create mode 100644 src/knot/nameserver/xfr.c create mode 100644 src/knot/nameserver/xfr.h create mode 100644 src/knot/query/capture.c create mode 100644 src/knot/query/capture.h create mode 100644 src/knot/query/layer.h create mode 100644 src/knot/query/query.c create mode 100644 src/knot/query/query.h create mode 100644 src/knot/query/requestor.c create mode 100644 src/knot/query/requestor.h create mode 100644 src/knot/server/dthreads.c create mode 100644 src/knot/server/dthreads.h create mode 100644 src/knot/server/proxyv2.c create mode 100644 src/knot/server/proxyv2.h create mode 100644 src/knot/server/server.c create mode 100644 src/knot/server/server.h create mode 100644 src/knot/server/tcp-handler.c create mode 100644 src/knot/server/tcp-handler.h create mode 100644 src/knot/server/udp-handler.c create mode 100644 src/knot/server/udp-handler.h create mode 100644 src/knot/server/xdp-handler.c create mode 100644 src/knot/server/xdp-handler.h create mode 100644 src/knot/updates/acl.c create mode 100644 src/knot/updates/acl.h create mode 100644 src/knot/updates/apply.c create mode 100644 src/knot/updates/apply.h create mode 100644 src/knot/updates/changesets.c create mode 100644 src/knot/updates/changesets.h create mode 100644 src/knot/updates/ddns.c create mode 100644 src/knot/updates/ddns.h create mode 100644 src/knot/updates/zone-update.c create mode 100644 src/knot/updates/zone-update.h create mode 100644 src/knot/worker/pool.c create mode 100644 src/knot/worker/pool.h create mode 100644 src/knot/worker/queue.c create mode 100644 src/knot/worker/queue.h create mode 100644 src/knot/zone/adds_tree.c create mode 100644 src/knot/zone/adds_tree.h create mode 100644 src/knot/zone/adjust.c create mode 100644 src/knot/zone/adjust.h create mode 100644 src/knot/zone/backup.c create mode 100644 src/knot/zone/backup.h create mode 100644 src/knot/zone/backup_dir.c create mode 100644 src/knot/zone/backup_dir.h create mode 100644 src/knot/zone/contents.c create mode 100644 src/knot/zone/contents.h create mode 100644 src/knot/zone/digest.c create mode 100644 src/knot/zone/digest.h create mode 100644 src/knot/zone/measure.c create mode 100644 src/knot/zone/measure.h create mode 100644 src/knot/zone/node.c create mode 100644 src/knot/zone/node.h create mode 100644 src/knot/zone/semantic-check.c create mode 100644 src/knot/zone/semantic-check.h create mode 100644 src/knot/zone/serial.c create mode 100644 src/knot/zone/serial.h create mode 100644 src/knot/zone/timers.c create mode 100644 src/knot/zone/timers.h create mode 100644 src/knot/zone/zone-diff.c create mode 100644 src/knot/zone/zone-diff.h create mode 100644 src/knot/zone/zone-dump.c create mode 100644 src/knot/zone/zone-dump.h create mode 100644 src/knot/zone/zone-load.c create mode 100644 src/knot/zone/zone-load.h create mode 100644 src/knot/zone/zone-tree.c create mode 100644 src/knot/zone/zone-tree.h create mode 100644 src/knot/zone/zone.c create mode 100644 src/knot/zone/zone.h create mode 100644 src/knot/zone/zonedb-load.c create mode 100644 src/knot/zone/zonedb-load.h create mode 100644 src/knot/zone/zonedb.c create mode 100644 src/knot/zone/zonedb.h create mode 100644 src/knot/zone/zonefile.c create mode 100644 src/knot/zone/zonefile.h create mode 100644 src/knotd.pc.in create mode 100644 src/libdnssec.pc.in create mode 100644 src/libdnssec/Makefile.inc create mode 100644 src/libdnssec/binary.c create mode 100644 src/libdnssec/binary.h create mode 100644 src/libdnssec/crypto.c create mode 100644 src/libdnssec/crypto.h create mode 100644 src/libdnssec/digest.c create mode 100644 src/libdnssec/digest.h create mode 100644 src/libdnssec/dnssec.h create mode 100644 src/libdnssec/error.c create mode 100644 src/libdnssec/error.h create mode 100644 src/libdnssec/key.h create mode 100644 src/libdnssec/key/algorithm.c create mode 100644 src/libdnssec/key/algorithm.h create mode 100644 src/libdnssec/key/convert.c create mode 100644 src/libdnssec/key/convert.h create mode 100644 src/libdnssec/key/dnskey.c create mode 100644 src/libdnssec/key/dnskey.h create mode 100644 src/libdnssec/key/ds.c create mode 100644 src/libdnssec/key/internal.h create mode 100644 src/libdnssec/key/key.c create mode 100644 src/libdnssec/key/keytag.c create mode 100644 src/libdnssec/key/privkey.c create mode 100644 src/libdnssec/key/privkey.h create mode 100644 src/libdnssec/key/simple.c create mode 100644 src/libdnssec/keyid.c create mode 100644 src/libdnssec/keyid.h create mode 100644 src/libdnssec/keystore.h create mode 100644 src/libdnssec/keystore/internal.h create mode 100644 src/libdnssec/keystore/keystore.c create mode 100644 src/libdnssec/keystore/pkcs11.c create mode 100644 src/libdnssec/keystore/pkcs8.c create mode 100644 src/libdnssec/keytag.h create mode 100644 src/libdnssec/nsec.h create mode 100644 src/libdnssec/nsec/bitmap.c create mode 100644 src/libdnssec/nsec/hash.c create mode 100644 src/libdnssec/nsec/nsec.c create mode 100644 src/libdnssec/p11/p11.c create mode 100644 src/libdnssec/p11/p11.h create mode 100644 src/libdnssec/pem.c create mode 100644 src/libdnssec/pem.h create mode 100644 src/libdnssec/random.c create mode 100644 src/libdnssec/random.h create mode 100644 src/libdnssec/shared/bignum.c create mode 100644 src/libdnssec/shared/bignum.h create mode 100644 src/libdnssec/shared/binary_wire.h create mode 100644 src/libdnssec/shared/dname.c create mode 100644 src/libdnssec/shared/dname.h create mode 100644 src/libdnssec/shared/keyid_gnutls.c create mode 100644 src/libdnssec/shared/keyid_gnutls.h create mode 100644 src/libdnssec/shared/shared.h create mode 100644 src/libdnssec/sign.h create mode 100644 src/libdnssec/sign/der.c create mode 100644 src/libdnssec/sign/der.h create mode 100644 src/libdnssec/sign/sign.c create mode 100644 src/libdnssec/tsig.c create mode 100644 src/libdnssec/tsig.h create mode 100644 src/libdnssec/version.h create mode 100644 src/libdnssec/version.h.in create mode 100644 src/libknot.pc.in create mode 100755 src/libknot/Makefile.inc create mode 100644 src/libknot/attribute.h create mode 100644 src/libknot/codes.c create mode 100644 src/libknot/codes.h create mode 100644 src/libknot/consts.h create mode 100644 src/libknot/control/control.c create mode 100644 src/libknot/control/control.h create mode 100644 src/libknot/cookies.c create mode 100644 src/libknot/cookies.h create mode 100644 src/libknot/db/db.h create mode 100644 src/libknot/db/db_lmdb.c create mode 100644 src/libknot/db/db_lmdb.h create mode 100644 src/libknot/db/db_trie.c create mode 100644 src/libknot/db/db_trie.h create mode 100644 src/libknot/descriptor.c create mode 100644 src/libknot/descriptor.h create mode 100644 src/libknot/dname.c create mode 100644 src/libknot/dname.h create mode 100644 src/libknot/dynarray.h create mode 100644 src/libknot/endian.h create mode 100644 src/libknot/errcode.h create mode 100644 src/libknot/error.c create mode 100644 src/libknot/error.h create mode 100644 src/libknot/libknot.h create mode 100644 src/libknot/lookup.h create mode 100644 src/libknot/mm_ctx.h create mode 100644 src/libknot/packet/compr.h create mode 100644 src/libknot/packet/pkt.c create mode 100644 src/libknot/packet/pkt.h create mode 100644 src/libknot/packet/rrset-wire.c create mode 100644 src/libknot/packet/rrset-wire.h create mode 100644 src/libknot/packet/wire.h create mode 100644 src/libknot/probe/data.c create mode 100644 src/libknot/probe/data.h create mode 100644 src/libknot/probe/probe.c create mode 100644 src/libknot/probe/probe.h create mode 100644 src/libknot/rdata.h create mode 100644 src/libknot/rdataset.c create mode 100644 src/libknot/rdataset.h create mode 100644 src/libknot/rrset-dump.c create mode 100644 src/libknot/rrset-dump.h create mode 100644 src/libknot/rrset.c create mode 100644 src/libknot/rrset.h create mode 100644 src/libknot/rrtype/dnskey.h create mode 100644 src/libknot/rrtype/ds.h create mode 100644 src/libknot/rrtype/naptr.c create mode 100644 src/libknot/rrtype/naptr.h create mode 100644 src/libknot/rrtype/nsec.h create mode 100644 src/libknot/rrtype/nsec3.h create mode 100644 src/libknot/rrtype/nsec3param.h create mode 100644 src/libknot/rrtype/opt.c create mode 100644 src/libknot/rrtype/opt.h create mode 100644 src/libknot/rrtype/rdname.h create mode 100644 src/libknot/rrtype/rrsig.h create mode 100644 src/libknot/rrtype/soa.h create mode 100644 src/libknot/rrtype/svcb.h create mode 100644 src/libknot/rrtype/tsig.c create mode 100644 src/libknot/rrtype/tsig.h create mode 100644 src/libknot/rrtype/zonemd.h create mode 100644 src/libknot/tsig-op.c create mode 100644 src/libknot/tsig-op.h create mode 100644 src/libknot/tsig.c create mode 100644 src/libknot/tsig.h create mode 100644 src/libknot/version.h create mode 100644 src/libknot/version.h.in create mode 100644 src/libknot/wire.h create mode 100644 src/libknot/xdp.h create mode 100644 src/libknot/xdp/Makefile.am create mode 100644 src/libknot/xdp/Makefile.in create mode 100644 src/libknot/xdp/bpf-consts.h create mode 100644 src/libknot/xdp/bpf-kernel-obj.c create mode 100644 src/libknot/xdp/bpf-kernel-obj.h create mode 100644 src/libknot/xdp/bpf-kernel.c create mode 100644 src/libknot/xdp/bpf-user.c create mode 100644 src/libknot/xdp/bpf-user.h create mode 100644 src/libknot/xdp/eth.c create mode 100644 src/libknot/xdp/eth.h create mode 100644 src/libknot/xdp/msg.h create mode 100644 src/libknot/xdp/msg_init.h create mode 100644 src/libknot/xdp/protocols.h create mode 100644 src/libknot/xdp/quic.c create mode 100644 src/libknot/xdp/quic.h create mode 100644 src/libknot/xdp/quic_conn.c create mode 100644 src/libknot/xdp/quic_conn.h create mode 100644 src/libknot/xdp/tcp.c create mode 100644 src/libknot/xdp/tcp.h create mode 100644 src/libknot/xdp/tcp_iobuf.c create mode 100644 src/libknot/xdp/tcp_iobuf.h create mode 100644 src/libknot/xdp/xdp.c create mode 100644 src/libknot/xdp/xdp.h create mode 100644 src/libknot/yparser/yparser.c create mode 100644 src/libknot/yparser/yparser.h create mode 100644 src/libknot/yparser/ypbody.c create mode 100644 src/libknot/yparser/ypformat.c create mode 100644 src/libknot/yparser/ypformat.h create mode 100644 src/libknot/yparser/ypschema.c create mode 100644 src/libknot/yparser/ypschema.h create mode 100644 src/libknot/yparser/yptrafo.c create mode 100644 src/libknot/yparser/yptrafo.h create mode 100644 src/libzscanner.pc.in create mode 100644 src/libzscanner/Makefile.inc create mode 100644 src/libzscanner/error.c create mode 100644 src/libzscanner/error.h create mode 100644 src/libzscanner/functions.c create mode 100644 src/libzscanner/functions.h create mode 100644 src/libzscanner/scanner.c.g2 create mode 100644 src/libzscanner/scanner.c.t0 create mode 100644 src/libzscanner/scanner.h create mode 100644 src/libzscanner/scanner.rl create mode 100644 src/libzscanner/scanner_body.rl create mode 100644 src/libzscanner/version.h create mode 100644 src/libzscanner/version.h.in create mode 100644 src/utils/Makefile.inc create mode 100644 src/utils/common/cert.c create mode 100644 src/utils/common/cert.h create mode 100644 src/utils/common/exec.c create mode 100644 src/utils/common/exec.h create mode 100644 src/utils/common/hex.c create mode 100644 src/utils/common/hex.h create mode 100644 src/utils/common/https.c create mode 100644 src/utils/common/https.h create mode 100644 src/utils/common/lookup.c create mode 100644 src/utils/common/lookup.h create mode 100644 src/utils/common/msg.c create mode 100644 src/utils/common/msg.h create mode 100644 src/utils/common/netio.c create mode 100644 src/utils/common/netio.h create mode 100644 src/utils/common/params.c create mode 100644 src/utils/common/params.h create mode 100644 src/utils/common/quic.c create mode 100644 src/utils/common/quic.h create mode 100644 src/utils/common/resolv.c create mode 100644 src/utils/common/resolv.h create mode 100644 src/utils/common/sign.c create mode 100644 src/utils/common/sign.h create mode 100644 src/utils/common/tls.c create mode 100644 src/utils/common/tls.h create mode 100644 src/utils/common/token.c create mode 100644 src/utils/common/token.h create mode 100644 src/utils/common/util_conf.c create mode 100644 src/utils/common/util_conf.h create mode 100644 src/utils/kcatalogprint/main.c create mode 100644 src/utils/kdig/kdig_exec.c create mode 100644 src/utils/kdig/kdig_exec.h create mode 100644 src/utils/kdig/kdig_main.c create mode 100644 src/utils/kdig/kdig_params.c create mode 100644 src/utils/kdig/kdig_params.h create mode 100644 src/utils/keymgr/bind_privkey.c create mode 100644 src/utils/keymgr/bind_privkey.h create mode 100644 src/utils/keymgr/functions.c create mode 100644 src/utils/keymgr/functions.h create mode 100644 src/utils/keymgr/main.c create mode 100644 src/utils/keymgr/offline_ksk.c create mode 100644 src/utils/keymgr/offline_ksk.h create mode 100644 src/utils/khost/khost_main.c create mode 100644 src/utils/khost/khost_params.c create mode 100644 src/utils/khost/khost_params.h create mode 100644 src/utils/kjournalprint/main.c create mode 100644 src/utils/knotc/commands.c create mode 100644 src/utils/knotc/commands.h create mode 100644 src/utils/knotc/interactive.c create mode 100644 src/utils/knotc/interactive.h create mode 100644 src/utils/knotc/main.c create mode 100644 src/utils/knotc/process.c create mode 100644 src/utils/knotc/process.h create mode 100644 src/utils/knotd/main.c create mode 100644 src/utils/knsec3hash/knsec3hash.c create mode 100644 src/utils/knsupdate/knsupdate_exec.c create mode 100644 src/utils/knsupdate/knsupdate_exec.h create mode 100644 src/utils/knsupdate/knsupdate_interactive.c create mode 100644 src/utils/knsupdate/knsupdate_interactive.h create mode 100644 src/utils/knsupdate/knsupdate_main.c create mode 100644 src/utils/knsupdate/knsupdate_params.c create mode 100644 src/utils/knsupdate/knsupdate_params.h create mode 100644 src/utils/kxdpgun/ip_route.c create mode 100644 src/utils/kxdpgun/ip_route.h create mode 100644 src/utils/kxdpgun/load_queries.c create mode 100644 src/utils/kxdpgun/load_queries.h create mode 100644 src/utils/kxdpgun/main.c create mode 100644 src/utils/kzonecheck/main.c create mode 100644 src/utils/kzonecheck/zone_check.c create mode 100644 src/utils/kzonecheck/zone_check.h create mode 100644 src/utils/kzonesign/main.c (limited to 'src') 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 &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 &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 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 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 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 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 header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETDB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define to 1 if you have the 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 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 header file. */ +#undef HAVE_STDATOMIC_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the 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 header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UIO_H + +/* Define to 1 if you have the 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include "contrib/base32hex.h" +#include "libknot/errcode.h" + +#include +#include + +/*! \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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 + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include "contrib/base64.h" +#include "libknot/errcode.h" + +#include +#include + +/*! \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \brief Base64 implementation (RFC 4648). + */ + +#pragma once + +#include + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include "contrib/base64url.h" +#include "libknot/errcode.h" + +#include +#include +#include + +/*! \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \brief Base64url implementation (RFC 4648). + */ + +#pragma once + +#include + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \brief Locale-independent ctype functions. + */ + +#pragma once + +#include +#include +#include + +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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \brief Dnstap identifiers conversions. + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \author Robert Edmonds + * + * \brief Public interface for dnstap. + */ + +#pragma once + +#include +#include + +#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: +// +// . + +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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \author Robert Edmonds + * + * \brief Dnstap message interface. + */ + +#pragma once + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \brief Dnstap file reader. + */ + +#pragma once + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \author Robert Edmonds + * + * \brief Dnstap file writer. + */ + +#pragma once + +#include +#include + +/*! \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include + +/*! + * 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +// FreeBSD POSIX2008 getline +#ifndef _WITH_GETLINE +#define _WITH_GETLINE +#endif + +#include "contrib/getline.h" + +#include // getline or fgetln +#include // free +#include // 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \brief Multiplatform getline wrapper. + */ + +#pragma once + +#include +#include + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#pragma once + +#include +#include +#include + +/*! + * 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 + * Copyright (C) 2015 Wang Nan + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#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 + * Copyright (C) 2015 Wang Nan + * 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 + */ +#ifndef __LIBBPF_BPF_H +#define __LIBBPF_BPF_H + +#include +#include +#include +#include + +#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 +#include + +/* 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: + * + * 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. + * * ```` 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= + * + * 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= + * + * 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 +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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, ¶m); +} + +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, ¶m); +} + +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, ¶m); +} + +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 +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#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 (*), 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 +#include +#include +#include +#include +#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 +#include +#ifdef __GLIBC__ +#include +#else +#include +#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 + * Copyright (C) 2015 Wang Nan + * Copyright (C) 2015 Huawei Inc. + * Copyright (C) 2017 Nicira, Inc. + * Copyright (C) 2019 Isovalent, Inc. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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__ 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 = (assignment with immediate operand); + * 2. rX += (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: + * [] () + => @, + * where 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 + * Copyright (C) 2015 Wang Nan + * Copyright (C) 2015 Huawei Inc. + */ +#ifndef __LIBBPF_LIBBPF_H +#define __LIBBPF_LIBBPF_H + +#include +#include +#include +#include +#include // for size_t +#include + +#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 "-" 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, ); + * 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 + * Copyright (C) 2015 Wang Nan + * Copyright (C) 2015 Huawei Inc. + * Copyright (C) 2017 Nicira, Inc. + */ + +#undef _GNU_SOURCE +#include +#include + +#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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 + +#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 +#include +#include +#include +#include +#include +#include +#include + +#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 + */ + +#include +#include "nlattr.h" +#include "libbpf_internal.h" +#include +#include +#include + +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 + */ + +#ifndef __LIBBPF_NLATTR_H +#define __LIBBPF_NLATTR_H + +#include +#include +/* 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 +#include +#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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + */ + +#ifndef __LIBBPF_XSK_H +#define __LIBBPF_XSK_H + +#include +#include +#include + +#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 + +#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 +#include + +#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 + +#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 + +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 +#include +#include + +#include +#include + +#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 +#include + +/* 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: + * + * 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. + * * ```` 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= + * 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= + * 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 + +#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 +#include + +/* 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 + * Magnus Karlsson + */ + +#ifndef _LINUX_IF_XDP_H +#define _LINUX_IF_XDP_H + +#include + +/* 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 +#include /* for __kernel_sa_family_t */ +#include + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +#include +#include + +#include +#include +#include + +#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 +# include +#else +# include +#endif + +#include +#include + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +/** + * @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) ` + * 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) + * ` 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) + * ` 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 + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 +#include + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +/* + * 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 + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +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 + +#if defined(_MSC_VER) +# include +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 +#include + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 +#include + +#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, ¶ms->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, ¶ms->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, ¶ms->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, ¶ms->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( + ¶ms, + 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, ¶ms); +} + +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, ¶ms->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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 +#include + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +/* + * 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 +#include + +#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, + ¶ms->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, + ¶ms->retry_scid); + } + break; + default: + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + len += cid_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID, + ¶ms->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, + ¶ms->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 = ¶ms->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 = ¶ms->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, + ¶ms->retry_scid); + } + } + + p = write_cid_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID, + ¶ms->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(¶ms->retry_scid, 0, sizeof(params->retry_scid)); + memset(¶ms->initial_scid, 0, sizeof(params->initial_scid)); + memset(¶ms->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(¶m_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(¶ms->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(¶ms->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(¶ms->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(¶ms->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(¶ms->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(¶ms->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(¶ms->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(¶ms->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(¶ms->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 = ¶ms->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 = ¶ms->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(¶ms->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(¶ms->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(¶ms->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(¶ms->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(¶ms->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(¶ms->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(¶ms->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(¶ms, exttype, data, datalen); + if (rv < 0) { + return rv; + } + + if (mem == NULL) { + mem = ngtcp2_mem_default(); + } + + return transport_params_copy_new(pparams, ¶ms, 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 +#include + +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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 + +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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 +#include +#include +#include + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +#include + +#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 +#ifdef HAVE_UNISTD_H +# include +#endif +#include +#include + +#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 + * + * + * + * : + * Log level. I=Info, W=Warning, E=Error + * + * : + * Timestamp relative to ngtcp2_log.ts field in milliseconds + * resolution. + * + * : + * Source Connection ID in hex string. + * + * : + * Event. pkt=packet, frm=frame, rcv=recovery, cry=crypto, + * con=connection(catch all) + * + * # Frame event + * + * () + * + * : + * Flow direction. tx=transmission, rx=reception + * + * : + * Packet number. + * + * : + * Packet name. (e.g., Initial, Handshake, 1RTT) + * + * : + * Frame name. (e.g., STREAM, ACK, PING) + * + * : + * 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 = ¶ms->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 = ¶ms->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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +#include + +#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 +#include +#include + +#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 \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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 + +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 +#endif /* HAVE_CONFIG_H */ + +#include + +/* 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 +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_ARPA_INET_H +# include +#endif /* HAVE_ARPA_INET_H */ + +#ifdef HAVE_NETINET_IN_H +# include +#endif /* HAVE_NETINET_IN_H */ + +#ifdef HAVE_BYTESWAP_H +# include +#endif /* HAVE_BYTESWAP_H */ + +#ifdef HAVE_ENDIAN_H +# include +#endif /* HAVE_ENDIAN_H */ + +#ifdef HAVE_SYS_ENDIAN_H +# include +#endif /* HAVE_SYS_ENDIAN_H */ + +#include + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +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 + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +/* + * 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 +#include + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +/* 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 & + * ~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 & + * ~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 + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +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 +#include + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 +#include + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 + +#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", ¶ms->initial_scid); + *p++ = ','; + if (side == (server ? NGTCP2_QLOG_SIDE_LOCAL : NGTCP2_QLOG_SIDE_REMOTE)) { + p = write_pair_cid(p, "original_destination_connection_id", + ¶ms->original_dcid); + *p++ = ','; + } + if (params->retry_scid_present) { + p = write_pair_cid(p, "retry_source_connection_id", ¶ms->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 = ¶ms->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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +/* 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 +#ifdef WIN32 +# include +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 +#include + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 +#include + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +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 +#include + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 +#include +#ifdef HAVE_UNISTD_H +# include +#endif /* HAVE_UNISTD_H */ +#include +#ifdef WIN32 +# include +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 +#include + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 +#endif /* HAVE_CONFIG_H */ + +#include + +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 + +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 +#endif /* HAVE_CONFIG_H */ + +#include + +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 +#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 +#else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ +# include +#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ +#include +#include +#include + +#ifndef NGTCP2_USE_GENERIC_SOCKADDR +# ifdef WIN32 +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +# else +# include +# include +# 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 + +#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_* + * `. + */ + 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_* + * `. 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 + * ` 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 + * ` + * + * 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 `. It also + * extracts the pointer to the Destination Connection ID and its + * length and assigns them to :member:`dest->dcid + * ` and :member:`dest->dcidlen + * ` respectively. Similarly, it extracts + * the pointer to the Source Connection ID and its length and assigns + * them to :member:`dest->scid ` and + * :member:`dest->scidlen ` respectively. + * + * If the given packet is Short header packet, :member:`dest->version + * ` will be 0, :member:`dest->scid + * ` will be ``NULL``, and + * :member:`dest->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 + * `. |short_dcidlen| is assigned to + * :member:`dest->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 `, clears + * :macro:`NGTCP2_PKT_FLAG_LONG_FORM` flag from :member:`dest->flags + * `, and sets 0 to :member:`dest->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 ` 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_* + * `. 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_* + * `. |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_* + * `. |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 `, a client source + * address must be set to :member:`dest->local `. + * If a client source address does not change for the new server + * address, leave :member:`dest->local ` + * unmodified, or copy the value of :member:`local + * ` field of the current network path obtained + * from `ngtcp2_conn_get_path()`. Both :member:`dest->local.addr + * ` and :member:`dest->remote.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 + * ` 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 + * `. + */ +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 + * `. + */ +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_* `. + * + * 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 ` 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) ` != 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) ` != 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 ` = + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT` + * - :member:`error_code ` = + * :macro:`NGTCP2_NO_ERROR`. + * - :member:`frame_type ` = + * 0 + * - :member:`reason ` = NULL + * - :member:`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 ` to + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`, + * and :member:`ccerr->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 ` is set + * to + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_VERSION_NEGOTIATION`, + * and :member:`ccerr->error_code + * ` to + * :macro:`NGTCP2_NO_ERROR`. If |liberr| is + * :macro:`NGTCP2_ERR_IDLE_CLOSE`, :member:`ccerr->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 + * ` to + * :macro:`NGTCP2_NO_ERROR`. Otherwise, :member:`ccerr->type + * ` is set to + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`, + * and :member:`ccerr->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 ` to + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`, + * and :member:`ccerr->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 ` to + * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION`, + * and :member:`ccerr->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 ` == + * :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 ` == + * :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 `. + * :member:`dest->addrlen ` is updated to have + * |addrlen|. This function assumes that :member:`dest->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 ` = + * :enum:`ngtcp2_cc_algo.NGTCP2_CC_ALGO_CUBIC` + * * :type:`initial_rtt ` = + * :macro:`NGTCP2_DEFAULT_INITIAL_RTT` + * * :type:`ack_thresh ` = 2 + * * :type:`max_tx_udp_payload_size + * ` = 1452 + * * :type:`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 + * ` = + * :macro:`NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE` + * * :type:`ack_delay_exponent + * ` = + * :macro:`NGTCP2_DEFAULT_ACK_DELAY_EXPONENT` + * * :type:`max_ack_delay ` = + * :macro:`NGTCP2_DEFAULT_MAX_ACK_DELAY` + * * :type:`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 ` and :member:`src->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 + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef WIN32 +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +#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 ` + * 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 ` + * 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) `, + * and the length of packet protection IV is + * `ngtcp2_crypto_packet_protection_ivlen(ctx->aead) + * ` 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) `, + * and the length of packet protection IV is + * `ngtcp2_crypto_packet_protection_ivlen(ctx->aead) + * ` 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) `, + * and the length of packet protection IV is + * `ngtcp2_crypto_packet_protection_ivlen(ctx->aead) + * ` 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 + +#include + +#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. + + + Copyright (C) + + This program is free software; you can 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. + + , 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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 +#include +#include +#include + +#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 + +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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include // OpenBSD +#include // TCP_FASTOPEN +#include +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include +#include + +// 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 + * 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 + +#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 + * 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 +#include + +#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 + * + * 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 +#include + +#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 + * + * 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 + * + * 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 +#include + +#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 + * + * 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include + +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. + 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 . + */ + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include + +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. + Copyright (C) 2018 Tony Finch + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + The code originated from https://github.com/fanf2/qp/blob/master/qp.c + at revision 5f6d93753. + */ + +#include +#include +#include +#include + +#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(©->mm); + } + if (copy->weight) { + if (!dup_trie(©->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. + Copyright (C) 2018 Tony Finch + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include "semaphore.h" + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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: \
[@], 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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 +#elif defined(HAVE_STDATOMIC) +# include +# include +#elif defined(APPLE_SPIN_NEW) +# include +#elif defined(APPLE_SPIN_OLD) +# include +#else /* POSIX pthread spinlock. */ +# include +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#if defined(HAVE_EXPLICIT_BZERO) + #if defined(HAVE_BSD_STRING_H) + #include + #endif + /* #include is needed. */ +#elif defined(HAVE_EXPLICIT_MEMSET) + /* #include is needed. */ +#elif defined(HAVE_GNUTLS_MEMSET) + #include +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \brief String manipulations. + */ + +#pragma once + +#include +#include +#include + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "contrib/time.h" +#include "contrib/ctype.h" +#ifndef HAVE_CLOCK_GETTIME + #include +#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, <) == NULL ? -1 : + strftime(dst, dst_len, "%Y-%m-%dT%H:%M:%SZ", <)); + 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include +#include +#include + +#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: + * | - The pipe sign separates two time format specifications. Leftmost + * specification matching the timespec is used. + * '' - Matches exactly (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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \brief Microsoft Toeplitz-based hash implementation + */ + +#pragma once + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \brief Table for converting ASCII characters to lowercase. + */ + +#pragma once + +#include + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \brief Heap memory trimmer. + */ + +#pragma once + +#ifdef HAVE_MALLOC_TRIM +#include +#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 + * + * 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= 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 + * + * 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 + * + * 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 <> 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 +#include +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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 + * (c) 2015, 2020-2022 CZ.NIC, z.s.p.o. + * + * 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 +#include +#include + +#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 + * (c) 2015, 2020-2022 CZ.NIC, z.s.p.o. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#pragma once + +#include +#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 + * (c) 2007 Pavel Charvat + * (c) 2015, 2017 CZ.NIC, z.s.p.o. + * + * This software may be freely distributed and used according to the terms + * of the GNU Lesser General Public License. + */ + +#undef LOCAL_DEBUG + +#include +#include +#include +#include +#include +#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 +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 + * (c) 2007 Pavel Charvat + * (c) 2015, 2017 CZ.NIC, z.s.p.o. + * + * This software may be freely distributed and used according to the terms + * of the GNU Lesser General Public License. + */ + +#pragma once + +#include +#include + +#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 <>, see <>. + **/ +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 <>, see <>. + **/ +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 <>. + **/ +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 +#include +#include + +#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 + +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 + * + * 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 + +#include +#include +#include +#include +#include + +#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 + * + * 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 +#include + +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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \brief Event scheduler. + */ + +#pragma once + +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \brief I/O multiplexing with context and timeouts for each fd. + */ + +#pragma once + +#include +#include +#include +#include + +#ifdef HAVE_EPOLL +#include +#elif HAVE_KQUEUE +#include +#else +#include +#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 + * , 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_SYSTEMD +#define SD_JOURNAL_SUPPRESS_LOCATION 1 +#include +#include +#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, <) != NULL) { + strftime(tstr, sizeof(tstr), KNOT_LOG_TIME_FORMAT " ", <); + } + } + + // 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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 +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \brief Functions for POSIX process handling. + */ + +#pragma once + +#include +#include + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#include "knot/common/systemd.h" +#include "contrib/strtonum.h" + +#ifdef ENABLE_SYSTEMD +#include + +#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 + +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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef ENABLE_XDP +#include +#include +#include +#include +#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", \ + §ion[1], &old_item[1], \ + §ion[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", \ + §ion[1], &old_item[1], \ + §ion[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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include "knot/dnssec/kasp/kasp_db.h" + +#include +#include + +#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, ¶ms->keytag, ¶ms->algorithm, &flags, + ¶ms->timing.created, ¶ms->timing.pre_active, ¶ms->timing.publish, + ¶ms->timing.ready, ¶ms->timing.active, ¶ms->timing.retire_active, + ¶ms->timing.retire, ¶ms->timing.post_active, ¶ms->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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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, ¶ms->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, ¶ms->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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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, ¶ms->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, ¶ms, nsec_ttl); + if (ret != KNOT_EOK) { + return ret; + } + + if (ctx->policy->nsec3_enabled) { + ret = knot_nsec3_create_chain(update->new_cont, ¶ms, 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, ¶ms, 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, ¶ms, + 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, ¶ms) + : 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, ¶ms) : + 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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, ¬ify, &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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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, ¶ms, &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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief Knot DNS module interface. + * + * \addtogroup module + * @{ + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +/*** 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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 + +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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include // snprintf +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +#include +#include +#include +#include + +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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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, ¤t_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, ¶ms); + 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, ¶ms) != 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, ¶ms) != 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` module must also + be enabled and configured after :ref:`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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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` 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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`. + +.. _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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#if HAVE_MAXMINDDB +#include +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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 `_ +or the free version `GeoLite2 `_. + +The module can be enabled only per zone. + +.. NOTE:: + If :ref:`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 ` 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 ` 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 ` 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` module may be combined with the +:ref:`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` + 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` 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 `_ 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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 `). 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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(©->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`. + +* 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` 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` and :ref:`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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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 `_ 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` + +.. _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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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`. + +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`. + +.. 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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 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 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 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 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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 +`_, 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` 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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`). + +*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 . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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 . + + 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 + // 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_PTHREAD_NP_H +#include +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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 + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#define __APPLE_USE_RFC_3542 + +#include +#include // OpenBSD +#include // TCP_FASTOPEN +#include + +#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 +#endif + +#ifdef SO_ATTACH_REUSEPORT_CBPF +#include +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_UIO_H // struct iovec (OpenBSD) +#include +#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, ¶ms); + + /* 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#define __APPLE_USE_RFC_3542 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_UIO_H // struct iovec (OpenBSD) +#include +#endif /* HAVE_SYS_UIO_H */ +#include + +#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, ¶ms); + + /* 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#ifdef ENABLE_XDP + +#include +#include +#include + +#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, ¶ms); + if (ctx->tcp) { + handle_tcp(ctx, layer, ¶ms); + } + handle_quic(ctx, layer, ¶ms); + + 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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(¶m)) { + 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include + +#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. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#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. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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(¤t_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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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, ¶m); + 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, ¶m); +} + +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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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, ¶ms->buf, ¶ms->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, ¶ms->buf, ¶ms->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, ¶ms->buf, ¶ms->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, ¶ms); + 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, ¶ms); + 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, ¶ms); + 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, ¶ms); + 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, ¶ms); + 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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 +#include + +/*! + * 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include "libdnssec/digest.h" + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * Convenient header to include all library modules. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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 + +/*! + * 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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 +#include + +#include + +/*! + * 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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 +#include + +/*! + * 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \addtogroup keyid + * + * \brief DNSSEC key ID manipulation. + * + * The module contains auxiliary functions for manipulation with key IDs. + * + * @{ + */ + +#pragma once + +#include +#include + +/*! + * 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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 +#include + +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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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: ;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: " " + */ +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, + >_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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \addtogroup keytag + * + * \brief Low-level key tag computation API. + * + * The module provides simple interface for DNSKEY key id computation. + * + * @{ + */ + +#pragma once + +#include +#include + +/*! + * 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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 +#include +#include + +#include + +/*! + * 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include + +#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, ¶ms->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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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(¶ms->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(¶ms1->salt, ¶ms2->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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \addtogroup pem + * + * \brief PEM key format operations. + * + * @{ + */ + +#pragma once + +#include + +#include + +/*! + * 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \addtogroup random + * + * \brief Pseudo-random number generating API. + * + * The module provides generating of pseudo-random numbers and buffers. + * + * @{ + */ + +#pragma once + +#include +#include + +/*! + * 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_(). + */ +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include + +/*! + * 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \addtogroup sign + * + * \brief DNSSEC signing API + * + * The module provides the low level DNSSEC signing and verification. + * + * @{ + */ + +#pragma once + +#include +#include + +#include +#include + +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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \addtogroup tsig + * + * \brief Low-level TSIG signing API. + * + * @{ + */ + +#pragma once + +#include + +#include + +/*! + * 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief DNS cookies processing. + * + * \addtogroup libknot + * @{ + */ + +#pragma once + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "libknot/attribute.h" +#include "libknot/errcode.h" +#include "libknot/db/db_lmdb.h" +#include "contrib/files.h" +#include "contrib/mempattern.h" + +#include + +/* 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief DNS resource record descriptions. + * + * \addtogroup rr + * @{ + */ + +#pragma once + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief Domain name structure and API for manipulating it. + * + * \addtogroup dname + * @{ + */ + +#pragma once + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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 +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief Endian dependent integer operations. + * + * \addtogroup wire + * @{ + */ + +#pragma once + +#if defined(__linux__) || defined(__gnu_hurd__) || \ + (defined(__FreeBSD_kernel__) && defined(__GLIBC__)) +# include +#elif defined(__FreeBSD__) || defined(__NetBSD__) +# include +#elif defined(__OpenBSD__) || defined(__sun) || defined(__CYGWIN__) +# include +#elif defined(__APPLE__) +# include +# 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief Knot error codes. + * + * \addtogroup libknot + * @{ + */ + +#pragma once + +#include + +/*! \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! +* \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief A general purpose lookup table. + * + * \addtogroup libknot + * @{ + */ + +#pragma once + +#include +#include + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief Memory allocation context. + * + * \addtogroup libknot + * @{ + */ + +#pragma once + +#include + +/* 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief Name compression API. + * + * \addtogroup pkt + * @{ + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief Structure for holding DNS packet data and metadata. + * + * \addtogroup pkt + * @{ + */ + +#pragma once + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief Functions for manipulating and parsing raw data in DNS packets. + * + * \addtogroup wire + * @{ + */ + +#pragma once + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief A DNS traffic probe data structure. + * + * \addtogroup probe + * @{ + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief API for manipulating rdata. + * + * \addtogroup rr + * @{ + */ + +#pragma once + +#include +#include +#include +#include + +/*!< \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief API for manipulating RR arrays. + * + * \addtogroup rr + * @{ + */ + +#pragma once + +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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(×tamp, &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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief RRset text dump facility. + * + * \addtogroup rr + * @{ + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief RRSet structure and API for manipulating it. + * + * \addtogroup rr + * @{ + */ + +#pragma once + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \addtogroup rrtype + * @{ + */ + +#pragma once + +#include + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief Functions for manipulating the EDNS OPT pseudo-RR. + * + * \addtogroup rrtype + * @{ + */ + +#pragma once + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief TSIG manipulation. + * + * \addtogroup rrtype + * @{ + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief TSIG signing and validating. + * + * \addtogroup knot-tsig + * @{ + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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, ©.secret) != DNSSEC_EOK) { + knot_tsig_key_deinit(©); + 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief Wire integer operations. + * + * \addtogroup wire + * @{ + */ + +#pragma once + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief XDP filter configuration constants. + * + * \addtogroup xdp + * @{ + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#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(ð_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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief XDP socket interface. + * + * \addtogroup xdp + * @{ + */ + +#pragma once + +#if USE_LIBXDP + #include +#else + #include +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief Ethernet device info interface. + * + * \addtogroup xdp + * @{ + */ + +#pragma once + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief XDP message description. + * + * \addtogroup xdp + * @{ + */ + +#pragma once + +#include +#include +#include +#include + +/*! \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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(¶ms); + + 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, ¶ms, NULL, user_data); + } else { + return ngtcp2_conn_client_new(pconn, dcid, scid, path, version, &callbacks, &settings, ¶ms, 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief QUIC connection management. + * + * \addtogroup xdp + * @{ + */ + +#pragma once + +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief TCP buffer helpers. + * + * \addtogroup xdp + * @{ + */ + +#pragma once + +#include +#include +#include +#include + +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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief XDP IO interface. + * + * \addtogroup xdp + * @{ + */ + +#pragma once + +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief Simple parser (Yparser) of a YAML-inspired data format. + * + * \addtogroup yparser + * @{ + */ + +#pragma once + +#include +#include + +/*! 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief Schema layer for Yparser. + * + * \addtogroup yparser + * @{ + */ + +#pragma once + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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(¶m_key, param_pos, 2); + param_key = be16toh(param_key); + memcpy(¶m_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(¶m_key, param_pos, 2); + memcpy(¶m_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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief Zone scanner auxiliary functions. + * + * \addtogroup zone_scanner + * @{ + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, ×tamp); + + 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, ×tamp); + + 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, ×tamp); + + 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, ×tamp); + + 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, ×tamp); + + 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, ×tamp); + + 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, ×tamp); + + 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, ×tamp); + + 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, ×tamp); + + 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, ×tamp); + + 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, ×tamp); + + 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \file + * + * \brief Zone scanner core interface. + * + * \addtogroup zscanner + * @{ + */ + +#pragma once + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +%%{ + 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, ×tamp); + + 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +/*! \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 +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include // OpenBSD +#include // TCP_FASTOPEN +#include + +#ifdef HAVE_SYS_UIO_H +#include +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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 +#include +#include + +#include +#include + +#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(¶ms); + 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, ¶ms, 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +/*! \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 +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#define GNUTLS_VERSION_FASTOPEN_READY 0x030503 +#if GNUTLS_VERSION_NUMBER >= GNUTLS_VERSION_FASTOPEN_READY +#include +#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(¶ms->ca_files); + init_list(¶ms->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(¶ms->ca_files, NULL); + + WALK_LIST_DELSAFE(node, nxt, params->pins) { + free(node->d); + } + ptrlist_free(¶ms->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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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 ] [parameters]\n" + "\n" + "Parameters:\n" + " -c, --config Path to a textual configuration file.\n" + " (default %s)\n" + " -C, --confdb Path to a configuration database directory.\n" + " (default %s)\n" + " -D, --dir Path to a catalog database directory, use default\n" + " configuration.\n" + " -a, --catalog Filter the output by catalog zone name.\n" + " -m, --member 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(";; \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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(¶ms, argc, argv) == KNOT_EOK) { + if (!params.stop) { + dnssec_crypto_init(); + if (kdig_exec(¶ms) != KNOT_EOK) { + ret = EXIT_FAILURE; + } + dnssec_crypto_cleanup(); + } + } else { + ret = EXIT_FAILURE; + } + + kdig_clean(¶ms); + 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#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(¶ms->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(¶ms->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, ¬ify) != 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(¶ms->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, ¶ms->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, ¶ms->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(¶ms->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(¶ms->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(¶ms->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, ¶ms->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(¶ms->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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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(¶ms, line, read); + if (r != DNSSEC_EOK) { + bind_privkey_free(¶ms); + 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(¶ms->modulus); + gnutls_datum_t e = binary_to_datum(¶ms->public_exponent); + gnutls_datum_t d = binary_to_datum(¶ms->private_exponent); + gnutls_datum_t p = binary_to_datum(¶ms->prime_one); + gnutls_datum_t q = binary_to_datum(¶ms->prime_two); + gnutls_datum_t u = binary_to_datum(¶ms->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(¶ms->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(¶ms->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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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 ] [...]\n" + " %s [-c | -C | -D ] -l\n" + " %s -t [ []]\n" + "\n" + "Parameters:\n" + " -c, --config Path to a textual configuration file.\n" + " (default %s)\n" + " -C, --confdb Path to a configuration database directory.\n" + " (default %s)\n" + " -D, --dir Path to a KASP database directory, use default configuration.\n" + " -t, --tsig [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 =...)\n" + " import-bind Import BIND-style key file pair (.key + .private).\n" + " (syntax: import-bind )\n" + " import-pub Import public-only key to be published in the zone (in BIND .key format).\n" + " (syntax: import-pub )\n" + " import-pem Import key in PEM format. Specify its parameters manually.\n" + " (syntax: import-pem =...)\n" + " import-pkcs11 Import key stored in PKCS11 storage. Specify its parameters manually.\n" + " (syntax: import-pkcs11 =...)\n" + " nsec3-salt Print current NSEC3 salt. If a parameter is specified, set new salt.\n" + " (syntax: nsec3-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 )\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 )\n" + " ds Generate DS record(s) for specified key.\n" + " (syntax: ds )\n" + " dnskey Generate DNSKEY record for specified key.\n" + " (syntax: dnskey )\n" + " share Share an existing key of another zone with the specified zone.\n" + " (syntax: share \n" + " delete Remove the specified key from zone.\n" + " (syntax: delete )\n" + " set Set existing key's timing attribute.\n" + " (syntax: set =...)\n" + "\n" + "Commands related to Offline KSK feature:\n" + " pregenerate Pre-generate ZSKs for later rollovers with offline KSK.\n" + " (syntax: pregenerate [] )\n" + " show-offline Print pre-generated offline key-related records for specified time interval (possibly to infinity).\n" + " (syntax: show-offline [] [])\n" + " del-offline Delete pre-generated offline key-related records in specified time interval.\n" + " (syntax: del-offline )\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 [] )\n" + " sign-ksr Read KeySigningRequest from a file, sign it and print SignedKeyResponse to stdout.\n" + " (syntax: sign-ksr )\n" + " validate-skr Validate RRSIGs in a SignedKeyResponse (if not corrupt).\n" + " (syntax: validate-skr )\n" + " import-skr Import DNSKEY record signatures from a SignedKeyResponse.\n" + " (syntax: import-skr )\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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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(¶ms, argc, argv) == KNOT_EOK) { + if (!params.stop) { + dnssec_crypto_init(); + if (kdig_exec(¶ms) != KNOT_EOK) { + ret = EXIT_FAILURE; + } + dnssec_crypto_cleanup(); + } + } else { + ret = EXIT_FAILURE; + } + + khost_clean(¶ms); + 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#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, ¬ify) + != 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], ¶ms->queries, conf) + != KNOT_EOK) { + return KNOT_EINVAL; + } + break; + default: + print_help(); + return KNOT_ENOTSUP; + } + + // Complete missing data in queries based on defaults. + complete_queries(¶ms->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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include + +#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 ] [parameters] \n" + " %s [-c | -C | -D ] -z\n" + "\n" + "Parameters:\n" + " -c, --config Path to a textual configuration file.\n" + " (default %s)\n" + " -C, --confdb Path to a configuration database directory.\n" + " (default %s)\n" + " -D, --dir 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 Read only newest changes.\n" + " -s, --serial 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, ¶ms.limit, 0, INT_MAX) != KNOT_EOK) { + print_help(); + goto failure; + } + break; + case 's': + if (str_to_u32(optarg, ¶ms.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, ¶ms); + 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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, "[]", "Check if the server is running." }, + { CMD_STOP, "", "Stop the server if running." }, + { CMD_RELOAD, "", "Reload the server configuration and modified zones." }, + { CMD_STATS, "[[.]]", "Show global statistics counter(s)." }, + { "", "", "" }, + { CMD_ZONE_CHECK, "[...]", "Check if the zone can be loaded. (*)" }, + { CMD_ZONE_STATUS, "[...] [...]", "Show the zone status." }, + { CMD_ZONE_RELOAD, "[...]", "Reload a zone from a disk. (#)" }, + { CMD_ZONE_REFRESH, "[...]", "Force slave zone refresh. (#)" }, + { CMD_ZONE_NOTIFY, "[...]", "Send a NOTIFY message to all configured remotes. (#)" }, + { CMD_ZONE_RETRANSFER, "[...]", "Force slave zone retransfer (no serial check). (#)" }, + { CMD_ZONE_FLUSH, "[...] [...]", "Flush zone journal into the zone file. (#)" }, + { CMD_ZONE_BACKUP, "[...] [...] +backupdir ", "Backup zone data and metadata. (#)" }, + { CMD_ZONE_RESTORE, "[...] [...] +backupdir ", "Restore zone data and metadata. (#)" }, + { CMD_ZONE_SIGN, "[...]", "Re-sign the automatically signed zone. (#)" }, + { CMD_ZONE_KEYS_LOAD, "[...]", "Re-load keys from KASP database, sign the zone. (#)" }, + { CMD_ZONE_KEY_ROLL, " ksk|zsk", "Trigger immediate key rollover. (#)" }, + { CMD_ZONE_KSK_SBM, " ...", "When KSK submission, confirm parent's DS presence. (#)" }, + { CMD_ZONE_FREEZE, "[...]", "Temporarily postpone automatic zone-changing events. (#)" }, + { CMD_ZONE_THAW, "[...]", "Dismiss zone freeze. (#)" }, + { CMD_ZONE_XFR_FREEZE, "[...]", "Temporarily disable outgoing AXFR/IXFR. (#)" }, + { CMD_ZONE_XFR_THAW, "[...]", "Dismiss outgoing XFR freeze. (#)" }, + { "", "", "" }, + { CMD_ZONE_READ, " [ []]", "Get zone data that are currently being presented." }, + { CMD_ZONE_BEGIN, "...", "Begin a zone transaction." }, + { CMD_ZONE_COMMIT, "...", "Commit the zone transaction." }, + { CMD_ZONE_ABORT, "...", "Abort the zone transaction." }, + { CMD_ZONE_DIFF, "", "Get zone changes within the transaction." }, + { CMD_ZONE_GET, " [ []]", "Get zone data within the transaction." }, + { CMD_ZONE_SET, " [] ", "Add zone record within the transaction." }, + { CMD_ZONE_UNSET, " [ []]", "Remove zone data within the transaction." }, + { CMD_ZONE_PURGE, "... [...]", "Purge zone data, zone file, journal, timers, and KASP data. (#)" }, + { CMD_ZONE_STATS, " [[.]]", "Show zone statistics counter(s)."}, + { "", "", "" }, + { CMD_CONF_INIT, "", "Initialize the confdb. (*)" }, + { CMD_CONF_CHECK, "", "Check the server configuration. (*)" }, + { CMD_CONF_IMPORT, " ", "Import a config file into the confdb. (*)" }, + { CMD_CONF_EXPORT, "[]", "Export the confdb into a config file or stdout. (*)" }, + { CMD_CONF_LIST, "[...]", "List the confdb sections or section items." }, + { CMD_CONF_READ, "[...]", "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, "[...]", "Get the item difference within the transaction." }, + { CMD_CONF_GET, "[...]", "Get the item data within the transaction." }, + { CMD_CONF_SET, " [...]", "Set the item data within the transaction." }, + { CMD_CONF_UNSET, "[] [...]", "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 '--' parameter means all zones or all zones with a transaction.\n" + " Type parameter in the form of
[]..\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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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, ¶ms); + 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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_args]\n" + "\n" + "Parameters:\n" + " -c, --config "SPACE"Use a textual configuration file.\n" + " "SPACE" (default %s)\n" + " -C, --confdb "SPACE"Use a binary configuration database directory.\n" + " "SPACE" (default %s)\n" + " -m, --max-conf-size "SPACE"Set maximum size of the configuration database (max 10000 MiB).\n" + " "SPACE" (default %d MiB)\n" + " -s, --socket "SPACE"Use a control UNIX socket path.\n" + " "SPACE" (default %s)\n" + " -t, --timeout "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, ¶ms.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, ¶ms.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(¶ms); + } else { + ret = process_cmd(argc - optind, (const char **)argv + optind, ¶ms); + } + + 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_CAP_NG +#include +#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 Use a textual configuration file.\n" + " (default %s)\n" + " -C, --confdb Use a binary configuration database directory.\n" + " (default %s)\n" + " -m, --max-conf-size Set maximum size of the configuration database (max 10000 MiB).\n" + " (default %d MiB)\n" + " -s, --socket 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#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 " \n"); + printf("Example: " PROGRAM_NAME " c01dcafe 1 10 knot-dns.cz\n"); + printf("Alternative usage: "PROGRAM_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 + new_params = true; + } else if (argc != 5) { + // knsec3hash + 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#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, ¶ms->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, ¶ms->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(¶ms->parser, lp) != KNOT_EOK) { + return KNOT_EPARSEFAIL; + } + + return rr_list_append(¶ms->parser, ¶ms->update_list, ¶ms->mm); +} + +int cmd_del(const char* lp, knsupdate_params_t *params) +{ + DBG("%s: lp='%s'", __func__, lp); + + zs_scanner_t *rrp = ¶ms->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, ¶ms->update_list, ¶ms->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 = ¶ms->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, ¶ms->prereq_list, ¶ms->mm); +} + +int cmd_yxdomain(const char *lp, knsupdate_params_t *params) +{ + DBG("%s: lp='%s'", __func__, lp); + + zs_scanner_t *s = ¶ms->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, ¶ms->prereq_list, ¶ms->mm); +} + +int cmd_nxrrset(const char *lp, knsupdate_params_t *params) +{ + DBG("%s: lp='%s'", __func__, lp); + + zs_scanner_t *s = ¶ms->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, ¶ms->prereq_list, ¶ms->mm); +} + +int cmd_yxrrset(const char *lp, knsupdate_params_t *params) +{ + DBG("%s: lp='%s'", __func__, lp); + + zs_scanner_t *s = ¶ms->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, ¶ms->prereq_list, ¶ms->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, ¶ms->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, + ¶ms->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, ¶ms->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, ¶ms->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, ¶ms->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(¶ms->tsig_key); + + ret = knot_tsig_key_init_str(¶ms->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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include + +#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(¶ms, argc, argv) == KNOT_EOK) { + if (!params.stop) { + dnssec_crypto_init(); + if (knsupdate_exec(¶ms) != KNOT_EOK) { + ret = EXIT_FAILURE; + } + dnssec_crypto_cleanup(); + } + } else { + ret = EXIT_FAILURE; + } + + knsupdate_clean(¶ms); + 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#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(¶ms->qfiles); + init_list(¶ms->update_list); + init_list(¶ms->prereq_list); + + /* Initialize memory context. */ + mm_ctx_mempool(¶ms->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(¶ms->parser, ".", params->class_num, 3600) != 0 || + zs_set_processing(¶ms->parser, NULL, NULL, NULL) != 0) { + zs_deinit(¶ms->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, ¶ms->mm); + params->answer = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, ¶ms->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(¶ms->qfiles, ¶ms->mm); + + srv_info_free(params->server); + srv_info_free(params->srcif); + free(params->zone); + zs_deinit(¶ms->parser); + knot_pkt_free(params->query); + knot_pkt_free(params->answer); + knot_tsig_key_deinit(¶ms->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(¶ms->update_list, ¶ms->mm); + + /* Free PREREQ RRSets. */ + rr_list_free(¶ms->prereq_list, ¶ms->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, ¶ms->retries); + if (ret != KNOT_EOK) { + ERR("invalid retries '%s'", optarg); + return ret; + } + break; + case 't': + ret = params_parse_wait(optarg, ¶ms->wait); + if (ret != KNOT_EOK) { + ERR("invalid timeout '%s'", optarg); + return ret; + } + break; + case 'y': + knot_tsig_key_deinit(¶ms->tsig_key); + ret = knot_tsig_key_init_str(¶ms->tsig_key, optarg); + if (ret != KNOT_EOK) { + ERR("failed to parse key '%s'", optarg); + return ret; + } + break; + case 'k': + knot_tsig_key_deinit(¶ms->tsig_key); + ret = knot_tsig_key_init_file(¶ms->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(¶ms->qfiles, argv[optind], ¶ms->mm); + } + + return ret; +} + +int knsupdate_set_ttl(knsupdate_params_t *params, const uint32_t ttl) +{ + int ret = parser_set_default(¶ms->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(¶ms->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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include + +/*! + * \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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#pragma once + +#include +#include + +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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "libknot/libknot.h" +#include "libknot/xdp.h" +#include "libknot/xdp/tcp_iobuf.h" +#ifdef ENABLE_QUIC +#include +#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 \n" + "\n" + "Parameters:\n" + " -t, --duration "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 "SPACE"Number of queries-per-second (approximately) to be sent.\n" + " "SPACE" (default is %"PRIu64" qps)\n" + " -b, --batch "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 "SPACE"Remote destination port.\n" + " "SPACE" (default is %d for UDP/TCP, %u for QUIC)\n" + " -F, --affinity "SPACE"CPU affinity in the format [][s].\n" + " "SPACE" (default is %s)\n" + " -i, --infile "SPACE"Path to a file with query templates.\n" + " -I, --interface "SPACE"Override auto-detected interface for outgoing communication.\n" + " -l, --local "SPACE"Override auto-detected source IP address or subnet.\n" + " -L, --local-mac "SPACE"Override auto-detected local MAC address.\n" + " -R, --remote-mac "SPACE"Override auto-detected remote MAC address.\n" + " -v, --vlan "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" + " "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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include + +#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] \n" + "\n" + "Parameters:\n" + " -o, --origin Zone name.\n" + " (default filename without .zone)\n" + " -d, --dnssec Also check DNSSEC-related records.\n" + " -t, --time 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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#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. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include + +#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 ] [parameters] \n" + "\n" + "Parameters:\n" + " -c, --config Path to a textual configuration file.\n" + " (default %s)\n" + " -C, --confdb Path to a configuration database directory.\n" + " (default %s)\n" + " -o, --outdir 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 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(¶ms) != KNOT_EOK) { + goto failure; + } + +success: + util_conf_deinit(); + return EXIT_SUCCESS; +failure: + util_conf_deinit(); + return EXIT_FAILURE; +} -- cgit v1.2.3