summaryrefslogtreecommitdiffstats
path: root/lib/isc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/isc/Makefile.am253
-rw-r--r--lib/isc/Makefile.in2073
-rw-r--r--lib/isc/aes.c71
-rw-r--r--lib/isc/app.c421
-rw-r--r--lib/isc/assertions.c110
-rw-r--r--lib/isc/astack.c85
-rw-r--r--lib/isc/backtrace.c89
-rw-r--r--lib/isc/base32.c443
-rw-r--r--lib/isc/base64.c270
-rw-r--r--lib/isc/buffer.c352
-rw-r--r--lib/isc/commandline.c265
-rw-r--r--lib/isc/condition.c64
-rw-r--r--lib/isc/counter.c112
-rw-r--r--lib/isc/crc64.c140
-rw-r--r--lib/isc/dir.c268
-rw-r--r--lib/isc/entropy.c28
-rw-r--r--lib/isc/entropy_private.h37
-rw-r--r--lib/isc/errno.c24
-rw-r--r--lib/isc/errno2result.c133
-rw-r--r--lib/isc/errno2result.h34
-rw-r--r--lib/isc/error.c91
-rw-r--r--lib/isc/event.c95
-rw-r--r--lib/isc/file.c792
-rw-r--r--lib/isc/glob.c53
-rw-r--r--lib/isc/hash.c149
-rw-r--r--lib/isc/heap.c280
-rw-r--r--lib/isc/hex.c212
-rw-r--r--lib/isc/hmac.c167
-rw-r--r--lib/isc/ht.c573
-rw-r--r--lib/isc/httpd.c1147
-rw-r--r--lib/isc/include/isc/aes.h40
-rw-r--r--lib/isc/include/isc/align.h20
-rw-r--r--lib/isc/include/isc/app.h281
-rw-r--r--lib/isc/include/isc/assertions.h73
-rw-r--r--lib/isc/include/isc/astack.h49
-rw-r--r--lib/isc/include/isc/atomic.h79
-rw-r--r--lib/isc/include/isc/attributes.h82
-rw-r--r--lib/isc/include/isc/backtrace.h101
-rw-r--r--lib/isc/include/isc/barrier.h39
-rw-r--r--lib/isc/include/isc/base32.h143
-rw-r--r--lib/isc/include/isc/base64.h98
-rw-r--r--lib/isc/include/isc/buffer.h1023
-rw-r--r--lib/isc/include/isc/cmocka.h55
-rw-r--r--lib/isc/include/isc/commandline.h56
-rw-r--r--lib/isc/include/isc/condition.h53
-rw-r--r--lib/isc/include/isc/counter.h84
-rw-r--r--lib/isc/include/isc/crc64.h55
-rw-r--r--lib/isc/include/isc/deprecated.h20
-rw-r--r--lib/isc/include/isc/dir.h80
-rw-r--r--lib/isc/include/isc/endian.h166
-rw-r--r--lib/isc/include/isc/errno.h27
-rw-r--r--lib/isc/include/isc/error.h45
-rw-r--r--lib/isc/include/isc/event.h117
-rw-r--r--lib/isc/include/isc/eventclass.h45
-rw-r--r--lib/isc/include/isc/file.h381
-rw-r--r--lib/isc/include/isc/formatcheck.h33
-rw-r--r--lib/isc/include/isc/fuzz.h23
-rw-r--r--lib/isc/include/isc/glob.h43
-rw-r--r--lib/isc/include/isc/hash.h63
-rw-r--r--lib/isc/include/isc/heap.h161
-rw-r--r--lib/isc/include/isc/hex.h98
-rw-r--r--lib/isc/include/isc/hmac.h148
-rw-r--r--lib/isc/include/isc/ht.h191
-rw-r--r--lib/isc/include/isc/httpd.h63
-rw-r--r--lib/isc/include/isc/interfaceiter.h127
-rw-r--r--lib/isc/include/isc/iterated_hash.h38
-rw-r--r--lib/isc/include/isc/lang.h24
-rw-r--r--lib/isc/include/isc/lex.h444
-rw-r--r--lib/isc/include/isc/list.h229
-rw-r--r--lib/isc/include/isc/log.h853
-rw-r--r--lib/isc/include/isc/magic.h31
-rw-r--r--lib/isc/include/isc/managers.h30
-rw-r--r--lib/isc/include/isc/md.h204
-rw-r--r--lib/isc/include/isc/mem.h572
-rw-r--r--lib/isc/include/isc/meminfo.h30
-rw-r--r--lib/isc/include/isc/mutex.h50
-rw-r--r--lib/isc/include/isc/mutexblock.h54
-rw-r--r--lib/isc/include/isc/net.h292
-rw-r--r--lib/isc/include/isc/netaddr.h197
-rw-r--r--lib/isc/include/isc/netdb.h48
-rw-r--r--lib/isc/include/isc/netmgr.h811
-rw-r--r--lib/isc/include/isc/netscope.h36
-rw-r--r--lib/isc/include/isc/nonce.h33
-rw-r--r--lib/isc/include/isc/offset.h25
-rw-r--r--lib/isc/include/isc/once.h29
-rw-r--r--lib/isc/include/isc/os.h52
-rw-r--r--lib/isc/include/isc/parseint.h57
-rw-r--r--lib/isc/include/isc/pool.h138
-rw-r--r--lib/isc/include/isc/portset.h135
-rw-r--r--lib/isc/include/isc/print.h29
-rw-r--r--lib/isc/include/isc/quota.h154
-rw-r--r--lib/isc/include/isc/radix.h221
-rw-r--r--lib/isc/include/isc/random.h65
-rw-r--r--lib/isc/include/isc/ratelimiter.h145
-rw-r--r--lib/isc/include/isc/refcount.h244
-rw-r--r--lib/isc/include/isc/regex.h33
-rw-r--r--lib/isc/include/isc/region.h95
-rw-r--r--lib/isc/include/isc/resource.h87
-rw-r--r--lib/isc/include/isc/result.h293
-rw-r--r--lib/isc/include/isc/rwlock.h103
-rw-r--r--lib/isc/include/isc/safe.h44
-rw-r--r--lib/isc/include/isc/serial.h69
-rw-r--r--lib/isc/include/isc/siphash.h36
-rw-r--r--lib/isc/include/isc/sockaddr.h248
-rw-r--r--lib/isc/include/isc/stat.h44
-rw-r--r--lib/isc/include/isc/stats.h253
-rw-r--r--lib/isc/include/isc/stdatomic.h125
-rw-r--r--lib/isc/include/isc/stdio.h71
-rw-r--r--lib/isc/include/isc/stdtime.h61
-rw-r--r--lib/isc/include/isc/strerr.h29
-rw-r--r--lib/isc/include/isc/string.h42
-rw-r--r--lib/isc/include/isc/symtab.h135
-rw-r--r--lib/isc/include/isc/syslog.h38
-rw-r--r--lib/isc/include/isc/task.h646
-rw-r--r--lib/isc/include/isc/taskpool.h135
-rw-r--r--lib/isc/include/isc/thread.h53
-rw-r--r--lib/isc/include/isc/time.h468
-rw-r--r--lib/isc/include/isc/timer.h271
-rw-r--r--lib/isc/include/isc/tls.h587
-rw-r--r--lib/isc/include/isc/tm.h39
-rw-r--r--lib/isc/include/isc/types.h133
-rw-r--r--lib/isc/include/isc/url.h85
-rw-r--r--lib/isc/include/isc/utf8.h43
-rw-r--r--lib/isc/include/isc/util.h377
-rw-r--r--lib/isc/interfaceiter.c518
-rw-r--r--lib/isc/iterated_hash.c139
-rw-r--r--lib/isc/jemalloc_shim.h149
-rw-r--r--lib/isc/lex.c1136
-rw-r--r--lib/isc/lib.c55
-rw-r--r--lib/isc/log.c1909
-rw-r--r--lib/isc/managers.c117
-rw-r--r--lib/isc/md.c185
-rw-r--r--lib/isc/mem.c1908
-rw-r--r--lib/isc/mem_p.h36
-rw-r--r--lib/isc/meminfo.c50
-rw-r--r--lib/isc/mutex.c53
-rw-r--r--lib/isc/mutexblock.c35
-rw-r--r--lib/isc/net.c497
-rw-r--r--lib/isc/netaddr.c467
-rw-r--r--lib/isc/netmgr/http.c3755
-rw-r--r--lib/isc/netmgr/netmgr-int.h2273
-rw-r--r--lib/isc/netmgr/netmgr.c3991
-rw-r--r--lib/isc/netmgr/tcp.c1456
-rw-r--r--lib/isc/netmgr/tcpdns.c1500
-rw-r--r--lib/isc/netmgr/timer.c120
-rw-r--r--lib/isc/netmgr/tlsdns.c2363
-rw-r--r--lib/isc/netmgr/tlsstream.c1348
-rw-r--r--lib/isc/netmgr/udp.c1405
-rw-r--r--lib/isc/netmgr/uv-compat.c140
-rw-r--r--lib/isc/netmgr/uv-compat.h126
-rw-r--r--lib/isc/netmgr/uverr2result.c105
-rw-r--r--lib/isc/netmgr_p.h38
-rw-r--r--lib/isc/netscope.c76
-rw-r--r--lib/isc/nonce.c21
-rw-r--r--lib/isc/openssl_shim.c198
-rw-r--r--lib/isc/openssl_shim.h137
-rw-r--r--lib/isc/os.c113
-rw-r--r--lib/isc/os_p.h26
-rw-r--r--lib/isc/parseint.c79
-rw-r--r--lib/isc/picohttpparser.c727
-rw-r--r--lib/isc/picohttpparser.h100
-rw-r--r--lib/isc/pool.c163
-rw-r--r--lib/isc/portset.c135
-rw-r--r--lib/isc/quota.c202
-rw-r--r--lib/isc/radix.c706
-rw-r--r--lib/isc/random.c206
-rw-r--r--lib/isc/ratelimiter.c372
-rw-r--r--lib/isc/regex.c436
-rw-r--r--lib/isc/region.c39
-rw-r--r--lib/isc/resource.c218
-rw-r--r--lib/isc/result.c540
-rw-r--r--lib/isc/rwlock.c644
-rw-r--r--lib/isc/safe.c26
-rw-r--r--lib/isc/serial.c55
-rw-r--r--lib/isc/siphash.c235
-rw-r--r--lib/isc/sockaddr.c499
-rw-r--r--lib/isc/stats.c198
-rw-r--r--lib/isc/stdio.c152
-rw-r--r--lib/isc/stdtime.c63
-rw-r--r--lib/isc/string.c143
-rw-r--r--lib/isc/symtab.c294
-rw-r--r--lib/isc/syslog.c73
-rw-r--r--lib/isc/task.c1368
-rw-r--r--lib/isc/task_p.h106
-rw-r--r--lib/isc/taskpool.c157
-rw-r--r--lib/isc/thread.c121
-rw-r--r--lib/isc/time.c563
-rw-r--r--lib/isc/timer.c741
-rw-r--r--lib/isc/timer_p.h67
-rw-r--r--lib/isc/tls.c1678
-rw-r--r--lib/isc/tls_p.h20
-rw-r--r--lib/isc/tm.c469
-rw-r--r--lib/isc/trampoline.c194
-rw-r--r--lib/isc/trampoline_p.h90
-rw-r--r--lib/isc/url.c671
-rw-r--r--lib/isc/utf8.c89
-rw-r--r--lib/isccc/Makefile.am38
-rw-r--r--lib/isccc/Makefile.in956
-rw-r--r--lib/isccc/alist.c320
-rw-r--r--lib/isccc/base64.c74
-rw-r--r--lib/isccc/cc.c1057
-rw-r--r--lib/isccc/ccmsg.c185
-rw-r--r--lib/isccc/include/isccc/alist.h86
-rw-r--r--lib/isccc/include/isccc/base64.h79
-rw-r--r--lib/isccc/include/isccc/cc.h131
-rw-r--r--lib/isccc/include/isccc/ccmsg.h137
-rw-r--r--lib/isccc/include/isccc/events.h43
-rw-r--r--lib/isccc/include/isccc/sexpr.h119
-rw-r--r--lib/isccc/include/isccc/symtab.h133
-rw-r--r--lib/isccc/include/isccc/symtype.h37
-rw-r--r--lib/isccc/include/isccc/types.h52
-rw-r--r--lib/isccc/include/isccc/util.h208
-rw-r--r--lib/isccc/sexpr.c317
-rw-r--r--lib/isccc/symtab.c293
-rw-r--r--lib/isccfg/Makefile.am37
-rw-r--r--lib/isccfg/Makefile.in967
-rw-r--r--lib/isccfg/aclconf.c1010
-rw-r--r--lib/isccfg/dnsconf.c57
-rw-r--r--lib/isccfg/duration.c239
-rw-r--r--lib/isccfg/include/isccfg/aclconf.h91
-rw-r--r--lib/isccfg/include/isccfg/cfg.h609
-rw-r--r--lib/isccfg/include/isccfg/duration.h87
-rw-r--r--lib/isccfg/include/isccfg/grammar.h590
-rw-r--r--lib/isccfg/include/isccfg/kaspconf.h56
-rw-r--r--lib/isccfg/include/isccfg/log.h46
-rw-r--r--lib/isccfg/include/isccfg/namedconf.h54
-rw-r--r--lib/isccfg/kaspconf.c576
-rw-r--r--lib/isccfg/log.c38
-rw-r--r--lib/isccfg/namedconf.c3998
-rw-r--r--lib/isccfg/parser.c3901
230 files changed, 78611 insertions, 0 deletions
diff --git a/lib/isc/Makefile.am b/lib/isc/Makefile.am
new file mode 100644
index 0000000..b962f68
--- /dev/null
+++ b/lib/isc/Makefile.am
@@ -0,0 +1,253 @@
+include $(top_srcdir)/Makefile.top
+
+lib_LTLIBRARIES = libisc.la
+
+libisc_ladir = $(includedir)/isc
+libisc_la_HEADERS = \
+ include/isc/aes.h \
+ include/isc/align.h \
+ include/isc/app.h \
+ include/isc/assertions.h \
+ include/isc/astack.h \
+ include/isc/atomic.h \
+ include/isc/attributes.h \
+ include/isc/backtrace.h \
+ include/isc/barrier.h \
+ include/isc/base32.h \
+ include/isc/base64.h \
+ include/isc/buffer.h \
+ include/isc/cmocka.h \
+ include/isc/commandline.h \
+ include/isc/condition.h \
+ include/isc/counter.h \
+ include/isc/crc64.h \
+ include/isc/deprecated.h \
+ include/isc/dir.h \
+ include/isc/endian.h \
+ include/isc/errno.h \
+ include/isc/error.h \
+ include/isc/event.h \
+ include/isc/eventclass.h \
+ include/isc/file.h \
+ include/isc/formatcheck.h \
+ include/isc/fuzz.h \
+ include/isc/glob.h \
+ include/isc/hash.h \
+ include/isc/heap.h \
+ include/isc/hex.h \
+ include/isc/hmac.h \
+ include/isc/ht.h \
+ include/isc/httpd.h \
+ include/isc/interfaceiter.h \
+ include/isc/iterated_hash.h \
+ include/isc/lang.h \
+ include/isc/lex.h \
+ include/isc/list.h \
+ include/isc/log.h \
+ include/isc/magic.h \
+ include/isc/managers.h \
+ include/isc/md.h \
+ include/isc/mem.h \
+ include/isc/meminfo.h \
+ include/isc/mutex.h \
+ include/isc/mutexblock.h \
+ include/isc/net.h \
+ include/isc/netaddr.h \
+ include/isc/netdb.h \
+ include/isc/netmgr.h \
+ include/isc/netscope.h \
+ include/isc/nonce.h \
+ include/isc/offset.h \
+ include/isc/once.h \
+ include/isc/os.h \
+ include/isc/parseint.h \
+ include/isc/pool.h \
+ include/isc/portset.h \
+ include/isc/print.h \
+ include/isc/quota.h \
+ include/isc/radix.h \
+ include/isc/random.h \
+ include/isc/ratelimiter.h \
+ include/isc/refcount.h \
+ include/isc/regex.h \
+ include/isc/region.h \
+ include/isc/resource.h \
+ include/isc/result.h \
+ include/isc/rwlock.h \
+ include/isc/safe.h \
+ include/isc/serial.h \
+ include/isc/siphash.h \
+ include/isc/sockaddr.h \
+ include/isc/stat.h \
+ include/isc/stats.h \
+ include/isc/stdatomic.h \
+ include/isc/stdio.h \
+ include/isc/stdtime.h \
+ include/isc/strerr.h \
+ include/isc/string.h \
+ include/isc/symtab.h \
+ include/isc/syslog.h \
+ include/isc/task.h \
+ include/isc/taskpool.h \
+ include/isc/thread.h \
+ include/isc/time.h \
+ include/isc/timer.h \
+ include/isc/tls.h \
+ include/isc/tm.h \
+ include/isc/types.h \
+ include/isc/url.h \
+ include/isc/utf8.h \
+ include/isc/util.h
+
+libisc_la_SOURCES = \
+ $(libisc_la_HEADERS) \
+ netmgr/netmgr-int.h \
+ netmgr/netmgr.c \
+ netmgr/tcp.c \
+ netmgr/tcpdns.c \
+ netmgr/timer.c \
+ netmgr/tlsdns.c \
+ netmgr/udp.c \
+ netmgr/uv-compat.c \
+ netmgr/uv-compat.h \
+ netmgr/uverr2result.c \
+ aes.c \
+ app.c \
+ assertions.c \
+ astack.c \
+ backtrace.c \
+ base32.c \
+ base64.c \
+ buffer.c \
+ commandline.c \
+ condition.c \
+ counter.c \
+ crc64.c \
+ dir.c \
+ entropy.c \
+ entropy_private.h \
+ errno.c \
+ errno2result.c \
+ errno2result.h \
+ error.c \
+ event.c \
+ file.c \
+ glob.c \
+ hash.c \
+ heap.c \
+ hex.c \
+ hmac.c \
+ ht.c \
+ httpd.c \
+ interfaceiter.c \
+ iterated_hash.c \
+ jemalloc_shim.h \
+ lex.c \
+ lib.c \
+ log.c \
+ managers.c \
+ md.c \
+ mem.c \
+ mem_p.h \
+ meminfo.c \
+ mutex.c \
+ mutexblock.c \
+ net.c \
+ netaddr.c \
+ netmgr_p.h \
+ netscope.c \
+ nonce.c \
+ openssl_shim.c \
+ openssl_shim.h \
+ os.c \
+ os_p.h \
+ parseint.c \
+ pool.c \
+ picohttpparser.c \
+ picohttpparser.h \
+ portset.c \
+ quota.c \
+ radix.c \
+ random.c \
+ ratelimiter.c \
+ regex.c \
+ region.c \
+ resource.c \
+ result.c \
+ rwlock.c \
+ safe.c \
+ serial.c \
+ siphash.c \
+ sockaddr.c \
+ stats.c \
+ stdio.c \
+ stdtime.c \
+ string.c \
+ symtab.c \
+ syslog.c \
+ task.c \
+ task_p.h \
+ taskpool.c \
+ thread.c \
+ time.c \
+ timer.c \
+ timer_p.h \
+ tls.c \
+ tls_p.h \
+ tm.c \
+ trampoline.c \
+ trampoline_p.h \
+ url.c \
+ utf8.c
+
+libisc_la_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ $(LIBISC_CFLAGS) \
+ $(LIBUV_CFLAGS) \
+ $(OPENSSL_CFLAGS) \
+ $(ZLIB_CFLAGS)
+
+libisc_la_LDFLAGS = \
+ $(AM_LDFLAGS) \
+ -release "$(PACKAGE_VERSION)"
+
+libisc_la_LIBADD = \
+ $(LIBUV_LIBS) \
+ $(OPENSSL_LIBS) \
+ $(ZLIB_LIBS)
+
+if HAVE_JEMALLOC
+libisc_la_CPPFLAGS += \
+ $(JEMALLOC_CFLAGS)
+
+libisc_la_LIBADD += \
+ $(JEMALLOC_LIBS)
+endif HAVE_JEMALLOC
+
+if HAVE_JSON_C
+libisc_la_CPPFLAGS += \
+ $(JSON_C_CFLAGS)
+
+libisc_la_LIBADD += \
+ $(JSON_C_LIBS)
+endif HAVE_JSON_C
+
+if HAVE_LIBNGHTTP2
+libisc_la_SOURCES += \
+ netmgr/http.c \
+ netmgr/tlsstream.c
+
+libisc_la_CPPFLAGS += \
+ $(LIBNGHTTP2_CFLAGS)
+
+libisc_la_LIBADD += \
+ $(LIBNGHTTP2_LIBS)
+endif
+
+if HAVE_LIBXML2
+libisc_la_CPPFLAGS += \
+ $(LIBXML2_CFLAGS)
+
+libisc_la_LIBADD += \
+ $(LIBXML2_LIBS)
+endif HAVE_LIBXML2
diff --git a/lib/isc/Makefile.in b/lib/isc/Makefile.in
new file mode 100644
index 0000000..50ec06e
--- /dev/null
+++ b/lib/isc/Makefile.in
@@ -0,0 +1,2073 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 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@
+
+# Hey Emacs, this is -*- makefile-automake -*- file!
+# vim: filetype=automake
+
+
+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@
+target_triplet = @target@
+@HOST_MACOS_TRUE@am__append_1 = \
+@HOST_MACOS_TRUE@ -Wl,-flat_namespace
+
+@HAVE_JEMALLOC_TRUE@am__append_2 = \
+@HAVE_JEMALLOC_TRUE@ $(JEMALLOC_CFLAGS)
+
+@HAVE_JEMALLOC_TRUE@am__append_3 = \
+@HAVE_JEMALLOC_TRUE@ $(JEMALLOC_LIBS)
+
+@HAVE_JSON_C_TRUE@am__append_4 = \
+@HAVE_JSON_C_TRUE@ $(JSON_C_CFLAGS)
+
+@HAVE_JSON_C_TRUE@am__append_5 = \
+@HAVE_JSON_C_TRUE@ $(JSON_C_LIBS)
+
+@HAVE_LIBNGHTTP2_TRUE@am__append_6 = \
+@HAVE_LIBNGHTTP2_TRUE@ netmgr/http.c \
+@HAVE_LIBNGHTTP2_TRUE@ netmgr/tlsstream.c
+
+@HAVE_LIBNGHTTP2_TRUE@am__append_7 = \
+@HAVE_LIBNGHTTP2_TRUE@ $(LIBNGHTTP2_CFLAGS)
+
+@HAVE_LIBNGHTTP2_TRUE@am__append_8 = \
+@HAVE_LIBNGHTTP2_TRUE@ $(LIBNGHTTP2_LIBS)
+
+@HAVE_LIBXML2_TRUE@am__append_9 = \
+@HAVE_LIBXML2_TRUE@ $(LIBXML2_CFLAGS)
+
+@HAVE_LIBXML2_TRUE@am__append_10 = \
+@HAVE_LIBXML2_TRUE@ $(LIBXML2_LIBS)
+
+subdir = lib/isc
+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/ax_check_openssl.m4 \
+ $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4/ax_jemalloc.m4 \
+ $(top_srcdir)/m4/ax_lib_lmdb.m4 \
+ $(top_srcdir)/m4/ax_perl_module.m4 \
+ $(top_srcdir)/m4/ax_posix_shell.m4 \
+ $(top_srcdir)/m4/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4/ax_pthread.m4 \
+ $(top_srcdir)/m4/ax_python_module.m4 \
+ $(top_srcdir)/m4/ax_restore_flags.m4 \
+ $(top_srcdir)/m4/ax_save_flags.m4 $(top_srcdir)/m4/ax_tls.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(libisc_la_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libisc_ladir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+@HAVE_JEMALLOC_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1)
+@HAVE_JSON_C_TRUE@am__DEPENDENCIES_3 = $(am__DEPENDENCIES_1)
+@HAVE_LIBNGHTTP2_TRUE@am__DEPENDENCIES_4 = $(am__DEPENDENCIES_1)
+@HAVE_LIBXML2_TRUE@am__DEPENDENCIES_5 = $(am__DEPENDENCIES_1)
+libisc_la_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) \
+ $(am__DEPENDENCIES_3) $(am__DEPENDENCIES_4) \
+ $(am__DEPENDENCIES_5)
+am__libisc_la_SOURCES_DIST = include/isc/aes.h include/isc/align.h \
+ include/isc/app.h include/isc/assertions.h \
+ include/isc/astack.h include/isc/atomic.h \
+ include/isc/attributes.h include/isc/backtrace.h \
+ include/isc/barrier.h include/isc/base32.h \
+ include/isc/base64.h include/isc/buffer.h include/isc/cmocka.h \
+ include/isc/commandline.h include/isc/condition.h \
+ include/isc/counter.h include/isc/crc64.h \
+ include/isc/deprecated.h include/isc/dir.h \
+ include/isc/endian.h include/isc/errno.h include/isc/error.h \
+ include/isc/event.h include/isc/eventclass.h \
+ include/isc/file.h include/isc/formatcheck.h \
+ include/isc/fuzz.h include/isc/glob.h include/isc/hash.h \
+ include/isc/heap.h include/isc/hex.h include/isc/hmac.h \
+ include/isc/ht.h include/isc/httpd.h \
+ include/isc/interfaceiter.h include/isc/iterated_hash.h \
+ include/isc/lang.h include/isc/lex.h include/isc/list.h \
+ include/isc/log.h include/isc/magic.h include/isc/managers.h \
+ include/isc/md.h include/isc/mem.h include/isc/meminfo.h \
+ include/isc/mutex.h include/isc/mutexblock.h include/isc/net.h \
+ include/isc/netaddr.h include/isc/netdb.h include/isc/netmgr.h \
+ include/isc/netscope.h include/isc/nonce.h \
+ include/isc/offset.h include/isc/once.h include/isc/os.h \
+ include/isc/parseint.h include/isc/pool.h \
+ include/isc/portset.h include/isc/print.h include/isc/quota.h \
+ include/isc/radix.h include/isc/random.h \
+ include/isc/ratelimiter.h include/isc/refcount.h \
+ include/isc/regex.h include/isc/region.h \
+ include/isc/resource.h include/isc/result.h \
+ include/isc/rwlock.h include/isc/safe.h include/isc/serial.h \
+ include/isc/siphash.h include/isc/sockaddr.h \
+ include/isc/stat.h include/isc/stats.h include/isc/stdatomic.h \
+ include/isc/stdio.h include/isc/stdtime.h include/isc/strerr.h \
+ include/isc/string.h include/isc/symtab.h include/isc/syslog.h \
+ include/isc/task.h include/isc/taskpool.h include/isc/thread.h \
+ include/isc/time.h include/isc/timer.h include/isc/tls.h \
+ include/isc/tm.h include/isc/types.h include/isc/url.h \
+ include/isc/utf8.h include/isc/util.h netmgr/netmgr-int.h \
+ netmgr/netmgr.c netmgr/tcp.c netmgr/tcpdns.c netmgr/timer.c \
+ netmgr/tlsdns.c netmgr/udp.c netmgr/uv-compat.c \
+ netmgr/uv-compat.h netmgr/uverr2result.c aes.c app.c \
+ assertions.c astack.c backtrace.c base32.c base64.c buffer.c \
+ commandline.c condition.c counter.c crc64.c dir.c entropy.c \
+ entropy_private.h errno.c errno2result.c errno2result.h \
+ error.c event.c file.c glob.c hash.c heap.c hex.c hmac.c ht.c \
+ httpd.c interfaceiter.c iterated_hash.c jemalloc_shim.h lex.c \
+ lib.c log.c managers.c md.c mem.c mem_p.h meminfo.c mutex.c \
+ mutexblock.c net.c netaddr.c netmgr_p.h netscope.c nonce.c \
+ openssl_shim.c openssl_shim.h os.c os_p.h parseint.c pool.c \
+ picohttpparser.c picohttpparser.h portset.c quota.c radix.c \
+ random.c ratelimiter.c regex.c region.c resource.c result.c \
+ rwlock.c safe.c serial.c siphash.c sockaddr.c stats.c stdio.c \
+ stdtime.c string.c symtab.c syslog.c task.c task_p.h \
+ taskpool.c thread.c time.c timer.c timer_p.h tls.c tls_p.h \
+ tm.c trampoline.c trampoline_p.h url.c utf8.c netmgr/http.c \
+ netmgr/tlsstream.c
+am__objects_1 =
+am__dirstamp = $(am__leading_dot)dirstamp
+@HAVE_LIBNGHTTP2_TRUE@am__objects_2 = netmgr/libisc_la-http.lo \
+@HAVE_LIBNGHTTP2_TRUE@ netmgr/libisc_la-tlsstream.lo
+am_libisc_la_OBJECTS = $(am__objects_1) netmgr/libisc_la-netmgr.lo \
+ netmgr/libisc_la-tcp.lo netmgr/libisc_la-tcpdns.lo \
+ netmgr/libisc_la-timer.lo netmgr/libisc_la-tlsdns.lo \
+ netmgr/libisc_la-udp.lo netmgr/libisc_la-uv-compat.lo \
+ netmgr/libisc_la-uverr2result.lo libisc_la-aes.lo \
+ libisc_la-app.lo libisc_la-assertions.lo libisc_la-astack.lo \
+ libisc_la-backtrace.lo libisc_la-base32.lo libisc_la-base64.lo \
+ libisc_la-buffer.lo libisc_la-commandline.lo \
+ libisc_la-condition.lo libisc_la-counter.lo libisc_la-crc64.lo \
+ libisc_la-dir.lo libisc_la-entropy.lo libisc_la-errno.lo \
+ libisc_la-errno2result.lo libisc_la-error.lo \
+ libisc_la-event.lo libisc_la-file.lo libisc_la-glob.lo \
+ libisc_la-hash.lo libisc_la-heap.lo libisc_la-hex.lo \
+ libisc_la-hmac.lo libisc_la-ht.lo libisc_la-httpd.lo \
+ libisc_la-interfaceiter.lo libisc_la-iterated_hash.lo \
+ libisc_la-lex.lo libisc_la-lib.lo libisc_la-log.lo \
+ libisc_la-managers.lo libisc_la-md.lo libisc_la-mem.lo \
+ libisc_la-meminfo.lo libisc_la-mutex.lo \
+ libisc_la-mutexblock.lo libisc_la-net.lo libisc_la-netaddr.lo \
+ libisc_la-netscope.lo libisc_la-nonce.lo \
+ libisc_la-openssl_shim.lo libisc_la-os.lo \
+ libisc_la-parseint.lo libisc_la-pool.lo \
+ libisc_la-picohttpparser.lo libisc_la-portset.lo \
+ libisc_la-quota.lo libisc_la-radix.lo libisc_la-random.lo \
+ libisc_la-ratelimiter.lo libisc_la-regex.lo \
+ libisc_la-region.lo libisc_la-resource.lo libisc_la-result.lo \
+ libisc_la-rwlock.lo libisc_la-safe.lo libisc_la-serial.lo \
+ libisc_la-siphash.lo libisc_la-sockaddr.lo libisc_la-stats.lo \
+ libisc_la-stdio.lo libisc_la-stdtime.lo libisc_la-string.lo \
+ libisc_la-symtab.lo libisc_la-syslog.lo libisc_la-task.lo \
+ libisc_la-taskpool.lo libisc_la-thread.lo libisc_la-time.lo \
+ libisc_la-timer.lo libisc_la-tls.lo libisc_la-tm.lo \
+ libisc_la-trampoline.lo libisc_la-url.lo libisc_la-utf8.lo \
+ $(am__objects_2)
+libisc_la_OBJECTS = $(am_libisc_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 =
+libisc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libisc_la_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@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/libisc_la-aes.Plo \
+ ./$(DEPDIR)/libisc_la-app.Plo \
+ ./$(DEPDIR)/libisc_la-assertions.Plo \
+ ./$(DEPDIR)/libisc_la-astack.Plo \
+ ./$(DEPDIR)/libisc_la-backtrace.Plo \
+ ./$(DEPDIR)/libisc_la-base32.Plo \
+ ./$(DEPDIR)/libisc_la-base64.Plo \
+ ./$(DEPDIR)/libisc_la-buffer.Plo \
+ ./$(DEPDIR)/libisc_la-commandline.Plo \
+ ./$(DEPDIR)/libisc_la-condition.Plo \
+ ./$(DEPDIR)/libisc_la-counter.Plo \
+ ./$(DEPDIR)/libisc_la-crc64.Plo ./$(DEPDIR)/libisc_la-dir.Plo \
+ ./$(DEPDIR)/libisc_la-entropy.Plo \
+ ./$(DEPDIR)/libisc_la-errno.Plo \
+ ./$(DEPDIR)/libisc_la-errno2result.Plo \
+ ./$(DEPDIR)/libisc_la-error.Plo \
+ ./$(DEPDIR)/libisc_la-event.Plo ./$(DEPDIR)/libisc_la-file.Plo \
+ ./$(DEPDIR)/libisc_la-glob.Plo ./$(DEPDIR)/libisc_la-hash.Plo \
+ ./$(DEPDIR)/libisc_la-heap.Plo ./$(DEPDIR)/libisc_la-hex.Plo \
+ ./$(DEPDIR)/libisc_la-hmac.Plo ./$(DEPDIR)/libisc_la-ht.Plo \
+ ./$(DEPDIR)/libisc_la-httpd.Plo \
+ ./$(DEPDIR)/libisc_la-interfaceiter.Plo \
+ ./$(DEPDIR)/libisc_la-iterated_hash.Plo \
+ ./$(DEPDIR)/libisc_la-lex.Plo ./$(DEPDIR)/libisc_la-lib.Plo \
+ ./$(DEPDIR)/libisc_la-log.Plo \
+ ./$(DEPDIR)/libisc_la-managers.Plo \
+ ./$(DEPDIR)/libisc_la-md.Plo ./$(DEPDIR)/libisc_la-mem.Plo \
+ ./$(DEPDIR)/libisc_la-meminfo.Plo \
+ ./$(DEPDIR)/libisc_la-mutex.Plo \
+ ./$(DEPDIR)/libisc_la-mutexblock.Plo \
+ ./$(DEPDIR)/libisc_la-net.Plo \
+ ./$(DEPDIR)/libisc_la-netaddr.Plo \
+ ./$(DEPDIR)/libisc_la-netscope.Plo \
+ ./$(DEPDIR)/libisc_la-nonce.Plo \
+ ./$(DEPDIR)/libisc_la-openssl_shim.Plo \
+ ./$(DEPDIR)/libisc_la-os.Plo \
+ ./$(DEPDIR)/libisc_la-parseint.Plo \
+ ./$(DEPDIR)/libisc_la-picohttpparser.Plo \
+ ./$(DEPDIR)/libisc_la-pool.Plo \
+ ./$(DEPDIR)/libisc_la-portset.Plo \
+ ./$(DEPDIR)/libisc_la-quota.Plo \
+ ./$(DEPDIR)/libisc_la-radix.Plo \
+ ./$(DEPDIR)/libisc_la-random.Plo \
+ ./$(DEPDIR)/libisc_la-ratelimiter.Plo \
+ ./$(DEPDIR)/libisc_la-regex.Plo \
+ ./$(DEPDIR)/libisc_la-region.Plo \
+ ./$(DEPDIR)/libisc_la-resource.Plo \
+ ./$(DEPDIR)/libisc_la-result.Plo \
+ ./$(DEPDIR)/libisc_la-rwlock.Plo \
+ ./$(DEPDIR)/libisc_la-safe.Plo \
+ ./$(DEPDIR)/libisc_la-serial.Plo \
+ ./$(DEPDIR)/libisc_la-siphash.Plo \
+ ./$(DEPDIR)/libisc_la-sockaddr.Plo \
+ ./$(DEPDIR)/libisc_la-stats.Plo \
+ ./$(DEPDIR)/libisc_la-stdio.Plo \
+ ./$(DEPDIR)/libisc_la-stdtime.Plo \
+ ./$(DEPDIR)/libisc_la-string.Plo \
+ ./$(DEPDIR)/libisc_la-symtab.Plo \
+ ./$(DEPDIR)/libisc_la-syslog.Plo \
+ ./$(DEPDIR)/libisc_la-task.Plo \
+ ./$(DEPDIR)/libisc_la-taskpool.Plo \
+ ./$(DEPDIR)/libisc_la-thread.Plo \
+ ./$(DEPDIR)/libisc_la-time.Plo ./$(DEPDIR)/libisc_la-timer.Plo \
+ ./$(DEPDIR)/libisc_la-tls.Plo ./$(DEPDIR)/libisc_la-tm.Plo \
+ ./$(DEPDIR)/libisc_la-trampoline.Plo \
+ ./$(DEPDIR)/libisc_la-url.Plo ./$(DEPDIR)/libisc_la-utf8.Plo \
+ netmgr/$(DEPDIR)/libisc_la-http.Plo \
+ netmgr/$(DEPDIR)/libisc_la-netmgr.Plo \
+ netmgr/$(DEPDIR)/libisc_la-tcp.Plo \
+ netmgr/$(DEPDIR)/libisc_la-tcpdns.Plo \
+ netmgr/$(DEPDIR)/libisc_la-timer.Plo \
+ netmgr/$(DEPDIR)/libisc_la-tlsdns.Plo \
+ netmgr/$(DEPDIR)/libisc_la-tlsstream.Plo \
+ netmgr/$(DEPDIR)/libisc_la-udp.Plo \
+ netmgr/$(DEPDIR)/libisc_la-uv-compat.Plo \
+ netmgr/$(DEPDIR)/libisc_la-uverr2result.Plo
+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 = $(libisc_la_SOURCES)
+DIST_SOURCES = $(am__libisc_la_SOURCES_DIST)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(libisc_la_HEADERS)
+am__extra_recursive_targets = test-recursive unit-recursive \
+ doc-recursive
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/Makefile.top \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CURL = @CURL@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DEVELOPER_MODE = @DEVELOPER_MODE@
+DLLTOOL = @DLLTOOL@
+DNSTAP_CFLAGS = @DNSTAP_CFLAGS@
+DNSTAP_LIBS = @DNSTAP_LIBS@
+DOXYGEN = @DOXYGEN@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILECMD = @FILECMD@
+FSTRM_CAPTURE = @FSTRM_CAPTURE@
+FUZZ_LDFLAGS = @FUZZ_LDFLAGS@
+FUZZ_LOG_COMPILER = @FUZZ_LOG_COMPILER@
+GREP = @GREP@
+GSSAPI_CFLAGS = @GSSAPI_CFLAGS@
+GSSAPI_LIBS = @GSSAPI_LIBS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@
+JEMALLOC_LIBS = @JEMALLOC_LIBS@
+JSON_C_CFLAGS = @JSON_C_CFLAGS@
+JSON_C_LIBS = @JSON_C_LIBS@
+KRB5_CFLAGS = @KRB5_CFLAGS@
+KRB5_CONFIG = @KRB5_CONFIG@
+KRB5_LIBS = @KRB5_LIBS@
+LATEXMK = @LATEXMK@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBCAP_LIBS = @LIBCAP_LIBS@
+LIBIDN2_CFLAGS = @LIBIDN2_CFLAGS@
+LIBIDN2_LIBS = @LIBIDN2_LIBS@
+LIBNGHTTP2_CFLAGS = @LIBNGHTTP2_CFLAGS@
+LIBNGHTTP2_LIBS = @LIBNGHTTP2_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUV_CFLAGS = @LIBUV_CFLAGS@
+LIBUV_LIBS = @LIBUV_LIBS@
+LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
+LIBXML2_LIBS = @LIBXML2_LIBS@
+LIPO = @LIPO@
+LMDB_CFLAGS = @LMDB_CFLAGS@
+LMDB_LIBS = @LMDB_LIBS@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MAXMINDDB_CFLAGS = @MAXMINDDB_CFLAGS@
+MAXMINDDB_LIBS = @MAXMINDDB_LIBS@
+MAXMINDDB_PREFIX = @MAXMINDDB_PREFIX@
+MKDIR_P = @MKDIR_P@
+NC = @NC@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENSSL_CFLAGS = @OPENSSL_CFLAGS@
+OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@
+OPENSSL_LIBS = @OPENSSL_LIBS@
+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@
+PERL = @PERL@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PROTOC_C = @PROTOC_C@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_CXX = @PTHREAD_CXX@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+PYTEST = @PYTEST@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+READLINE_CFLAGS = @READLINE_CFLAGS@
+READLINE_LIBS = @READLINE_LIBS@
+RELEASE_DATE = @RELEASE_DATE@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINX_BUILD = @SPHINX_BUILD@
+STD_CFLAGS = @STD_CFLAGS@
+STD_CPPFLAGS = @STD_CPPFLAGS@
+STD_LDFLAGS = @STD_LDFLAGS@
+STRIP = @STRIP@
+TEST_CFLAGS = @TEST_CFLAGS@
+VERSION = @VERSION@
+XELATEX = @XELATEX@
+XSLTPROC = @XSLTPROC@
+ZLIB_CFLAGS = @ZLIB_CFLAGS@
+ZLIB_LIBS = @ZLIB_LIBS@
+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_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+ax_pthread_config = @ax_pthread_config@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+ACLOCAL_AMFLAGS = -I $(top_srcdir)/m4
+AM_CFLAGS = \
+ $(STD_CFLAGS)
+
+AM_CPPFLAGS = \
+ $(STD_CPPFLAGS) \
+ -include $(top_builddir)/config.h \
+ -I$(srcdir)/include
+
+AM_LDFLAGS = $(STD_LDFLAGS) $(am__append_1)
+LDADD =
+LIBISC_CFLAGS = \
+ -I$(top_srcdir)/include \
+ -I$(top_srcdir)/lib/isc/include \
+ -I$(top_builddir)/lib/isc/include
+
+LIBISC_LIBS = $(top_builddir)/lib/isc/libisc.la
+LIBDNS_CFLAGS = \
+ -I$(top_srcdir)/lib/dns/include \
+ -I$(top_builddir)/lib/dns/include
+
+LIBDNS_LIBS = \
+ $(top_builddir)/lib/dns/libdns.la
+
+LIBNS_CFLAGS = \
+ -I$(top_srcdir)/lib/ns/include
+
+LIBNS_LIBS = \
+ $(top_builddir)/lib/ns/libns.la
+
+LIBIRS_CFLAGS = \
+ -I$(top_srcdir)/lib/irs/include
+
+LIBIRS_LIBS = \
+ $(top_builddir)/lib/irs/libirs.la
+
+LIBISCCFG_CFLAGS = \
+ -I$(top_srcdir)/lib/isccfg/include
+
+LIBISCCFG_LIBS = \
+ $(top_builddir)/lib/isccfg/libisccfg.la
+
+LIBISCCC_CFLAGS = \
+ -I$(top_srcdir)/lib/isccc/include/
+
+LIBISCCC_LIBS = \
+ $(top_builddir)/lib/isccc/libisccc.la
+
+LIBBIND9_CFLAGS = \
+ -I$(top_srcdir)/lib/bind9/include
+
+LIBBIND9_LIBS = \
+ $(top_builddir)/lib/bind9/libbind9.la
+
+lib_LTLIBRARIES = libisc.la
+libisc_ladir = $(includedir)/isc
+libisc_la_HEADERS = \
+ include/isc/aes.h \
+ include/isc/align.h \
+ include/isc/app.h \
+ include/isc/assertions.h \
+ include/isc/astack.h \
+ include/isc/atomic.h \
+ include/isc/attributes.h \
+ include/isc/backtrace.h \
+ include/isc/barrier.h \
+ include/isc/base32.h \
+ include/isc/base64.h \
+ include/isc/buffer.h \
+ include/isc/cmocka.h \
+ include/isc/commandline.h \
+ include/isc/condition.h \
+ include/isc/counter.h \
+ include/isc/crc64.h \
+ include/isc/deprecated.h \
+ include/isc/dir.h \
+ include/isc/endian.h \
+ include/isc/errno.h \
+ include/isc/error.h \
+ include/isc/event.h \
+ include/isc/eventclass.h \
+ include/isc/file.h \
+ include/isc/formatcheck.h \
+ include/isc/fuzz.h \
+ include/isc/glob.h \
+ include/isc/hash.h \
+ include/isc/heap.h \
+ include/isc/hex.h \
+ include/isc/hmac.h \
+ include/isc/ht.h \
+ include/isc/httpd.h \
+ include/isc/interfaceiter.h \
+ include/isc/iterated_hash.h \
+ include/isc/lang.h \
+ include/isc/lex.h \
+ include/isc/list.h \
+ include/isc/log.h \
+ include/isc/magic.h \
+ include/isc/managers.h \
+ include/isc/md.h \
+ include/isc/mem.h \
+ include/isc/meminfo.h \
+ include/isc/mutex.h \
+ include/isc/mutexblock.h \
+ include/isc/net.h \
+ include/isc/netaddr.h \
+ include/isc/netdb.h \
+ include/isc/netmgr.h \
+ include/isc/netscope.h \
+ include/isc/nonce.h \
+ include/isc/offset.h \
+ include/isc/once.h \
+ include/isc/os.h \
+ include/isc/parseint.h \
+ include/isc/pool.h \
+ include/isc/portset.h \
+ include/isc/print.h \
+ include/isc/quota.h \
+ include/isc/radix.h \
+ include/isc/random.h \
+ include/isc/ratelimiter.h \
+ include/isc/refcount.h \
+ include/isc/regex.h \
+ include/isc/region.h \
+ include/isc/resource.h \
+ include/isc/result.h \
+ include/isc/rwlock.h \
+ include/isc/safe.h \
+ include/isc/serial.h \
+ include/isc/siphash.h \
+ include/isc/sockaddr.h \
+ include/isc/stat.h \
+ include/isc/stats.h \
+ include/isc/stdatomic.h \
+ include/isc/stdio.h \
+ include/isc/stdtime.h \
+ include/isc/strerr.h \
+ include/isc/string.h \
+ include/isc/symtab.h \
+ include/isc/syslog.h \
+ include/isc/task.h \
+ include/isc/taskpool.h \
+ include/isc/thread.h \
+ include/isc/time.h \
+ include/isc/timer.h \
+ include/isc/tls.h \
+ include/isc/tm.h \
+ include/isc/types.h \
+ include/isc/url.h \
+ include/isc/utf8.h \
+ include/isc/util.h
+
+libisc_la_SOURCES = $(libisc_la_HEADERS) netmgr/netmgr-int.h \
+ netmgr/netmgr.c netmgr/tcp.c netmgr/tcpdns.c netmgr/timer.c \
+ netmgr/tlsdns.c netmgr/udp.c netmgr/uv-compat.c \
+ netmgr/uv-compat.h netmgr/uverr2result.c aes.c app.c \
+ assertions.c astack.c backtrace.c base32.c base64.c buffer.c \
+ commandline.c condition.c counter.c crc64.c dir.c entropy.c \
+ entropy_private.h errno.c errno2result.c errno2result.h \
+ error.c event.c file.c glob.c hash.c heap.c hex.c hmac.c ht.c \
+ httpd.c interfaceiter.c iterated_hash.c jemalloc_shim.h lex.c \
+ lib.c log.c managers.c md.c mem.c mem_p.h meminfo.c mutex.c \
+ mutexblock.c net.c netaddr.c netmgr_p.h netscope.c nonce.c \
+ openssl_shim.c openssl_shim.h os.c os_p.h parseint.c pool.c \
+ picohttpparser.c picohttpparser.h portset.c quota.c radix.c \
+ random.c ratelimiter.c regex.c region.c resource.c result.c \
+ rwlock.c safe.c serial.c siphash.c sockaddr.c stats.c stdio.c \
+ stdtime.c string.c symtab.c syslog.c task.c task_p.h \
+ taskpool.c thread.c time.c timer.c timer_p.h tls.c tls_p.h \
+ tm.c trampoline.c trampoline_p.h url.c utf8.c $(am__append_6)
+libisc_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBISC_CFLAGS) $(LIBUV_CFLAGS) \
+ $(OPENSSL_CFLAGS) $(ZLIB_CFLAGS) $(am__append_2) \
+ $(am__append_4) $(am__append_7) $(am__append_9)
+libisc_la_LDFLAGS = \
+ $(AM_LDFLAGS) \
+ -release "$(PACKAGE_VERSION)"
+
+libisc_la_LIBADD = $(LIBUV_LIBS) $(OPENSSL_LIBS) $(ZLIB_LIBS) \
+ $(am__append_3) $(am__append_5) $(am__append_8) \
+ $(am__append_10)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.top $(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 lib/isc/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign lib/isc/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_srcdir)/Makefile.top $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+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}; \
+ }
+netmgr/$(am__dirstamp):
+ @$(MKDIR_P) netmgr
+ @: > netmgr/$(am__dirstamp)
+netmgr/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) netmgr/$(DEPDIR)
+ @: > netmgr/$(DEPDIR)/$(am__dirstamp)
+netmgr/libisc_la-netmgr.lo: netmgr/$(am__dirstamp) \
+ netmgr/$(DEPDIR)/$(am__dirstamp)
+netmgr/libisc_la-tcp.lo: netmgr/$(am__dirstamp) \
+ netmgr/$(DEPDIR)/$(am__dirstamp)
+netmgr/libisc_la-tcpdns.lo: netmgr/$(am__dirstamp) \
+ netmgr/$(DEPDIR)/$(am__dirstamp)
+netmgr/libisc_la-timer.lo: netmgr/$(am__dirstamp) \
+ netmgr/$(DEPDIR)/$(am__dirstamp)
+netmgr/libisc_la-tlsdns.lo: netmgr/$(am__dirstamp) \
+ netmgr/$(DEPDIR)/$(am__dirstamp)
+netmgr/libisc_la-udp.lo: netmgr/$(am__dirstamp) \
+ netmgr/$(DEPDIR)/$(am__dirstamp)
+netmgr/libisc_la-uv-compat.lo: netmgr/$(am__dirstamp) \
+ netmgr/$(DEPDIR)/$(am__dirstamp)
+netmgr/libisc_la-uverr2result.lo: netmgr/$(am__dirstamp) \
+ netmgr/$(DEPDIR)/$(am__dirstamp)
+netmgr/libisc_la-http.lo: netmgr/$(am__dirstamp) \
+ netmgr/$(DEPDIR)/$(am__dirstamp)
+netmgr/libisc_la-tlsstream.lo: netmgr/$(am__dirstamp) \
+ netmgr/$(DEPDIR)/$(am__dirstamp)
+
+libisc.la: $(libisc_la_OBJECTS) $(libisc_la_DEPENDENCIES) $(EXTRA_libisc_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libisc_la_LINK) -rpath $(libdir) $(libisc_la_OBJECTS) $(libisc_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+ -rm -f netmgr/*.$(OBJEXT)
+ -rm -f netmgr/*.lo
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-aes.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-app.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-assertions.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-astack.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-backtrace.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-base32.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-base64.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-buffer.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-commandline.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-condition.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-counter.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-crc64.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-dir.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-entropy.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-errno.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-errno2result.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-error.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-event.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-file.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-glob.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-hash.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-heap.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-hex.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-hmac.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-ht.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-httpd.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-interfaceiter.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-iterated_hash.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-lex.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-lib.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-log.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-managers.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-md.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-mem.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-meminfo.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-mutex.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-mutexblock.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-net.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-netaddr.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-netscope.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-nonce.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-openssl_shim.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-os.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-parseint.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-picohttpparser.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-pool.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-portset.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-quota.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-radix.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-random.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-ratelimiter.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-regex.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-region.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-resource.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-result.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-rwlock.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-safe.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-serial.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-siphash.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-sockaddr.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-stats.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-stdio.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-stdtime.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-string.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-symtab.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-syslog.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-task.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-taskpool.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-thread.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-time.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-timer.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-tls.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-tm.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-trampoline.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-url.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisc_la-utf8.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@netmgr/$(DEPDIR)/libisc_la-http.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@netmgr/$(DEPDIR)/libisc_la-netmgr.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@netmgr/$(DEPDIR)/libisc_la-tcp.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@netmgr/$(DEPDIR)/libisc_la-tcpdns.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@netmgr/$(DEPDIR)/libisc_la-timer.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@netmgr/$(DEPDIR)/libisc_la-tlsdns.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@netmgr/$(DEPDIR)/libisc_la-tlsstream.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@netmgr/$(DEPDIR)/libisc_la-udp.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@netmgr/$(DEPDIR)/libisc_la-uv-compat.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@netmgr/$(DEPDIR)/libisc_la-uverr2result.Plo@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 $@ $<
+
+netmgr/libisc_la-netmgr.lo: netmgr/netmgr.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT netmgr/libisc_la-netmgr.lo -MD -MP -MF netmgr/$(DEPDIR)/libisc_la-netmgr.Tpo -c -o netmgr/libisc_la-netmgr.lo `test -f 'netmgr/netmgr.c' || echo '$(srcdir)/'`netmgr/netmgr.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) netmgr/$(DEPDIR)/libisc_la-netmgr.Tpo netmgr/$(DEPDIR)/libisc_la-netmgr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='netmgr/netmgr.c' object='netmgr/libisc_la-netmgr.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o netmgr/libisc_la-netmgr.lo `test -f 'netmgr/netmgr.c' || echo '$(srcdir)/'`netmgr/netmgr.c
+
+netmgr/libisc_la-tcp.lo: netmgr/tcp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT netmgr/libisc_la-tcp.lo -MD -MP -MF netmgr/$(DEPDIR)/libisc_la-tcp.Tpo -c -o netmgr/libisc_la-tcp.lo `test -f 'netmgr/tcp.c' || echo '$(srcdir)/'`netmgr/tcp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) netmgr/$(DEPDIR)/libisc_la-tcp.Tpo netmgr/$(DEPDIR)/libisc_la-tcp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='netmgr/tcp.c' object='netmgr/libisc_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) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o netmgr/libisc_la-tcp.lo `test -f 'netmgr/tcp.c' || echo '$(srcdir)/'`netmgr/tcp.c
+
+netmgr/libisc_la-tcpdns.lo: netmgr/tcpdns.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT netmgr/libisc_la-tcpdns.lo -MD -MP -MF netmgr/$(DEPDIR)/libisc_la-tcpdns.Tpo -c -o netmgr/libisc_la-tcpdns.lo `test -f 'netmgr/tcpdns.c' || echo '$(srcdir)/'`netmgr/tcpdns.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) netmgr/$(DEPDIR)/libisc_la-tcpdns.Tpo netmgr/$(DEPDIR)/libisc_la-tcpdns.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='netmgr/tcpdns.c' object='netmgr/libisc_la-tcpdns.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o netmgr/libisc_la-tcpdns.lo `test -f 'netmgr/tcpdns.c' || echo '$(srcdir)/'`netmgr/tcpdns.c
+
+netmgr/libisc_la-timer.lo: netmgr/timer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT netmgr/libisc_la-timer.lo -MD -MP -MF netmgr/$(DEPDIR)/libisc_la-timer.Tpo -c -o netmgr/libisc_la-timer.lo `test -f 'netmgr/timer.c' || echo '$(srcdir)/'`netmgr/timer.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) netmgr/$(DEPDIR)/libisc_la-timer.Tpo netmgr/$(DEPDIR)/libisc_la-timer.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='netmgr/timer.c' object='netmgr/libisc_la-timer.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o netmgr/libisc_la-timer.lo `test -f 'netmgr/timer.c' || echo '$(srcdir)/'`netmgr/timer.c
+
+netmgr/libisc_la-tlsdns.lo: netmgr/tlsdns.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT netmgr/libisc_la-tlsdns.lo -MD -MP -MF netmgr/$(DEPDIR)/libisc_la-tlsdns.Tpo -c -o netmgr/libisc_la-tlsdns.lo `test -f 'netmgr/tlsdns.c' || echo '$(srcdir)/'`netmgr/tlsdns.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) netmgr/$(DEPDIR)/libisc_la-tlsdns.Tpo netmgr/$(DEPDIR)/libisc_la-tlsdns.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='netmgr/tlsdns.c' object='netmgr/libisc_la-tlsdns.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o netmgr/libisc_la-tlsdns.lo `test -f 'netmgr/tlsdns.c' || echo '$(srcdir)/'`netmgr/tlsdns.c
+
+netmgr/libisc_la-udp.lo: netmgr/udp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT netmgr/libisc_la-udp.lo -MD -MP -MF netmgr/$(DEPDIR)/libisc_la-udp.Tpo -c -o netmgr/libisc_la-udp.lo `test -f 'netmgr/udp.c' || echo '$(srcdir)/'`netmgr/udp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) netmgr/$(DEPDIR)/libisc_la-udp.Tpo netmgr/$(DEPDIR)/libisc_la-udp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='netmgr/udp.c' object='netmgr/libisc_la-udp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o netmgr/libisc_la-udp.lo `test -f 'netmgr/udp.c' || echo '$(srcdir)/'`netmgr/udp.c
+
+netmgr/libisc_la-uv-compat.lo: netmgr/uv-compat.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT netmgr/libisc_la-uv-compat.lo -MD -MP -MF netmgr/$(DEPDIR)/libisc_la-uv-compat.Tpo -c -o netmgr/libisc_la-uv-compat.lo `test -f 'netmgr/uv-compat.c' || echo '$(srcdir)/'`netmgr/uv-compat.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) netmgr/$(DEPDIR)/libisc_la-uv-compat.Tpo netmgr/$(DEPDIR)/libisc_la-uv-compat.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='netmgr/uv-compat.c' object='netmgr/libisc_la-uv-compat.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o netmgr/libisc_la-uv-compat.lo `test -f 'netmgr/uv-compat.c' || echo '$(srcdir)/'`netmgr/uv-compat.c
+
+netmgr/libisc_la-uverr2result.lo: netmgr/uverr2result.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT netmgr/libisc_la-uverr2result.lo -MD -MP -MF netmgr/$(DEPDIR)/libisc_la-uverr2result.Tpo -c -o netmgr/libisc_la-uverr2result.lo `test -f 'netmgr/uverr2result.c' || echo '$(srcdir)/'`netmgr/uverr2result.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) netmgr/$(DEPDIR)/libisc_la-uverr2result.Tpo netmgr/$(DEPDIR)/libisc_la-uverr2result.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='netmgr/uverr2result.c' object='netmgr/libisc_la-uverr2result.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o netmgr/libisc_la-uverr2result.lo `test -f 'netmgr/uverr2result.c' || echo '$(srcdir)/'`netmgr/uverr2result.c
+
+libisc_la-aes.lo: aes.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-aes.lo -MD -MP -MF $(DEPDIR)/libisc_la-aes.Tpo -c -o libisc_la-aes.lo `test -f 'aes.c' || echo '$(srcdir)/'`aes.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-aes.Tpo $(DEPDIR)/libisc_la-aes.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='aes.c' object='libisc_la-aes.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-aes.lo `test -f 'aes.c' || echo '$(srcdir)/'`aes.c
+
+libisc_la-app.lo: app.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-app.lo -MD -MP -MF $(DEPDIR)/libisc_la-app.Tpo -c -o libisc_la-app.lo `test -f 'app.c' || echo '$(srcdir)/'`app.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-app.Tpo $(DEPDIR)/libisc_la-app.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='app.c' object='libisc_la-app.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-app.lo `test -f 'app.c' || echo '$(srcdir)/'`app.c
+
+libisc_la-assertions.lo: assertions.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-assertions.lo -MD -MP -MF $(DEPDIR)/libisc_la-assertions.Tpo -c -o libisc_la-assertions.lo `test -f 'assertions.c' || echo '$(srcdir)/'`assertions.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-assertions.Tpo $(DEPDIR)/libisc_la-assertions.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='assertions.c' object='libisc_la-assertions.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-assertions.lo `test -f 'assertions.c' || echo '$(srcdir)/'`assertions.c
+
+libisc_la-astack.lo: astack.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-astack.lo -MD -MP -MF $(DEPDIR)/libisc_la-astack.Tpo -c -o libisc_la-astack.lo `test -f 'astack.c' || echo '$(srcdir)/'`astack.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-astack.Tpo $(DEPDIR)/libisc_la-astack.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='astack.c' object='libisc_la-astack.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-astack.lo `test -f 'astack.c' || echo '$(srcdir)/'`astack.c
+
+libisc_la-backtrace.lo: backtrace.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-backtrace.lo -MD -MP -MF $(DEPDIR)/libisc_la-backtrace.Tpo -c -o libisc_la-backtrace.lo `test -f 'backtrace.c' || echo '$(srcdir)/'`backtrace.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-backtrace.Tpo $(DEPDIR)/libisc_la-backtrace.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='backtrace.c' object='libisc_la-backtrace.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-backtrace.lo `test -f 'backtrace.c' || echo '$(srcdir)/'`backtrace.c
+
+libisc_la-base32.lo: base32.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-base32.lo -MD -MP -MF $(DEPDIR)/libisc_la-base32.Tpo -c -o libisc_la-base32.lo `test -f 'base32.c' || echo '$(srcdir)/'`base32.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-base32.Tpo $(DEPDIR)/libisc_la-base32.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='base32.c' object='libisc_la-base32.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-base32.lo `test -f 'base32.c' || echo '$(srcdir)/'`base32.c
+
+libisc_la-base64.lo: base64.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-base64.lo -MD -MP -MF $(DEPDIR)/libisc_la-base64.Tpo -c -o libisc_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-base64.Tpo $(DEPDIR)/libisc_la-base64.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='base64.c' object='libisc_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) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c
+
+libisc_la-buffer.lo: buffer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-buffer.lo -MD -MP -MF $(DEPDIR)/libisc_la-buffer.Tpo -c -o libisc_la-buffer.lo `test -f 'buffer.c' || echo '$(srcdir)/'`buffer.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-buffer.Tpo $(DEPDIR)/libisc_la-buffer.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='buffer.c' object='libisc_la-buffer.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-buffer.lo `test -f 'buffer.c' || echo '$(srcdir)/'`buffer.c
+
+libisc_la-commandline.lo: commandline.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-commandline.lo -MD -MP -MF $(DEPDIR)/libisc_la-commandline.Tpo -c -o libisc_la-commandline.lo `test -f 'commandline.c' || echo '$(srcdir)/'`commandline.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-commandline.Tpo $(DEPDIR)/libisc_la-commandline.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='commandline.c' object='libisc_la-commandline.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-commandline.lo `test -f 'commandline.c' || echo '$(srcdir)/'`commandline.c
+
+libisc_la-condition.lo: condition.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-condition.lo -MD -MP -MF $(DEPDIR)/libisc_la-condition.Tpo -c -o libisc_la-condition.lo `test -f 'condition.c' || echo '$(srcdir)/'`condition.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-condition.Tpo $(DEPDIR)/libisc_la-condition.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='condition.c' object='libisc_la-condition.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-condition.lo `test -f 'condition.c' || echo '$(srcdir)/'`condition.c
+
+libisc_la-counter.lo: counter.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-counter.lo -MD -MP -MF $(DEPDIR)/libisc_la-counter.Tpo -c -o libisc_la-counter.lo `test -f 'counter.c' || echo '$(srcdir)/'`counter.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-counter.Tpo $(DEPDIR)/libisc_la-counter.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='counter.c' object='libisc_la-counter.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-counter.lo `test -f 'counter.c' || echo '$(srcdir)/'`counter.c
+
+libisc_la-crc64.lo: crc64.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-crc64.lo -MD -MP -MF $(DEPDIR)/libisc_la-crc64.Tpo -c -o libisc_la-crc64.lo `test -f 'crc64.c' || echo '$(srcdir)/'`crc64.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-crc64.Tpo $(DEPDIR)/libisc_la-crc64.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crc64.c' object='libisc_la-crc64.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-crc64.lo `test -f 'crc64.c' || echo '$(srcdir)/'`crc64.c
+
+libisc_la-dir.lo: dir.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-dir.lo -MD -MP -MF $(DEPDIR)/libisc_la-dir.Tpo -c -o libisc_la-dir.lo `test -f 'dir.c' || echo '$(srcdir)/'`dir.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-dir.Tpo $(DEPDIR)/libisc_la-dir.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dir.c' object='libisc_la-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) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-dir.lo `test -f 'dir.c' || echo '$(srcdir)/'`dir.c
+
+libisc_la-entropy.lo: entropy.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-entropy.lo -MD -MP -MF $(DEPDIR)/libisc_la-entropy.Tpo -c -o libisc_la-entropy.lo `test -f 'entropy.c' || echo '$(srcdir)/'`entropy.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-entropy.Tpo $(DEPDIR)/libisc_la-entropy.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='entropy.c' object='libisc_la-entropy.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-entropy.lo `test -f 'entropy.c' || echo '$(srcdir)/'`entropy.c
+
+libisc_la-errno.lo: errno.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-errno.lo -MD -MP -MF $(DEPDIR)/libisc_la-errno.Tpo -c -o libisc_la-errno.lo `test -f 'errno.c' || echo '$(srcdir)/'`errno.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-errno.Tpo $(DEPDIR)/libisc_la-errno.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='errno.c' object='libisc_la-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) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-errno.lo `test -f 'errno.c' || echo '$(srcdir)/'`errno.c
+
+libisc_la-errno2result.lo: errno2result.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-errno2result.lo -MD -MP -MF $(DEPDIR)/libisc_la-errno2result.Tpo -c -o libisc_la-errno2result.lo `test -f 'errno2result.c' || echo '$(srcdir)/'`errno2result.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-errno2result.Tpo $(DEPDIR)/libisc_la-errno2result.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='errno2result.c' object='libisc_la-errno2result.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-errno2result.lo `test -f 'errno2result.c' || echo '$(srcdir)/'`errno2result.c
+
+libisc_la-error.lo: error.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-error.lo -MD -MP -MF $(DEPDIR)/libisc_la-error.Tpo -c -o libisc_la-error.lo `test -f 'error.c' || echo '$(srcdir)/'`error.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-error.Tpo $(DEPDIR)/libisc_la-error.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='error.c' object='libisc_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) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-error.lo `test -f 'error.c' || echo '$(srcdir)/'`error.c
+
+libisc_la-event.lo: event.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-event.lo -MD -MP -MF $(DEPDIR)/libisc_la-event.Tpo -c -o libisc_la-event.lo `test -f 'event.c' || echo '$(srcdir)/'`event.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-event.Tpo $(DEPDIR)/libisc_la-event.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='event.c' object='libisc_la-event.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-event.lo `test -f 'event.c' || echo '$(srcdir)/'`event.c
+
+libisc_la-file.lo: file.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-file.lo -MD -MP -MF $(DEPDIR)/libisc_la-file.Tpo -c -o libisc_la-file.lo `test -f 'file.c' || echo '$(srcdir)/'`file.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-file.Tpo $(DEPDIR)/libisc_la-file.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file.c' object='libisc_la-file.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-file.lo `test -f 'file.c' || echo '$(srcdir)/'`file.c
+
+libisc_la-glob.lo: glob.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-glob.lo -MD -MP -MF $(DEPDIR)/libisc_la-glob.Tpo -c -o libisc_la-glob.lo `test -f 'glob.c' || echo '$(srcdir)/'`glob.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-glob.Tpo $(DEPDIR)/libisc_la-glob.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='glob.c' object='libisc_la-glob.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-glob.lo `test -f 'glob.c' || echo '$(srcdir)/'`glob.c
+
+libisc_la-hash.lo: hash.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-hash.lo -MD -MP -MF $(DEPDIR)/libisc_la-hash.Tpo -c -o libisc_la-hash.lo `test -f 'hash.c' || echo '$(srcdir)/'`hash.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-hash.Tpo $(DEPDIR)/libisc_la-hash.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hash.c' object='libisc_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) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-hash.lo `test -f 'hash.c' || echo '$(srcdir)/'`hash.c
+
+libisc_la-heap.lo: heap.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-heap.lo -MD -MP -MF $(DEPDIR)/libisc_la-heap.Tpo -c -o libisc_la-heap.lo `test -f 'heap.c' || echo '$(srcdir)/'`heap.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-heap.Tpo $(DEPDIR)/libisc_la-heap.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='heap.c' object='libisc_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) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-heap.lo `test -f 'heap.c' || echo '$(srcdir)/'`heap.c
+
+libisc_la-hex.lo: hex.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-hex.lo -MD -MP -MF $(DEPDIR)/libisc_la-hex.Tpo -c -o libisc_la-hex.lo `test -f 'hex.c' || echo '$(srcdir)/'`hex.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-hex.Tpo $(DEPDIR)/libisc_la-hex.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hex.c' object='libisc_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) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-hex.lo `test -f 'hex.c' || echo '$(srcdir)/'`hex.c
+
+libisc_la-hmac.lo: hmac.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-hmac.lo -MD -MP -MF $(DEPDIR)/libisc_la-hmac.Tpo -c -o libisc_la-hmac.lo `test -f 'hmac.c' || echo '$(srcdir)/'`hmac.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-hmac.Tpo $(DEPDIR)/libisc_la-hmac.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hmac.c' object='libisc_la-hmac.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-hmac.lo `test -f 'hmac.c' || echo '$(srcdir)/'`hmac.c
+
+libisc_la-ht.lo: ht.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-ht.lo -MD -MP -MF $(DEPDIR)/libisc_la-ht.Tpo -c -o libisc_la-ht.lo `test -f 'ht.c' || echo '$(srcdir)/'`ht.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-ht.Tpo $(DEPDIR)/libisc_la-ht.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ht.c' object='libisc_la-ht.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-ht.lo `test -f 'ht.c' || echo '$(srcdir)/'`ht.c
+
+libisc_la-httpd.lo: httpd.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-httpd.lo -MD -MP -MF $(DEPDIR)/libisc_la-httpd.Tpo -c -o libisc_la-httpd.lo `test -f 'httpd.c' || echo '$(srcdir)/'`httpd.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-httpd.Tpo $(DEPDIR)/libisc_la-httpd.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='httpd.c' object='libisc_la-httpd.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-httpd.lo `test -f 'httpd.c' || echo '$(srcdir)/'`httpd.c
+
+libisc_la-interfaceiter.lo: interfaceiter.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-interfaceiter.lo -MD -MP -MF $(DEPDIR)/libisc_la-interfaceiter.Tpo -c -o libisc_la-interfaceiter.lo `test -f 'interfaceiter.c' || echo '$(srcdir)/'`interfaceiter.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-interfaceiter.Tpo $(DEPDIR)/libisc_la-interfaceiter.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='interfaceiter.c' object='libisc_la-interfaceiter.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-interfaceiter.lo `test -f 'interfaceiter.c' || echo '$(srcdir)/'`interfaceiter.c
+
+libisc_la-iterated_hash.lo: iterated_hash.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-iterated_hash.lo -MD -MP -MF $(DEPDIR)/libisc_la-iterated_hash.Tpo -c -o libisc_la-iterated_hash.lo `test -f 'iterated_hash.c' || echo '$(srcdir)/'`iterated_hash.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-iterated_hash.Tpo $(DEPDIR)/libisc_la-iterated_hash.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iterated_hash.c' object='libisc_la-iterated_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) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-iterated_hash.lo `test -f 'iterated_hash.c' || echo '$(srcdir)/'`iterated_hash.c
+
+libisc_la-lex.lo: lex.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-lex.lo -MD -MP -MF $(DEPDIR)/libisc_la-lex.Tpo -c -o libisc_la-lex.lo `test -f 'lex.c' || echo '$(srcdir)/'`lex.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-lex.Tpo $(DEPDIR)/libisc_la-lex.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lex.c' object='libisc_la-lex.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-lex.lo `test -f 'lex.c' || echo '$(srcdir)/'`lex.c
+
+libisc_la-lib.lo: lib.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-lib.lo -MD -MP -MF $(DEPDIR)/libisc_la-lib.Tpo -c -o libisc_la-lib.lo `test -f 'lib.c' || echo '$(srcdir)/'`lib.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-lib.Tpo $(DEPDIR)/libisc_la-lib.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lib.c' object='libisc_la-lib.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-lib.lo `test -f 'lib.c' || echo '$(srcdir)/'`lib.c
+
+libisc_la-log.lo: log.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-log.lo -MD -MP -MF $(DEPDIR)/libisc_la-log.Tpo -c -o libisc_la-log.lo `test -f 'log.c' || echo '$(srcdir)/'`log.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-log.Tpo $(DEPDIR)/libisc_la-log.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='log.c' object='libisc_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) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-log.lo `test -f 'log.c' || echo '$(srcdir)/'`log.c
+
+libisc_la-managers.lo: managers.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-managers.lo -MD -MP -MF $(DEPDIR)/libisc_la-managers.Tpo -c -o libisc_la-managers.lo `test -f 'managers.c' || echo '$(srcdir)/'`managers.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-managers.Tpo $(DEPDIR)/libisc_la-managers.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='managers.c' object='libisc_la-managers.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-managers.lo `test -f 'managers.c' || echo '$(srcdir)/'`managers.c
+
+libisc_la-md.lo: md.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-md.lo -MD -MP -MF $(DEPDIR)/libisc_la-md.Tpo -c -o libisc_la-md.lo `test -f 'md.c' || echo '$(srcdir)/'`md.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-md.Tpo $(DEPDIR)/libisc_la-md.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='md.c' object='libisc_la-md.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-md.lo `test -f 'md.c' || echo '$(srcdir)/'`md.c
+
+libisc_la-mem.lo: mem.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-mem.lo -MD -MP -MF $(DEPDIR)/libisc_la-mem.Tpo -c -o libisc_la-mem.lo `test -f 'mem.c' || echo '$(srcdir)/'`mem.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-mem.Tpo $(DEPDIR)/libisc_la-mem.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mem.c' object='libisc_la-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) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-mem.lo `test -f 'mem.c' || echo '$(srcdir)/'`mem.c
+
+libisc_la-meminfo.lo: meminfo.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-meminfo.lo -MD -MP -MF $(DEPDIR)/libisc_la-meminfo.Tpo -c -o libisc_la-meminfo.lo `test -f 'meminfo.c' || echo '$(srcdir)/'`meminfo.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-meminfo.Tpo $(DEPDIR)/libisc_la-meminfo.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='meminfo.c' object='libisc_la-meminfo.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-meminfo.lo `test -f 'meminfo.c' || echo '$(srcdir)/'`meminfo.c
+
+libisc_la-mutex.lo: mutex.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-mutex.lo -MD -MP -MF $(DEPDIR)/libisc_la-mutex.Tpo -c -o libisc_la-mutex.lo `test -f 'mutex.c' || echo '$(srcdir)/'`mutex.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-mutex.Tpo $(DEPDIR)/libisc_la-mutex.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mutex.c' object='libisc_la-mutex.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-mutex.lo `test -f 'mutex.c' || echo '$(srcdir)/'`mutex.c
+
+libisc_la-mutexblock.lo: mutexblock.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-mutexblock.lo -MD -MP -MF $(DEPDIR)/libisc_la-mutexblock.Tpo -c -o libisc_la-mutexblock.lo `test -f 'mutexblock.c' || echo '$(srcdir)/'`mutexblock.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-mutexblock.Tpo $(DEPDIR)/libisc_la-mutexblock.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mutexblock.c' object='libisc_la-mutexblock.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-mutexblock.lo `test -f 'mutexblock.c' || echo '$(srcdir)/'`mutexblock.c
+
+libisc_la-net.lo: net.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-net.lo -MD -MP -MF $(DEPDIR)/libisc_la-net.Tpo -c -o libisc_la-net.lo `test -f 'net.c' || echo '$(srcdir)/'`net.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-net.Tpo $(DEPDIR)/libisc_la-net.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='net.c' object='libisc_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) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-net.lo `test -f 'net.c' || echo '$(srcdir)/'`net.c
+
+libisc_la-netaddr.lo: netaddr.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-netaddr.lo -MD -MP -MF $(DEPDIR)/libisc_la-netaddr.Tpo -c -o libisc_la-netaddr.lo `test -f 'netaddr.c' || echo '$(srcdir)/'`netaddr.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-netaddr.Tpo $(DEPDIR)/libisc_la-netaddr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='netaddr.c' object='libisc_la-netaddr.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-netaddr.lo `test -f 'netaddr.c' || echo '$(srcdir)/'`netaddr.c
+
+libisc_la-netscope.lo: netscope.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-netscope.lo -MD -MP -MF $(DEPDIR)/libisc_la-netscope.Tpo -c -o libisc_la-netscope.lo `test -f 'netscope.c' || echo '$(srcdir)/'`netscope.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-netscope.Tpo $(DEPDIR)/libisc_la-netscope.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='netscope.c' object='libisc_la-netscope.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-netscope.lo `test -f 'netscope.c' || echo '$(srcdir)/'`netscope.c
+
+libisc_la-nonce.lo: nonce.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-nonce.lo -MD -MP -MF $(DEPDIR)/libisc_la-nonce.Tpo -c -o libisc_la-nonce.lo `test -f 'nonce.c' || echo '$(srcdir)/'`nonce.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-nonce.Tpo $(DEPDIR)/libisc_la-nonce.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nonce.c' object='libisc_la-nonce.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-nonce.lo `test -f 'nonce.c' || echo '$(srcdir)/'`nonce.c
+
+libisc_la-openssl_shim.lo: openssl_shim.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-openssl_shim.lo -MD -MP -MF $(DEPDIR)/libisc_la-openssl_shim.Tpo -c -o libisc_la-openssl_shim.lo `test -f 'openssl_shim.c' || echo '$(srcdir)/'`openssl_shim.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-openssl_shim.Tpo $(DEPDIR)/libisc_la-openssl_shim.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='openssl_shim.c' object='libisc_la-openssl_shim.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-openssl_shim.lo `test -f 'openssl_shim.c' || echo '$(srcdir)/'`openssl_shim.c
+
+libisc_la-os.lo: os.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-os.lo -MD -MP -MF $(DEPDIR)/libisc_la-os.Tpo -c -o libisc_la-os.lo `test -f 'os.c' || echo '$(srcdir)/'`os.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-os.Tpo $(DEPDIR)/libisc_la-os.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='os.c' object='libisc_la-os.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-os.lo `test -f 'os.c' || echo '$(srcdir)/'`os.c
+
+libisc_la-parseint.lo: parseint.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-parseint.lo -MD -MP -MF $(DEPDIR)/libisc_la-parseint.Tpo -c -o libisc_la-parseint.lo `test -f 'parseint.c' || echo '$(srcdir)/'`parseint.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-parseint.Tpo $(DEPDIR)/libisc_la-parseint.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='parseint.c' object='libisc_la-parseint.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-parseint.lo `test -f 'parseint.c' || echo '$(srcdir)/'`parseint.c
+
+libisc_la-pool.lo: pool.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-pool.lo -MD -MP -MF $(DEPDIR)/libisc_la-pool.Tpo -c -o libisc_la-pool.lo `test -f 'pool.c' || echo '$(srcdir)/'`pool.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-pool.Tpo $(DEPDIR)/libisc_la-pool.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pool.c' object='libisc_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) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-pool.lo `test -f 'pool.c' || echo '$(srcdir)/'`pool.c
+
+libisc_la-picohttpparser.lo: picohttpparser.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-picohttpparser.lo -MD -MP -MF $(DEPDIR)/libisc_la-picohttpparser.Tpo -c -o libisc_la-picohttpparser.lo `test -f 'picohttpparser.c' || echo '$(srcdir)/'`picohttpparser.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-picohttpparser.Tpo $(DEPDIR)/libisc_la-picohttpparser.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='picohttpparser.c' object='libisc_la-picohttpparser.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-picohttpparser.lo `test -f 'picohttpparser.c' || echo '$(srcdir)/'`picohttpparser.c
+
+libisc_la-portset.lo: portset.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-portset.lo -MD -MP -MF $(DEPDIR)/libisc_la-portset.Tpo -c -o libisc_la-portset.lo `test -f 'portset.c' || echo '$(srcdir)/'`portset.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-portset.Tpo $(DEPDIR)/libisc_la-portset.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='portset.c' object='libisc_la-portset.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-portset.lo `test -f 'portset.c' || echo '$(srcdir)/'`portset.c
+
+libisc_la-quota.lo: quota.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-quota.lo -MD -MP -MF $(DEPDIR)/libisc_la-quota.Tpo -c -o libisc_la-quota.lo `test -f 'quota.c' || echo '$(srcdir)/'`quota.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-quota.Tpo $(DEPDIR)/libisc_la-quota.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='quota.c' object='libisc_la-quota.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-quota.lo `test -f 'quota.c' || echo '$(srcdir)/'`quota.c
+
+libisc_la-radix.lo: radix.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-radix.lo -MD -MP -MF $(DEPDIR)/libisc_la-radix.Tpo -c -o libisc_la-radix.lo `test -f 'radix.c' || echo '$(srcdir)/'`radix.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-radix.Tpo $(DEPDIR)/libisc_la-radix.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='radix.c' object='libisc_la-radix.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-radix.lo `test -f 'radix.c' || echo '$(srcdir)/'`radix.c
+
+libisc_la-random.lo: random.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-random.lo -MD -MP -MF $(DEPDIR)/libisc_la-random.Tpo -c -o libisc_la-random.lo `test -f 'random.c' || echo '$(srcdir)/'`random.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-random.Tpo $(DEPDIR)/libisc_la-random.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='random.c' object='libisc_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) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-random.lo `test -f 'random.c' || echo '$(srcdir)/'`random.c
+
+libisc_la-ratelimiter.lo: ratelimiter.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-ratelimiter.lo -MD -MP -MF $(DEPDIR)/libisc_la-ratelimiter.Tpo -c -o libisc_la-ratelimiter.lo `test -f 'ratelimiter.c' || echo '$(srcdir)/'`ratelimiter.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-ratelimiter.Tpo $(DEPDIR)/libisc_la-ratelimiter.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ratelimiter.c' object='libisc_la-ratelimiter.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-ratelimiter.lo `test -f 'ratelimiter.c' || echo '$(srcdir)/'`ratelimiter.c
+
+libisc_la-regex.lo: regex.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-regex.lo -MD -MP -MF $(DEPDIR)/libisc_la-regex.Tpo -c -o libisc_la-regex.lo `test -f 'regex.c' || echo '$(srcdir)/'`regex.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-regex.Tpo $(DEPDIR)/libisc_la-regex.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='regex.c' object='libisc_la-regex.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-regex.lo `test -f 'regex.c' || echo '$(srcdir)/'`regex.c
+
+libisc_la-region.lo: region.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-region.lo -MD -MP -MF $(DEPDIR)/libisc_la-region.Tpo -c -o libisc_la-region.lo `test -f 'region.c' || echo '$(srcdir)/'`region.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-region.Tpo $(DEPDIR)/libisc_la-region.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='region.c' object='libisc_la-region.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-region.lo `test -f 'region.c' || echo '$(srcdir)/'`region.c
+
+libisc_la-resource.lo: resource.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-resource.lo -MD -MP -MF $(DEPDIR)/libisc_la-resource.Tpo -c -o libisc_la-resource.lo `test -f 'resource.c' || echo '$(srcdir)/'`resource.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-resource.Tpo $(DEPDIR)/libisc_la-resource.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='resource.c' object='libisc_la-resource.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-resource.lo `test -f 'resource.c' || echo '$(srcdir)/'`resource.c
+
+libisc_la-result.lo: result.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-result.lo -MD -MP -MF $(DEPDIR)/libisc_la-result.Tpo -c -o libisc_la-result.lo `test -f 'result.c' || echo '$(srcdir)/'`result.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-result.Tpo $(DEPDIR)/libisc_la-result.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='result.c' object='libisc_la-result.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-result.lo `test -f 'result.c' || echo '$(srcdir)/'`result.c
+
+libisc_la-rwlock.lo: rwlock.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-rwlock.lo -MD -MP -MF $(DEPDIR)/libisc_la-rwlock.Tpo -c -o libisc_la-rwlock.lo `test -f 'rwlock.c' || echo '$(srcdir)/'`rwlock.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-rwlock.Tpo $(DEPDIR)/libisc_la-rwlock.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rwlock.c' object='libisc_la-rwlock.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-rwlock.lo `test -f 'rwlock.c' || echo '$(srcdir)/'`rwlock.c
+
+libisc_la-safe.lo: safe.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-safe.lo -MD -MP -MF $(DEPDIR)/libisc_la-safe.Tpo -c -o libisc_la-safe.lo `test -f 'safe.c' || echo '$(srcdir)/'`safe.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-safe.Tpo $(DEPDIR)/libisc_la-safe.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='safe.c' object='libisc_la-safe.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-safe.lo `test -f 'safe.c' || echo '$(srcdir)/'`safe.c
+
+libisc_la-serial.lo: serial.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-serial.lo -MD -MP -MF $(DEPDIR)/libisc_la-serial.Tpo -c -o libisc_la-serial.lo `test -f 'serial.c' || echo '$(srcdir)/'`serial.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-serial.Tpo $(DEPDIR)/libisc_la-serial.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='serial.c' object='libisc_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) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-serial.lo `test -f 'serial.c' || echo '$(srcdir)/'`serial.c
+
+libisc_la-siphash.lo: siphash.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-siphash.lo -MD -MP -MF $(DEPDIR)/libisc_la-siphash.Tpo -c -o libisc_la-siphash.lo `test -f 'siphash.c' || echo '$(srcdir)/'`siphash.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-siphash.Tpo $(DEPDIR)/libisc_la-siphash.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='siphash.c' object='libisc_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) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-siphash.lo `test -f 'siphash.c' || echo '$(srcdir)/'`siphash.c
+
+libisc_la-sockaddr.lo: sockaddr.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-sockaddr.lo -MD -MP -MF $(DEPDIR)/libisc_la-sockaddr.Tpo -c -o libisc_la-sockaddr.lo `test -f 'sockaddr.c' || echo '$(srcdir)/'`sockaddr.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-sockaddr.Tpo $(DEPDIR)/libisc_la-sockaddr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sockaddr.c' object='libisc_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) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-sockaddr.lo `test -f 'sockaddr.c' || echo '$(srcdir)/'`sockaddr.c
+
+libisc_la-stats.lo: stats.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-stats.lo -MD -MP -MF $(DEPDIR)/libisc_la-stats.Tpo -c -o libisc_la-stats.lo `test -f 'stats.c' || echo '$(srcdir)/'`stats.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-stats.Tpo $(DEPDIR)/libisc_la-stats.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='stats.c' object='libisc_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) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-stats.lo `test -f 'stats.c' || echo '$(srcdir)/'`stats.c
+
+libisc_la-stdio.lo: stdio.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-stdio.lo -MD -MP -MF $(DEPDIR)/libisc_la-stdio.Tpo -c -o libisc_la-stdio.lo `test -f 'stdio.c' || echo '$(srcdir)/'`stdio.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-stdio.Tpo $(DEPDIR)/libisc_la-stdio.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='stdio.c' object='libisc_la-stdio.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-stdio.lo `test -f 'stdio.c' || echo '$(srcdir)/'`stdio.c
+
+libisc_la-stdtime.lo: stdtime.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-stdtime.lo -MD -MP -MF $(DEPDIR)/libisc_la-stdtime.Tpo -c -o libisc_la-stdtime.lo `test -f 'stdtime.c' || echo '$(srcdir)/'`stdtime.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-stdtime.Tpo $(DEPDIR)/libisc_la-stdtime.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='stdtime.c' object='libisc_la-stdtime.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-stdtime.lo `test -f 'stdtime.c' || echo '$(srcdir)/'`stdtime.c
+
+libisc_la-string.lo: string.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-string.lo -MD -MP -MF $(DEPDIR)/libisc_la-string.Tpo -c -o libisc_la-string.lo `test -f 'string.c' || echo '$(srcdir)/'`string.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-string.Tpo $(DEPDIR)/libisc_la-string.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='string.c' object='libisc_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) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-string.lo `test -f 'string.c' || echo '$(srcdir)/'`string.c
+
+libisc_la-symtab.lo: symtab.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-symtab.lo -MD -MP -MF $(DEPDIR)/libisc_la-symtab.Tpo -c -o libisc_la-symtab.lo `test -f 'symtab.c' || echo '$(srcdir)/'`symtab.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-symtab.Tpo $(DEPDIR)/libisc_la-symtab.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='symtab.c' object='libisc_la-symtab.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-symtab.lo `test -f 'symtab.c' || echo '$(srcdir)/'`symtab.c
+
+libisc_la-syslog.lo: syslog.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-syslog.lo -MD -MP -MF $(DEPDIR)/libisc_la-syslog.Tpo -c -o libisc_la-syslog.lo `test -f 'syslog.c' || echo '$(srcdir)/'`syslog.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-syslog.Tpo $(DEPDIR)/libisc_la-syslog.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='syslog.c' object='libisc_la-syslog.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-syslog.lo `test -f 'syslog.c' || echo '$(srcdir)/'`syslog.c
+
+libisc_la-task.lo: task.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-task.lo -MD -MP -MF $(DEPDIR)/libisc_la-task.Tpo -c -o libisc_la-task.lo `test -f 'task.c' || echo '$(srcdir)/'`task.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-task.Tpo $(DEPDIR)/libisc_la-task.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='task.c' object='libisc_la-task.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-task.lo `test -f 'task.c' || echo '$(srcdir)/'`task.c
+
+libisc_la-taskpool.lo: taskpool.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-taskpool.lo -MD -MP -MF $(DEPDIR)/libisc_la-taskpool.Tpo -c -o libisc_la-taskpool.lo `test -f 'taskpool.c' || echo '$(srcdir)/'`taskpool.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-taskpool.Tpo $(DEPDIR)/libisc_la-taskpool.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='taskpool.c' object='libisc_la-taskpool.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-taskpool.lo `test -f 'taskpool.c' || echo '$(srcdir)/'`taskpool.c
+
+libisc_la-thread.lo: thread.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-thread.lo -MD -MP -MF $(DEPDIR)/libisc_la-thread.Tpo -c -o libisc_la-thread.lo `test -f 'thread.c' || echo '$(srcdir)/'`thread.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-thread.Tpo $(DEPDIR)/libisc_la-thread.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='thread.c' object='libisc_la-thread.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-thread.lo `test -f 'thread.c' || echo '$(srcdir)/'`thread.c
+
+libisc_la-time.lo: time.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-time.lo -MD -MP -MF $(DEPDIR)/libisc_la-time.Tpo -c -o libisc_la-time.lo `test -f 'time.c' || echo '$(srcdir)/'`time.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-time.Tpo $(DEPDIR)/libisc_la-time.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='time.c' object='libisc_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) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-time.lo `test -f 'time.c' || echo '$(srcdir)/'`time.c
+
+libisc_la-timer.lo: timer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-timer.lo -MD -MP -MF $(DEPDIR)/libisc_la-timer.Tpo -c -o libisc_la-timer.lo `test -f 'timer.c' || echo '$(srcdir)/'`timer.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-timer.Tpo $(DEPDIR)/libisc_la-timer.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='timer.c' object='libisc_la-timer.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-timer.lo `test -f 'timer.c' || echo '$(srcdir)/'`timer.c
+
+libisc_la-tls.lo: tls.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-tls.lo -MD -MP -MF $(DEPDIR)/libisc_la-tls.Tpo -c -o libisc_la-tls.lo `test -f 'tls.c' || echo '$(srcdir)/'`tls.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-tls.Tpo $(DEPDIR)/libisc_la-tls.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tls.c' object='libisc_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) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-tls.lo `test -f 'tls.c' || echo '$(srcdir)/'`tls.c
+
+libisc_la-tm.lo: tm.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-tm.lo -MD -MP -MF $(DEPDIR)/libisc_la-tm.Tpo -c -o libisc_la-tm.lo `test -f 'tm.c' || echo '$(srcdir)/'`tm.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-tm.Tpo $(DEPDIR)/libisc_la-tm.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tm.c' object='libisc_la-tm.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-tm.lo `test -f 'tm.c' || echo '$(srcdir)/'`tm.c
+
+libisc_la-trampoline.lo: trampoline.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-trampoline.lo -MD -MP -MF $(DEPDIR)/libisc_la-trampoline.Tpo -c -o libisc_la-trampoline.lo `test -f 'trampoline.c' || echo '$(srcdir)/'`trampoline.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-trampoline.Tpo $(DEPDIR)/libisc_la-trampoline.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='trampoline.c' object='libisc_la-trampoline.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-trampoline.lo `test -f 'trampoline.c' || echo '$(srcdir)/'`trampoline.c
+
+libisc_la-url.lo: url.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-url.lo -MD -MP -MF $(DEPDIR)/libisc_la-url.Tpo -c -o libisc_la-url.lo `test -f 'url.c' || echo '$(srcdir)/'`url.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-url.Tpo $(DEPDIR)/libisc_la-url.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='url.c' object='libisc_la-url.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-url.lo `test -f 'url.c' || echo '$(srcdir)/'`url.c
+
+libisc_la-utf8.lo: utf8.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisc_la-utf8.lo -MD -MP -MF $(DEPDIR)/libisc_la-utf8.Tpo -c -o libisc_la-utf8.lo `test -f 'utf8.c' || echo '$(srcdir)/'`utf8.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisc_la-utf8.Tpo $(DEPDIR)/libisc_la-utf8.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utf8.c' object='libisc_la-utf8.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisc_la-utf8.lo `test -f 'utf8.c' || echo '$(srcdir)/'`utf8.c
+
+netmgr/libisc_la-http.lo: netmgr/http.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT netmgr/libisc_la-http.lo -MD -MP -MF netmgr/$(DEPDIR)/libisc_la-http.Tpo -c -o netmgr/libisc_la-http.lo `test -f 'netmgr/http.c' || echo '$(srcdir)/'`netmgr/http.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) netmgr/$(DEPDIR)/libisc_la-http.Tpo netmgr/$(DEPDIR)/libisc_la-http.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='netmgr/http.c' object='netmgr/libisc_la-http.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o netmgr/libisc_la-http.lo `test -f 'netmgr/http.c' || echo '$(srcdir)/'`netmgr/http.c
+
+netmgr/libisc_la-tlsstream.lo: netmgr/tlsstream.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT netmgr/libisc_la-tlsstream.lo -MD -MP -MF netmgr/$(DEPDIR)/libisc_la-tlsstream.Tpo -c -o netmgr/libisc_la-tlsstream.lo `test -f 'netmgr/tlsstream.c' || echo '$(srcdir)/'`netmgr/tlsstream.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) netmgr/$(DEPDIR)/libisc_la-tlsstream.Tpo netmgr/$(DEPDIR)/libisc_la-tlsstream.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='netmgr/tlsstream.c' object='netmgr/libisc_la-tlsstream.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o netmgr/libisc_la-tlsstream.lo `test -f 'netmgr/tlsstream.c' || echo '$(srcdir)/'`netmgr/tlsstream.c
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+ -rm -rf netmgr/.libs netmgr/_libs
+install-libisc_laHEADERS: $(libisc_la_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(libisc_la_HEADERS)'; test -n "$(libisc_ladir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libisc_ladir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libisc_ladir)" || 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)$(libisc_ladir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(libisc_ladir)" || exit $$?; \
+ done
+
+uninstall-libisc_laHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libisc_la_HEADERS)'; test -n "$(libisc_ladir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(libisc_ladir)'; $(am__uninstall_files_from_dir)
+test-local:
+unit-local:
+doc-local:
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libisc_ladir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -rm -f netmgr/$(DEPDIR)/$(am__dirstamp)
+ -rm -f netmgr/$(am__dirstamp)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/libisc_la-aes.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-app.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-assertions.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-astack.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-backtrace.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-base32.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-base64.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-buffer.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-commandline.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-condition.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-counter.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-crc64.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-dir.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-entropy.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-errno.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-errno2result.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-error.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-event.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-file.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-glob.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-hash.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-heap.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-hex.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-hmac.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-ht.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-httpd.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-interfaceiter.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-iterated_hash.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-lex.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-lib.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-log.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-managers.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-md.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-mem.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-meminfo.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-mutex.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-mutexblock.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-net.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-netaddr.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-netscope.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-nonce.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-openssl_shim.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-os.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-parseint.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-picohttpparser.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-pool.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-portset.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-quota.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-radix.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-random.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-ratelimiter.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-regex.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-region.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-resource.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-result.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-rwlock.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-safe.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-serial.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-siphash.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-sockaddr.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-stats.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-stdio.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-stdtime.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-string.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-symtab.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-syslog.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-task.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-taskpool.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-thread.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-time.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-timer.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-tls.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-tm.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-trampoline.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-url.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-utf8.Plo
+ -rm -f netmgr/$(DEPDIR)/libisc_la-http.Plo
+ -rm -f netmgr/$(DEPDIR)/libisc_la-netmgr.Plo
+ -rm -f netmgr/$(DEPDIR)/libisc_la-tcp.Plo
+ -rm -f netmgr/$(DEPDIR)/libisc_la-tcpdns.Plo
+ -rm -f netmgr/$(DEPDIR)/libisc_la-timer.Plo
+ -rm -f netmgr/$(DEPDIR)/libisc_la-tlsdns.Plo
+ -rm -f netmgr/$(DEPDIR)/libisc_la-tlsstream.Plo
+ -rm -f netmgr/$(DEPDIR)/libisc_la-udp.Plo
+ -rm -f netmgr/$(DEPDIR)/libisc_la-uv-compat.Plo
+ -rm -f netmgr/$(DEPDIR)/libisc_la-uverr2result.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+doc: doc-am
+
+doc-am: doc-local
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-libisc_laHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/libisc_la-aes.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-app.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-assertions.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-astack.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-backtrace.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-base32.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-base64.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-buffer.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-commandline.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-condition.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-counter.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-crc64.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-dir.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-entropy.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-errno.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-errno2result.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-error.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-event.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-file.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-glob.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-hash.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-heap.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-hex.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-hmac.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-ht.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-httpd.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-interfaceiter.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-iterated_hash.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-lex.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-lib.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-log.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-managers.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-md.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-mem.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-meminfo.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-mutex.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-mutexblock.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-net.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-netaddr.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-netscope.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-nonce.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-openssl_shim.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-os.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-parseint.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-picohttpparser.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-pool.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-portset.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-quota.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-radix.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-random.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-ratelimiter.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-regex.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-region.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-resource.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-result.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-rwlock.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-safe.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-serial.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-siphash.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-sockaddr.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-stats.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-stdio.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-stdtime.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-string.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-symtab.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-syslog.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-task.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-taskpool.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-thread.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-time.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-timer.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-tls.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-tm.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-trampoline.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-url.Plo
+ -rm -f ./$(DEPDIR)/libisc_la-utf8.Plo
+ -rm -f netmgr/$(DEPDIR)/libisc_la-http.Plo
+ -rm -f netmgr/$(DEPDIR)/libisc_la-netmgr.Plo
+ -rm -f netmgr/$(DEPDIR)/libisc_la-tcp.Plo
+ -rm -f netmgr/$(DEPDIR)/libisc_la-tcpdns.Plo
+ -rm -f netmgr/$(DEPDIR)/libisc_la-timer.Plo
+ -rm -f netmgr/$(DEPDIR)/libisc_la-tlsdns.Plo
+ -rm -f netmgr/$(DEPDIR)/libisc_la-tlsstream.Plo
+ -rm -f netmgr/$(DEPDIR)/libisc_la-udp.Plo
+ -rm -f netmgr/$(DEPDIR)/libisc_la-uv-compat.Plo
+ -rm -f netmgr/$(DEPDIR)/libisc_la-uverr2result.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+test: test-am
+
+test-am: test-local
+
+uninstall-am: uninstall-libLTLIBRARIES uninstall-libisc_laHEADERS
+
+unit: unit-am
+
+unit-am: unit-local
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libLTLIBRARIES clean-libtool cscopelist-am \
+ ctags ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir doc-am doc-local 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-libLTLIBRARIES \
+ install-libisc_laHEADERS install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am test-am test-local uninstall uninstall-am \
+ uninstall-libLTLIBRARIES uninstall-libisc_laHEADERS unit-am \
+ unit-local
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/lib/isc/aes.c b/lib/isc/aes.c
new file mode 100644
index 0000000..d136bd4
--- /dev/null
+++ b/lib/isc/aes.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file isc/aes.c */
+
+#include <openssl/evp.h>
+#include <openssl/opensslv.h>
+
+#include <isc/aes.h>
+#include <isc/assertions.h>
+#include <isc/string.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+void
+isc_aes128_crypt(const unsigned char *key, const unsigned char *in,
+ unsigned char *out) {
+ EVP_CIPHER_CTX *c;
+ int len;
+
+ c = EVP_CIPHER_CTX_new();
+ RUNTIME_CHECK(c != NULL);
+ RUNTIME_CHECK(EVP_EncryptInit(c, EVP_aes_128_ecb(), key, NULL) == 1);
+ EVP_CIPHER_CTX_set_padding(c, 0);
+ RUNTIME_CHECK(
+ EVP_EncryptUpdate(c, out, &len, in, ISC_AES_BLOCK_LENGTH) == 1);
+ RUNTIME_CHECK(len == ISC_AES_BLOCK_LENGTH);
+ EVP_CIPHER_CTX_free(c);
+}
+
+void
+isc_aes192_crypt(const unsigned char *key, const unsigned char *in,
+ unsigned char *out) {
+ EVP_CIPHER_CTX *c;
+ int len;
+
+ c = EVP_CIPHER_CTX_new();
+ RUNTIME_CHECK(c != NULL);
+ RUNTIME_CHECK(EVP_EncryptInit(c, EVP_aes_192_ecb(), key, NULL) == 1);
+ EVP_CIPHER_CTX_set_padding(c, 0);
+ RUNTIME_CHECK(
+ EVP_EncryptUpdate(c, out, &len, in, ISC_AES_BLOCK_LENGTH) == 1);
+ RUNTIME_CHECK(len == ISC_AES_BLOCK_LENGTH);
+ EVP_CIPHER_CTX_free(c);
+}
+
+void
+isc_aes256_crypt(const unsigned char *key, const unsigned char *in,
+ unsigned char *out) {
+ EVP_CIPHER_CTX *c;
+ int len;
+
+ c = EVP_CIPHER_CTX_new();
+ RUNTIME_CHECK(c != NULL);
+ RUNTIME_CHECK(EVP_EncryptInit(c, EVP_aes_256_ecb(), key, NULL) == 1);
+ EVP_CIPHER_CTX_set_padding(c, 0);
+ RUNTIME_CHECK(
+ EVP_EncryptUpdate(c, out, &len, in, ISC_AES_BLOCK_LENGTH) == 1);
+ RUNTIME_CHECK(len == ISC_AES_BLOCK_LENGTH);
+ EVP_CIPHER_CTX_free(c);
+}
diff --git a/lib/isc/app.c b/lib/isc/app.c
new file mode 100644
index 0000000..da438d7
--- /dev/null
+++ b/lib/isc/app.c
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <isc/app.h>
+#include <isc/atomic.h>
+#include <isc/condition.h>
+#include <isc/event.h>
+#include <isc/mem.h>
+#include <isc/mutex.h>
+#include <isc/string.h>
+#include <isc/task.h>
+#include <isc/thread.h>
+#include <isc/time.h>
+#include <isc/util.h>
+
+/*%
+ * For BIND9 applications built with threads, we use a single app
+ * context and let multiple taskmgr and netmgr threads do actual jobs.
+ */
+
+static isc_thread_t blockedthread;
+static atomic_bool is_running = 0;
+
+/*
+ * The application context of this module.
+ */
+#define APPCTX_MAGIC ISC_MAGIC('A', 'p', 'c', 'x')
+#define VALID_APPCTX(c) ISC_MAGIC_VALID(c, APPCTX_MAGIC)
+
+struct isc_appctx {
+ unsigned int magic;
+ isc_mem_t *mctx;
+ isc_mutex_t lock;
+ isc_eventlist_t on_run;
+ atomic_bool shutdown_requested;
+ atomic_bool running;
+ atomic_bool want_shutdown;
+ atomic_bool want_reload;
+ atomic_bool blocked;
+ isc_mutex_t readylock;
+ isc_condition_t ready;
+};
+
+static isc_appctx_t isc_g_appctx;
+
+static void
+handle_signal(int sig, void (*handler)(int)) {
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = handler;
+
+ if (sigfillset(&sa.sa_mask) != 0 || sigaction(sig, &sa, NULL) < 0) {
+ FATAL_SYSERROR(errno, "signal %d", sig);
+ }
+}
+
+isc_result_t
+isc_app_ctxstart(isc_appctx_t *ctx) {
+ int presult;
+ sigset_t sset;
+
+ REQUIRE(VALID_APPCTX(ctx));
+
+ /*
+ * Start an ISC library application.
+ */
+
+ isc_mutex_init(&ctx->lock);
+
+ isc_mutex_init(&ctx->readylock);
+ isc_condition_init(&ctx->ready);
+
+ ISC_LIST_INIT(ctx->on_run);
+
+ atomic_init(&ctx->shutdown_requested, false);
+ atomic_init(&ctx->running, false);
+ atomic_init(&ctx->want_shutdown, false);
+ atomic_init(&ctx->want_reload, false);
+ atomic_init(&ctx->blocked, false);
+
+ /*
+ * Always ignore SIGPIPE.
+ */
+ handle_signal(SIGPIPE, SIG_IGN);
+
+ handle_signal(SIGHUP, SIG_DFL);
+ handle_signal(SIGTERM, SIG_DFL);
+ handle_signal(SIGINT, SIG_DFL);
+
+ /*
+ * Block SIGHUP, SIGINT, SIGTERM.
+ *
+ * If isc_app_start() is called from the main thread before any other
+ * threads have been created, then the pthread_sigmask() call below
+ * will result in all threads having SIGHUP, SIGINT and SIGTERM
+ * blocked by default, ensuring that only the thread that calls
+ * sigwait() for them will get those signals.
+ */
+ if (sigemptyset(&sset) != 0 || sigaddset(&sset, SIGHUP) != 0 ||
+ sigaddset(&sset, SIGINT) != 0 || sigaddset(&sset, SIGTERM) != 0)
+ {
+ FATAL_SYSERROR(errno, "sigsetops");
+ }
+ presult = pthread_sigmask(SIG_BLOCK, &sset, NULL);
+ if (presult != 0) {
+ FATAL_SYSERROR(presult, "pthread_sigmask()");
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_app_start(void) {
+ isc_g_appctx.magic = APPCTX_MAGIC;
+ isc_g_appctx.mctx = NULL;
+ /* The remaining members will be initialized in ctxstart() */
+
+ return (isc_app_ctxstart(&isc_g_appctx));
+}
+
+isc_result_t
+isc_app_onrun(isc_mem_t *mctx, isc_task_t *task, isc_taskaction_t action,
+ void *arg) {
+ return (isc_app_ctxonrun(&isc_g_appctx, mctx, task, action, arg));
+}
+
+isc_result_t
+isc_app_ctxonrun(isc_appctx_t *ctx, isc_mem_t *mctx, isc_task_t *task,
+ isc_taskaction_t action, void *arg) {
+ isc_event_t *event;
+ isc_task_t *cloned_task = NULL;
+
+ if (atomic_load_acquire(&ctx->running)) {
+ return (ISC_R_ALREADYRUNNING);
+ }
+
+ /*
+ * Note that we store the task to which we're going to send the event
+ * in the event's "sender" field.
+ */
+ isc_task_attach(task, &cloned_task);
+ event = isc_event_allocate(mctx, cloned_task, ISC_APPEVENT_SHUTDOWN,
+ action, arg, sizeof(*event));
+
+ LOCK(&ctx->lock);
+ ISC_LINK_INIT(event, ev_link);
+ ISC_LIST_APPEND(ctx->on_run, event, ev_link);
+ UNLOCK(&ctx->lock);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_app_ctxrun(isc_appctx_t *ctx) {
+ isc_event_t *event, *next_event;
+ isc_task_t *task;
+
+ REQUIRE(VALID_APPCTX(ctx));
+
+ if (atomic_compare_exchange_strong_acq_rel(&ctx->running,
+ &(bool){ false }, true))
+ {
+ /*
+ * Post any on-run events (in FIFO order).
+ */
+ LOCK(&ctx->lock);
+ for (event = ISC_LIST_HEAD(ctx->on_run); event != NULL;
+ event = next_event)
+ {
+ next_event = ISC_LIST_NEXT(event, ev_link);
+ ISC_LIST_UNLINK(ctx->on_run, event, ev_link);
+ task = event->ev_sender;
+ event->ev_sender = NULL;
+ isc_task_sendanddetach(&task, &event);
+ }
+ UNLOCK(&ctx->lock);
+ }
+
+ /*
+ * There is no danger if isc_app_shutdown() is called before we
+ * wait for signals. Signals are blocked, so any such signal will
+ * simply be made pending and we will get it when we call
+ * sigwait().
+ */
+ while (!atomic_load_acquire(&ctx->want_shutdown)) {
+ if (ctx == &isc_g_appctx) {
+ sigset_t sset;
+ int sig;
+ /*
+ * Wait for SIGHUP, SIGINT, or SIGTERM.
+ */
+ if (sigemptyset(&sset) != 0 ||
+ sigaddset(&sset, SIGHUP) != 0 ||
+ sigaddset(&sset, SIGINT) != 0 ||
+ sigaddset(&sset, SIGTERM) != 0)
+ {
+ FATAL_SYSERROR(errno, "sigsetops");
+ }
+
+ if (sigwait(&sset, &sig) == 0) {
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ atomic_store_release(
+ &ctx->want_shutdown, true);
+ break;
+ case SIGHUP:
+ atomic_store_release(&ctx->want_reload,
+ true);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+ } else {
+ /*
+ * Tools using multiple contexts don't
+ * rely on a signal, just wait until woken
+ * up.
+ */
+ if (atomic_load_acquire(&ctx->want_shutdown)) {
+ break;
+ }
+ if (!atomic_load_acquire(&ctx->want_reload)) {
+ LOCK(&ctx->readylock);
+ WAIT(&ctx->ready, &ctx->readylock);
+ UNLOCK(&ctx->readylock);
+ }
+ }
+ if (atomic_compare_exchange_strong_acq_rel(
+ &ctx->want_reload, &(bool){ true }, false))
+ {
+ return (ISC_R_RELOAD);
+ }
+
+ if (atomic_load_acquire(&ctx->want_shutdown) &&
+ atomic_load_acquire(&ctx->blocked))
+ {
+ exit(1);
+ }
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_app_run(void) {
+ isc_result_t result;
+
+ atomic_compare_exchange_enforced(&is_running, &(bool){ false }, true);
+
+ result = isc_app_ctxrun(&isc_g_appctx);
+ atomic_store_release(&is_running, false);
+
+ return (result);
+}
+
+bool
+isc_app_isrunning(void) {
+ return (atomic_load_acquire(&is_running));
+}
+
+void
+isc_app_ctxshutdown(isc_appctx_t *ctx) {
+ REQUIRE(VALID_APPCTX(ctx));
+
+ REQUIRE(atomic_load_acquire(&ctx->running));
+
+ /* If ctx->shutdown_requested == true, we are already shutting
+ * down and we want to just bail out.
+ */
+ if (atomic_compare_exchange_strong_acq_rel(&ctx->shutdown_requested,
+ &(bool){ false }, true))
+ {
+ if (ctx != &isc_g_appctx) {
+ /* Tool using multiple contexts */
+ atomic_store_release(&ctx->want_shutdown, true);
+ SIGNAL(&ctx->ready);
+ } else {
+ /* Normal single BIND9 context */
+ if (kill(getpid(), SIGTERM) < 0) {
+ FATAL_SYSERROR(errno, "kill");
+ }
+ }
+ }
+}
+
+void
+isc_app_shutdown(void) {
+ isc_app_ctxshutdown(&isc_g_appctx);
+}
+
+void
+isc_app_ctxsuspend(isc_appctx_t *ctx) {
+ REQUIRE(VALID_APPCTX(ctx));
+
+ REQUIRE(atomic_load(&ctx->running));
+
+ /*
+ * Don't send the reload signal if we're shutting down.
+ */
+ if (!atomic_load_acquire(&ctx->shutdown_requested)) {
+ if (ctx != &isc_g_appctx) {
+ /* Tool using multiple contexts */
+ atomic_store_release(&ctx->want_reload, true);
+ SIGNAL(&ctx->ready);
+ } else {
+ /* Normal single BIND9 context */
+ if (kill(getpid(), SIGHUP) < 0) {
+ FATAL_SYSERROR(errno, "kill");
+ }
+ }
+ }
+}
+
+void
+isc_app_reload(void) {
+ isc_app_ctxsuspend(&isc_g_appctx);
+}
+
+void
+isc_app_ctxfinish(isc_appctx_t *ctx) {
+ REQUIRE(VALID_APPCTX(ctx));
+
+ isc_mutex_destroy(&ctx->lock);
+ isc_mutex_destroy(&ctx->readylock);
+ isc_condition_destroy(&ctx->ready);
+}
+
+void
+isc_app_finish(void) {
+ isc_app_ctxfinish(&isc_g_appctx);
+}
+
+void
+isc_app_block(void) {
+ sigset_t sset;
+
+ REQUIRE(atomic_load_acquire(&isc_g_appctx.running));
+
+ atomic_compare_exchange_enforced(&isc_g_appctx.blocked,
+ &(bool){ false }, true);
+
+ blockedthread = pthread_self();
+ RUNTIME_CHECK(sigemptyset(&sset) == 0 &&
+ sigaddset(&sset, SIGINT) == 0 &&
+ sigaddset(&sset, SIGTERM) == 0);
+ RUNTIME_CHECK(pthread_sigmask(SIG_UNBLOCK, &sset, NULL) == 0);
+}
+
+void
+isc_app_unblock(void) {
+ sigset_t sset;
+
+ REQUIRE(atomic_load_acquire(&isc_g_appctx.running));
+ REQUIRE(blockedthread == pthread_self());
+
+ atomic_compare_exchange_enforced(&isc_g_appctx.blocked, &(bool){ true },
+ false);
+
+ RUNTIME_CHECK(sigemptyset(&sset) == 0 &&
+ sigaddset(&sset, SIGINT) == 0 &&
+ sigaddset(&sset, SIGTERM) == 0);
+ RUNTIME_CHECK(pthread_sigmask(SIG_BLOCK, &sset, NULL) == 0);
+}
+
+isc_result_t
+isc_appctx_create(isc_mem_t *mctx, isc_appctx_t **ctxp) {
+ isc_appctx_t *ctx;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(ctxp != NULL && *ctxp == NULL);
+
+ ctx = isc_mem_get(mctx, sizeof(*ctx));
+ *ctx = (isc_appctx_t){ .magic = 0 };
+
+ isc_mem_attach(mctx, &ctx->mctx);
+ ctx->magic = APPCTX_MAGIC;
+
+ *ctxp = ctx;
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_appctx_destroy(isc_appctx_t **ctxp) {
+ isc_appctx_t *ctx;
+
+ REQUIRE(ctxp != NULL);
+ ctx = *ctxp;
+ *ctxp = NULL;
+ REQUIRE(VALID_APPCTX(ctx));
+
+ ctx->magic = 0;
+
+ isc_mem_putanddetach(&ctx->mctx, ctx, sizeof(*ctx));
+}
diff --git a/lib/isc/assertions.c b/lib/isc/assertions.c
new file mode 100644
index 0000000..e9a7935
--- /dev/null
+++ b/lib/isc/assertions.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <isc/assertions.h>
+#include <isc/backtrace.h>
+#include <isc/print.h>
+#include <isc/result.h>
+
+/*
+ * The maximum number of stack frames to dump on assertion failure.
+ */
+#ifndef BACKTRACE_MAXFRAME
+#define BACKTRACE_MAXFRAME 128
+#endif /* ifndef BACKTRACE_MAXFRAME */
+
+/*%
+ * Forward.
+ */
+static void
+default_callback(const char *, int, isc_assertiontype_t, const char *);
+
+static isc_assertioncallback_t isc_assertion_failed_cb = default_callback;
+
+/*%
+ * Public.
+ */
+
+/*% assertion failed handler */
+/* coverity[+kill] */
+void
+isc_assertion_failed(const char *file, int line, isc_assertiontype_t type,
+ const char *cond) {
+ isc_assertion_failed_cb(file, line, type, cond);
+ abort();
+}
+
+/*% Set callback. */
+void
+isc_assertion_setcallback(isc_assertioncallback_t cb) {
+ if (cb == NULL) {
+ isc_assertion_failed_cb = default_callback;
+ } else {
+ isc_assertion_failed_cb = cb;
+ }
+}
+
+/*% Type to Text */
+const char *
+isc_assertion_typetotext(isc_assertiontype_t type) {
+ const char *result;
+
+ /*
+ * These strings have purposefully not been internationalized
+ * because they are considered to essentially be keywords of
+ * the ISC development environment.
+ */
+ switch (type) {
+ case isc_assertiontype_require:
+ result = "REQUIRE";
+ break;
+ case isc_assertiontype_ensure:
+ result = "ENSURE";
+ break;
+ case isc_assertiontype_insist:
+ result = "INSIST";
+ break;
+ case isc_assertiontype_invariant:
+ result = "INVARIANT";
+ break;
+ default:
+ result = "UNKNOWN";
+ }
+ return (result);
+}
+
+/*
+ * Private.
+ */
+
+static void
+default_callback(const char *file, int line, isc_assertiontype_t type,
+ const char *cond) {
+ void *tracebuf[BACKTRACE_MAXFRAME];
+ int nframes = isc_backtrace(tracebuf, BACKTRACE_MAXFRAME);
+
+ fprintf(stderr, "%s:%d: %s(%s) failed%s\n", file, line,
+ isc_assertion_typetotext(type), cond,
+ (nframes > 0) ? ", back trace" : ".");
+
+ if (nframes > 0) {
+ isc_backtrace_symbols_fd(tracebuf, nframes, fileno(stderr));
+ }
+
+ fflush(stderr);
+}
diff --git a/lib/isc/astack.c b/lib/isc/astack.c
new file mode 100644
index 0000000..484ef26
--- /dev/null
+++ b/lib/isc/astack.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <inttypes.h>
+#include <string.h>
+
+#include <isc/astack.h>
+#include <isc/atomic.h>
+#include <isc/mem.h>
+#include <isc/mutex.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+struct isc_astack {
+ isc_mem_t *mctx;
+ size_t size;
+ size_t pos;
+ isc_mutex_t lock;
+ uintptr_t nodes[];
+};
+
+isc_astack_t *
+isc_astack_new(isc_mem_t *mctx, size_t size) {
+ isc_astack_t *stack = isc_mem_get(
+ mctx, sizeof(isc_astack_t) + size * sizeof(uintptr_t));
+
+ *stack = (isc_astack_t){
+ .size = size,
+ };
+ isc_mem_attach(mctx, &stack->mctx);
+ memset(stack->nodes, 0, size * sizeof(uintptr_t));
+ isc_mutex_init(&stack->lock);
+ return (stack);
+}
+
+bool
+isc_astack_trypush(isc_astack_t *stack, void *obj) {
+ if (!isc_mutex_trylock(&stack->lock)) {
+ if (stack->pos >= stack->size) {
+ UNLOCK(&stack->lock);
+ return (false);
+ }
+ stack->nodes[stack->pos++] = (uintptr_t)obj;
+ UNLOCK(&stack->lock);
+ return (true);
+ } else {
+ return (false);
+ }
+}
+
+void *
+isc_astack_pop(isc_astack_t *stack) {
+ LOCK(&stack->lock);
+ uintptr_t rv;
+ if (stack->pos == 0) {
+ rv = 0;
+ } else {
+ rv = stack->nodes[--stack->pos];
+ }
+ UNLOCK(&stack->lock);
+ return ((void *)rv);
+}
+
+void
+isc_astack_destroy(isc_astack_t *stack) {
+ LOCK(&stack->lock);
+ REQUIRE(stack->pos == 0);
+ UNLOCK(&stack->lock);
+
+ isc_mutex_destroy(&stack->lock);
+
+ isc_mem_putanddetach(&stack->mctx, stack,
+ sizeof(struct isc_astack) +
+ stack->size * sizeof(uintptr_t));
+}
diff --git a/lib/isc/backtrace.c b/lib/isc/backtrace.c
new file mode 100644
index 0000000..3ac23c8
--- /dev/null
+++ b/lib/isc/backtrace.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_BACKTRACE_SYMBOLS
+#include <execinfo.h>
+#endif /* HAVE_BACKTRACE_SYMBOLS */
+
+#include <isc/backtrace.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/util.h>
+
+#if HAVE_BACKTRACE_SYMBOLS
+int
+isc_backtrace(void **addrs, int maxaddrs) {
+ int n;
+
+ /*
+ * Validate the arguments: intentionally avoid using REQUIRE().
+ * See notes in backtrace.h.
+ */
+ if (addrs == NULL || maxaddrs <= 0) {
+ return (-1);
+ }
+
+ /*
+ * backtrace(3) includes this function itself in the address array,
+ * which should be eliminated from the returned sequence.
+ */
+ n = backtrace(addrs, maxaddrs);
+ if (n < 2) {
+ return (-1);
+ }
+ n--;
+ memmove(addrs, &addrs[1], sizeof(addrs[0]) * n);
+
+ return (n);
+}
+
+char **
+isc_backtrace_symbols(void *const *buffer, int size) {
+ return (backtrace_symbols(buffer, size));
+}
+
+void
+isc_backtrace_symbols_fd(void *const *buffer, int size, int fd) {
+ backtrace_symbols_fd(buffer, size, fd);
+}
+
+#else /* HAVE_BACKTRACE_SYMBOLS */
+
+int
+isc_backtrace(void **addrs, int maxaddrs) {
+ UNUSED(addrs);
+ UNUSED(maxaddrs);
+
+ return (-1);
+}
+
+char **
+isc_backtrace_symbols(void *const *buffer, int size) {
+ UNUSED(buffer);
+ UNUSED(size);
+
+ return (NULL);
+}
+
+void
+isc_backtrace_symbols_fd(void *const *buffer, int size, int fd) {
+ UNUSED(buffer);
+ UNUSED(size);
+ UNUSED(fd);
+}
+
+#endif /* HAVE_BACKTRACE_SYMBOLS */
diff --git a/lib/isc/base32.c b/lib/isc/base32.c
new file mode 100644
index 0000000..90c37c7
--- /dev/null
+++ b/lib/isc/base32.c
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <stdbool.h>
+
+#include <isc/base32.h>
+#include <isc/buffer.h>
+#include <isc/lex.h>
+#include <isc/region.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#define RETERR(x) \
+ do { \
+ isc_result_t _r = (x); \
+ if (_r != ISC_R_SUCCESS) \
+ return ((_r)); \
+ } while (0)
+
+/*@{*/
+/*!
+ * These static functions are also present in lib/dns/rdata.c. I'm not
+ * sure where they should go. -- bwelling
+ */
+static isc_result_t
+str_totext(const char *source, isc_buffer_t *target);
+
+static isc_result_t
+mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length);
+
+/*@}*/
+
+static const char base32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567="
+ "abcdefghijklmnopqrstuvwxyz234567";
+static const char base32hex[] = "0123456789ABCDEFGHIJKLMNOPQRSTUV="
+ "0123456789abcdefghijklmnopqrstuv";
+
+static isc_result_t
+base32_totext(isc_region_t *source, int wordlength, const char *wordbreak,
+ isc_buffer_t *target, const char base[], char pad) {
+ char buf[9];
+ unsigned int loops = 0;
+
+ if (wordlength >= 0 && wordlength < 8) {
+ wordlength = 8;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ while (source->length > 0) {
+ buf[0] = base[((source->base[0] >> 3) & 0x1f)]; /* 5 + */
+ if (source->length == 1) {
+ buf[1] = base[(source->base[0] << 2) & 0x1c];
+ buf[2] = buf[3] = buf[4] = pad;
+ buf[5] = buf[6] = buf[7] = pad;
+ RETERR(str_totext(buf, target));
+ break;
+ }
+ buf[1] = base[((source->base[0] << 2) & 0x1c) | /* 3 = 8 */
+ ((source->base[1] >> 6) & 0x03)]; /* 2 + */
+ buf[2] = base[((source->base[1] >> 1) & 0x1f)]; /* 5 + */
+ if (source->length == 2) {
+ buf[3] = base[(source->base[1] << 4) & 0x10];
+ buf[4] = buf[5] = buf[6] = buf[7] = pad;
+ RETERR(str_totext(buf, target));
+ break;
+ }
+ buf[3] = base[((source->base[1] << 4) & 0x10) | /* 1 = 8 */
+ ((source->base[2] >> 4) & 0x0f)]; /* 4 + */
+ if (source->length == 3) {
+ buf[4] = base[(source->base[2] << 1) & 0x1e];
+ buf[5] = buf[6] = buf[7] = pad;
+ RETERR(str_totext(buf, target));
+ break;
+ }
+ buf[4] = base[((source->base[2] << 1) & 0x1e) | /* 4 = 8 */
+ ((source->base[3] >> 7) & 0x01)]; /* 1 + */
+ buf[5] = base[((source->base[3] >> 2) & 0x1f)]; /* 5 + */
+ if (source->length == 4) {
+ buf[6] = base[(source->base[3] << 3) & 0x18];
+ buf[7] = pad;
+ RETERR(str_totext(buf, target));
+ break;
+ }
+ buf[6] = base[((source->base[3] << 3) & 0x18) | /* 2 = 8 */
+ ((source->base[4] >> 5) & 0x07)]; /* 3 + */
+ buf[7] = base[source->base[4] & 0x1f]; /* 5 = 8 */
+ RETERR(str_totext(buf, target));
+ isc_region_consume(source, 5);
+
+ loops++;
+ if (source->length != 0 && wordlength >= 0 &&
+ (int)((loops + 1) * 8) >= wordlength)
+ {
+ loops = 0;
+ RETERR(str_totext(wordbreak, target));
+ }
+ }
+ if (source->length > 0) {
+ isc_region_consume(source, source->length);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_base32_totext(isc_region_t *source, int wordlength, const char *wordbreak,
+ isc_buffer_t *target) {
+ return (base32_totext(source, wordlength, wordbreak, target, base32,
+ '='));
+}
+
+isc_result_t
+isc_base32hex_totext(isc_region_t *source, int wordlength,
+ const char *wordbreak, isc_buffer_t *target) {
+ return (base32_totext(source, wordlength, wordbreak, target, base32hex,
+ '='));
+}
+
+isc_result_t
+isc_base32hexnp_totext(isc_region_t *source, int wordlength,
+ const char *wordbreak, isc_buffer_t *target) {
+ return (base32_totext(source, wordlength, wordbreak, target, base32hex,
+ 0));
+}
+
+/*%
+ * State of a base32 decoding process in progress.
+ */
+typedef struct {
+ int length; /*%< Desired length of binary data or -1 */
+ isc_buffer_t *target; /*%< Buffer for resulting binary data */
+ int digits; /*%< Number of buffered base32 digits */
+ bool seen_end; /*%< True if "=" end marker seen */
+ int val[8];
+ const char *base; /*%< Which encoding we are using */
+ int seen_32; /*%< Number of significant bytes if non
+ * zero */
+ bool pad; /*%< Expect padding */
+} base32_decode_ctx_t;
+
+static void
+base32_decode_init(base32_decode_ctx_t *ctx, int length, const char base[],
+ bool pad, isc_buffer_t *target) {
+ ctx->digits = 0;
+ ctx->seen_end = false;
+ ctx->seen_32 = 0;
+ ctx->length = length;
+ ctx->target = target;
+ ctx->base = base;
+ ctx->pad = pad;
+}
+
+static isc_result_t
+base32_decode_char(base32_decode_ctx_t *ctx, int c) {
+ const char *s;
+ unsigned int last;
+
+ if (ctx->seen_end) {
+ return (ISC_R_BADBASE32);
+ }
+ if ((s = strchr(ctx->base, c)) == NULL) {
+ return (ISC_R_BADBASE32);
+ }
+ last = (unsigned int)(s - ctx->base);
+
+ /*
+ * Handle lower case.
+ */
+ if (last > 32) {
+ last -= 33;
+ }
+
+ /*
+ * Check that padding is contiguous.
+ */
+ if (last != 32 && ctx->seen_32 != 0) {
+ return (ISC_R_BADBASE32);
+ }
+
+ /*
+ * If padding is not permitted flag padding as a error.
+ */
+ if (last == 32 && !ctx->pad) {
+ return (ISC_R_BADBASE32);
+ }
+
+ /*
+ * Check that padding starts at the right place and that
+ * bits that should be zero are.
+ * Record how many significant bytes in answer (seen_32).
+ */
+ if (last == 32 && ctx->seen_32 == 0) {
+ switch (ctx->digits) {
+ case 0:
+ case 1:
+ return (ISC_R_BADBASE32);
+ case 2:
+ if ((ctx->val[1] & 0x03) != 0) {
+ return (ISC_R_BADBASE32);
+ }
+ ctx->seen_32 = 1;
+ break;
+ case 3:
+ return (ISC_R_BADBASE32);
+ case 4:
+ if ((ctx->val[3] & 0x0f) != 0) {
+ return (ISC_R_BADBASE32);
+ }
+ ctx->seen_32 = 2;
+ break;
+ case 5:
+ if ((ctx->val[4] & 0x01) != 0) {
+ return (ISC_R_BADBASE32);
+ }
+ ctx->seen_32 = 3;
+ break;
+ case 6:
+ return (ISC_R_BADBASE32);
+ case 7:
+ if ((ctx->val[6] & 0x07) != 0) {
+ return (ISC_R_BADBASE32);
+ }
+ ctx->seen_32 = 4;
+ break;
+ }
+ }
+
+ /*
+ * Zero fill pad values.
+ */
+ ctx->val[ctx->digits++] = (last == 32) ? 0 : last;
+
+ if (ctx->digits == 8) {
+ int n = 5;
+ unsigned char buf[5];
+
+ if (ctx->seen_32 != 0) {
+ ctx->seen_end = true;
+ n = ctx->seen_32;
+ }
+ buf[0] = (ctx->val[0] << 3) | (ctx->val[1] >> 2);
+ buf[1] = (ctx->val[1] << 6) | (ctx->val[2] << 1) |
+ (ctx->val[3] >> 4);
+ buf[2] = (ctx->val[3] << 4) | (ctx->val[4] >> 1);
+ buf[3] = (ctx->val[4] << 7) | (ctx->val[5] << 2) |
+ (ctx->val[6] >> 3);
+ buf[4] = (ctx->val[6] << 5) | (ctx->val[7]);
+ RETERR(mem_tobuffer(ctx->target, buf, n));
+ if (ctx->length >= 0) {
+ if (n > ctx->length) {
+ return (ISC_R_BADBASE32);
+ } else {
+ ctx->length -= n;
+ }
+ }
+ ctx->digits = 0;
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+base32_decode_finish(base32_decode_ctx_t *ctx) {
+ if (ctx->length > 0) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ /*
+ * Add missing padding if required.
+ */
+ if (!ctx->pad && ctx->digits != 0) {
+ ctx->pad = true;
+ do {
+ RETERR(base32_decode_char(ctx, '='));
+ } while (ctx->digits != 0);
+ }
+ if (ctx->digits != 0) {
+ return (ISC_R_BADBASE32);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+base32_tobuffer(isc_lex_t *lexer, const char base[], bool pad,
+ isc_buffer_t *target, int length) {
+ unsigned int before, after;
+ base32_decode_ctx_t ctx;
+ isc_textregion_t *tr;
+ isc_token_t token;
+ bool eol;
+
+ REQUIRE(length >= -2);
+
+ base32_decode_init(&ctx, length, base, pad, target);
+
+ before = isc_buffer_usedlength(target);
+ while (!ctx.seen_end && (ctx.length != 0)) {
+ unsigned int i;
+
+ if (length > 0) {
+ eol = false;
+ } else {
+ eol = true;
+ }
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_string, eol));
+ if (token.type != isc_tokentype_string) {
+ break;
+ }
+ tr = &token.value.as_textregion;
+ for (i = 0; i < tr->length; i++) {
+ RETERR(base32_decode_char(&ctx, tr->base[i]));
+ }
+ }
+ after = isc_buffer_usedlength(target);
+ if (ctx.length < 0 && !ctx.seen_end) {
+ isc_lex_ungettoken(lexer, &token);
+ }
+ RETERR(base32_decode_finish(&ctx));
+ if (length == -2 && before == after) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_base32_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) {
+ return (base32_tobuffer(lexer, base32, true, target, length));
+}
+
+isc_result_t
+isc_base32hex_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) {
+ return (base32_tobuffer(lexer, base32hex, true, target, length));
+}
+
+isc_result_t
+isc_base32hexnp_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) {
+ return (base32_tobuffer(lexer, base32hex, false, target, length));
+}
+
+static isc_result_t
+base32_decodestring(const char *cstr, const char base[], bool pad,
+ isc_buffer_t *target) {
+ base32_decode_ctx_t ctx;
+
+ base32_decode_init(&ctx, -1, base, pad, target);
+ for (;;) {
+ int c = *cstr++;
+ if (c == '\0') {
+ break;
+ }
+ if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
+ continue;
+ }
+ RETERR(base32_decode_char(&ctx, c));
+ }
+ RETERR(base32_decode_finish(&ctx));
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_base32_decodestring(const char *cstr, isc_buffer_t *target) {
+ return (base32_decodestring(cstr, base32, true, target));
+}
+
+isc_result_t
+isc_base32hex_decodestring(const char *cstr, isc_buffer_t *target) {
+ return (base32_decodestring(cstr, base32hex, true, target));
+}
+
+isc_result_t
+isc_base32hexnp_decodestring(const char *cstr, isc_buffer_t *target) {
+ return (base32_decodestring(cstr, base32hex, false, target));
+}
+
+static isc_result_t
+base32_decoderegion(isc_region_t *source, const char base[], bool pad,
+ isc_buffer_t *target) {
+ base32_decode_ctx_t ctx;
+
+ base32_decode_init(&ctx, -1, base, pad, target);
+ while (source->length != 0) {
+ int c = *source->base;
+ RETERR(base32_decode_char(&ctx, c));
+ isc_region_consume(source, 1);
+ }
+ RETERR(base32_decode_finish(&ctx));
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_base32_decoderegion(isc_region_t *source, isc_buffer_t *target) {
+ return (base32_decoderegion(source, base32, true, target));
+}
+
+isc_result_t
+isc_base32hex_decoderegion(isc_region_t *source, isc_buffer_t *target) {
+ return (base32_decoderegion(source, base32hex, true, target));
+}
+
+isc_result_t
+isc_base32hexnp_decoderegion(isc_region_t *source, isc_buffer_t *target) {
+ return (base32_decoderegion(source, base32hex, false, target));
+}
+
+static isc_result_t
+str_totext(const char *source, isc_buffer_t *target) {
+ unsigned int l;
+ isc_region_t region;
+
+ isc_buffer_availableregion(target, &region);
+ l = strlen(source);
+
+ if (l > region.length) {
+ return (ISC_R_NOSPACE);
+ }
+
+ memmove(region.base, source, l);
+ isc_buffer_add(target, l);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length) {
+ isc_region_t tr;
+
+ isc_buffer_availableregion(target, &tr);
+ if (length > tr.length) {
+ return (ISC_R_NOSPACE);
+ }
+ memmove(tr.base, base, length);
+ isc_buffer_add(target, length);
+ return (ISC_R_SUCCESS);
+}
diff --git a/lib/isc/base64.c b/lib/isc/base64.c
new file mode 100644
index 0000000..958ef4f
--- /dev/null
+++ b/lib/isc/base64.c
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <stdbool.h>
+
+#include <isc/base64.h>
+#include <isc/buffer.h>
+#include <isc/lex.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#define RETERR(x) \
+ do { \
+ isc_result_t _r = (x); \
+ if (_r != ISC_R_SUCCESS) \
+ return ((_r)); \
+ } while (0)
+
+/*@{*/
+/*!
+ * These static functions are also present in lib/dns/rdata.c. I'm not
+ * sure where they should go. -- bwelling
+ */
+static isc_result_t
+str_totext(const char *source, isc_buffer_t *target);
+
+static isc_result_t
+mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length);
+
+static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw"
+ "xyz0123456789+/=";
+/*@}*/
+
+isc_result_t
+isc_base64_totext(isc_region_t *source, int wordlength, const char *wordbreak,
+ isc_buffer_t *target) {
+ char buf[5];
+ unsigned int loops = 0;
+
+ if (wordlength < 4) {
+ wordlength = 4;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ while (source->length > 2) {
+ buf[0] = base64[(source->base[0] >> 2) & 0x3f];
+ buf[1] = base64[((source->base[0] << 4) & 0x30) |
+ ((source->base[1] >> 4) & 0x0f)];
+ buf[2] = base64[((source->base[1] << 2) & 0x3c) |
+ ((source->base[2] >> 6) & 0x03)];
+ buf[3] = base64[source->base[2] & 0x3f];
+ RETERR(str_totext(buf, target));
+ isc_region_consume(source, 3);
+
+ loops++;
+ if (source->length != 0 && (int)((loops + 1) * 4) >= wordlength)
+ {
+ loops = 0;
+ RETERR(str_totext(wordbreak, target));
+ }
+ }
+ if (source->length == 2) {
+ buf[0] = base64[(source->base[0] >> 2) & 0x3f];
+ buf[1] = base64[((source->base[0] << 4) & 0x30) |
+ ((source->base[1] >> 4) & 0x0f)];
+ buf[2] = base64[((source->base[1] << 2) & 0x3c)];
+ buf[3] = '=';
+ RETERR(str_totext(buf, target));
+ isc_region_consume(source, 2);
+ } else if (source->length == 1) {
+ buf[0] = base64[(source->base[0] >> 2) & 0x3f];
+ buf[1] = base64[((source->base[0] << 4) & 0x30)];
+ buf[2] = buf[3] = '=';
+ RETERR(str_totext(buf, target));
+ isc_region_consume(source, 1);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * State of a base64 decoding process in progress.
+ */
+typedef struct {
+ int length; /*%< Desired length of binary data or -1 */
+ isc_buffer_t *target; /*%< Buffer for resulting binary data */
+ int digits; /*%< Number of buffered base64 digits */
+ bool seen_end; /*%< True if "=" end marker seen */
+ int val[4];
+} base64_decode_ctx_t;
+
+static void
+base64_decode_init(base64_decode_ctx_t *ctx, int length, isc_buffer_t *target) {
+ ctx->digits = 0;
+ ctx->seen_end = false;
+ ctx->length = length;
+ ctx->target = target;
+}
+
+static isc_result_t
+base64_decode_char(base64_decode_ctx_t *ctx, int c) {
+ const char *s;
+
+ if (ctx->seen_end) {
+ return (ISC_R_BADBASE64);
+ }
+ if ((s = strchr(base64, c)) == NULL) {
+ return (ISC_R_BADBASE64);
+ }
+ ctx->val[ctx->digits++] = (int)(s - base64);
+ if (ctx->digits == 4) {
+ int n;
+ unsigned char buf[3];
+ if (ctx->val[0] == 64 || ctx->val[1] == 64) {
+ return (ISC_R_BADBASE64);
+ }
+ if (ctx->val[2] == 64 && ctx->val[3] != 64) {
+ return (ISC_R_BADBASE64);
+ }
+ /*
+ * Check that bits that should be zero are.
+ */
+ if (ctx->val[2] == 64 && (ctx->val[1] & 0xf) != 0) {
+ return (ISC_R_BADBASE64);
+ }
+ /*
+ * We don't need to test for ctx->val[2] != 64 as
+ * the bottom two bits of 64 are zero.
+ */
+ if (ctx->val[3] == 64 && (ctx->val[2] & 0x3) != 0) {
+ return (ISC_R_BADBASE64);
+ }
+ n = (ctx->val[2] == 64) ? 1 : (ctx->val[3] == 64) ? 2 : 3;
+ if (n != 3) {
+ ctx->seen_end = true;
+ if (ctx->val[2] == 64) {
+ ctx->val[2] = 0;
+ }
+ if (ctx->val[3] == 64) {
+ ctx->val[3] = 0;
+ }
+ }
+ buf[0] = (ctx->val[0] << 2) | (ctx->val[1] >> 4);
+ buf[1] = (ctx->val[1] << 4) | (ctx->val[2] >> 2);
+ buf[2] = (ctx->val[2] << 6) | (ctx->val[3]);
+ RETERR(mem_tobuffer(ctx->target, buf, n));
+ if (ctx->length >= 0) {
+ if (n > ctx->length) {
+ return (ISC_R_BADBASE64);
+ } else {
+ ctx->length -= n;
+ }
+ }
+ ctx->digits = 0;
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+base64_decode_finish(base64_decode_ctx_t *ctx) {
+ if (ctx->length > 0) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ if (ctx->digits != 0) {
+ return (ISC_R_BADBASE64);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_base64_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) {
+ unsigned int before, after;
+ base64_decode_ctx_t ctx;
+ isc_textregion_t *tr;
+ isc_token_t token;
+ bool eol;
+
+ REQUIRE(length >= -2);
+
+ base64_decode_init(&ctx, length, target);
+
+ before = isc_buffer_usedlength(target);
+ while (!ctx.seen_end && (ctx.length != 0)) {
+ unsigned int i;
+
+ if (length > 0) {
+ eol = false;
+ } else {
+ eol = true;
+ }
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_string, eol));
+ if (token.type != isc_tokentype_string) {
+ break;
+ }
+ tr = &token.value.as_textregion;
+ for (i = 0; i < tr->length; i++) {
+ RETERR(base64_decode_char(&ctx, tr->base[i]));
+ }
+ }
+ after = isc_buffer_usedlength(target);
+ if (ctx.length < 0 && !ctx.seen_end) {
+ isc_lex_ungettoken(lexer, &token);
+ }
+ RETERR(base64_decode_finish(&ctx));
+ if (length == -2 && before == after) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_base64_decodestring(const char *cstr, isc_buffer_t *target) {
+ base64_decode_ctx_t ctx;
+
+ base64_decode_init(&ctx, -1, target);
+ for (;;) {
+ int c = *cstr++;
+ if (c == '\0') {
+ break;
+ }
+ if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
+ continue;
+ }
+ RETERR(base64_decode_char(&ctx, c));
+ }
+ RETERR(base64_decode_finish(&ctx));
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+str_totext(const char *source, isc_buffer_t *target) {
+ unsigned int l;
+ isc_region_t region;
+
+ isc_buffer_availableregion(target, &region);
+ l = strlen(source);
+
+ if (l > region.length) {
+ return (ISC_R_NOSPACE);
+ }
+
+ memmove(region.base, source, l);
+ isc_buffer_add(target, l);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length) {
+ isc_region_t tr;
+
+ isc_buffer_availableregion(target, &tr);
+ if (length > tr.length) {
+ return (ISC_R_NOSPACE);
+ }
+ memmove(tr.base, base, length);
+ isc_buffer_add(target, length);
+ return (ISC_R_SUCCESS);
+}
diff --git a/lib/isc/buffer.c b/lib/isc/buffer.c
new file mode 100644
index 0000000..e7e84df
--- /dev/null
+++ b/lib/isc/buffer.c
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdbool.h>
+
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/region.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+void
+isc_buffer_reinit(isc_buffer_t *b, void *base, unsigned int length) {
+ /*
+ * Re-initialize the buffer enough to reconfigure the base of the
+ * buffer. We will swap in the new buffer, after copying any
+ * data we contain into the new buffer and adjusting all of our
+ * internal pointers.
+ *
+ * The buffer must not be smaller than the length of the original
+ * buffer.
+ */
+ REQUIRE(b->length <= length);
+ REQUIRE(base != NULL);
+ REQUIRE(!b->autore);
+
+ if (b->length > 0U) {
+ (void)memmove(base, b->base, b->length);
+ }
+
+ b->base = base;
+ b->length = length;
+}
+
+void
+isc_buffer_setautorealloc(isc_buffer_t *b, bool enable) {
+ REQUIRE(ISC_BUFFER_VALID(b));
+ REQUIRE(b->mctx != NULL);
+ b->autore = enable;
+}
+
+void
+isc_buffer_compact(isc_buffer_t *b) {
+ unsigned int length;
+ void *src;
+
+ /*
+ * Compact the used region by moving the remaining region so it occurs
+ * at the start of the buffer. The used region is shrunk by the size
+ * of the consumed region, and the consumed region is then made empty.
+ */
+
+ REQUIRE(ISC_BUFFER_VALID(b));
+
+ src = isc_buffer_current(b);
+ length = isc_buffer_remaininglength(b);
+ if (length > 0U) {
+ (void)memmove(b->base, src, (size_t)length);
+ }
+
+ if (b->active > b->current) {
+ b->active -= b->current;
+ } else {
+ b->active = 0;
+ }
+ b->current = 0;
+ b->used = length;
+}
+
+uint8_t
+isc_buffer_getuint8(isc_buffer_t *b) {
+ unsigned char *cp;
+ uint8_t result;
+
+ /*
+ * Read an unsigned 8-bit integer from 'b' and return it.
+ */
+
+ REQUIRE(ISC_BUFFER_VALID(b));
+ REQUIRE(b->used - b->current >= 1);
+
+ cp = isc_buffer_current(b);
+ b->current += 1;
+ result = ((uint8_t)(cp[0]));
+
+ return (result);
+}
+
+uint16_t
+isc_buffer_getuint16(isc_buffer_t *b) {
+ unsigned char *cp;
+ uint16_t result;
+
+ /*
+ * Read an unsigned 16-bit integer in network byte order from 'b',
+ * convert it to host byte order, and return it.
+ */
+
+ REQUIRE(ISC_BUFFER_VALID(b));
+ REQUIRE(b->used - b->current >= 2);
+
+ cp = isc_buffer_current(b);
+ b->current += 2;
+ result = ((unsigned int)(cp[0])) << 8;
+ result |= ((unsigned int)(cp[1]));
+
+ return (result);
+}
+
+uint32_t
+isc_buffer_getuint32(isc_buffer_t *b) {
+ unsigned char *cp;
+ uint32_t result;
+
+ /*
+ * Read an unsigned 32-bit integer in network byte order from 'b',
+ * convert it to host byte order, and return it.
+ */
+
+ REQUIRE(ISC_BUFFER_VALID(b));
+ REQUIRE(b->used - b->current >= 4);
+
+ cp = isc_buffer_current(b);
+ b->current += 4;
+ result = ((unsigned int)(cp[0])) << 24;
+ result |= ((unsigned int)(cp[1])) << 16;
+ result |= ((unsigned int)(cp[2])) << 8;
+ result |= ((unsigned int)(cp[3]));
+
+ return (result);
+}
+
+uint64_t
+isc_buffer_getuint48(isc_buffer_t *b) {
+ unsigned char *cp;
+ uint64_t result;
+
+ /*
+ * Read an unsigned 48-bit integer in network byte order from 'b',
+ * convert it to host byte order, and return it.
+ */
+
+ REQUIRE(ISC_BUFFER_VALID(b));
+ REQUIRE(b->used - b->current >= 6);
+
+ cp = isc_buffer_current(b);
+ b->current += 6;
+ result = ((int64_t)(cp[0])) << 40;
+ result |= ((int64_t)(cp[1])) << 32;
+ result |= ((int64_t)(cp[2])) << 24;
+ result |= ((int64_t)(cp[3])) << 16;
+ result |= ((int64_t)(cp[4])) << 8;
+ result |= ((int64_t)(cp[5]));
+
+ return (result);
+}
+
+void
+isc_buffer_putdecint(isc_buffer_t *b, int64_t v) {
+ unsigned int l = 0;
+ unsigned char *cp;
+ char buf[21];
+ isc_result_t result;
+
+ REQUIRE(ISC_BUFFER_VALID(b));
+
+ /* xxxwpk do it more low-level way ? */
+ l = snprintf(buf, 21, "%" PRId64, v);
+ RUNTIME_CHECK(l <= 21);
+ if (b->autore) {
+ result = isc_buffer_reserve(&b, l);
+ REQUIRE(result == ISC_R_SUCCESS);
+ }
+ REQUIRE(isc_buffer_availablelength(b) >= l);
+
+ cp = isc_buffer_used(b);
+ memmove(cp, buf, l);
+ b->used += l;
+}
+
+isc_result_t
+isc_buffer_dup(isc_mem_t *mctx, isc_buffer_t **dstp, const isc_buffer_t *src) {
+ isc_buffer_t *dst = NULL;
+ isc_region_t region;
+ isc_result_t result;
+
+ REQUIRE(dstp != NULL && *dstp == NULL);
+ REQUIRE(ISC_BUFFER_VALID(src));
+
+ isc_buffer_usedregion(src, &region);
+
+ isc_buffer_allocate(mctx, &dst, region.length);
+
+ result = isc_buffer_copyregion(dst, &region);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS); /* NOSPACE is impossible */
+ *dstp = dst;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_buffer_copyregion(isc_buffer_t *b, const isc_region_t *r) {
+ isc_result_t result;
+
+ REQUIRE(ISC_BUFFER_VALID(b));
+ REQUIRE(r != NULL);
+
+ if (b->autore) {
+ result = isc_buffer_reserve(&b, r->length);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ }
+
+ if (r->length > isc_buffer_availablelength(b)) {
+ return (ISC_R_NOSPACE);
+ }
+
+ if (r->length > 0U) {
+ memmove(isc_buffer_used(b), r->base, r->length);
+ b->used += r->length;
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_buffer_allocate(isc_mem_t *mctx, isc_buffer_t **dynbuffer,
+ unsigned int length) {
+ REQUIRE(dynbuffer != NULL && *dynbuffer == NULL);
+
+ isc_buffer_t *dbuf = isc_mem_get(mctx, sizeof(isc_buffer_t));
+ unsigned char *bdata = isc_mem_get(mctx, length);
+
+ isc_buffer_init(dbuf, bdata, length);
+
+ ENSURE(ISC_BUFFER_VALID(dbuf));
+
+ dbuf->mctx = mctx;
+
+ *dynbuffer = dbuf;
+}
+
+isc_result_t
+isc_buffer_reserve(isc_buffer_t **dynbuffer, unsigned int size) {
+ size_t len;
+
+ REQUIRE(dynbuffer != NULL);
+ REQUIRE(ISC_BUFFER_VALID(*dynbuffer));
+
+ len = (*dynbuffer)->length;
+ if ((len - (*dynbuffer)->used) >= size) {
+ return (ISC_R_SUCCESS);
+ }
+
+ if ((*dynbuffer)->mctx == NULL) {
+ return (ISC_R_NOSPACE);
+ }
+
+ /* Round to nearest buffer size increment */
+ len = size + (*dynbuffer)->used;
+ len = (len + ISC_BUFFER_INCR - 1 - ((len - 1) % ISC_BUFFER_INCR));
+
+ /* Cap at UINT_MAX */
+ if (len > UINT_MAX) {
+ len = UINT_MAX;
+ }
+
+ if ((len - (*dynbuffer)->used) < size) {
+ return (ISC_R_NOMEMORY);
+ }
+
+ (*dynbuffer)->base = isc_mem_reget((*dynbuffer)->mctx,
+ (*dynbuffer)->base,
+ (*dynbuffer)->length, len);
+ (*dynbuffer)->length = (unsigned int)len;
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_buffer_free(isc_buffer_t **dynbuffer) {
+ isc_buffer_t *dbuf;
+ isc_mem_t *mctx;
+
+ REQUIRE(dynbuffer != NULL);
+ REQUIRE(ISC_BUFFER_VALID(*dynbuffer));
+ REQUIRE((*dynbuffer)->mctx != NULL);
+
+ dbuf = *dynbuffer;
+ *dynbuffer = NULL; /* destroy external reference */
+ mctx = dbuf->mctx;
+ dbuf->mctx = NULL;
+
+ isc_mem_put(mctx, dbuf->base, dbuf->length);
+ isc_buffer_invalidate(dbuf);
+ isc_mem_put(mctx, dbuf, sizeof(isc_buffer_t));
+}
+
+isc_result_t
+isc_buffer_printf(isc_buffer_t *b, const char *format, ...) {
+ va_list ap;
+ int n;
+ isc_result_t result;
+
+ REQUIRE(ISC_BUFFER_VALID(b));
+
+ va_start(ap, format);
+ n = vsnprintf(NULL, 0, format, ap);
+ va_end(ap);
+
+ if (n < 0) {
+ return (ISC_R_FAILURE);
+ }
+
+ if (b->autore) {
+ result = isc_buffer_reserve(&b, n + 1);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ }
+
+ if (isc_buffer_availablelength(b) < (unsigned int)n + 1) {
+ return (ISC_R_NOSPACE);
+ }
+
+ va_start(ap, format);
+ n = vsnprintf(isc_buffer_used(b), n + 1, format, ap);
+ va_end(ap);
+
+ if (n < 0) {
+ return (ISC_R_FAILURE);
+ }
+
+ b->used += n;
+
+ return (ISC_R_SUCCESS);
+}
diff --git a/lib/isc/commandline.c b/lib/isc/commandline.c
new file mode 100644
index 0000000..1b4a33a
--- /dev/null
+++ b/lib/isc/commandline.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND BSD-3-Clause
+
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+/*
+ * Copyright (C) 1987, 1993, 1994 The Regents of the University of California.
+ *
+ * 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 University 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 REGENTS 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 REGENTS 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.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file
+ * This file was adapted from the NetBSD project's source tree, RCS ID:
+ * NetBSD: getopt.c,v 1.15 1999/09/20 04:39:37 lukem Exp
+ *
+ * The primary change has been to rename items to the ISC namespace
+ * and format in the ISC coding style.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <isc/commandline.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+/*% Index into parent argv vector. */
+int isc_commandline_index = 1;
+/*% Character checked for validity. */
+int isc_commandline_option;
+/*% Argument associated with option. */
+char *isc_commandline_argument;
+/*% For printing error messages. */
+char *isc_commandline_progname;
+/*% Print error messages. */
+bool isc_commandline_errprint = true;
+/*% Reset processing. */
+bool isc_commandline_reset = true;
+
+static char endopt = '\0';
+
+#define BADOPT '?'
+#define BADARG ':'
+#define ENDOPT &endopt
+
+/*!
+ * getopt --
+ * Parse argc/argv argument vector.
+ */
+int
+isc_commandline_parse(int argc, char *const *argv, const char *options) {
+ static char *place = ENDOPT;
+ const char *option; /* Index into *options of option. */
+
+ REQUIRE(argc >= 0 && argv != NULL && options != NULL);
+
+ /*
+ * Update scanning pointer, either because a reset was requested or
+ * the previous argv was finished.
+ */
+ if (isc_commandline_reset || *place == '\0') {
+ if (isc_commandline_reset) {
+ isc_commandline_index = 1;
+ isc_commandline_reset = false;
+ }
+
+ if (isc_commandline_progname == NULL) {
+ isc_commandline_progname = argv[0];
+ }
+
+ if (isc_commandline_index >= argc ||
+ *(place = argv[isc_commandline_index]) != '-')
+ {
+ /*
+ * Index out of range or points to non-option.
+ */
+ place = ENDOPT;
+ return (-1);
+ }
+
+ if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
+ /*
+ * Found '--' to signal end of options. Advance
+ * index to next argv, the first non-option.
+ */
+ isc_commandline_index++;
+ place = ENDOPT;
+ return (-1);
+ }
+ }
+
+ isc_commandline_option = *place++;
+ option = strchr(options, isc_commandline_option);
+
+ /*
+ * Ensure valid option has been passed as specified by options string.
+ * '-:' is never a valid command line option because it could not
+ * distinguish ':' from the argument specifier in the options string.
+ */
+ if (isc_commandline_option == ':' || option == NULL) {
+ if (*place == '\0') {
+ isc_commandline_index++;
+ }
+
+ if (isc_commandline_errprint && *options != ':') {
+ fprintf(stderr, "%s: illegal option -- %c\n",
+ isc_commandline_progname,
+ isc_commandline_option);
+ }
+
+ return (BADOPT);
+ }
+
+ if (*++option != ':') {
+ /*
+ * Option does not take an argument.
+ */
+ isc_commandline_argument = NULL;
+
+ /*
+ * Skip to next argv if at the end of the current argv.
+ */
+ if (*place == '\0') {
+ ++isc_commandline_index;
+ }
+ } else {
+ /*
+ * Option needs an argument.
+ */
+ if (*place != '\0') {
+ /*
+ * Option is in this argv, -D1 style.
+ */
+ isc_commandline_argument = place;
+ } else if (argc > ++isc_commandline_index) {
+ /*
+ * Option is next argv, -D 1 style.
+ */
+ isc_commandline_argument = argv[isc_commandline_index];
+ } else {
+ /*
+ * Argument needed, but no more argv.
+ */
+ place = ENDOPT;
+
+ /*
+ * Silent failure with "missing argument" return
+ * when ':' starts options string, per historical spec.
+ */
+ if (*options == ':') {
+ return (BADARG);
+ }
+
+ if (isc_commandline_errprint) {
+ fprintf(stderr,
+ "%s: option requires an argument -- "
+ "%c\n",
+ isc_commandline_progname,
+ isc_commandline_option);
+ }
+
+ return (BADOPT);
+ }
+
+ place = ENDOPT;
+
+ /*
+ * Point to argv that follows argument.
+ */
+ isc_commandline_index++;
+ }
+
+ return (isc_commandline_option);
+}
+
+isc_result_t
+isc_commandline_strtoargv(isc_mem_t *mctx, char *s, unsigned int *argcp,
+ char ***argvp, unsigned int n) {
+ isc_result_t result;
+
+restart:
+ /* Discard leading whitespace. */
+ while (*s == ' ' || *s == '\t') {
+ s++;
+ }
+
+ if (*s == '\0') {
+ /* We have reached the end of the string. */
+ *argcp = n;
+ *argvp = isc_mem_get(mctx, n * sizeof(char *));
+ } else {
+ char *p = s;
+ while (*p != ' ' && *p != '\t' && *p != '\0' && *p != '{') {
+ if (*p == '\n') {
+ *p = ' ';
+ goto restart;
+ }
+ p++;
+ }
+
+ /* do "grouping", items between { and } are one arg */
+ if (*p == '{') {
+ char *t = p;
+ /*
+ * shift all characters to left by 1 to get rid of '{'
+ */
+ while (*t != '\0') {
+ t++;
+ *(t - 1) = *t;
+ }
+ while (*p != '\0' && *p != '}') {
+ p++;
+ }
+ /* get rid of '}' character */
+ if (*p == '}') {
+ *p = '\0';
+ p++;
+ }
+ /* normal case, no "grouping" */
+ } else if (*p != '\0') {
+ *p++ = '\0';
+ }
+
+ result = isc_commandline_strtoargv(mctx, p, argcp, argvp,
+ n + 1);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ (*argvp)[n] = s;
+ }
+
+ return (ISC_R_SUCCESS);
+}
diff --git a/lib/isc/condition.c b/lib/isc/condition.c
new file mode 100644
index 0000000..18fab69
--- /dev/null
+++ b/lib/isc/condition.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <errno.h>
+
+#include <isc/condition.h>
+#include <isc/string.h>
+#include <isc/time.h>
+#include <isc/util.h>
+
+isc_result_t
+isc_condition_waituntil(isc_condition_t *c, isc_mutex_t *m, isc_time_t *t) {
+ int presult;
+ isc_result_t result;
+ struct timespec ts;
+
+ REQUIRE(c != NULL && m != NULL && t != NULL);
+
+ /*
+ * POSIX defines a timespec's tv_sec as time_t.
+ */
+ result = isc_time_secondsastimet(t, &ts.tv_sec);
+
+ /*
+ * If we have a range error ts.tv_sec is most probably a signed
+ * 32 bit value. Set ts.tv_sec to INT_MAX. This is a kludge.
+ */
+ if (result == ISC_R_RANGE) {
+ ts.tv_sec = INT_MAX;
+ } else if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ /*!
+ * POSIX defines a timespec's tv_nsec as long. isc_time_nanoseconds
+ * ensures its return value is < 1 billion, which will fit in a long.
+ */
+ ts.tv_nsec = (long)isc_time_nanoseconds(t);
+
+ do {
+ presult = pthread_cond_timedwait(c, m, &ts);
+ if (presult == 0) {
+ return (ISC_R_SUCCESS);
+ }
+ if (presult == ETIMEDOUT) {
+ return (ISC_R_TIMEDOUT);
+ }
+ } while (presult == EINTR);
+
+ UNEXPECTED_SYSERROR(presult, "pthread_cond_timedwait()");
+ return (ISC_R_UNEXPECTED);
+}
diff --git a/lib/isc/counter.c b/lib/isc/counter.c
new file mode 100644
index 0000000..0c0ccd6
--- /dev/null
+++ b/lib/isc/counter.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#include <isc/atomic.h>
+#include <isc/counter.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/refcount.h>
+#include <isc/util.h>
+
+#define COUNTER_MAGIC ISC_MAGIC('C', 'n', 't', 'r')
+#define VALID_COUNTER(r) ISC_MAGIC_VALID(r, COUNTER_MAGIC)
+
+struct isc_counter {
+ unsigned int magic;
+ isc_mem_t *mctx;
+ isc_refcount_t references;
+ atomic_uint_fast32_t limit;
+ atomic_uint_fast32_t used;
+};
+
+isc_result_t
+isc_counter_create(isc_mem_t *mctx, int limit, isc_counter_t **counterp) {
+ isc_counter_t *counter;
+
+ REQUIRE(counterp != NULL && *counterp == NULL);
+
+ counter = isc_mem_get(mctx, sizeof(*counter));
+
+ counter->mctx = NULL;
+ isc_mem_attach(mctx, &counter->mctx);
+
+ isc_refcount_init(&counter->references, 1);
+ atomic_init(&counter->limit, limit);
+ atomic_init(&counter->used, 0);
+
+ counter->magic = COUNTER_MAGIC;
+ *counterp = counter;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_counter_increment(isc_counter_t *counter) {
+ uint32_t used = atomic_fetch_add_relaxed(&counter->used, 1) + 1;
+ uint32_t limit = atomic_load_acquire(&counter->limit);
+
+ if (limit != 0 && used >= limit) {
+ return (ISC_R_QUOTA);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+unsigned int
+isc_counter_used(isc_counter_t *counter) {
+ REQUIRE(VALID_COUNTER(counter));
+
+ return (atomic_load_acquire(&counter->used));
+}
+
+void
+isc_counter_setlimit(isc_counter_t *counter, int limit) {
+ REQUIRE(VALID_COUNTER(counter));
+
+ atomic_store(&counter->limit, limit);
+}
+
+void
+isc_counter_attach(isc_counter_t *source, isc_counter_t **targetp) {
+ REQUIRE(VALID_COUNTER(source));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ isc_refcount_increment(&source->references);
+
+ *targetp = source;
+}
+
+static void
+destroy(isc_counter_t *counter) {
+ isc_refcount_destroy(&counter->references);
+ counter->magic = 0;
+ isc_mem_putanddetach(&counter->mctx, counter, sizeof(*counter));
+}
+
+void
+isc_counter_detach(isc_counter_t **counterp) {
+ isc_counter_t *counter;
+
+ REQUIRE(counterp != NULL && *counterp != NULL);
+ counter = *counterp;
+ *counterp = NULL;
+ REQUIRE(VALID_COUNTER(counter));
+
+ if (isc_refcount_decrement(&counter->references) == 1) {
+ destroy(counter);
+ }
+}
diff --git a/lib/isc/crc64.c b/lib/isc/crc64.c
new file mode 100644
index 0000000..4b6c213
--- /dev/null
+++ b/lib/isc/crc64.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <inttypes.h>
+
+#include <isc/assertions.h>
+#include <isc/crc64.h>
+#include <isc/string.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+/*%<
+ * ECMA-182 CRC64 polynomial.
+ */
+static const uint64_t crc64_table[256] = {
+ 0x0000000000000000ULL, 0x42F0E1EBA9EA3693ULL, 0x85E1C3D753D46D26ULL,
+ 0xC711223CFA3E5BB5ULL, 0x493366450E42ECDFULL, 0x0BC387AEA7A8DA4CULL,
+ 0xCCD2A5925D9681F9ULL, 0x8E224479F47CB76AULL, 0x9266CC8A1C85D9BEULL,
+ 0xD0962D61B56FEF2DULL, 0x17870F5D4F51B498ULL, 0x5577EEB6E6BB820BULL,
+ 0xDB55AACF12C73561ULL, 0x99A54B24BB2D03F2ULL, 0x5EB4691841135847ULL,
+ 0x1C4488F3E8F96ED4ULL, 0x663D78FF90E185EFULL, 0x24CD9914390BB37CULL,
+ 0xE3DCBB28C335E8C9ULL, 0xA12C5AC36ADFDE5AULL, 0x2F0E1EBA9EA36930ULL,
+ 0x6DFEFF5137495FA3ULL, 0xAAEFDD6DCD770416ULL, 0xE81F3C86649D3285ULL,
+ 0xF45BB4758C645C51ULL, 0xB6AB559E258E6AC2ULL, 0x71BA77A2DFB03177ULL,
+ 0x334A9649765A07E4ULL, 0xBD68D2308226B08EULL, 0xFF9833DB2BCC861DULL,
+ 0x388911E7D1F2DDA8ULL, 0x7A79F00C7818EB3BULL, 0xCC7AF1FF21C30BDEULL,
+ 0x8E8A101488293D4DULL, 0x499B3228721766F8ULL, 0x0B6BD3C3DBFD506BULL,
+ 0x854997BA2F81E701ULL, 0xC7B97651866BD192ULL, 0x00A8546D7C558A27ULL,
+ 0x4258B586D5BFBCB4ULL, 0x5E1C3D753D46D260ULL, 0x1CECDC9E94ACE4F3ULL,
+ 0xDBFDFEA26E92BF46ULL, 0x990D1F49C77889D5ULL, 0x172F5B3033043EBFULL,
+ 0x55DFBADB9AEE082CULL, 0x92CE98E760D05399ULL, 0xD03E790CC93A650AULL,
+ 0xAA478900B1228E31ULL, 0xE8B768EB18C8B8A2ULL, 0x2FA64AD7E2F6E317ULL,
+ 0x6D56AB3C4B1CD584ULL, 0xE374EF45BF6062EEULL, 0xA1840EAE168A547DULL,
+ 0x66952C92ECB40FC8ULL, 0x2465CD79455E395BULL, 0x3821458AADA7578FULL,
+ 0x7AD1A461044D611CULL, 0xBDC0865DFE733AA9ULL, 0xFF3067B657990C3AULL,
+ 0x711223CFA3E5BB50ULL, 0x33E2C2240A0F8DC3ULL, 0xF4F3E018F031D676ULL,
+ 0xB60301F359DBE0E5ULL, 0xDA050215EA6C212FULL, 0x98F5E3FE438617BCULL,
+ 0x5FE4C1C2B9B84C09ULL, 0x1D14202910527A9AULL, 0x93366450E42ECDF0ULL,
+ 0xD1C685BB4DC4FB63ULL, 0x16D7A787B7FAA0D6ULL, 0x5427466C1E109645ULL,
+ 0x4863CE9FF6E9F891ULL, 0x0A932F745F03CE02ULL, 0xCD820D48A53D95B7ULL,
+ 0x8F72ECA30CD7A324ULL, 0x0150A8DAF8AB144EULL, 0x43A04931514122DDULL,
+ 0x84B16B0DAB7F7968ULL, 0xC6418AE602954FFBULL, 0xBC387AEA7A8DA4C0ULL,
+ 0xFEC89B01D3679253ULL, 0x39D9B93D2959C9E6ULL, 0x7B2958D680B3FF75ULL,
+ 0xF50B1CAF74CF481FULL, 0xB7FBFD44DD257E8CULL, 0x70EADF78271B2539ULL,
+ 0x321A3E938EF113AAULL, 0x2E5EB66066087D7EULL, 0x6CAE578BCFE24BEDULL,
+ 0xABBF75B735DC1058ULL, 0xE94F945C9C3626CBULL, 0x676DD025684A91A1ULL,
+ 0x259D31CEC1A0A732ULL, 0xE28C13F23B9EFC87ULL, 0xA07CF2199274CA14ULL,
+ 0x167FF3EACBAF2AF1ULL, 0x548F120162451C62ULL, 0x939E303D987B47D7ULL,
+ 0xD16ED1D631917144ULL, 0x5F4C95AFC5EDC62EULL, 0x1DBC74446C07F0BDULL,
+ 0xDAAD56789639AB08ULL, 0x985DB7933FD39D9BULL, 0x84193F60D72AF34FULL,
+ 0xC6E9DE8B7EC0C5DCULL, 0x01F8FCB784FE9E69ULL, 0x43081D5C2D14A8FAULL,
+ 0xCD2A5925D9681F90ULL, 0x8FDAB8CE70822903ULL, 0x48CB9AF28ABC72B6ULL,
+ 0x0A3B7B1923564425ULL, 0x70428B155B4EAF1EULL, 0x32B26AFEF2A4998DULL,
+ 0xF5A348C2089AC238ULL, 0xB753A929A170F4ABULL, 0x3971ED50550C43C1ULL,
+ 0x7B810CBBFCE67552ULL, 0xBC902E8706D82EE7ULL, 0xFE60CF6CAF321874ULL,
+ 0xE224479F47CB76A0ULL, 0xA0D4A674EE214033ULL, 0x67C58448141F1B86ULL,
+ 0x253565A3BDF52D15ULL, 0xAB1721DA49899A7FULL, 0xE9E7C031E063ACECULL,
+ 0x2EF6E20D1A5DF759ULL, 0x6C0603E6B3B7C1CAULL, 0xF6FAE5C07D3274CDULL,
+ 0xB40A042BD4D8425EULL, 0x731B26172EE619EBULL, 0x31EBC7FC870C2F78ULL,
+ 0xBFC9838573709812ULL, 0xFD39626EDA9AAE81ULL, 0x3A28405220A4F534ULL,
+ 0x78D8A1B9894EC3A7ULL, 0x649C294A61B7AD73ULL, 0x266CC8A1C85D9BE0ULL,
+ 0xE17DEA9D3263C055ULL, 0xA38D0B769B89F6C6ULL, 0x2DAF4F0F6FF541ACULL,
+ 0x6F5FAEE4C61F773FULL, 0xA84E8CD83C212C8AULL, 0xEABE6D3395CB1A19ULL,
+ 0x90C79D3FEDD3F122ULL, 0xD2377CD44439C7B1ULL, 0x15265EE8BE079C04ULL,
+ 0x57D6BF0317EDAA97ULL, 0xD9F4FB7AE3911DFDULL, 0x9B041A914A7B2B6EULL,
+ 0x5C1538ADB04570DBULL, 0x1EE5D94619AF4648ULL, 0x02A151B5F156289CULL,
+ 0x4051B05E58BC1E0FULL, 0x87409262A28245BAULL, 0xC5B073890B687329ULL,
+ 0x4B9237F0FF14C443ULL, 0x0962D61B56FEF2D0ULL, 0xCE73F427ACC0A965ULL,
+ 0x8C8315CC052A9FF6ULL, 0x3A80143F5CF17F13ULL, 0x7870F5D4F51B4980ULL,
+ 0xBF61D7E80F251235ULL, 0xFD913603A6CF24A6ULL, 0x73B3727A52B393CCULL,
+ 0x31439391FB59A55FULL, 0xF652B1AD0167FEEAULL, 0xB4A25046A88DC879ULL,
+ 0xA8E6D8B54074A6ADULL, 0xEA16395EE99E903EULL, 0x2D071B6213A0CB8BULL,
+ 0x6FF7FA89BA4AFD18ULL, 0xE1D5BEF04E364A72ULL, 0xA3255F1BE7DC7CE1ULL,
+ 0x64347D271DE22754ULL, 0x26C49CCCB40811C7ULL, 0x5CBD6CC0CC10FAFCULL,
+ 0x1E4D8D2B65FACC6FULL, 0xD95CAF179FC497DAULL, 0x9BAC4EFC362EA149ULL,
+ 0x158E0A85C2521623ULL, 0x577EEB6E6BB820B0ULL, 0x906FC95291867B05ULL,
+ 0xD29F28B9386C4D96ULL, 0xCEDBA04AD0952342ULL, 0x8C2B41A1797F15D1ULL,
+ 0x4B3A639D83414E64ULL, 0x09CA82762AAB78F7ULL, 0x87E8C60FDED7CF9DULL,
+ 0xC51827E4773DF90EULL, 0x020905D88D03A2BBULL, 0x40F9E43324E99428ULL,
+ 0x2CFFE7D5975E55E2ULL, 0x6E0F063E3EB46371ULL, 0xA91E2402C48A38C4ULL,
+ 0xEBEEC5E96D600E57ULL, 0x65CC8190991CB93DULL, 0x273C607B30F68FAEULL,
+ 0xE02D4247CAC8D41BULL, 0xA2DDA3AC6322E288ULL, 0xBE992B5F8BDB8C5CULL,
+ 0xFC69CAB42231BACFULL, 0x3B78E888D80FE17AULL, 0x7988096371E5D7E9ULL,
+ 0xF7AA4D1A85996083ULL, 0xB55AACF12C735610ULL, 0x724B8ECDD64D0DA5ULL,
+ 0x30BB6F267FA73B36ULL, 0x4AC29F2A07BFD00DULL, 0x08327EC1AE55E69EULL,
+ 0xCF235CFD546BBD2BULL, 0x8DD3BD16FD818BB8ULL, 0x03F1F96F09FD3CD2ULL,
+ 0x41011884A0170A41ULL, 0x86103AB85A2951F4ULL, 0xC4E0DB53F3C36767ULL,
+ 0xD8A453A01B3A09B3ULL, 0x9A54B24BB2D03F20ULL, 0x5D45907748EE6495ULL,
+ 0x1FB5719CE1045206ULL, 0x919735E51578E56CULL, 0xD367D40EBC92D3FFULL,
+ 0x1476F63246AC884AULL, 0x568617D9EF46BED9ULL, 0xE085162AB69D5E3CULL,
+ 0xA275F7C11F7768AFULL, 0x6564D5FDE549331AULL, 0x279434164CA30589ULL,
+ 0xA9B6706FB8DFB2E3ULL, 0xEB46918411358470ULL, 0x2C57B3B8EB0BDFC5ULL,
+ 0x6EA7525342E1E956ULL, 0x72E3DAA0AA188782ULL, 0x30133B4B03F2B111ULL,
+ 0xF7021977F9CCEAA4ULL, 0xB5F2F89C5026DC37ULL, 0x3BD0BCE5A45A6B5DULL,
+ 0x79205D0E0DB05DCEULL, 0xBE317F32F78E067BULL, 0xFCC19ED95E6430E8ULL,
+ 0x86B86ED5267CDBD3ULL, 0xC4488F3E8F96ED40ULL, 0x0359AD0275A8B6F5ULL,
+ 0x41A94CE9DC428066ULL, 0xCF8B0890283E370CULL, 0x8D7BE97B81D4019FULL,
+ 0x4A6ACB477BEA5A2AULL, 0x089A2AACD2006CB9ULL, 0x14DEA25F3AF9026DULL,
+ 0x562E43B4931334FEULL, 0x913F6188692D6F4BULL, 0xD3CF8063C0C759D8ULL,
+ 0x5DEDC41A34BBEEB2ULL, 0x1F1D25F19D51D821ULL, 0xD80C07CD676F8394ULL,
+ 0x9AFCE626CE85B507ULL
+};
+
+void
+isc_crc64_init(uint64_t *crc) {
+ REQUIRE(crc != NULL);
+
+ *crc = 0xffffffffffffffffULL;
+}
+
+void
+isc_crc64_update(uint64_t *crc, const void *data, size_t len) {
+ const unsigned char *p = data;
+ int i;
+
+ REQUIRE(crc != NULL);
+ REQUIRE(data != NULL);
+
+ while (len-- > 0U) {
+ i = ((int)(*crc >> 56) ^ *p++) & 0xff;
+ *crc = crc64_table[i] ^ (*crc << 8);
+ }
+}
+
+void
+isc_crc64_final(uint64_t *crc) {
+ REQUIRE(crc != NULL);
+
+ *crc ^= 0xffffffffffffffffULL;
+}
diff --git a/lib/isc/dir.c b/lib/isc/dir.c
new file mode 100644
index 0000000..b7eabe9
--- /dev/null
+++ b/lib/isc/dir.c
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <ctype.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <isc/dir.h>
+#include <isc/magic.h>
+#include <isc/netdb.h>
+#include <isc/print.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include "errno2result.h"
+
+#define ISC_DIR_MAGIC ISC_MAGIC('D', 'I', 'R', '*')
+#define VALID_DIR(dir) ISC_MAGIC_VALID(dir, ISC_DIR_MAGIC)
+
+void
+isc_dir_init(isc_dir_t *dir) {
+ REQUIRE(dir != NULL);
+
+ dir->entry.name[0] = '\0';
+ dir->entry.length = 0;
+
+ dir->handle = NULL;
+
+ dir->magic = ISC_DIR_MAGIC;
+}
+
+/*!
+ * \brief Allocate workspace and open directory stream. If either one fails,
+ * NULL will be returned.
+ */
+isc_result_t
+isc_dir_open(isc_dir_t *dir, const char *dirname) {
+ char *p;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ REQUIRE(VALID_DIR(dir));
+ REQUIRE(dirname != NULL);
+
+ /*
+ * Copy directory name. Need to have enough space for the name,
+ * a possible path separator, the wildcard, and the final NUL.
+ */
+ if (strlen(dirname) + 3 > sizeof(dir->dirname)) {
+ /* XXXDCL ? */
+ return (ISC_R_NOSPACE);
+ }
+ strlcpy(dir->dirname, dirname, sizeof(dir->dirname));
+
+ /*
+ * Append path separator, if needed, and "*".
+ */
+ p = dir->dirname + strlen(dir->dirname);
+ if (dir->dirname < p && *(p - 1) != '/') {
+ *p++ = '/';
+ }
+ *p++ = '*';
+ *p = '\0';
+
+ /*
+ * Open stream.
+ */
+ dir->handle = opendir(dirname);
+
+ if (dir->handle == NULL) {
+ return (isc__errno2result(errno));
+ }
+
+ return (result);
+}
+
+/*!
+ * \brief Return previously retrieved file or get next one.
+ *
+ * Unix's dirent has
+ * separate open and read functions, but the Win32 and DOS interfaces open
+ * the dir stream and reads the first file in one operation.
+ */
+isc_result_t
+isc_dir_read(isc_dir_t *dir) {
+ struct dirent *entry;
+
+ REQUIRE(VALID_DIR(dir) && dir->handle != NULL);
+
+ /*
+ * Fetch next file in directory.
+ */
+ entry = readdir(dir->handle);
+
+ if (entry == NULL) {
+ return (ISC_R_NOMORE);
+ }
+
+ /*
+ * Make sure that the space for the name is long enough.
+ */
+ if (sizeof(dir->entry.name) <= strlen(entry->d_name)) {
+ return (ISC_R_UNEXPECTED);
+ }
+
+ strlcpy(dir->entry.name, entry->d_name, sizeof(dir->entry.name));
+
+ /*
+ * Some dirents have d_namlen, but it is not portable.
+ */
+ dir->entry.length = strlen(entry->d_name);
+
+ return (ISC_R_SUCCESS);
+}
+
+/*!
+ * \brief Close directory stream.
+ */
+void
+isc_dir_close(isc_dir_t *dir) {
+ REQUIRE(VALID_DIR(dir) && dir->handle != NULL);
+
+ (void)closedir(dir->handle);
+ dir->handle = NULL;
+}
+
+/*!
+ * \brief Reposition directory stream at start.
+ */
+isc_result_t
+isc_dir_reset(isc_dir_t *dir) {
+ REQUIRE(VALID_DIR(dir) && dir->handle != NULL);
+
+ rewinddir(dir->handle);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_dir_chdir(const char *dirname) {
+ /*!
+ * \brief Change the current directory to 'dirname'.
+ */
+
+ REQUIRE(dirname != NULL);
+
+ if (chdir(dirname) < 0) {
+ return (isc__errno2result(errno));
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_dir_chroot(const char *dirname) {
+#ifdef HAVE_CHROOT
+ void *tmp;
+#endif /* ifdef HAVE_CHROOT */
+
+ REQUIRE(dirname != NULL);
+
+#ifdef HAVE_CHROOT
+ /*
+ * Try to use getservbyname and getprotobyname before chroot.
+ * If WKS records are used in a zone under chroot, Name Service Switch
+ * may fail to load library in chroot.
+ * Do not report errors if it fails, we do not need any result now.
+ */
+ tmp = getprotobyname("udp");
+ if (tmp != NULL) {
+ (void)getservbyname("domain", "udp");
+ }
+
+ if (chroot(dirname) < 0 || chdir("/") < 0) {
+ return (isc__errno2result(errno));
+ }
+
+ return (ISC_R_SUCCESS);
+#else /* ifdef HAVE_CHROOT */
+ return (ISC_R_NOTIMPLEMENTED);
+#endif /* ifdef HAVE_CHROOT */
+}
+
+isc_result_t
+isc_dir_createunique(char *templet) {
+ isc_result_t result;
+ char *x;
+ char *p;
+ int i;
+ int pid;
+
+ REQUIRE(templet != NULL);
+
+ /*!
+ * \brief mkdtemp is not portable, so this emulates it.
+ */
+
+ pid = getpid();
+
+ /*
+ * Replace trailing Xs with the process-id, zero-filled.
+ */
+ for (x = templet + strlen(templet) - 1; *x == 'X' && x >= templet;
+ x--, pid /= 10)
+ {
+ *x = pid % 10 + '0';
+ }
+
+ x++; /* Set x to start of ex-Xs. */
+
+ do {
+ i = mkdir(templet, 0700);
+ if (i == 0 || errno != EEXIST) {
+ break;
+ }
+
+ /*
+ * The BSD algorithm.
+ */
+ p = x;
+ while (*p != '\0') {
+ if (isdigit((unsigned char)*p)) {
+ *p = 'a';
+ } else if (*p != 'z') {
+ ++*p;
+ } else {
+ /*
+ * Reset character and move to next.
+ */
+ *p++ = 'a';
+ continue;
+ }
+
+ break;
+ }
+
+ if (*p == '\0') {
+ /*
+ * Tried all combinations. errno should already
+ * be EEXIST, but ensure it is anyway for
+ * isc__errno2result().
+ */
+ errno = EEXIST;
+ break;
+ }
+ } while (1);
+
+ if (i == -1) {
+ result = isc__errno2result(errno);
+ } else {
+ result = ISC_R_SUCCESS;
+ }
+
+ return (result);
+}
diff --git a/lib/isc/entropy.c b/lib/isc/entropy.c
new file mode 100644
index 0000000..38fcfa0
--- /dev/null
+++ b/lib/isc/entropy.c
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <openssl/err.h>
+#include <openssl/rand.h>
+
+#include <isc/types.h>
+#include <isc/util.h>
+
+#include "entropy_private.h"
+
+void
+isc_entropy_get(void *buf, size_t buflen) {
+ if (RAND_bytes(buf, buflen) < 1) {
+ FATAL_ERROR("RAND_bytes(): %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ }
+}
diff --git a/lib/isc/entropy_private.h b/lib/isc/entropy_private.h
new file mode 100644
index 0000000..df9a382
--- /dev/null
+++ b/lib/isc/entropy_private.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <stdlib.h>
+
+#include <isc/lang.h>
+
+/*! \file isc/entropy_private.h
+ * \brief Implements wrapper around CSPRNG cryptographic library calls
+ * for getting cryptographically secure pseudo-random numbers.
+ *
+ * - If OpenSSL is used, it uses RAND_bytes()
+ * - If PKCS#11 is used, it uses pkcs_C_GenerateRandom()
+ *
+ */
+
+ISC_LANG_BEGINDECLS
+
+void
+isc_entropy_get(void *buf, size_t buflen);
+/*!<
+ * \brief Get cryptographically-secure pseudo-random data.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/errno.c b/lib/isc/errno.c
new file mode 100644
index 0000000..5875f3b
--- /dev/null
+++ b/lib/isc/errno.c
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <isc/errno.h>
+#include <isc/util.h>
+
+#include "errno2result.h"
+
+isc_result_t
+isc_errno_toresult(int err) {
+ return (isc___errno2result(err, false, 0, 0));
+}
diff --git a/lib/isc/errno2result.c b/lib/isc/errno2result.c
new file mode 100644
index 0000000..1ef0c88
--- /dev/null
+++ b/lib/isc/errno2result.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include "errno2result.h"
+#include <stdbool.h>
+
+#include <isc/result.h>
+#include <isc/strerr.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+/*%
+ * Convert a POSIX errno value into an isc_result_t. The
+ * list of supported errno values is not complete; new users
+ * of this function should add any expected errors that are
+ * not already there.
+ */
+isc_result_t
+isc___errno2result(int posixerrno, bool dolog, const char *file,
+ unsigned int line) {
+ char strbuf[ISC_STRERRORSIZE];
+
+ switch (posixerrno) {
+ case ENOTDIR:
+ case ELOOP:
+ case EINVAL: /* XXX sometimes this is not for files */
+ case ENAMETOOLONG:
+ case EBADF:
+ return (ISC_R_INVALIDFILE);
+ case EISDIR:
+ return (ISC_R_NOTFILE);
+ case ENOENT:
+ return (ISC_R_FILENOTFOUND);
+ case EACCES:
+ case EPERM:
+ case EROFS:
+ return (ISC_R_NOPERM);
+ case EEXIST:
+ return (ISC_R_FILEEXISTS);
+ case EIO:
+ return (ISC_R_IOERROR);
+ case ENOMEM:
+ return (ISC_R_NOMEMORY);
+ case ENFILE:
+ case EMFILE:
+ return (ISC_R_TOOMANYOPENFILES);
+#ifdef EDQUOT
+ case EDQUOT:
+ return (ISC_R_DISCQUOTA);
+#endif /* ifdef EDQUOT */
+ case ENOSPC:
+ return (ISC_R_DISCFULL);
+#ifdef EOVERFLOW
+ case EOVERFLOW:
+ return (ISC_R_RANGE);
+#endif /* ifdef EOVERFLOW */
+ case EPIPE:
+#ifdef ECONNRESET
+ case ECONNRESET:
+#endif /* ifdef ECONNRESET */
+#ifdef ECONNABORTED
+ case ECONNABORTED:
+#endif /* ifdef ECONNABORTED */
+ return (ISC_R_CONNECTIONRESET);
+#ifdef ENOTCONN
+ case ENOTCONN:
+ return (ISC_R_NOTCONNECTED);
+#endif /* ifdef ENOTCONN */
+#ifdef ETIMEDOUT
+ case ETIMEDOUT:
+ return (ISC_R_TIMEDOUT);
+#endif /* ifdef ETIMEDOUT */
+#ifdef ENOBUFS
+ case ENOBUFS:
+ return (ISC_R_NORESOURCES);
+#endif /* ifdef ENOBUFS */
+#ifdef EAFNOSUPPORT
+ case EAFNOSUPPORT:
+ return (ISC_R_FAMILYNOSUPPORT);
+#endif /* ifdef EAFNOSUPPORT */
+#ifdef ENETDOWN
+ case ENETDOWN:
+ return (ISC_R_NETDOWN);
+#endif /* ifdef ENETDOWN */
+#ifdef EHOSTDOWN
+ case EHOSTDOWN:
+ return (ISC_R_HOSTDOWN);
+#endif /* ifdef EHOSTDOWN */
+#ifdef ENETUNREACH
+ case ENETUNREACH:
+ return (ISC_R_NETUNREACH);
+#endif /* ifdef ENETUNREACH */
+#ifdef EHOSTUNREACH
+ case EHOSTUNREACH:
+ return (ISC_R_HOSTUNREACH);
+#endif /* ifdef EHOSTUNREACH */
+#ifdef EADDRINUSE
+ case EADDRINUSE:
+ return (ISC_R_ADDRINUSE);
+#endif /* ifdef EADDRINUSE */
+ case EADDRNOTAVAIL:
+ return (ISC_R_ADDRNOTAVAIL);
+ case ECONNREFUSED:
+ return (ISC_R_CONNREFUSED);
+ default:
+ if (dolog) {
+ strerror_r(posixerrno, strbuf, sizeof(strbuf));
+ UNEXPECTED_ERROR(file, line,
+ "unable to convert errno "
+ "to isc_result: %d: %s",
+ posixerrno, strbuf);
+ }
+ /*
+ * XXXDCL would be nice if perhaps this function could
+ * return the system's error string, so the caller
+ * might have something more descriptive than "unexpected
+ * error" to log with.
+ */
+ return (ISC_R_UNEXPECTED);
+ }
+}
diff --git a/lib/isc/errno2result.h b/lib/isc/errno2result.h
new file mode 100644
index 0000000..80fee69
--- /dev/null
+++ b/lib/isc/errno2result.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file */
+
+/* XXXDCL this should be moved to lib/isc/include/isc/errno2result.h. */
+
+#include <errno.h> /* Provides errno. */
+#include <stdbool.h>
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+#define isc__errno2result(x) isc___errno2result(x, true, __FILE__, __LINE__)
+
+isc_result_t
+isc___errno2result(int posixerrno, bool dolog, const char *file,
+ unsigned int line);
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/error.c b/lib/isc/error.c
new file mode 100644
index 0000000..051a6c4
--- /dev/null
+++ b/lib/isc/error.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <isc/error.h>
+#include <isc/print.h>
+
+/*% Default unexpected callback. */
+static void
+default_unexpected_callback(const char *, int, const char *, const char *,
+ va_list) ISC_FORMAT_PRINTF(4, 0);
+
+/*% Default fatal callback. */
+static void
+default_fatal_callback(const char *, int, const char *, const char *, va_list)
+ ISC_FORMAT_PRINTF(3, 0);
+
+/*% unexpected_callback */
+static isc_errorcallback_t unexpected_callback = default_unexpected_callback;
+static isc_errorcallback_t fatal_callback = default_fatal_callback;
+
+void
+isc_error_setunexpected(isc_errorcallback_t cb) {
+ if (cb == NULL) {
+ unexpected_callback = default_unexpected_callback;
+ } else {
+ unexpected_callback = cb;
+ }
+}
+
+void
+isc_error_setfatal(isc_errorcallback_t cb) {
+ if (cb == NULL) {
+ fatal_callback = default_fatal_callback;
+ } else {
+ fatal_callback = cb;
+ }
+}
+
+void
+isc_error_unexpected(const char *file, int line, const char *func,
+ const char *format, ...) {
+ va_list args;
+
+ va_start(args, format);
+ (unexpected_callback)(file, line, func, format, args);
+ va_end(args);
+}
+
+void
+isc_error_fatal(const char *file, int line, const char *func,
+ const char *format, ...) {
+ va_list args;
+
+ va_start(args, format);
+ (fatal_callback)(file, line, func, format, args);
+ va_end(args);
+ abort();
+}
+
+static void
+default_unexpected_callback(const char *file, int line, const char *func,
+ const char *format, va_list args) {
+ fprintf(stderr, "%s:%d:%s(): ", file, line, func);
+ vfprintf(stderr, format, args);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+}
+
+static void
+default_fatal_callback(const char *file, int line, const char *func,
+ const char *format, va_list args) {
+ fprintf(stderr, "%s:%d:%s(): fatal error: ", file, line, func);
+ vfprintf(stderr, format, args);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+}
diff --git a/lib/isc/event.c b/lib/isc/event.c
new file mode 100644
index 0000000..4849d01
--- /dev/null
+++ b/lib/isc/event.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*!
+ * \file
+ */
+
+#include <isc/event.h>
+#include <isc/mem.h>
+#include <isc/util.h>
+
+/***
+ *** Events.
+ ***/
+
+static void
+destroy(isc_event_t *event) {
+ isc_mem_t *mctx = event->ev_destroy_arg;
+
+ isc_mem_put(mctx, event, event->ev_size);
+}
+
+isc_event_t *
+isc_event_allocate(isc_mem_t *mctx, void *sender, isc_eventtype_t type,
+ isc_taskaction_t action, void *arg, size_t size) {
+ isc_event_t *event;
+
+ REQUIRE(size >= sizeof(struct isc_event));
+ REQUIRE(action != NULL);
+
+ event = isc_mem_get(mctx, size);
+
+ ISC_EVENT_INIT(event, size, 0, NULL, type, action, arg, sender, destroy,
+ mctx);
+
+ return (event);
+}
+
+isc_event_t *
+isc_event_constallocate(isc_mem_t *mctx, void *sender, isc_eventtype_t type,
+ isc_taskaction_t action, const void *arg, size_t size) {
+ isc_event_t *event;
+ void *deconst_arg;
+
+ REQUIRE(size >= sizeof(struct isc_event));
+ REQUIRE(action != NULL);
+
+ event = isc_mem_get(mctx, size);
+
+ /*
+ * Removing the const attribute from "arg" is the best of two
+ * evils here. If the event->ev_arg member is made const, then
+ * it affects a great many users of the task/event subsystem
+ * which are not passing in an "arg" which starts its life as
+ * const. Changing isc_event_allocate() and isc_task_onshutdown()
+ * to not have "arg" prototyped as const (which is quite legitimate,
+ * because neither of those functions modify arg) can cause
+ * compiler whining anytime someone does want to use a const
+ * arg that they themselves never modify, such as with
+ * gcc -Wwrite-strings and using a string "arg".
+ */
+ DE_CONST(arg, deconst_arg);
+
+ ISC_EVENT_INIT(event, size, 0, NULL, type, action, deconst_arg, sender,
+ destroy, mctx);
+
+ return (event);
+}
+
+void
+isc_event_free(isc_event_t **eventp) {
+ isc_event_t *event;
+
+ REQUIRE(eventp != NULL);
+ event = *eventp;
+ *eventp = NULL;
+ REQUIRE(event != NULL);
+
+ REQUIRE(!ISC_LINK_LINKED(event, ev_link));
+ REQUIRE(!ISC_LINK_LINKED(event, ev_ratelink));
+
+ if (event->ev_destroy != NULL) {
+ (event->ev_destroy)(event);
+ }
+}
diff --git a/lib/isc/file.c b/lib/isc/file.c
new file mode 100644
index 0000000..e3a61de
--- /dev/null
+++ b/lib/isc/file.c
@@ -0,0 +1,792 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Portions Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University 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 REGENTS 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 REGENTS 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.
+ */
+
+/*! \file */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <time.h> /* Required for utimes on some platforms. */
+#include <unistd.h> /* Required for mkstemp on NetBSD. */
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif /* ifdef HAVE_SYS_MMAN_H */
+
+#include <isc/dir.h>
+#include <isc/file.h>
+#include <isc/log.h>
+#include <isc/md.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/random.h>
+#include <isc/string.h>
+#include <isc/time.h>
+#include <isc/util.h>
+
+#include "errno2result.h"
+
+/*
+ * XXXDCL As the API for accessing file statistics undoubtedly gets expanded,
+ * it might be good to provide a mechanism that allows for the results
+ * of a previous stat() to be used again without having to do another stat,
+ * such as perl's mechanism of using "_" in place of a file name to indicate
+ * that the results of the last stat should be used. But then you get into
+ * annoying MP issues. BTW, Win32 has stat().
+ */
+static isc_result_t
+file_stats(const char *file, struct stat *stats) {
+ isc_result_t result = ISC_R_SUCCESS;
+
+ REQUIRE(file != NULL);
+ REQUIRE(stats != NULL);
+
+ if (stat(file, stats) != 0) {
+ result = isc__errno2result(errno);
+ }
+
+ return (result);
+}
+
+static isc_result_t
+fd_stats(int fd, struct stat *stats) {
+ isc_result_t result = ISC_R_SUCCESS;
+
+ REQUIRE(stats != NULL);
+
+ if (fstat(fd, stats) != 0) {
+ result = isc__errno2result(errno);
+ }
+
+ return (result);
+}
+
+isc_result_t
+isc_file_getsizefd(int fd, off_t *size) {
+ isc_result_t result;
+ struct stat stats;
+
+ REQUIRE(size != NULL);
+
+ result = fd_stats(fd, &stats);
+
+ if (result == ISC_R_SUCCESS) {
+ *size = stats.st_size;
+ }
+
+ return (result);
+}
+
+isc_result_t
+isc_file_mode(const char *file, mode_t *modep) {
+ isc_result_t result;
+ struct stat stats;
+
+ REQUIRE(modep != NULL);
+
+ result = file_stats(file, &stats);
+ if (result == ISC_R_SUCCESS) {
+ *modep = (stats.st_mode & 07777);
+ }
+
+ return (result);
+}
+
+isc_result_t
+isc_file_getmodtime(const char *file, isc_time_t *modtime) {
+ isc_result_t result;
+ struct stat stats;
+
+ REQUIRE(file != NULL);
+ REQUIRE(modtime != NULL);
+
+ result = file_stats(file, &stats);
+
+ if (result == ISC_R_SUCCESS) {
+#if defined(HAVE_STAT_NSEC)
+ isc_time_set(modtime, stats.st_mtime, stats.st_mtim.tv_nsec);
+#else /* if defined(HAVE_STAT_NSEC) */
+ isc_time_set(modtime, stats.st_mtime, 0);
+#endif /* if defined(HAVE_STAT_NSEC) */
+ }
+
+ return (result);
+}
+
+isc_result_t
+isc_file_getsize(const char *file, off_t *size) {
+ isc_result_t result;
+ struct stat stats;
+
+ REQUIRE(file != NULL);
+ REQUIRE(size != NULL);
+
+ result = file_stats(file, &stats);
+
+ if (result == ISC_R_SUCCESS) {
+ *size = stats.st_size;
+ }
+
+ return (result);
+}
+
+isc_result_t
+isc_file_settime(const char *file, isc_time_t *when) {
+ struct timeval times[2];
+
+ REQUIRE(file != NULL && when != NULL);
+
+ /*
+ * tv_sec is at least a 32 bit quantity on all platforms we're
+ * dealing with, but it is signed on most (all?) of them,
+ * so we need to make sure the high bit isn't set. This unfortunately
+ * loses when either:
+ * * tv_sec becomes a signed 64 bit integer but long is 32 bits
+ * and isc_time_seconds > LONG_MAX, or
+ * * isc_time_seconds is changed to be > 32 bits but long is 32 bits
+ * and isc_time_seconds has at least 33 significant bits.
+ */
+ times[0].tv_sec = times[1].tv_sec = (long)isc_time_seconds(when);
+
+ /*
+ * Here is the real check for the high bit being set.
+ */
+ if ((times[0].tv_sec &
+ (1ULL << (sizeof(times[0].tv_sec) * CHAR_BIT - 1))) != 0)
+ {
+ return (ISC_R_RANGE);
+ }
+
+ /*
+ * isc_time_nanoseconds guarantees a value that divided by 1000 will
+ * fit into the minimum possible size tv_usec field.
+ */
+ times[0].tv_usec = times[1].tv_usec =
+ (int32_t)(isc_time_nanoseconds(when) / 1000);
+
+ if (utimes(file, times) < 0) {
+ return (isc__errno2result(errno));
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+#undef TEMPLATE
+#define TEMPLATE "tmp-XXXXXXXXXX" /*%< 14 characters. */
+
+isc_result_t
+isc_file_mktemplate(const char *path, char *buf, size_t buflen) {
+ return (isc_file_template(path, TEMPLATE, buf, buflen));
+}
+
+isc_result_t
+isc_file_template(const char *path, const char *templet, char *buf,
+ size_t buflen) {
+ const char *s;
+
+ REQUIRE(templet != NULL);
+ REQUIRE(buf != NULL);
+
+ if (path == NULL) {
+ path = "";
+ }
+
+ s = strrchr(templet, '/');
+ if (s != NULL) {
+ templet = s + 1;
+ }
+
+ s = strrchr(path, '/');
+
+ if (s != NULL) {
+ size_t prefixlen = s - path + 1;
+ if ((prefixlen + strlen(templet) + 1) > buflen) {
+ return (ISC_R_NOSPACE);
+ }
+
+ /* Copy 'prefixlen' bytes and NUL terminate. */
+ strlcpy(buf, path, ISC_MIN(prefixlen + 1, buflen));
+ strlcat(buf, templet, buflen);
+ } else {
+ if ((strlen(templet) + 1) > buflen) {
+ return (ISC_R_NOSPACE);
+ }
+
+ strlcpy(buf, templet, buflen);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static const char alphnum[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv"
+ "wxyz0123456789";
+
+isc_result_t
+isc_file_renameunique(const char *file, char *templet) {
+ char *x;
+ char *cp;
+
+ REQUIRE(file != NULL);
+ REQUIRE(templet != NULL);
+
+ cp = templet;
+ while (*cp != '\0') {
+ cp++;
+ }
+ if (cp == templet) {
+ return (ISC_R_FAILURE);
+ }
+
+ x = cp--;
+ while (cp >= templet && *cp == 'X') {
+ *cp = alphnum[isc_random_uniform(sizeof(alphnum) - 1)];
+ x = cp--;
+ }
+ while (link(file, templet) == -1) {
+ if (errno != EEXIST) {
+ return (isc__errno2result(errno));
+ }
+ for (cp = x;;) {
+ const char *t;
+ if (*cp == '\0') {
+ return (ISC_R_FAILURE);
+ }
+ t = strchr(alphnum, *cp);
+ if (t == NULL || *++t == '\0') {
+ *cp++ = alphnum[0];
+ } else {
+ *cp = *t;
+ break;
+ }
+ }
+ }
+ if (unlink(file) < 0) {
+ if (errno != ENOENT) {
+ return (isc__errno2result(errno));
+ }
+ }
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_file_openunique(char *templet, FILE **fp) {
+ int mode = S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ return (isc_file_openuniquemode(templet, mode, fp));
+}
+
+isc_result_t
+isc_file_openuniqueprivate(char *templet, FILE **fp) {
+ int mode = S_IWUSR | S_IRUSR;
+ return (isc_file_openuniquemode(templet, mode, fp));
+}
+
+isc_result_t
+isc_file_openuniquemode(char *templet, int mode, FILE **fp) {
+ int fd;
+ FILE *f;
+ isc_result_t result = ISC_R_SUCCESS;
+ char *x;
+ char *cp;
+
+ REQUIRE(templet != NULL);
+ REQUIRE(fp != NULL && *fp == NULL);
+
+ cp = templet;
+ while (*cp != '\0') {
+ cp++;
+ }
+ if (cp == templet) {
+ return (ISC_R_FAILURE);
+ }
+
+ x = cp--;
+ while (cp >= templet && *cp == 'X') {
+ *cp = alphnum[isc_random_uniform(sizeof(alphnum) - 1)];
+ x = cp--;
+ }
+
+ while ((fd = open(templet, O_RDWR | O_CREAT | O_EXCL, mode)) == -1) {
+ if (errno != EEXIST) {
+ return (isc__errno2result(errno));
+ }
+ for (cp = x;;) {
+ char *t;
+ if (*cp == '\0') {
+ return (ISC_R_FAILURE);
+ }
+ t = strchr(alphnum, *cp);
+ if (t == NULL || *++t == '\0') {
+ *cp++ = alphnum[0];
+ } else {
+ *cp = *t;
+ break;
+ }
+ }
+ }
+ f = fdopen(fd, "w+");
+ if (f == NULL) {
+ result = isc__errno2result(errno);
+ if (remove(templet) < 0) {
+ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_FILE, ISC_LOG_ERROR,
+ "remove '%s': failed", templet);
+ }
+ (void)close(fd);
+ } else {
+ *fp = f;
+ }
+
+ return (result);
+}
+
+isc_result_t
+isc_file_bopenunique(char *templet, FILE **fp) {
+ int mode = S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ return (isc_file_openuniquemode(templet, mode, fp));
+}
+
+isc_result_t
+isc_file_bopenuniqueprivate(char *templet, FILE **fp) {
+ int mode = S_IWUSR | S_IRUSR;
+ return (isc_file_openuniquemode(templet, mode, fp));
+}
+
+isc_result_t
+isc_file_bopenuniquemode(char *templet, int mode, FILE **fp) {
+ return (isc_file_openuniquemode(templet, mode, fp));
+}
+
+isc_result_t
+isc_file_remove(const char *filename) {
+ int r;
+
+ REQUIRE(filename != NULL);
+
+ r = unlink(filename);
+ if (r == 0) {
+ return (ISC_R_SUCCESS);
+ } else {
+ return (isc__errno2result(errno));
+ }
+}
+
+isc_result_t
+isc_file_rename(const char *oldname, const char *newname) {
+ int r;
+
+ REQUIRE(oldname != NULL);
+ REQUIRE(newname != NULL);
+
+ r = rename(oldname, newname);
+ if (r == 0) {
+ return (ISC_R_SUCCESS);
+ } else {
+ return (isc__errno2result(errno));
+ }
+}
+
+bool
+isc_file_exists(const char *pathname) {
+ struct stat stats;
+
+ REQUIRE(pathname != NULL);
+
+ return (file_stats(pathname, &stats) == ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_file_isplainfile(const char *filename) {
+ /*
+ * This function returns success if filename is a plain file.
+ */
+ struct stat filestat;
+ memset(&filestat, 0, sizeof(struct stat));
+
+ if ((stat(filename, &filestat)) == -1) {
+ return (isc__errno2result(errno));
+ }
+
+ if (!S_ISREG(filestat.st_mode)) {
+ return (ISC_R_INVALIDFILE);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_file_isplainfilefd(int fd) {
+ /*
+ * This function returns success if filename is a plain file.
+ */
+ struct stat filestat;
+ memset(&filestat, 0, sizeof(struct stat));
+
+ if ((fstat(fd, &filestat)) == -1) {
+ return (isc__errno2result(errno));
+ }
+
+ if (!S_ISREG(filestat.st_mode)) {
+ return (ISC_R_INVALIDFILE);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_file_isdirectory(const char *filename) {
+ /*
+ * This function returns success if filename exists and is a
+ * directory.
+ */
+ struct stat filestat;
+ memset(&filestat, 0, sizeof(struct stat));
+
+ if ((stat(filename, &filestat)) == -1) {
+ return (isc__errno2result(errno));
+ }
+
+ if (!S_ISDIR(filestat.st_mode)) {
+ return (ISC_R_INVALIDFILE);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+bool
+isc_file_isabsolute(const char *filename) {
+ REQUIRE(filename != NULL);
+ return (filename[0] == '/');
+}
+
+bool
+isc_file_iscurrentdir(const char *filename) {
+ REQUIRE(filename != NULL);
+ return (filename[0] == '.' && filename[1] == '\0');
+}
+
+bool
+isc_file_ischdiridempotent(const char *filename) {
+ REQUIRE(filename != NULL);
+ if (isc_file_isabsolute(filename)) {
+ return (true);
+ }
+ if (isc_file_iscurrentdir(filename)) {
+ return (true);
+ }
+ return (false);
+}
+
+const char *
+isc_file_basename(const char *filename) {
+ const char *s;
+
+ REQUIRE(filename != NULL);
+
+ s = strrchr(filename, '/');
+ if (s == NULL) {
+ return (filename);
+ }
+
+ return (s + 1);
+}
+
+isc_result_t
+isc_file_progname(const char *filename, char *buf, size_t buflen) {
+ const char *base;
+ size_t len;
+
+ REQUIRE(filename != NULL);
+ REQUIRE(buf != NULL);
+
+ base = isc_file_basename(filename);
+ len = strlen(base) + 1;
+
+ if (len > buflen) {
+ return (ISC_R_NOSPACE);
+ }
+ memmove(buf, base, len);
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Put the absolute name of the current directory into 'dirname', which is
+ * a buffer of at least 'length' characters. End the string with the
+ * appropriate path separator, such that the final product could be
+ * concatenated with a relative pathname to make a valid pathname string.
+ */
+static isc_result_t
+dir_current(char *dirname, size_t length) {
+ char *cwd;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ REQUIRE(dirname != NULL);
+ REQUIRE(length > 0U);
+
+ cwd = getcwd(dirname, length);
+
+ if (cwd == NULL) {
+ if (errno == ERANGE) {
+ result = ISC_R_NOSPACE;
+ } else {
+ result = isc__errno2result(errno);
+ }
+ } else {
+ if (strlen(dirname) + 1 == length) {
+ result = ISC_R_NOSPACE;
+ } else if (dirname[1] != '\0') {
+ strlcat(dirname, "/", length);
+ }
+ }
+
+ return (result);
+}
+
+isc_result_t
+isc_file_absolutepath(const char *filename, char *path, size_t pathlen) {
+ isc_result_t result;
+ result = dir_current(path, pathlen);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ if (strlen(path) + strlen(filename) + 1 > pathlen) {
+ return (ISC_R_NOSPACE);
+ }
+ strlcat(path, filename, pathlen);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_file_truncate(const char *filename, isc_offset_t size) {
+ isc_result_t result = ISC_R_SUCCESS;
+
+ if (truncate(filename, size) < 0) {
+ result = isc__errno2result(errno);
+ }
+ return (result);
+}
+
+isc_result_t
+isc_file_safecreate(const char *filename, FILE **fp) {
+ isc_result_t result;
+ int flags;
+ struct stat sb;
+ FILE *f;
+ int fd;
+
+ REQUIRE(filename != NULL);
+ REQUIRE(fp != NULL && *fp == NULL);
+
+ result = file_stats(filename, &sb);
+ if (result == ISC_R_SUCCESS) {
+ if ((sb.st_mode & S_IFREG) == 0) {
+ return (ISC_R_INVALIDFILE);
+ }
+ flags = O_WRONLY | O_TRUNC;
+ } else if (result == ISC_R_FILENOTFOUND) {
+ flags = O_WRONLY | O_CREAT | O_EXCL;
+ } else {
+ return (result);
+ }
+
+ fd = open(filename, flags, S_IRUSR | S_IWUSR);
+ if (fd == -1) {
+ return (isc__errno2result(errno));
+ }
+
+ f = fdopen(fd, "w");
+ if (f == NULL) {
+ result = isc__errno2result(errno);
+ close(fd);
+ return (result);
+ }
+
+ *fp = f;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_file_splitpath(isc_mem_t *mctx, const char *path, char **dirname,
+ char const **bname) {
+ char *dir;
+ const char *file, *slash;
+
+ if (path == NULL) {
+ return (ISC_R_INVALIDFILE);
+ }
+
+ slash = strrchr(path, '/');
+
+ if (slash == path) {
+ file = ++slash;
+ dir = isc_mem_strdup(mctx, "/");
+ } else if (slash != NULL) {
+ file = ++slash;
+ dir = isc_mem_allocate(mctx, slash - path);
+ strlcpy(dir, path, slash - path);
+ } else {
+ file = path;
+ dir = isc_mem_strdup(mctx, ".");
+ }
+
+ if (dir == NULL) {
+ return (ISC_R_NOMEMORY);
+ }
+
+ if (*file == '\0') {
+ isc_mem_free(mctx, dir);
+ return (ISC_R_INVALIDFILE);
+ }
+
+ *dirname = dir;
+ *bname = file;
+
+ return (ISC_R_SUCCESS);
+}
+
+#define DISALLOW "\\/ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+static isc_result_t
+digest2hex(unsigned char *digest, unsigned int digestlen, char *hash,
+ size_t hashlen) {
+ unsigned int i;
+ int ret;
+ for (i = 0; i < digestlen; i++) {
+ size_t left = hashlen - i * 2;
+ ret = snprintf(hash + i * 2, left, "%02x", digest[i]);
+ if (ret < 0 || (size_t)ret >= left) {
+ return (ISC_R_NOSPACE);
+ }
+ }
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_file_sanitize(const char *dir, const char *base, const char *ext,
+ char *path, size_t length) {
+ char buf[PATH_MAX];
+ unsigned char digest[ISC_MAX_MD_SIZE];
+ unsigned int digestlen;
+ char hash[ISC_MAX_MD_SIZE * 2 + 1];
+ size_t l = 0;
+ isc_result_t err;
+
+ REQUIRE(base != NULL);
+ REQUIRE(path != NULL);
+
+ l = strlen(base) + 1;
+
+ /*
+ * allow room for a full sha256 hash (64 chars
+ * plus null terminator)
+ */
+ if (l < 65U) {
+ l = 65;
+ }
+
+ if (dir != NULL) {
+ l += strlen(dir) + 1;
+ }
+ if (ext != NULL) {
+ l += strlen(ext) + 1;
+ }
+
+ if (l > length || l > (unsigned)PATH_MAX) {
+ return (ISC_R_NOSPACE);
+ }
+
+ /* Check whether the full-length SHA256 hash filename exists */
+ err = isc_md(ISC_MD_SHA256, (const unsigned char *)base, strlen(base),
+ digest, &digestlen);
+ if (err != ISC_R_SUCCESS) {
+ return (err);
+ }
+
+ err = digest2hex(digest, digestlen, hash, sizeof(hash));
+ if (err != ISC_R_SUCCESS) {
+ return (err);
+ }
+
+ snprintf(buf, sizeof(buf), "%s%s%s%s%s", dir != NULL ? dir : "",
+ dir != NULL ? "/" : "", hash, ext != NULL ? "." : "",
+ ext != NULL ? ext : "");
+ if (isc_file_exists(buf)) {
+ strlcpy(path, buf, length);
+ return (ISC_R_SUCCESS);
+ }
+
+ /* Check for a truncated SHA256 hash filename */
+ hash[16] = '\0';
+ snprintf(buf, sizeof(buf), "%s%s%s%s%s", dir != NULL ? dir : "",
+ dir != NULL ? "/" : "", hash, ext != NULL ? "." : "",
+ ext != NULL ? ext : "");
+ if (isc_file_exists(buf)) {
+ strlcpy(path, buf, length);
+ return (ISC_R_SUCCESS);
+ }
+
+ /*
+ * If neither hash filename already exists, then we'll use
+ * the original base name if it has no disallowed characters,
+ * or the truncated hash name if it does.
+ */
+ if (strpbrk(base, DISALLOW) != NULL) {
+ strlcpy(path, buf, length);
+ return (ISC_R_SUCCESS);
+ }
+
+ snprintf(buf, sizeof(buf), "%s%s%s%s%s", dir != NULL ? dir : "",
+ dir != NULL ? "/" : "", base, ext != NULL ? "." : "",
+ ext != NULL ? ext : "");
+ strlcpy(path, buf, length);
+ return (ISC_R_SUCCESS);
+}
+
+bool
+isc_file_isdirwritable(const char *path) {
+ return (access(path, W_OK | X_OK) == 0);
+}
diff --git a/lib/isc/glob.c b/lib/isc/glob.c
new file mode 100644
index 0000000..66427b3
--- /dev/null
+++ b/lib/isc/glob.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <errno.h>
+#include <glob.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <isc/errno.h>
+#include <isc/glob.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+isc_result_t
+isc_glob(const char *pattern, glob_t *pglob) {
+ REQUIRE(pattern != NULL);
+ REQUIRE(*pattern != '\0');
+ REQUIRE(pglob != NULL);
+
+ int rc = glob(pattern, GLOB_ERR, NULL, pglob);
+
+ switch (rc) {
+ case 0:
+ return (ISC_R_SUCCESS);
+
+ case GLOB_NOMATCH:
+ return (ISC_R_FILENOTFOUND);
+
+ case GLOB_NOSPACE:
+ return (ISC_R_NOMEMORY);
+
+ default:
+ return (errno != 0 ? isc_errno_toresult(errno) : ISC_R_IOERROR);
+ }
+}
+
+void
+isc_globfree(glob_t *pglob) {
+ REQUIRE(pglob != NULL);
+ globfree(pglob);
+}
diff --git a/lib/isc/hash.c b/lib/isc/hash.c
new file mode 100644
index 0000000..522e140
--- /dev/null
+++ b/lib/isc/hash.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#include "entropy_private.h"
+#include "isc/hash.h" /* IWYU pragma: keep */
+#include "isc/once.h"
+#include "isc/random.h"
+#include "isc/result.h"
+#include "isc/siphash.h"
+#include "isc/string.h"
+#include "isc/types.h"
+#include "isc/util.h"
+
+static uint8_t isc_hash_key[16];
+static uint8_t isc_hash32_key[8];
+static bool hash_initialized = false;
+static isc_once_t isc_hash_once = ISC_ONCE_INIT;
+
+static void
+isc_hash_initialize(void) {
+ /*
+ * Set a constant key to help in problem reproduction should
+ * fuzzing find a crash or a hang.
+ */
+ uint64_t key[2] = { 0, 1 };
+#if !FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ isc_entropy_get(key, sizeof(key));
+#endif /* if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
+ memmove(isc_hash_key, key, sizeof(isc_hash_key));
+#if !FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ isc_entropy_get(key, sizeof(key));
+#endif /* if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
+ memmove(isc_hash32_key, key, sizeof(isc_hash32_key));
+ hash_initialized = true;
+}
+
+static uint8_t maptolower[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
+ 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+ 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
+ 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73,
+ 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b,
+ 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
+ 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b,
+ 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3,
+ 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb,
+ 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3,
+ 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb,
+ 0xfc, 0xfd, 0xfe, 0xff
+};
+
+const void *
+isc_hash_get_initializer(void) {
+ if (!hash_initialized) {
+ RUNTIME_CHECK(
+ isc_once_do(&isc_hash_once, isc_hash_initialize) ==
+ ISC_R_SUCCESS);
+ }
+
+ return (isc_hash_key);
+}
+
+void
+isc_hash_set_initializer(const void *initializer) {
+ REQUIRE(initializer != NULL);
+
+ /*
+ * Ensure that isc_hash_initialize() is not called after
+ * isc_hash_set_initializer() is called.
+ */
+ if (!hash_initialized) {
+ RUNTIME_CHECK(
+ isc_once_do(&isc_hash_once, isc_hash_initialize) ==
+ ISC_R_SUCCESS);
+ }
+
+ memmove(isc_hash_key, initializer, sizeof(isc_hash_key));
+}
+
+uint64_t
+isc_hash64(const void *data, const size_t length, const bool case_sensitive) {
+ uint64_t hval;
+
+ REQUIRE(length == 0 || data != NULL);
+
+ RUNTIME_CHECK(isc_once_do(&isc_hash_once, isc_hash_initialize) ==
+ ISC_R_SUCCESS);
+
+ if (case_sensitive) {
+ isc_siphash24(isc_hash_key, data, length, (uint8_t *)&hval);
+ } else {
+ uint8_t input[1024];
+ REQUIRE(length <= 1024);
+ for (unsigned int i = 0; i < length; i++) {
+ input[i] = maptolower[((const uint8_t *)data)[i]];
+ }
+ isc_siphash24(isc_hash_key, input, length, (uint8_t *)&hval);
+ }
+
+ return (hval);
+}
+
+uint32_t
+isc_hash32(const void *data, const size_t length, const bool case_sensitive) {
+ uint32_t hval;
+
+ REQUIRE(length == 0 || data != NULL);
+
+ RUNTIME_CHECK(isc_once_do(&isc_hash_once, isc_hash_initialize) ==
+ ISC_R_SUCCESS);
+
+ if (case_sensitive) {
+ isc_halfsiphash24(isc_hash_key, data, length, (uint8_t *)&hval);
+ } else {
+ uint8_t input[1024];
+ REQUIRE(length <= 1024);
+ for (unsigned int i = 0; i < length; i++) {
+ input[i] = maptolower[((const uint8_t *)data)[i]];
+ }
+ isc_halfsiphash24(isc_hash_key, input, length,
+ (uint8_t *)&hval);
+ }
+
+ return (hval);
+}
diff --git a/lib/isc/heap.c b/lib/isc/heap.c
new file mode 100644
index 0000000..7b0cc28
--- /dev/null
+++ b/lib/isc/heap.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file
+ * Heap implementation of priority queues adapted from the following:
+ *
+ * \li "Introduction to Algorithms," Cormen, Leiserson, and Rivest,
+ * MIT Press / McGraw Hill, 1990, ISBN 0-262-03141-8, chapter 7.
+ *
+ * \li "Algorithms," Second Edition, Sedgewick, Addison-Wesley, 1988,
+ * ISBN 0-201-06673-4, chapter 11.
+ */
+
+#include <stdbool.h>
+
+#include <isc/heap.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/string.h> /* Required for memmove. */
+#include <isc/util.h>
+
+/*@{*/
+/*%
+ * Note: to make heap_parent and heap_left easy to compute, the first
+ * element of the heap array is not used; i.e. heap subscripts are 1-based,
+ * not 0-based. The parent is index/2, and the left-child is index*2.
+ * The right child is index*2+1.
+ */
+#define heap_parent(i) ((i) >> 1)
+#define heap_left(i) ((i) << 1)
+/*@}*/
+
+#define SIZE_INCREMENT 1024
+
+#define HEAP_MAGIC ISC_MAGIC('H', 'E', 'A', 'P')
+#define VALID_HEAP(h) ISC_MAGIC_VALID(h, HEAP_MAGIC)
+
+/*%
+ * When the heap is in a consistent state, the following invariant
+ * holds true: for every element i > 1, heap_parent(i) has a priority
+ * higher than or equal to that of i.
+ */
+#define HEAPCONDITION(i) \
+ ((i) == 1 || \
+ !heap->compare(heap->array[(i)], heap->array[heap_parent(i)]))
+
+/*% ISC heap structure. */
+struct isc_heap {
+ unsigned int magic;
+ isc_mem_t *mctx;
+ unsigned int size;
+ unsigned int size_increment;
+ unsigned int last;
+ void **array;
+ isc_heapcompare_t compare;
+ isc_heapindex_t index;
+};
+
+#ifdef ISC_HEAP_CHECK
+static void
+heap_check(isc_heap_t *heap) {
+ unsigned int i;
+ for (i = 1; i <= heap->last; i++) {
+ INSIST(HEAPCONDITION(i));
+ }
+}
+#else /* ifdef ISC_HEAP_CHECK */
+#define heap_check(x) (void)0
+#endif /* ifdef ISC_HEAP_CHECK */
+
+void
+isc_heap_create(isc_mem_t *mctx, isc_heapcompare_t compare, isc_heapindex_t idx,
+ unsigned int size_increment, isc_heap_t **heapp) {
+ isc_heap_t *heap;
+
+ REQUIRE(heapp != NULL && *heapp == NULL);
+ REQUIRE(compare != NULL);
+
+ heap = isc_mem_get(mctx, sizeof(*heap));
+ heap->magic = HEAP_MAGIC;
+ heap->size = 0;
+ heap->mctx = NULL;
+ isc_mem_attach(mctx, &heap->mctx);
+ if (size_increment == 0) {
+ heap->size_increment = SIZE_INCREMENT;
+ } else {
+ heap->size_increment = size_increment;
+ }
+ heap->last = 0;
+ heap->array = NULL;
+ heap->compare = compare;
+ heap->index = idx;
+
+ *heapp = heap;
+}
+
+void
+isc_heap_destroy(isc_heap_t **heapp) {
+ isc_heap_t *heap;
+
+ REQUIRE(heapp != NULL);
+ heap = *heapp;
+ *heapp = NULL;
+ REQUIRE(VALID_HEAP(heap));
+
+ if (heap->array != NULL) {
+ isc_mem_put(heap->mctx, heap->array,
+ heap->size * sizeof(void *));
+ }
+ heap->magic = 0;
+ isc_mem_putanddetach(&heap->mctx, heap, sizeof(*heap));
+}
+
+static void
+resize(isc_heap_t *heap) {
+ void **new_array;
+ unsigned int new_size;
+
+ REQUIRE(VALID_HEAP(heap));
+
+ new_size = heap->size + heap->size_increment;
+ new_array = isc_mem_get(heap->mctx, new_size * sizeof(void *));
+ if (heap->array != NULL) {
+ memmove(new_array, heap->array, heap->size * sizeof(void *));
+ isc_mem_put(heap->mctx, heap->array,
+ heap->size * sizeof(void *));
+ }
+ heap->size = new_size;
+ heap->array = new_array;
+}
+
+static void
+float_up(isc_heap_t *heap, unsigned int i, void *elt) {
+ unsigned int p;
+
+ for (p = heap_parent(i); i > 1 && heap->compare(elt, heap->array[p]);
+ i = p, p = heap_parent(i))
+ {
+ heap->array[i] = heap->array[p];
+ if (heap->index != NULL) {
+ (heap->index)(heap->array[i], i);
+ }
+ }
+ heap->array[i] = elt;
+ if (heap->index != NULL) {
+ (heap->index)(heap->array[i], i);
+ }
+
+ INSIST(HEAPCONDITION(i));
+ heap_check(heap);
+}
+
+static void
+sink_down(isc_heap_t *heap, unsigned int i, void *elt) {
+ unsigned int j, size, half_size;
+ size = heap->last;
+ half_size = size / 2;
+ while (i <= half_size) {
+ /* Find the smallest of the (at most) two children. */
+ j = heap_left(i);
+ if (j < size &&
+ heap->compare(heap->array[j + 1], heap->array[j]))
+ {
+ j++;
+ }
+ if (heap->compare(elt, heap->array[j])) {
+ break;
+ }
+ heap->array[i] = heap->array[j];
+ if (heap->index != NULL) {
+ (heap->index)(heap->array[i], i);
+ }
+ i = j;
+ }
+ heap->array[i] = elt;
+ if (heap->index != NULL) {
+ (heap->index)(heap->array[i], i);
+ }
+
+ INSIST(HEAPCONDITION(i));
+ heap_check(heap);
+}
+
+void
+isc_heap_insert(isc_heap_t *heap, void *elt) {
+ unsigned int new_last;
+
+ REQUIRE(VALID_HEAP(heap));
+
+ heap_check(heap);
+ new_last = heap->last + 1;
+ RUNTIME_CHECK(new_last > 0); /* overflow check */
+ if (new_last >= heap->size) {
+ resize(heap);
+ }
+ heap->last = new_last;
+
+ float_up(heap, new_last, elt);
+}
+
+void
+isc_heap_delete(isc_heap_t *heap, unsigned int idx) {
+ void *elt;
+ bool less;
+
+ REQUIRE(VALID_HEAP(heap));
+ REQUIRE(idx >= 1 && idx <= heap->last);
+
+ heap_check(heap);
+ if (heap->index != NULL) {
+ (heap->index)(heap->array[idx], 0);
+ }
+ if (idx == heap->last) {
+ heap->array[heap->last] = NULL;
+ heap->last--;
+ heap_check(heap);
+ } else {
+ elt = heap->array[heap->last];
+ heap->array[heap->last] = NULL;
+ heap->last--;
+
+ less = heap->compare(elt, heap->array[idx]);
+ heap->array[idx] = elt;
+ if (less) {
+ float_up(heap, idx, heap->array[idx]);
+ } else {
+ sink_down(heap, idx, heap->array[idx]);
+ }
+ }
+}
+
+void
+isc_heap_increased(isc_heap_t *heap, unsigned int idx) {
+ REQUIRE(VALID_HEAP(heap));
+ REQUIRE(idx >= 1 && idx <= heap->last);
+
+ float_up(heap, idx, heap->array[idx]);
+}
+
+void
+isc_heap_decreased(isc_heap_t *heap, unsigned int idx) {
+ REQUIRE(VALID_HEAP(heap));
+ REQUIRE(idx >= 1 && idx <= heap->last);
+
+ sink_down(heap, idx, heap->array[idx]);
+}
+
+void *
+isc_heap_element(isc_heap_t *heap, unsigned int idx) {
+ REQUIRE(VALID_HEAP(heap));
+ REQUIRE(idx >= 1);
+
+ heap_check(heap);
+ if (idx <= heap->last) {
+ return (heap->array[idx]);
+ }
+ return (NULL);
+}
+
+void
+isc_heap_foreach(isc_heap_t *heap, isc_heapaction_t action, void *uap) {
+ unsigned int i;
+
+ REQUIRE(VALID_HEAP(heap));
+ REQUIRE(action != NULL);
+
+ for (i = 1; i <= heap->last; i++) {
+ (action)(heap->array[i], uap);
+ }
+}
diff --git a/lib/isc/hex.c b/lib/isc/hex.c
new file mode 100644
index 0000000..be67f03
--- /dev/null
+++ b/lib/isc/hex.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <ctype.h>
+#include <stdbool.h>
+
+#include <isc/buffer.h>
+#include <isc/hex.h>
+#include <isc/lex.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#define RETERR(x) \
+ do { \
+ isc_result_t _r = (x); \
+ if (_r != ISC_R_SUCCESS) \
+ return ((_r)); \
+ } while (0)
+
+/*
+ * BEW: These static functions are copied from lib/dns/rdata.c.
+ */
+static isc_result_t
+str_totext(const char *source, isc_buffer_t *target);
+
+static isc_result_t
+mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length);
+
+static const char hex[] = "0123456789ABCDEF";
+
+isc_result_t
+isc_hex_totext(isc_region_t *source, int wordlength, const char *wordbreak,
+ isc_buffer_t *target) {
+ char buf[3];
+ unsigned int loops = 0;
+
+ if (wordlength < 2) {
+ wordlength = 2;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ while (source->length > 0) {
+ buf[0] = hex[(source->base[0] >> 4) & 0xf];
+ buf[1] = hex[(source->base[0]) & 0xf];
+ RETERR(str_totext(buf, target));
+ isc_region_consume(source, 1);
+
+ loops++;
+ if (source->length != 0 && (int)((loops + 1) * 2) >= wordlength)
+ {
+ loops = 0;
+ RETERR(str_totext(wordbreak, target));
+ }
+ }
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * State of a hex decoding process in progress.
+ */
+typedef struct {
+ int length; /*%< Desired length of binary data or -1 */
+ isc_buffer_t *target; /*%< Buffer for resulting binary data */
+ int digits; /*%< Number of buffered hex digits */
+ int val[2];
+} hex_decode_ctx_t;
+
+static void
+hex_decode_init(hex_decode_ctx_t *ctx, int length, isc_buffer_t *target) {
+ ctx->digits = 0;
+ ctx->length = length;
+ ctx->target = target;
+}
+
+static isc_result_t
+hex_decode_char(hex_decode_ctx_t *ctx, int c) {
+ const char *s;
+
+ if ((s = strchr(hex, toupper(c))) == NULL) {
+ return (ISC_R_BADHEX);
+ }
+ ctx->val[ctx->digits++] = (int)(s - hex);
+ if (ctx->digits == 2) {
+ unsigned char num;
+
+ num = (ctx->val[0] << 4) + (ctx->val[1]);
+ RETERR(mem_tobuffer(ctx->target, &num, 1));
+ if (ctx->length >= 0) {
+ if (ctx->length == 0) {
+ return (ISC_R_BADHEX);
+ } else {
+ ctx->length -= 1;
+ }
+ }
+ ctx->digits = 0;
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+hex_decode_finish(hex_decode_ctx_t *ctx) {
+ if (ctx->length > 0) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ if (ctx->digits != 0) {
+ return (ISC_R_BADHEX);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_hex_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) {
+ unsigned int before, after;
+ hex_decode_ctx_t ctx;
+ isc_textregion_t *tr;
+ isc_token_t token;
+ bool eol;
+
+ REQUIRE(length >= -2);
+
+ hex_decode_init(&ctx, length, target);
+
+ before = isc_buffer_usedlength(target);
+ while (ctx.length != 0) {
+ unsigned int i;
+
+ if (length > 0) {
+ eol = false;
+ } else {
+ eol = true;
+ }
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_string, eol));
+ if (token.type != isc_tokentype_string) {
+ break;
+ }
+ tr = &token.value.as_textregion;
+ for (i = 0; i < tr->length; i++) {
+ RETERR(hex_decode_char(&ctx, tr->base[i]));
+ }
+ }
+ after = isc_buffer_usedlength(target);
+ if (ctx.length < 0) {
+ isc_lex_ungettoken(lexer, &token);
+ }
+ RETERR(hex_decode_finish(&ctx));
+ if (length == -2 && before == after) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_hex_decodestring(const char *cstr, isc_buffer_t *target) {
+ hex_decode_ctx_t ctx;
+
+ hex_decode_init(&ctx, -1, target);
+ for (;;) {
+ int c = *cstr++;
+ if (c == '\0') {
+ break;
+ }
+ if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
+ continue;
+ }
+ RETERR(hex_decode_char(&ctx, c));
+ }
+ RETERR(hex_decode_finish(&ctx));
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+str_totext(const char *source, isc_buffer_t *target) {
+ unsigned int l;
+ isc_region_t region;
+
+ isc_buffer_availableregion(target, &region);
+ l = strlen(source);
+
+ if (l > region.length) {
+ return (ISC_R_NOSPACE);
+ }
+
+ memmove(region.base, source, l);
+ isc_buffer_add(target, l);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length) {
+ isc_region_t tr;
+
+ isc_buffer_availableregion(target, &tr);
+ if (length > tr.length) {
+ return (ISC_R_NOSPACE);
+ }
+ memmove(tr.base, base, length);
+ isc_buffer_add(target, length);
+ return (ISC_R_SUCCESS);
+}
diff --git a/lib/isc/hmac.c b/lib/isc/hmac.c
new file mode 100644
index 0000000..bc35bef
--- /dev/null
+++ b/lib/isc/hmac.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/opensslv.h>
+
+#include <isc/assertions.h>
+#include <isc/hmac.h>
+#include <isc/md.h>
+#include <isc/safe.h>
+#include <isc/string.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+#include "openssl_shim.h"
+
+isc_hmac_t *
+isc_hmac_new(void) {
+ EVP_MD_CTX *hmac = EVP_MD_CTX_new();
+ RUNTIME_CHECK(hmac != NULL);
+ return ((isc_hmac_t *)hmac);
+}
+
+void
+isc_hmac_free(isc_hmac_t *hmac) {
+ if (hmac == NULL) {
+ return;
+ }
+
+ EVP_MD_CTX_free((EVP_MD_CTX *)hmac);
+}
+
+isc_result_t
+isc_hmac_init(isc_hmac_t *hmac, const void *key, const size_t keylen,
+ const isc_md_type_t *md_type) {
+ EVP_PKEY *pkey;
+
+ REQUIRE(hmac != NULL);
+ REQUIRE(key != NULL);
+ REQUIRE(keylen <= INT_MAX);
+
+ if (md_type == NULL) {
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+
+ pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, NULL, key, keylen);
+ if (pkey == NULL) {
+ ERR_clear_error();
+ return (ISC_R_CRYPTOFAILURE);
+ }
+
+ if (EVP_DigestSignInit(hmac, NULL, md_type, NULL, pkey) != 1) {
+ EVP_PKEY_free(pkey);
+ ERR_clear_error();
+ return (ISC_R_CRYPTOFAILURE);
+ }
+
+ EVP_PKEY_free(pkey);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_hmac_reset(isc_hmac_t *hmac) {
+ REQUIRE(hmac != NULL);
+
+ if (EVP_MD_CTX_reset(hmac) != 1) {
+ ERR_clear_error();
+ return (ISC_R_CRYPTOFAILURE);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_hmac_update(isc_hmac_t *hmac, const unsigned char *buf, const size_t len) {
+ REQUIRE(hmac != NULL);
+
+ if (buf == NULL || len == 0) {
+ return (ISC_R_SUCCESS);
+ }
+
+ if (EVP_DigestSignUpdate(hmac, buf, len) != 1) {
+ ERR_clear_error();
+ return (ISC_R_CRYPTOFAILURE);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_hmac_final(isc_hmac_t *hmac, unsigned char *digest,
+ unsigned int *digestlen) {
+ REQUIRE(hmac != NULL);
+ REQUIRE(digest != NULL);
+ REQUIRE(digestlen != NULL);
+
+ size_t len = *digestlen;
+
+ if (EVP_DigestSignFinal(hmac, digest, &len) != 1) {
+ ERR_clear_error();
+ return (ISC_R_CRYPTOFAILURE);
+ }
+
+ *digestlen = (unsigned int)len;
+
+ return (ISC_R_SUCCESS);
+}
+
+const isc_md_type_t *
+isc_hmac_get_md_type(isc_hmac_t *hmac) {
+ REQUIRE(hmac != NULL);
+
+ return (EVP_MD_CTX_get0_md(hmac));
+}
+
+size_t
+isc_hmac_get_size(isc_hmac_t *hmac) {
+ REQUIRE(hmac != NULL);
+
+ return ((size_t)EVP_MD_CTX_size(hmac));
+}
+
+int
+isc_hmac_get_block_size(isc_hmac_t *hmac) {
+ REQUIRE(hmac != NULL);
+
+ return (EVP_MD_CTX_block_size(hmac));
+}
+
+isc_result_t
+isc_hmac(const isc_md_type_t *type, const void *key, const size_t keylen,
+ const unsigned char *buf, const size_t len, unsigned char *digest,
+ unsigned int *digestlen) {
+ isc_result_t res;
+ isc_hmac_t *hmac = isc_hmac_new();
+
+ res = isc_hmac_init(hmac, key, keylen, type);
+ if (res != ISC_R_SUCCESS) {
+ goto end;
+ }
+
+ res = isc_hmac_update(hmac, buf, len);
+ if (res != ISC_R_SUCCESS) {
+ goto end;
+ }
+
+ res = isc_hmac_final(hmac, digest, digestlen);
+ if (res != ISC_R_SUCCESS) {
+ goto end;
+ }
+end:
+ isc_hmac_free(hmac);
+
+ return (res);
+}
diff --git a/lib/isc/ht.c b/lib/isc/ht.c
new file mode 100644
index 0000000..eaf2b3c
--- /dev/null
+++ b/lib/isc/ht.c
@@ -0,0 +1,573 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <inttypes.h>
+#include <string.h>
+
+#include <isc/hash.h>
+#include <isc/ht.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/result.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+typedef struct isc_ht_node isc_ht_node_t;
+
+#define ISC_HT_MAGIC ISC_MAGIC('H', 'T', 'a', 'b')
+#define ISC_HT_VALID(ht) ISC_MAGIC_VALID(ht, ISC_HT_MAGIC)
+
+#define HT_NO_BITS 0
+#define HT_MIN_BITS 1
+#define HT_MAX_BITS 32
+#define HT_OVERCOMMIT 3
+
+#define HT_NEXTTABLE(idx) ((idx == 0) ? 1 : 0)
+#define TRY_NEXTTABLE(idx, ht) (idx == ht->hindex && rehashing_in_progress(ht))
+
+#define GOLDEN_RATIO_32 0x61C88647
+
+#define HASHSIZE(bits) (UINT64_C(1) << (bits))
+
+struct isc_ht_node {
+ void *value;
+ isc_ht_node_t *next;
+ uint32_t hashval;
+ size_t keysize;
+ unsigned char key[];
+};
+
+struct isc_ht {
+ unsigned int magic;
+ isc_mem_t *mctx;
+ size_t count;
+ bool case_sensitive;
+ size_t size[2];
+ uint8_t hashbits[2];
+ isc_ht_node_t **table[2];
+ uint8_t hindex;
+ uint32_t hiter; /* rehashing iterator */
+};
+
+struct isc_ht_iter {
+ isc_ht_t *ht;
+ size_t i;
+ uint8_t hindex;
+ isc_ht_node_t *cur;
+};
+
+static isc_ht_node_t *
+isc__ht_find(const isc_ht_t *ht, const unsigned char *key,
+ const uint32_t keysize, const uint32_t hashval, const uint8_t idx);
+static void
+isc__ht_add(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
+ const uint32_t hashval, const uint8_t idx, void *value);
+static isc_result_t
+isc__ht_delete(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
+ const uint32_t hashval, const uint8_t idx);
+
+static uint32_t
+rehash_bits(isc_ht_t *ht, size_t newcount);
+
+static void
+hashtable_new(isc_ht_t *ht, const uint8_t idx, const uint8_t bits);
+static void
+hashtable_free(isc_ht_t *ht, const uint8_t idx);
+static void
+hashtable_rehash(isc_ht_t *ht, uint32_t newbits);
+static void
+hashtable_rehash_one(isc_ht_t *ht);
+static void
+maybe_rehash(isc_ht_t *ht, size_t newcount);
+
+static isc_result_t
+isc__ht_iter_next(isc_ht_iter_t *it);
+
+static bool
+isc__ht_node_match(isc_ht_node_t *node, const uint32_t hashval,
+ const uint8_t *key, uint32_t keysize) {
+ return (node->hashval == hashval && node->keysize == keysize &&
+ memcmp(node->key, key, keysize) == 0);
+}
+
+static uint32_t
+hash_32(uint32_t val, unsigned int bits) {
+ REQUIRE(bits <= HT_MAX_BITS);
+ /* High bits are more random. */
+ return (val * GOLDEN_RATIO_32 >> (32 - bits));
+}
+
+static bool
+rehashing_in_progress(const isc_ht_t *ht) {
+ return (ht->table[HT_NEXTTABLE(ht->hindex)] != NULL);
+}
+
+static bool
+hashtable_is_overcommited(isc_ht_t *ht) {
+ return (ht->count >= (ht->size[ht->hindex] * HT_OVERCOMMIT));
+}
+
+static uint32_t
+rehash_bits(isc_ht_t *ht, size_t newcount) {
+ uint32_t newbits = ht->hashbits[ht->hindex];
+
+ while (newcount >= HASHSIZE(newbits) && newbits <= HT_MAX_BITS) {
+ newbits += 1;
+ }
+
+ return (newbits);
+}
+
+/*
+ * Rebuild the hashtable to reduce the load factor
+ */
+static void
+hashtable_rehash(isc_ht_t *ht, uint32_t newbits) {
+ uint8_t oldindex = ht->hindex;
+ uint32_t oldbits = ht->hashbits[oldindex];
+ uint8_t newindex = HT_NEXTTABLE(oldindex);
+
+ REQUIRE(ht->hashbits[oldindex] >= HT_MIN_BITS);
+ REQUIRE(ht->hashbits[oldindex] <= HT_MAX_BITS);
+ REQUIRE(ht->table[oldindex] != NULL);
+
+ REQUIRE(newbits <= HT_MAX_BITS);
+ REQUIRE(ht->hashbits[newindex] == HT_NO_BITS);
+ REQUIRE(ht->table[newindex] == NULL);
+
+ REQUIRE(newbits > oldbits);
+
+ hashtable_new(ht, newindex, newbits);
+
+ ht->hindex = newindex;
+
+ hashtable_rehash_one(ht);
+}
+
+static void
+hashtable_rehash_one(isc_ht_t *ht) {
+ isc_ht_node_t **newtable = ht->table[ht->hindex];
+ uint32_t oldsize = ht->size[HT_NEXTTABLE(ht->hindex)];
+ isc_ht_node_t **oldtable = ht->table[HT_NEXTTABLE(ht->hindex)];
+ isc_ht_node_t *node = NULL;
+ isc_ht_node_t *nextnode;
+
+ /* Find first non-empty node */
+ while (ht->hiter < oldsize && oldtable[ht->hiter] == NULL) {
+ ht->hiter++;
+ }
+
+ /* Rehashing complete */
+ if (ht->hiter == oldsize) {
+ hashtable_free(ht, HT_NEXTTABLE(ht->hindex));
+ ht->hiter = 0;
+ return;
+ }
+
+ /* Move the first non-empty node from old hashtable to new hashtable */
+ for (node = oldtable[ht->hiter]; node != NULL; node = nextnode) {
+ uint32_t hash = hash_32(node->hashval,
+ ht->hashbits[ht->hindex]);
+ nextnode = node->next;
+ node->next = newtable[hash];
+ newtable[hash] = node;
+ }
+
+ oldtable[ht->hiter] = NULL;
+
+ ht->hiter++;
+}
+
+static void
+maybe_rehash(isc_ht_t *ht, size_t newcount) {
+ uint32_t newbits = rehash_bits(ht, newcount);
+
+ if (ht->hashbits[ht->hindex] < newbits && newbits <= HT_MAX_BITS) {
+ hashtable_rehash(ht, newbits);
+ }
+}
+
+static void
+hashtable_new(isc_ht_t *ht, const uint8_t idx, const uint8_t bits) {
+ size_t size;
+ REQUIRE(ht->hashbits[idx] == HT_NO_BITS);
+ REQUIRE(ht->table[idx] == NULL);
+ REQUIRE(bits >= HT_MIN_BITS);
+ REQUIRE(bits <= HT_MAX_BITS);
+
+ ht->hashbits[idx] = bits;
+ ht->size[idx] = HASHSIZE(ht->hashbits[idx]);
+
+ size = ht->size[idx] * sizeof(isc_ht_node_t *);
+
+ ht->table[idx] = isc_mem_get(ht->mctx, size);
+ memset(ht->table[idx], 0, size);
+}
+
+static void
+hashtable_free(isc_ht_t *ht, const uint8_t idx) {
+ size_t size = ht->size[idx] * sizeof(isc_ht_node_t *);
+
+ for (size_t i = 0; i < ht->size[idx]; i++) {
+ isc_ht_node_t *node = ht->table[idx][i];
+ while (node != NULL) {
+ isc_ht_node_t *next = node->next;
+ ht->count--;
+ isc_mem_put(ht->mctx, node,
+ sizeof(*node) + node->keysize);
+ node = next;
+ }
+ }
+
+ isc_mem_put(ht->mctx, ht->table[idx], size);
+ ht->hashbits[idx] = HT_NO_BITS;
+ ht->table[idx] = NULL;
+}
+
+void
+isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits,
+ unsigned int options) {
+ isc_ht_t *ht = NULL;
+ bool case_sensitive = ((options & ISC_HT_CASE_INSENSITIVE) == 0);
+
+ REQUIRE(htp != NULL && *htp == NULL);
+ REQUIRE(mctx != NULL);
+ REQUIRE(bits >= 1 && bits <= HT_MAX_BITS);
+
+ ht = isc_mem_get(mctx, sizeof(*ht));
+ *ht = (isc_ht_t){
+ .case_sensitive = case_sensitive,
+ };
+
+ isc_mem_attach(mctx, &ht->mctx);
+
+ hashtable_new(ht, 0, bits);
+
+ ht->magic = ISC_HT_MAGIC;
+
+ *htp = ht;
+}
+
+void
+isc_ht_destroy(isc_ht_t **htp) {
+ isc_ht_t *ht;
+
+ REQUIRE(htp != NULL);
+ REQUIRE(ISC_HT_VALID(*htp));
+
+ ht = *htp;
+ *htp = NULL;
+ ht->magic = 0;
+
+ for (size_t i = 0; i <= 1; i++) {
+ if (ht->table[i] != NULL) {
+ hashtable_free(ht, i);
+ }
+ }
+
+ INSIST(ht->count == 0);
+
+ isc_mem_putanddetach(&ht->mctx, ht, sizeof(*ht));
+}
+
+static void
+isc__ht_add(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
+ const uint32_t hashval, const uint8_t idx, void *value) {
+ isc_ht_node_t *node;
+ uint32_t hash;
+
+ hash = hash_32(hashval, ht->hashbits[idx]);
+
+ node = isc_mem_get(ht->mctx, sizeof(*node) + keysize);
+ *node = (isc_ht_node_t){
+ .keysize = keysize,
+ .hashval = hashval,
+ .next = ht->table[idx][hash],
+ .value = value,
+ };
+
+ memmove(node->key, key, keysize);
+
+ ht->count++;
+ ht->table[idx][hash] = node;
+}
+
+isc_result_t
+isc_ht_add(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
+ void *value) {
+ uint32_t hashval;
+
+ REQUIRE(ISC_HT_VALID(ht));
+ REQUIRE(key != NULL && keysize > 0);
+
+ if (rehashing_in_progress(ht)) {
+ /* Rehash in progress */
+ hashtable_rehash_one(ht);
+ } else if (hashtable_is_overcommited(ht)) {
+ /* Rehash requested */
+ maybe_rehash(ht, ht->count);
+ }
+
+ hashval = isc_hash32(key, keysize, ht->case_sensitive);
+
+ if (isc__ht_find(ht, key, keysize, hashval, ht->hindex) != NULL) {
+ return (ISC_R_EXISTS);
+ }
+
+ isc__ht_add(ht, key, keysize, hashval, ht->hindex, value);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_ht_node_t *
+isc__ht_find(const isc_ht_t *ht, const unsigned char *key,
+ const uint32_t keysize, const uint32_t hashval,
+ const uint8_t idx) {
+ uint32_t hash;
+ uint8_t findex = idx;
+
+nexttable:
+ hash = hash_32(hashval, ht->hashbits[findex]);
+ for (isc_ht_node_t *node = ht->table[findex][hash]; node != NULL;
+ node = node->next)
+ {
+ if (isc__ht_node_match(node, hashval, key, keysize)) {
+ return (node);
+ }
+ }
+ if (TRY_NEXTTABLE(findex, ht)) {
+ /*
+ * Rehashing in progress, check the other table
+ */
+ findex = HT_NEXTTABLE(findex);
+ goto nexttable;
+ }
+
+ return (NULL);
+}
+
+isc_result_t
+isc_ht_find(const isc_ht_t *ht, const unsigned char *key,
+ const uint32_t keysize, void **valuep) {
+ uint32_t hashval;
+ isc_ht_node_t *node;
+
+ REQUIRE(ISC_HT_VALID(ht));
+ REQUIRE(key != NULL && keysize > 0);
+ REQUIRE(valuep == NULL || *valuep == NULL);
+
+ hashval = isc_hash32(key, keysize, ht->case_sensitive);
+
+ node = isc__ht_find(ht, key, keysize, hashval, ht->hindex);
+ if (node == NULL) {
+ return (ISC_R_NOTFOUND);
+ }
+
+ if (valuep != NULL) {
+ *valuep = node->value;
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+isc__ht_delete(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
+ const uint32_t hashval, const uint8_t idx) {
+ isc_ht_node_t *prev = NULL;
+ uint32_t hash;
+
+ hash = hash_32(hashval, ht->hashbits[idx]);
+
+ for (isc_ht_node_t *node = ht->table[idx][hash]; node != NULL;
+ prev = node, node = node->next)
+ {
+ if (isc__ht_node_match(node, hashval, key, keysize)) {
+ if (prev == NULL) {
+ ht->table[idx][hash] = node->next;
+ } else {
+ prev->next = node->next;
+ }
+ isc_mem_put(ht->mctx, node,
+ sizeof(*node) + node->keysize);
+ ht->count--;
+
+ return (ISC_R_SUCCESS);
+ }
+ }
+
+ return (ISC_R_NOTFOUND);
+}
+
+isc_result_t
+isc_ht_delete(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize) {
+ uint32_t hashval;
+ uint8_t hindex;
+ isc_result_t result;
+
+ REQUIRE(ISC_HT_VALID(ht));
+ REQUIRE(key != NULL && keysize > 0);
+
+ if (rehashing_in_progress(ht)) {
+ /* Rehash in progress */
+ hashtable_rehash_one(ht);
+ }
+
+ hindex = ht->hindex;
+ hashval = isc_hash32(key, keysize, ht->case_sensitive);
+nexttable:
+ result = isc__ht_delete(ht, key, keysize, hashval, hindex);
+
+ if (result == ISC_R_NOTFOUND && TRY_NEXTTABLE(hindex, ht)) {
+ /*
+ * Rehashing in progress, check the other table
+ */
+ hindex = HT_NEXTTABLE(hindex);
+ goto nexttable;
+ }
+
+ return (result);
+}
+
+void
+isc_ht_iter_create(isc_ht_t *ht, isc_ht_iter_t **itp) {
+ isc_ht_iter_t *it;
+
+ REQUIRE(ISC_HT_VALID(ht));
+ REQUIRE(itp != NULL && *itp == NULL);
+
+ it = isc_mem_get(ht->mctx, sizeof(isc_ht_iter_t));
+ *it = (isc_ht_iter_t){
+ .ht = ht,
+ .hindex = ht->hindex,
+ };
+
+ *itp = it;
+}
+
+void
+isc_ht_iter_destroy(isc_ht_iter_t **itp) {
+ isc_ht_iter_t *it;
+ isc_ht_t *ht;
+
+ REQUIRE(itp != NULL && *itp != NULL);
+
+ it = *itp;
+ *itp = NULL;
+ ht = it->ht;
+ isc_mem_put(ht->mctx, it, sizeof(*it));
+}
+
+isc_result_t
+isc_ht_iter_first(isc_ht_iter_t *it) {
+ isc_ht_t *ht;
+
+ REQUIRE(it != NULL);
+
+ ht = it->ht;
+
+ it->hindex = ht->hindex;
+ it->i = 0;
+
+ return (isc__ht_iter_next(it));
+}
+
+static isc_result_t
+isc__ht_iter_next(isc_ht_iter_t *it) {
+ isc_ht_t *ht = it->ht;
+
+ while (it->i < ht->size[it->hindex] &&
+ ht->table[it->hindex][it->i] == NULL)
+ {
+ it->i++;
+ }
+
+ if (it->i < ht->size[it->hindex]) {
+ it->cur = ht->table[it->hindex][it->i];
+
+ return (ISC_R_SUCCESS);
+ }
+
+ if (TRY_NEXTTABLE(it->hindex, ht)) {
+ it->hindex = HT_NEXTTABLE(it->hindex);
+ it->i = 0;
+ return (isc__ht_iter_next(it));
+ }
+
+ return (ISC_R_NOMORE);
+}
+
+isc_result_t
+isc_ht_iter_next(isc_ht_iter_t *it) {
+ REQUIRE(it != NULL);
+ REQUIRE(it->cur != NULL);
+
+ it->cur = it->cur->next;
+
+ if (it->cur != NULL) {
+ return (ISC_R_SUCCESS);
+ }
+
+ it->i++;
+
+ return (isc__ht_iter_next(it));
+}
+
+isc_result_t
+isc_ht_iter_delcurrent_next(isc_ht_iter_t *it) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_ht_node_t *dnode = NULL;
+ uint8_t dindex;
+ isc_ht_t *ht;
+ isc_result_t dresult;
+
+ REQUIRE(it != NULL);
+ REQUIRE(it->cur != NULL);
+
+ ht = it->ht;
+ dnode = it->cur;
+ dindex = it->hindex;
+
+ result = isc_ht_iter_next(it);
+
+ dresult = isc__ht_delete(ht, dnode->key, dnode->keysize, dnode->hashval,
+ dindex);
+ INSIST(dresult == ISC_R_SUCCESS);
+
+ return (result);
+}
+
+void
+isc_ht_iter_current(isc_ht_iter_t *it, void **valuep) {
+ REQUIRE(it != NULL);
+ REQUIRE(it->cur != NULL);
+ REQUIRE(valuep != NULL && *valuep == NULL);
+
+ *valuep = it->cur->value;
+}
+
+void
+isc_ht_iter_currentkey(isc_ht_iter_t *it, unsigned char **key,
+ size_t *keysize) {
+ REQUIRE(it != NULL);
+ REQUIRE(it->cur != NULL);
+ REQUIRE(key != NULL && *key == NULL);
+
+ *key = it->cur->key;
+ *keysize = it->cur->keysize;
+}
+
+size_t
+isc_ht_count(const isc_ht_t *ht) {
+ REQUIRE(ISC_HT_VALID(ht));
+
+ return (ht->count);
+}
diff --git a/lib/isc/httpd.c b/lib/isc/httpd.c
new file mode 100644
index 0000000..b15cc45
--- /dev/null
+++ b/lib/isc/httpd.c
@@ -0,0 +1,1147 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include <isc/buffer.h>
+#include <isc/httpd.h>
+#include <isc/mem.h>
+#include <isc/netmgr.h>
+#include <isc/print.h>
+#include <isc/refcount.h>
+#include <isc/sockaddr.h>
+#include <isc/string.h>
+#include <isc/time.h>
+#include <isc/url.h>
+#include <isc/util.h>
+
+#include "netmgr/netmgr-int.h"
+#include "picohttpparser.h"
+
+#ifdef HAVE_ZLIB
+#include <zlib.h>
+#endif /* ifdef HAVE_ZLIB */
+
+#define CHECK(m) \
+ do { \
+ result = (m); \
+ if (result != ISC_R_SUCCESS) { \
+ goto cleanup; \
+ } \
+ } while (0)
+
+/*
+ * Size the recv buffer to hold at maximum two full buffers from isc_nm_read(),
+ * so we don't have to handle the truncation.
+ */
+#define HTTP_RECVLEN ISC_NETMGR_TCP_RECVBUF_SIZE * 2
+#define HTTP_SENDLEN ISC_NETMGR_TCP_RECVBUF_SIZE
+#define HTTP_HEADERS_NUM 100
+#define HTTP_MAX_REQUEST_LEN 4096
+
+typedef enum httpd_flags {
+ CONNECTION_CLOSE = 1 << 0, /* connection must close */
+ CONNECTION_KEEP_ALIVE = 1 << 1, /* response needs a keep-alive header */
+ ACCEPT_DEFLATE = 1 << 2, /* response can be compressed */
+} httpd_flags_t;
+
+#define HTTPD_MAGIC ISC_MAGIC('H', 't', 'p', 'd')
+#define VALID_HTTPD(m) ISC_MAGIC_VALID(m, HTTPD_MAGIC)
+
+#define HTTPDMGR_MAGIC ISC_MAGIC('H', 'p', 'd', 'm')
+#define VALID_HTTPDMGR(m) ISC_MAGIC_VALID(m, HTTPDMGR_MAGIC)
+
+/*%
+ * HTTP methods.
+ */
+typedef enum { METHOD_UNKNOWN = 0, METHOD_GET = 1, METHOD_POST = 2 } method_t;
+
+/*%
+ * HTTP urls. These are the URLs we manage, and the function to call to
+ * provide the data for it. We pass in the base url (so the same function
+ * can handle multiple requests), and a structure to fill in to return a
+ * result to the client. We also pass in a pointer to be filled in for
+ * the data cleanup function.
+ */
+struct isc_httpdurl {
+ char *url;
+ isc_httpdaction_t *action;
+ void *action_arg;
+ bool isstatic;
+ isc_time_t loadtime;
+ ISC_LINK(isc_httpdurl_t) link;
+};
+
+/*% http client */
+struct isc_httpd {
+ unsigned int magic; /* HTTPD_MAGIC */
+
+ isc_httpdmgr_t *mgr; /*%< our parent */
+ ISC_LINK(isc_httpd_t) link;
+
+ isc_nmhandle_t *handle; /* Permanent pointer to handle */
+ isc_nmhandle_t *readhandle; /* Waiting for a read callback */
+
+ /*%
+ * Received data state.
+ */
+ char recvbuf[HTTP_RECVLEN]; /*%< receive buffer */
+ size_t recvlen; /*%< length recv'd */
+ size_t consume; /*%< length of last command */
+
+ method_t method;
+ int minor_version;
+ httpd_flags_t flags;
+ const char *path;
+ isc_url_parser_t up;
+ isc_time_t if_modified_since;
+};
+
+struct isc_httpdmgr {
+ unsigned int magic; /* HTTPDMGR_MAGIC */
+ isc_refcount_t references;
+ isc_mem_t *mctx;
+ isc_nmsocket_t *sock;
+
+ isc_httpdclientok_t *client_ok; /*%< client validator */
+ isc_httpdondestroy_t *ondestroy; /*%< cleanup callback */
+ void *cb_arg; /*%< argument for the above */
+
+ unsigned int flags;
+ ISC_LIST(isc_httpd_t) running; /*%< running clients */
+
+ isc_mutex_t lock;
+
+ ISC_LIST(isc_httpdurl_t) urls; /*%< urls we manage */
+ isc_httpdaction_t *render_404;
+ isc_httpdaction_t *render_500;
+};
+
+typedef struct isc_httpd_sendreq {
+ isc_mem_t *mctx;
+ isc_httpd_t *httpd;
+ isc_nmhandle_t *handle;
+
+ /*%
+ * Transmit data state.
+ *
+ * This is the data buffer we will transmit.
+ *
+ * This free function pointer is filled in by the rendering function
+ * we call. The free function is called after the data is transmitted
+ * to the client.
+ *
+ * We currently use three buffers total:
+ *
+ * sendbuffer - gets filled as we gather the data
+ *
+ * bodybuffer - for the client to fill in (which it manages, it provides
+ * the space for it, etc) -- we will pass that buffer structure back to
+ * the caller, who is responsible for managing the space it may have
+ * allocated as backing store for it. we only allocate the buffer
+ * itself, not the backing store.
+ *
+ * compbuffer - managed by us, that contains the compressed HTTP data,
+ * if compression is used.
+ */
+ isc_buffer_t *sendbuffer;
+ isc_buffer_t *compbuffer;
+
+ isc_buffer_t bodybuffer;
+
+ const char *mimetype;
+ unsigned int retcode;
+ const char *retmsg;
+ isc_httpdfree_t *freecb;
+ void *freecb_arg;
+
+} isc_httpd_sendreq_t;
+
+static isc_result_t
+httpd_newconn(isc_nmhandle_t *, isc_result_t, void *);
+static void
+httpd_request(isc_nmhandle_t *, isc_result_t, isc_region_t *, void *);
+static void
+httpd_senddone(isc_nmhandle_t *, isc_result_t, void *);
+static void
+httpd_reset(void *);
+static void
+httpd_put(void *);
+
+static void
+httpd_addheader(isc_httpd_sendreq_t *, const char *, const char *);
+static void
+httpd_addheaderuint(isc_httpd_sendreq_t *, const char *, int);
+static void
+httpd_endheaders(isc_httpd_sendreq_t *);
+static void
+httpd_response(isc_httpd_t *, isc_httpd_sendreq_t *);
+
+static isc_result_t
+process_request(isc_httpd_t *, size_t);
+
+static isc_httpdaction_t render_404;
+static isc_httpdaction_t render_500;
+
+#if ENABLE_AFL
+static void (*finishhook)(void) = NULL;
+#endif /* ENABLE_AFL */
+
+static void
+destroy_httpdmgr(isc_httpdmgr_t *);
+
+static void
+httpdmgr_attach(isc_httpdmgr_t *, isc_httpdmgr_t **);
+static void
+httpdmgr_detach(isc_httpdmgr_t **);
+
+isc_result_t
+isc_httpdmgr_create(isc_nm_t *nm, isc_mem_t *mctx, isc_sockaddr_t *addr,
+ isc_httpdclientok_t *client_ok,
+ isc_httpdondestroy_t *ondestroy, void *cb_arg,
+ isc_httpdmgr_t **httpdmgrp) {
+ isc_result_t result;
+ isc_httpdmgr_t *httpdmgr = NULL;
+
+ REQUIRE(nm != NULL);
+ REQUIRE(mctx != NULL);
+ REQUIRE(httpdmgrp != NULL && *httpdmgrp == NULL);
+
+ httpdmgr = isc_mem_get(mctx, sizeof(isc_httpdmgr_t));
+ *httpdmgr = (isc_httpdmgr_t){ .client_ok = client_ok,
+ .ondestroy = ondestroy,
+ .cb_arg = cb_arg,
+ .render_404 = render_404,
+ .render_500 = render_500 };
+
+ isc_mutex_init(&httpdmgr->lock);
+ isc_mem_attach(mctx, &httpdmgr->mctx);
+
+ ISC_LIST_INIT(httpdmgr->running);
+ ISC_LIST_INIT(httpdmgr->urls);
+
+ isc_refcount_init(&httpdmgr->references, 1);
+
+ CHECK(isc_nm_listentcp(nm, addr, httpd_newconn, httpdmgr,
+ sizeof(isc_httpd_t), 5, NULL, &httpdmgr->sock));
+
+ httpdmgr->magic = HTTPDMGR_MAGIC;
+ *httpdmgrp = httpdmgr;
+
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ httpdmgr->magic = 0;
+ isc_refcount_decrementz(&httpdmgr->references);
+ isc_refcount_destroy(&httpdmgr->references);
+ isc_mem_detach(&httpdmgr->mctx);
+ isc_mutex_destroy(&httpdmgr->lock);
+ isc_mem_put(mctx, httpdmgr, sizeof(isc_httpdmgr_t));
+
+ return (result);
+}
+
+static void
+httpdmgr_attach(isc_httpdmgr_t *source, isc_httpdmgr_t **targetp) {
+ REQUIRE(VALID_HTTPDMGR(source));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ isc_refcount_increment(&source->references);
+
+ *targetp = source;
+}
+
+static void
+httpdmgr_detach(isc_httpdmgr_t **httpdmgrp) {
+ isc_httpdmgr_t *httpdmgr = NULL;
+
+ REQUIRE(httpdmgrp != NULL);
+ REQUIRE(VALID_HTTPDMGR(*httpdmgrp));
+
+ httpdmgr = *httpdmgrp;
+ *httpdmgrp = NULL;
+
+ if (isc_refcount_decrement(&httpdmgr->references) == 1) {
+ destroy_httpdmgr(httpdmgr);
+ }
+}
+
+static void
+destroy_httpdmgr(isc_httpdmgr_t *httpdmgr) {
+ isc_httpdurl_t *url;
+
+ isc_refcount_destroy(&httpdmgr->references);
+
+ LOCK(&httpdmgr->lock);
+
+ REQUIRE((httpdmgr->flags & ISC_HTTPDMGR_SHUTTINGDOWN) != 0);
+ REQUIRE(ISC_LIST_EMPTY(httpdmgr->running));
+
+ httpdmgr->magic = 0;
+
+ if (httpdmgr->sock != NULL) {
+ isc_nmsocket_close(&httpdmgr->sock);
+ }
+
+ /*
+ * Clear out the list of all actions we know about. Just free the
+ * memory.
+ */
+ url = ISC_LIST_HEAD(httpdmgr->urls);
+ while (url != NULL) {
+ isc_mem_free(httpdmgr->mctx, url->url);
+ ISC_LIST_UNLINK(httpdmgr->urls, url, link);
+ isc_mem_put(httpdmgr->mctx, url, sizeof(isc_httpdurl_t));
+ url = ISC_LIST_HEAD(httpdmgr->urls);
+ }
+
+ UNLOCK(&httpdmgr->lock);
+ isc_mutex_destroy(&httpdmgr->lock);
+
+ if (httpdmgr->ondestroy != NULL) {
+ (httpdmgr->ondestroy)(httpdmgr->cb_arg);
+ }
+ isc_mem_putanddetach(&httpdmgr->mctx, httpdmgr, sizeof(isc_httpdmgr_t));
+}
+
+static bool
+name_match(const struct phr_header *header, const char *match) {
+ size_t match_len = strlen(match);
+ if (match_len != header->name_len) {
+ return (false);
+ }
+ return (strncasecmp(header->name, match, match_len) == 0);
+}
+
+static bool
+value_match(const struct phr_header *header, const char *match) {
+ size_t match_len = strlen(match);
+ size_t limit;
+
+ if (match_len > header->value_len) {
+ return (false);
+ }
+
+ limit = header->value_len - match_len + 1;
+
+ for (size_t i = 0; i < limit; i++) {
+ if (isspace(header->value[i])) {
+ while (i < limit && isspace(header->value[i])) {
+ i++;
+ }
+ continue;
+ }
+
+ if (strncasecmp(&header->value[i], match, match_len) == 0) {
+ i += match_len;
+ /*
+ * Sanity check; f.e. for 'deflate' match only
+ * 'deflate[,;]', but not 'deflateyou'
+ */
+ if (i == header->value_len || header->value[i] == ',' ||
+ header->value[i] == ';')
+ {
+ return (true);
+ }
+ }
+
+ while (i < limit && header->value[i] != ',') {
+ i++;
+ }
+ }
+ return (false);
+}
+
+static isc_result_t
+process_request(isc_httpd_t *httpd, size_t last_len) {
+ int pret;
+ const char *method = NULL;
+ size_t method_len = 0;
+ const char *path;
+ size_t path_len = 0;
+ struct phr_header headers[HTTP_HEADERS_NUM];
+ size_t num_headers;
+ isc_result_t result;
+
+ num_headers = ARRAY_SIZE(headers);
+
+ pret = phr_parse_request(httpd->recvbuf, httpd->recvlen, &method,
+ &method_len, &path, &path_len,
+ &httpd->minor_version, headers, &num_headers,
+ last_len);
+
+ if (pret == -1) {
+ /* Parse Error */
+ return (ISC_R_UNEXPECTED);
+ } else if (pret == -2) {
+ /* Need more data */
+ return (ISC_R_NOMORE);
+ }
+
+ INSIST(pret > 0);
+
+ if (pret > HTTP_MAX_REQUEST_LEN) {
+ return (ISC_R_RANGE);
+ }
+
+ httpd->consume = pret;
+
+ /*
+ * Determine if this is a POST or GET method. Any other values will
+ * cause an error to be returned.
+ */
+ if (strncmp(method, "GET ", method_len) == 0) {
+ httpd->method = METHOD_GET;
+ } else if (strncmp(method, "POST ", method_len) == 0) {
+ httpd->method = METHOD_POST;
+ } else {
+ return (ISC_R_RANGE);
+ }
+
+ /*
+ * Parse the URL
+ */
+ result = isc_url_parse(path, path_len, 0, &httpd->up);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ httpd->path = path;
+
+ /*
+ * Examine headers that can affect this request's response
+ */
+ httpd->flags = 0;
+
+ size_t content_len = 0;
+ bool keep_alive = false;
+ bool host_header = false;
+
+ isc_time_set(&httpd->if_modified_since, 0, 0);
+
+ for (size_t i = 0; i < num_headers; i++) {
+ struct phr_header *header = &headers[i];
+
+ if (name_match(header, "Content-Length")) {
+ char *endptr;
+ long val = strtol(header->value, &endptr, 10);
+
+ errno = 0;
+
+ /* ensure we consumed all digits */
+ if ((header->value + header->value_len) != endptr) {
+ return (ISC_R_BADNUMBER);
+ }
+ /* ensure there was no minus sign */
+ if (val < 0) {
+ return (ISC_R_BADNUMBER);
+ }
+ /* ensure it did not overflow */
+ if (errno != 0) {
+ return (ISC_R_RANGE);
+ }
+ content_len = val;
+ } else if (name_match(header, "Connection")) {
+ /* multiple fields in a connection header are allowed */
+ if (value_match(header, "close")) {
+ httpd->flags |= CONNECTION_CLOSE;
+ }
+ if (value_match(header, "keep-alive")) {
+ keep_alive = true;
+ }
+ } else if (name_match(header, "Host")) {
+ host_header = true;
+ } else if (name_match(header, "Accept-Encoding")) {
+ if (value_match(header, "deflate")) {
+ httpd->flags |= ACCEPT_DEFLATE;
+ }
+ } else if (name_match(header, "If-Modified-Since") &&
+ header->value_len < ISC_FORMATHTTPTIMESTAMP_SIZE)
+ {
+ char timestamp[ISC_FORMATHTTPTIMESTAMP_SIZE + 1];
+ memmove(timestamp, header->value, header->value_len);
+ timestamp[header->value_len] = 0;
+
+ /* Ignore the value if it can't be parsed */
+ (void)isc_time_parsehttptimestamp(
+ timestamp, &httpd->if_modified_since);
+ }
+ }
+
+ /*
+ * The Content-Length is optional in an HTTP request.
+ * For a GET the length must be zero.
+ */
+ if (httpd->method == METHOD_GET && content_len != 0) {
+ return (ISC_R_BADNUMBER);
+ }
+
+ if (content_len >= HTTP_MAX_REQUEST_LEN) {
+ return (ISC_R_RANGE);
+ }
+
+ size_t consume = httpd->consume + content_len;
+ if (consume > httpd->recvlen) {
+ /* The request data isn't complete yet. */
+ return (ISC_R_NOMORE);
+ }
+
+ /* Consume the request's data, which we do not use. */
+ httpd->consume = consume;
+
+ switch (httpd->minor_version) {
+ case 0:
+ /*
+ * RFC 9112 section 9.3 says close takes priority if
+ * keep-alive is also present
+ */
+ if ((httpd->flags & CONNECTION_CLOSE) == 0 && keep_alive) {
+ httpd->flags |= CONNECTION_KEEP_ALIVE;
+ } else {
+ httpd->flags |= CONNECTION_CLOSE;
+ }
+ break;
+ case 1:
+ if (!host_header) {
+ return (ISC_R_RANGE);
+ }
+ break;
+ default:
+ return (ISC_R_UNEXPECTED);
+ }
+
+ /*
+ * Looks like a a valid request, so now we know we won't have
+ * to process this buffer again. We can NULL-terminate the
+ * URL for the caller's benefit, and set recvlen to 0 so
+ * the next read will overwrite this one instead of appending
+ * to the buffer.
+ */
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+httpd_reset(void *arg) {
+ isc_httpd_t *httpd = (isc_httpd_t *)arg;
+ isc_httpdmgr_t *httpdmgr = NULL;
+
+ REQUIRE(VALID_HTTPD(httpd));
+
+ httpdmgr = httpd->mgr;
+
+ REQUIRE(VALID_HTTPDMGR(httpdmgr));
+
+ LOCK(&httpdmgr->lock);
+ ISC_LIST_UNLINK(httpdmgr->running, httpd, link);
+ UNLOCK(&httpdmgr->lock);
+
+ httpd->recvbuf[0] = 0;
+ httpd->recvlen = 0;
+ httpd->consume = 0;
+ httpd->method = METHOD_UNKNOWN;
+ httpd->flags = 0;
+
+ httpd->minor_version = -1;
+ httpd->path = NULL;
+ httpd->up = (isc_url_parser_t){ 0 };
+ isc_time_set(&httpd->if_modified_since, 0, 0);
+}
+
+static void
+isc__httpd_sendreq_free(isc_httpd_sendreq_t *req) {
+ /* Clean up buffers */
+
+ isc_buffer_free(&req->sendbuffer);
+
+ isc_mem_putanddetach(&req->mctx, req, sizeof(*req));
+}
+
+static isc_httpd_sendreq_t *
+isc__httpd_sendreq_new(isc_httpd_t *httpd) {
+ isc_httpdmgr_t *httpdmgr = httpd->mgr;
+ isc_httpd_sendreq_t *req;
+
+ REQUIRE(VALID_HTTPDMGR(httpdmgr));
+
+ req = isc_mem_get(httpdmgr->mctx, sizeof(*req));
+ *req = (isc_httpd_sendreq_t){ 0 };
+
+ isc_mem_attach(httpdmgr->mctx, &req->mctx);
+
+ /*
+ * Initialize the buffer for our headers.
+ */
+ isc_buffer_allocate(req->mctx, &req->sendbuffer, HTTP_SENDLEN);
+ isc_buffer_clear(req->sendbuffer);
+ isc_buffer_setautorealloc(req->sendbuffer, true);
+
+ isc_buffer_initnull(&req->bodybuffer);
+
+ return (req);
+}
+
+static void
+httpd_put(void *arg) {
+ isc_httpd_t *httpd = (isc_httpd_t *)arg;
+ isc_httpdmgr_t *mgr = NULL;
+
+ REQUIRE(VALID_HTTPD(httpd));
+
+ mgr = httpd->mgr;
+ REQUIRE(VALID_HTTPDMGR(mgr));
+
+ httpd->magic = 0;
+ httpd->mgr = NULL;
+
+ isc_mem_put(mgr->mctx, httpd, sizeof(*httpd));
+
+ httpdmgr_detach(&mgr);
+
+#if ENABLE_AFL
+ if (finishhook != NULL) {
+ finishhook();
+ }
+#endif /* ENABLE_AFL */
+}
+
+static void
+new_httpd(isc_httpdmgr_t *httpdmgr, isc_nmhandle_t *handle) {
+ isc_httpd_t *httpd = NULL;
+
+ REQUIRE(VALID_HTTPDMGR(httpdmgr));
+
+ httpd = isc_nmhandle_getdata(handle);
+ if (httpd == NULL) {
+ httpd = isc_mem_get(httpdmgr->mctx, sizeof(*httpd));
+ *httpd = (isc_httpd_t){ .handle = NULL };
+ httpdmgr_attach(httpdmgr, &httpd->mgr);
+ }
+
+ if (httpd->handle == NULL) {
+ isc_nmhandle_setdata(handle, httpd, httpd_reset, httpd_put);
+ httpd->handle = handle;
+ } else {
+ INSIST(httpd->handle == handle);
+ }
+
+ ISC_LINK_INIT(httpd, link);
+
+ httpd->magic = HTTPD_MAGIC;
+
+ LOCK(&httpdmgr->lock);
+ ISC_LIST_APPEND(httpdmgr->running, httpd, link);
+ UNLOCK(&httpdmgr->lock);
+
+ isc_nmhandle_attach(httpd->handle, &httpd->readhandle);
+ isc_nm_read(handle, httpd_request, httpdmgr);
+}
+
+static isc_result_t
+httpd_newconn(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
+ isc_httpdmgr_t *httpdmgr = (isc_httpdmgr_t *)arg;
+ isc_sockaddr_t peeraddr;
+
+ REQUIRE(VALID_HTTPDMGR(httpdmgr));
+
+ if ((httpdmgr->flags & ISC_HTTPDMGR_SHUTTINGDOWN) != 0) {
+ return (ISC_R_CANCELED);
+ } else if (result == ISC_R_CANCELED) {
+ isc_httpdmgr_shutdown(&httpdmgr);
+ return (result);
+ } else if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ peeraddr = isc_nmhandle_peeraddr(handle);
+ if (httpdmgr->client_ok != NULL &&
+ !(httpdmgr->client_ok)(&peeraddr, httpdmgr->cb_arg))
+ {
+ return (ISC_R_FAILURE);
+ }
+
+ new_httpd(httpdmgr, handle);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+render_404(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, void *arg,
+ unsigned int *retcode, const char **retmsg, const char **mimetype,
+ isc_buffer_t *b, isc_httpdfree_t **freecb, void **freecb_args) {
+ static char msg[] = "No such URL.\r\n";
+
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ UNUSED(arg);
+
+ *retcode = 404;
+ *retmsg = "No such URL";
+ *mimetype = "text/plain";
+ isc_buffer_reinit(b, msg, strlen(msg));
+ isc_buffer_add(b, strlen(msg));
+ *freecb = NULL;
+ *freecb_args = NULL;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+render_500(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, void *arg,
+ unsigned int *retcode, const char **retmsg, const char **mimetype,
+ isc_buffer_t *b, isc_httpdfree_t **freecb, void **freecb_args) {
+ static char msg[] = "Internal server failure.\r\n";
+
+ UNUSED(httpd);
+ UNUSED(urlinfo);
+ UNUSED(arg);
+
+ *retcode = 500;
+ *retmsg = "Internal server failure";
+ *mimetype = "text/plain";
+ isc_buffer_reinit(b, msg, strlen(msg));
+ isc_buffer_add(b, strlen(msg));
+ *freecb = NULL;
+ *freecb_args = NULL;
+
+ return (ISC_R_SUCCESS);
+}
+
+#ifdef HAVE_ZLIB
+/*%<
+ * Tries to compress httpd->bodybuffer to httpd->compbuffer, extending it
+ * if necessary.
+ *
+ * Requires:
+ *\li httpd a valid isc_httpd_t object
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS -- all is well.
+ *\li #ISC_R_NOMEMORY -- not enough memory to compress data
+ *\li #ISC_R_FAILURE -- error during compression or compressed
+ * data would be larger than input data
+ */
+static isc_result_t
+httpd_compress(isc_httpd_sendreq_t *req) {
+ z_stream zstr;
+ int ret, inputlen;
+
+ /*
+ * We're setting output buffer size to input size so it fails if the
+ * compressed data size would be bigger than the input size.
+ */
+ inputlen = isc_buffer_usedlength(&req->bodybuffer);
+ if (inputlen == 0) {
+ return (ISC_R_FAILURE);
+ }
+
+ isc_buffer_allocate(req->mctx, &req->compbuffer, inputlen);
+ isc_buffer_clear(req->compbuffer);
+
+ zstr = (z_stream){
+ .total_in = inputlen,
+ .avail_out = inputlen,
+ .avail_in = inputlen,
+ .next_in = isc_buffer_base(&req->bodybuffer),
+ .next_out = isc_buffer_base(req->compbuffer),
+ };
+
+ ret = deflateInit(&zstr, Z_DEFAULT_COMPRESSION);
+ if (ret == Z_OK) {
+ ret = deflate(&zstr, Z_FINISH);
+ }
+ deflateEnd(&zstr);
+ if (ret == Z_STREAM_END) {
+ isc_buffer_add(req->compbuffer, zstr.total_out);
+ return (ISC_R_SUCCESS);
+ } else {
+ isc_buffer_free(&req->compbuffer);
+ return (ISC_R_FAILURE);
+ }
+}
+#endif /* ifdef HAVE_ZLIB */
+
+static void
+prepare_response(isc_httpdmgr_t *mgr, isc_httpd_t *httpd,
+ isc_httpd_sendreq_t **reqp) {
+ isc_httpd_sendreq_t *req = NULL;
+ isc_time_t now;
+ char datebuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
+ const char *path = "/";
+ size_t path_len = 1;
+ bool is_compressed = false;
+ isc_httpdurl_t *url = NULL;
+ isc_result_t result;
+
+ REQUIRE(VALID_HTTPD(httpd));
+ REQUIRE(reqp != NULL && *reqp == NULL);
+
+ isc_time_now(&now);
+ isc_time_formathttptimestamp(&now, datebuf, sizeof(datebuf));
+
+ if (httpd->up.field_set & (1 << ISC_UF_PATH)) {
+ path = &httpd->path[httpd->up.field_data[ISC_UF_PATH].off];
+ path_len = httpd->up.field_data[ISC_UF_PATH].len;
+ }
+
+ LOCK(&mgr->lock);
+ url = ISC_LIST_HEAD(mgr->urls);
+ while (url != NULL) {
+ if (strncmp(path, url->url, path_len) == 0) {
+ break;
+ }
+ url = ISC_LIST_NEXT(url, link);
+ }
+ UNLOCK(&mgr->lock);
+
+ req = isc__httpd_sendreq_new(httpd);
+
+ if (url == NULL) {
+ result = mgr->render_404(httpd, NULL, NULL, &req->retcode,
+ &req->retmsg, &req->mimetype,
+ &req->bodybuffer, &req->freecb,
+ &req->freecb_arg);
+ } else {
+ result = url->action(httpd, url, url->action_arg, &req->retcode,
+ &req->retmsg, &req->mimetype,
+ &req->bodybuffer, &req->freecb,
+ &req->freecb_arg);
+ }
+ if (result != ISC_R_SUCCESS) {
+ result = mgr->render_500(httpd, url, NULL, &req->retcode,
+ &req->retmsg, &req->mimetype,
+ &req->bodybuffer, &req->freecb,
+ &req->freecb_arg);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ }
+
+#ifdef HAVE_ZLIB
+ if ((httpd->flags & ACCEPT_DEFLATE) != 0) {
+ result = httpd_compress(req);
+ if (result == ISC_R_SUCCESS) {
+ is_compressed = true;
+ }
+ }
+#endif /* ifdef HAVE_ZLIB */
+
+ httpd_response(httpd, req);
+ /* RFC 9112 § 9.6: SHOULD send Connection: close in last response */
+ if ((httpd->flags & CONNECTION_CLOSE) != 0) {
+ httpd_addheader(req, "Connection", "close");
+ } else if ((httpd->flags & CONNECTION_KEEP_ALIVE) != 0) {
+ httpd_addheader(req, "Connection", "Keep-Alive");
+ }
+ httpd_addheader(req, "Content-Type", req->mimetype);
+ httpd_addheader(req, "Date", datebuf);
+ httpd_addheader(req, "Expires", datebuf);
+
+ if (url != NULL && url->isstatic) {
+ char loadbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
+ isc_time_formathttptimestamp(&url->loadtime, loadbuf,
+ sizeof(loadbuf));
+ httpd_addheader(req, "Last-Modified", loadbuf);
+ httpd_addheader(req, "Cache-Control: public", NULL);
+ } else {
+ httpd_addheader(req, "Last-Modified", datebuf);
+ httpd_addheader(req, "Pragma: no-cache", NULL);
+ httpd_addheader(req, "Cache-Control: no-cache", NULL);
+ }
+
+ httpd_addheader(req, "Server: libisc", NULL);
+
+ if (is_compressed) {
+ httpd_addheader(req, "Content-Encoding", "deflate");
+ httpd_addheaderuint(req, "Content-Length",
+ isc_buffer_usedlength(req->compbuffer));
+ } else {
+ httpd_addheaderuint(req, "Content-Length",
+ isc_buffer_usedlength(&req->bodybuffer));
+ }
+
+ httpd_endheaders(req); /* done */
+
+ /*
+ * Append either the compressed or the non-compressed response body to
+ * the response headers and store the result in httpd->sendbuffer.
+ */
+ if (is_compressed) {
+ isc_buffer_putmem(req->sendbuffer,
+ isc_buffer_base(req->compbuffer),
+ isc_buffer_usedlength(req->compbuffer));
+ isc_buffer_free(&req->compbuffer);
+ } else {
+ isc_buffer_putmem(req->sendbuffer,
+ isc_buffer_base(&req->bodybuffer),
+ isc_buffer_usedlength(&req->bodybuffer));
+ }
+
+ /* Free the bodybuffer */
+ if (req->freecb != NULL && isc_buffer_length(&req->bodybuffer) > 0) {
+ req->freecb(&req->bodybuffer, req->freecb_arg);
+ }
+
+ /* Consume the request from the recv buffer. */
+ INSIST(httpd->consume != 0);
+ INSIST(httpd->consume <= httpd->recvlen);
+ if (httpd->consume < httpd->recvlen) {
+ memmove(httpd->recvbuf, httpd->recvbuf + httpd->consume,
+ httpd->recvlen - httpd->consume);
+ }
+ httpd->recvlen -= httpd->consume;
+ httpd->consume = 0;
+
+ /*
+ * We don't need to attach to httpd here because it gets only cleaned
+ * when the last handle has been detached
+ */
+ req->httpd = httpd;
+
+ *reqp = req;
+}
+
+static void
+httpd_request(isc_nmhandle_t *handle, isc_result_t eresult,
+ isc_region_t *region, void *arg) {
+ isc_result_t result;
+ isc_httpdmgr_t *mgr = arg;
+ isc_httpd_t *httpd = NULL;
+ isc_httpd_sendreq_t *req = NULL;
+ isc_region_t r;
+ size_t last_len = 0;
+
+ httpd = isc_nmhandle_getdata(handle);
+
+ REQUIRE(VALID_HTTPD(httpd));
+
+ REQUIRE(httpd->handle == handle);
+
+ if (httpd->readhandle == NULL) {
+ /* The channel has been already closed, just bail out */
+ return;
+ }
+
+ if (eresult != ISC_R_SUCCESS) {
+ goto close_readhandle;
+ }
+
+ REQUIRE(httpd->readhandle == handle);
+
+ isc_nm_pauseread(httpd->readhandle);
+
+ /*
+ * If we are being called from httpd_senddone(), the last HTTP request
+ * was processed successfully, reset the last_len to 0, even if there's
+ * data in the httpd->recvbuf.
+ */
+ last_len = (region == NULL) ? 0 : httpd->recvlen;
+
+ /* Store the received data into the recvbuf */
+ if (region != NULL) {
+ if (httpd->recvlen + region->length > sizeof(httpd->recvbuf)) {
+ goto close_readhandle;
+ }
+
+ memmove(httpd->recvbuf + httpd->recvlen, region->base,
+ region->length);
+ httpd->recvlen += region->length;
+ }
+
+ result = process_request(httpd, last_len);
+
+ if (result == ISC_R_NOMORE) {
+ if (httpd->recvlen > HTTP_MAX_REQUEST_LEN) {
+ goto close_readhandle;
+ }
+
+ /* Wait for more data, the readhandle is still attached */
+ isc_nm_resumeread(httpd->readhandle);
+ return;
+ }
+
+ /* XXXFANF it would be more polite to reply 400 bad request */
+ if (result != ISC_R_SUCCESS) {
+ goto close_readhandle;
+ }
+
+ prepare_response(mgr, httpd, &req);
+
+ /*
+ * Determine total response size.
+ */
+ isc_buffer_usedregion(req->sendbuffer, &r);
+
+ isc_nmhandle_attach(httpd->handle, &req->handle);
+ isc_nm_send(httpd->handle, &r, httpd_senddone, req);
+ return;
+
+close_readhandle:
+ isc_nm_pauseread(httpd->readhandle);
+ isc_nmhandle_detach(&httpd->readhandle);
+}
+
+void
+isc_httpdmgr_shutdown(isc_httpdmgr_t **httpdmgrp) {
+ isc_httpdmgr_t *httpdmgr;
+ isc_httpd_t *httpd;
+
+ REQUIRE(httpdmgrp != NULL);
+ REQUIRE(VALID_HTTPDMGR(*httpdmgrp));
+
+ httpdmgr = *httpdmgrp;
+ *httpdmgrp = NULL;
+
+ isc_nm_stoplistening(httpdmgr->sock);
+
+ LOCK(&httpdmgr->lock);
+ httpdmgr->flags |= ISC_HTTPDMGR_SHUTTINGDOWN;
+
+ httpd = ISC_LIST_HEAD(httpdmgr->running);
+ while (httpd != NULL) {
+ isc_nm_cancelread(httpd->readhandle);
+ httpd = ISC_LIST_NEXT(httpd, link);
+ }
+ UNLOCK(&httpdmgr->lock);
+
+ isc_nmsocket_close(&httpdmgr->sock);
+
+ httpdmgr_detach(&httpdmgr);
+}
+
+static void
+httpd_response(isc_httpd_t *httpd, isc_httpd_sendreq_t *req) {
+ isc_result_t result;
+
+ result = isc_buffer_printf(req->sendbuffer, "HTTP/1.%u %03u %s\r\n",
+ httpd->minor_version, req->retcode,
+ req->retmsg);
+
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+}
+
+static void
+httpd_addheader(isc_httpd_sendreq_t *req, const char *name, const char *val) {
+ isc_result_t result;
+
+ if (val != NULL) {
+ result = isc_buffer_printf(req->sendbuffer, "%s: %s\r\n", name,
+ val);
+ } else {
+ result = isc_buffer_printf(req->sendbuffer, "%s\r\n", name);
+ }
+
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+}
+
+static void
+httpd_endheaders(isc_httpd_sendreq_t *req) {
+ isc_result_t result;
+
+ result = isc_buffer_printf(req->sendbuffer, "\r\n");
+
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+}
+
+static void
+httpd_addheaderuint(isc_httpd_sendreq_t *req, const char *name, int val) {
+ isc_result_t result;
+
+ result = isc_buffer_printf(req->sendbuffer, "%s: %d\r\n", name, val);
+
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+}
+
+static void
+httpd_senddone(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) {
+ isc_httpd_sendreq_t *req = (isc_httpd_sendreq_t *)arg;
+ isc_httpd_t *httpd = req->httpd;
+
+ REQUIRE(VALID_HTTPD(httpd));
+
+ if (httpd->readhandle == NULL) {
+ goto detach;
+ }
+
+ if (eresult == ISC_R_SUCCESS && (httpd->flags & CONNECTION_CLOSE) == 0)
+ {
+ /*
+ * Calling httpd_request() with region NULL restarts
+ * reading.
+ */
+ httpd_request(handle, ISC_R_SUCCESS, NULL, httpd->mgr);
+ } else {
+ isc_nm_cancelread(httpd->readhandle);
+ }
+
+detach:
+ isc_nmhandle_detach(&handle);
+
+ isc__httpd_sendreq_free(req);
+}
+
+isc_result_t
+isc_httpdmgr_addurl(isc_httpdmgr_t *httpdmgr, const char *url, bool isstatic,
+ isc_httpdaction_t *func, void *arg) {
+ isc_httpdurl_t *item;
+
+ REQUIRE(VALID_HTTPDMGR(httpdmgr));
+
+ if (url == NULL) {
+ httpdmgr->render_404 = func;
+ return (ISC_R_SUCCESS);
+ }
+
+ item = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpdurl_t));
+
+ item->url = isc_mem_strdup(httpdmgr->mctx, url);
+
+ item->action = func;
+ item->action_arg = arg;
+ item->isstatic = isstatic;
+ isc_time_now(&item->loadtime);
+
+ ISC_LINK_INIT(item, link);
+
+ LOCK(&httpdmgr->lock);
+ ISC_LIST_APPEND(httpdmgr->urls, item, link);
+ UNLOCK(&httpdmgr->lock);
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_httpd_setfinishhook(void (*fn)(void)) {
+#if ENABLE_AFL
+ finishhook = fn;
+#else /* ENABLE_AFL */
+ UNUSED(fn);
+#endif /* ENABLE_AFL */
+}
+
+bool
+isc_httpdurl_isstatic(const isc_httpdurl_t *url) {
+ return (url->isstatic);
+}
+
+const isc_time_t *
+isc_httpdurl_loadtime(const isc_httpdurl_t *url) {
+ return (&url->loadtime);
+}
+
+const isc_time_t *
+isc_httpd_if_modified_since(const isc_httpd_t *httpd) {
+ return ((const isc_time_t *)&httpd->if_modified_since);
+}
diff --git a/lib/isc/include/isc/aes.h b/lib/isc/include/isc/aes.h
new file mode 100644
index 0000000..9657494
--- /dev/null
+++ b/lib/isc/include/isc/aes.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file isc/aes.h */
+
+#pragma once
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+#define ISC_AES128_KEYLENGTH 16U
+#define ISC_AES192_KEYLENGTH 24U
+#define ISC_AES256_KEYLENGTH 32U
+#define ISC_AES_BLOCK_LENGTH 16U
+
+ISC_LANG_BEGINDECLS
+
+void
+isc_aes128_crypt(const unsigned char *key, const unsigned char *in,
+ unsigned char *out);
+
+void
+isc_aes192_crypt(const unsigned char *key, const unsigned char *in,
+ unsigned char *out);
+
+void
+isc_aes256_crypt(const unsigned char *key, const unsigned char *in,
+ unsigned char *out);
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/align.h b/lib/isc/include/isc/align.h
new file mode 100644
index 0000000..7b72e9d
--- /dev/null
+++ b/lib/isc/include/isc/align.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#ifdef HAVE_STDALIGN_H
+#include <stdalign.h>
+#else /* ifdef HAVE_STDALIGN_H */
+#define alignas(x) __attribute__((__aligned__(x)))
+#endif /* ifdef HAVE_STDALIGN_H */
diff --git a/lib/isc/include/isc/app.h b/lib/isc/include/isc/app.h
new file mode 100644
index 0000000..1a42bd0
--- /dev/null
+++ b/lib/isc/include/isc/app.h
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*****
+***** Module Info
+*****/
+
+/*! \file isc/app.h
+ * \brief ISC Application Support
+ *
+ * Dealing with program termination can be difficult, especially in a
+ * multithreaded program. The routines in this module help coordinate
+ * the shutdown process. They are used as follows by the initial (main)
+ * thread of the application:
+ *
+ *\li isc_app_start(); Call very early in main(), before
+ * any other threads have been created.
+ *
+ *\li isc_app_run(); This will post any on-run events,
+ * and then block until application
+ * shutdown is requested. A shutdown
+ * request is made by calling
+ * isc_app_shutdown(), or by sending
+ * SIGINT or SIGTERM to the process.
+ * After isc_app_run() returns, the
+ * application should shutdown itself.
+ *
+ *\li isc_app_finish(); Call very late in main().
+ *
+ * Applications that want to use SIGHUP/isc_app_reload() to trigger reloading
+ * should check the result of isc_app_run() and call the reload routine if
+ * the result is ISC_R_RELOAD. They should then call isc_app_run() again
+ * to resume waiting for reload or termination.
+ *
+ * Use of this module is not required. In particular, isc_app_start() is
+ * NOT an ISC library initialization routine.
+ *
+ * This module also supports per-thread 'application contexts'. With this
+ * mode, a thread-based application will have a separate context, in which
+ * it uses other ISC library services such as tasks or timers. Signals are
+ * not caught in this mode, so that the application can handle the signals
+ * in its preferred way.
+ *
+ * \li MP:
+ * Clients must ensure that isc_app_start(), isc_app_run(), and
+ * isc_app_finish() are called at most once. isc_app_shutdown()
+ * is safe to use by any thread (provided isc_app_start() has been
+ * called previously).
+ *
+ * The same note applies to isc_app_ctxXXX() functions, but in this case
+ * it's a per-thread restriction. For example, a thread with an
+ * application context must ensure that isc_app_ctxstart() with the
+ * context is called at most once.
+ *
+ * \li Reliability:
+ * No anticipated impact.
+ *
+ * \li Resources:
+ * None.
+ *
+ * \li Security:
+ * No anticipated impact.
+ *
+ * \li Standards:
+ * None.
+ */
+
+#include <stdbool.h>
+
+#include <isc/eventclass.h>
+#include <isc/lang.h>
+#include <isc/magic.h>
+#include <isc/result.h>
+#include <isc/types.h>
+
+/***
+ *** Types
+ ***/
+
+typedef isc_event_t isc_appevent_t;
+
+#define ISC_APPEVENT_FIRSTEVENT (ISC_EVENTCLASS_APP + 0)
+#define ISC_APPEVENT_SHUTDOWN (ISC_EVENTCLASS_APP + 1)
+#define ISC_APPEVENT_LASTEVENT (ISC_EVENTCLASS_APP + 65535)
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+isc_app_ctxstart(isc_appctx_t *ctx);
+
+isc_result_t
+isc_app_start(void);
+/*!<
+ * \brief Start an ISC library application.
+ *
+ * Notes:
+ * This call should be made before any other ISC library call, and as
+ * close to the beginning of the application as possible.
+ *
+ * Requires:
+ *\li 'ctx' is a valid application context (for app_ctxstart()).
+ */
+
+isc_result_t
+isc_app_ctxonrun(isc_appctx_t *ctx, isc_mem_t *mctx, isc_task_t *task,
+ isc_taskaction_t action, void *arg);
+isc_result_t
+isc_app_onrun(isc_mem_t *mctx, isc_task_t *task, isc_taskaction_t action,
+ void *arg);
+/*!<
+ * \brief Request delivery of an event when the application is run.
+ *
+ * Requires:
+ *\li isc_app_start() has been called.
+ *\li 'ctx' is a valid application context (for app_ctxonrun()).
+ *
+ * Returns:
+ * ISC_R_SUCCESS
+ * ISC_R_NOMEMORY
+ */
+
+isc_result_t
+isc_app_ctxrun(isc_appctx_t *ctx);
+
+isc_result_t
+isc_app_run(void);
+/*!<
+ * \brief Run an ISC library application.
+ *
+ * Notes:
+ *\li The caller (typically the initial thread of an application) will
+ * block until shutdown is requested. When the call returns, the
+ * caller should start shutting down the application.
+ *
+ * Requires:
+ *\li isc_app_[ctx]start() has been called.
+ *
+ * Ensures:
+ *\li Any events requested via isc_app_onrun() will have been posted (in
+ * FIFO order) before isc_app_run() blocks.
+ *\li 'ctx' is a valid application context (for app_ctxrun()).
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS Shutdown has been requested.
+ *\li ISC_R_RELOAD Reload has been requested.
+ */
+
+bool
+isc_app_isrunning(void);
+/*!<
+ * \brief Return if the ISC library application is running.
+ *
+ * Returns:
+ *\li true App is running.
+ *\li false App is not running.
+ */
+
+void
+isc_app_ctxshutdown(isc_appctx_t *ctx);
+
+void
+isc_app_shutdown(void);
+/*!<
+ * \brief Request application shutdown.
+ *
+ * Notes:
+ *\li It is safe to call isc_app_shutdown() multiple times. Shutdown will
+ * only be triggered once.
+ *
+ * Requires:
+ *\li isc_app_[ctx]run() has been called.
+ *\li 'ctx' is a valid application context (for app_ctxshutdown()).
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ *\li ISC_R_UNEXPECTED
+ */
+
+void
+isc_app_ctxsuspend(isc_appctx_t *ctx);
+/*!<
+ * \brief This has the same behavior as isc_app_ctxsuspend().
+ */
+
+void
+isc_app_reload(void);
+/*!<
+ * \brief Request application reload.
+ *
+ * Requires:
+ *\li isc_app_run() has been called.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ *\li ISC_R_UNEXPECTED
+ */
+
+void
+isc_app_ctxfinish(isc_appctx_t *ctx);
+
+void
+isc_app_finish(void);
+/*!<
+ * \brief Finish an ISC library application.
+ *
+ * Notes:
+ *\li This call should be made at or near the end of main().
+ *
+ * Requires:
+ *\li isc_app_start() has been called.
+ *\li 'ctx' is a valid application context (for app_ctxfinish()).
+ *
+ * Ensures:
+ *\li Any resources allocated by isc_app_start() have been released.
+ */
+
+void
+isc_app_block(void);
+/*!<
+ * \brief Indicate that a blocking operation will be performed.
+ *
+ * Notes:
+ *\li If a blocking operation is in process, a call to isc_app_shutdown()
+ * or an external signal will abort the program, rather than allowing
+ * clean shutdown. This is primarily useful for reading user input.
+ *
+ * Requires:
+ * \li isc_app_start() has been called.
+ * \li No other blocking operations are in progress.
+ */
+
+void
+isc_app_unblock(void);
+/*!<
+ * \brief Indicate that a blocking operation is complete.
+ *
+ * Notes:
+ * \li When a blocking operation has completed, return the program to a
+ * state where a call to isc_app_shutdown() or an external signal will
+ * shutdown normally.
+ *
+ * Requires:
+ * \li isc_app_start() has been called.
+ * \li isc_app_block() has been called by the same thread.
+ */
+
+isc_result_t
+isc_appctx_create(isc_mem_t *mctx, isc_appctx_t **ctxp);
+/*!<
+ * \brief Create an application context.
+ *
+ * Requires:
+ *\li 'mctx' is a valid memory context.
+ *\li 'ctxp' != NULL && *ctxp == NULL.
+ */
+
+void
+isc_appctx_destroy(isc_appctx_t **ctxp);
+/*!<
+ * \brief Destroy an application context.
+ *
+ * Requires:
+ *\li '*ctxp' is a valid application context.
+ *
+ * Ensures:
+ *\li *ctxp == NULL.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/assertions.h b/lib/isc/include/isc/assertions.h
new file mode 100644
index 0000000..7eafa16
--- /dev/null
+++ b/lib/isc/include/isc/assertions.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file isc/assertions.h
+ */
+
+#pragma once
+
+#include <isc/attributes.h>
+#include <isc/lang.h>
+
+ISC_LANG_BEGINDECLS
+
+/*% isc assertion type */
+typedef enum {
+ isc_assertiontype_require,
+ isc_assertiontype_ensure,
+ isc_assertiontype_insist,
+ isc_assertiontype_invariant
+} isc_assertiontype_t;
+
+typedef void (*isc_assertioncallback_t)(const char *, int, isc_assertiontype_t,
+ const char *);
+
+/* coverity[+kill] */
+noreturn void
+isc_assertion_failed(const char *, int, isc_assertiontype_t, const char *);
+
+void isc_assertion_setcallback(isc_assertioncallback_t);
+
+const char *
+isc_assertion_typetotext(isc_assertiontype_t type);
+
+#define ISC_REQUIRE(cond) \
+ ((void)((cond) || \
+ ((isc_assertion_failed)(__FILE__, __LINE__, \
+ isc_assertiontype_require, #cond), \
+ 0)))
+
+#define ISC_ENSURE(cond) \
+ ((void)((cond) || \
+ ((isc_assertion_failed)(__FILE__, __LINE__, \
+ isc_assertiontype_ensure, #cond), \
+ 0)))
+
+#define ISC_INSIST(cond) \
+ ((void)((cond) || \
+ ((isc_assertion_failed)(__FILE__, __LINE__, \
+ isc_assertiontype_insist, #cond), \
+ 0)))
+
+#define ISC_INVARIANT(cond) \
+ ((void)((cond) || \
+ ((isc_assertion_failed)(__FILE__, __LINE__, \
+ isc_assertiontype_invariant, #cond), \
+ 0)))
+
+#define ISC_UNREACHABLE() \
+ (isc_assertion_failed(__FILE__, __LINE__, isc_assertiontype_insist, \
+ "unreachable"), \
+ __builtin_unreachable())
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/astack.h b/lib/isc/include/isc/astack.h
new file mode 100644
index 0000000..a4f6762
--- /dev/null
+++ b/lib/isc/include/isc/astack.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+
+#include <isc/mem.h>
+#include <isc/types.h>
+
+isc_astack_t *
+isc_astack_new(isc_mem_t *mctx, size_t size);
+/*%<
+ * Allocate and initialize a new array stack of size 'size'.
+ */
+
+void
+isc_astack_destroy(isc_astack_t *stack);
+/*%<
+ * Free an array stack 'stack'.
+ *
+ * Requires:
+ * \li 'stack' is empty.
+ */
+
+bool
+isc_astack_trypush(isc_astack_t *stack, void *obj);
+/*%<
+ * Try to push 'obj' onto array stack 'astack'. On failure, either
+ * because the stack size limit has been reached or because another
+ * thread has already changed the stack pointer, return 'false'.
+ */
+
+void *
+isc_astack_pop(isc_astack_t *stack);
+/*%<
+ * Pop an object off of array stack 'stack'. If the stack is empty,
+ * return NULL.
+ */
diff --git a/lib/isc/include/isc/atomic.h b/lib/isc/include/isc/atomic.h
new file mode 100644
index 0000000..5a415bc
--- /dev/null
+++ b/lib/isc/include/isc/atomic.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#if HAVE_STDATOMIC_H
+#include <stdatomic.h>
+#else
+#include <isc/stdatomic.h>
+#endif
+
+#include <isc/util.h>
+
+/*
+ * We define a few additional macros to make things easier
+ */
+
+/* Relaxed Memory Ordering */
+
+#define atomic_store_relaxed(o, v) \
+ atomic_store_explicit((o), (v), memory_order_relaxed)
+#define atomic_load_relaxed(o) atomic_load_explicit((o), memory_order_relaxed)
+#define atomic_fetch_add_relaxed(o, v) \
+ atomic_fetch_add_explicit((o), (v), memory_order_relaxed)
+#define atomic_fetch_sub_relaxed(o, v) \
+ atomic_fetch_sub_explicit((o), (v), memory_order_relaxed)
+#define atomic_fetch_or_relaxed(o, v) \
+ atomic_fetch_or_explicit((o), (v), memory_order_relaxed)
+#define atomic_fetch_and_relaxed(o, v) \
+ atomic_fetch_and_explicit((o), (v), memory_order_relaxed)
+#define atomic_exchange_relaxed(o, v) \
+ atomic_exchange_explicit((o), (v), memory_order_relaxed)
+#define atomic_compare_exchange_weak_relaxed(o, e, d) \
+ atomic_compare_exchange_weak_explicit( \
+ (o), (e), (d), memory_order_relaxed, memory_order_relaxed)
+#define atomic_compare_exchange_strong_relaxed(o, e, d) \
+ atomic_compare_exchange_strong_explicit( \
+ (o), (e), (d), memory_order_relaxed, memory_order_relaxed)
+#define atomic_compare_exchange_strong_acq_rel(o, e, d) \
+ atomic_compare_exchange_strong_explicit( \
+ (o), (e), (d), memory_order_acq_rel, memory_order_acquire)
+
+/* Acquire-Release Memory Ordering */
+
+#define atomic_store_release(o, v) \
+ atomic_store_explicit((o), (v), memory_order_release)
+#define atomic_load_acquire(o) atomic_load_explicit((o), memory_order_acquire)
+#define atomic_fetch_add_release(o, v) \
+ atomic_fetch_add_explicit((o), (v), memory_order_release)
+#define atomic_fetch_sub_release(o, v) \
+ atomic_fetch_sub_explicit((o), (v), memory_order_release)
+#define atomic_fetch_and_release(o, v) \
+ atomic_fetch_and_explicit((o), (v), memory_order_release)
+#define atomic_fetch_or_release(o, v) \
+ atomic_fetch_or_explicit((o), (v), memory_order_release)
+#define atomic_exchange_acq_rel(o, v) \
+ atomic_exchange_explicit((o), (v), memory_order_acq_rel)
+#define atomic_fetch_sub_acq_rel(o, v) \
+ atomic_fetch_sub_explicit((o), (v), memory_order_acq_rel)
+#define atomic_compare_exchange_weak_acq_rel(o, e, d) \
+ atomic_compare_exchange_weak_explicit( \
+ (o), (e), (d), memory_order_acq_rel, memory_order_acquire)
+#define atomic_compare_exchange_strong_acq_rel(o, e, d) \
+ atomic_compare_exchange_strong_explicit( \
+ (o), (e), (d), memory_order_acq_rel, memory_order_acquire)
+
+/* compare/exchange that MUST succeed */
+#define atomic_compare_exchange_enforced(o, e, d) \
+ RUNTIME_CHECK(atomic_compare_exchange_strong((o), (e), (d)))
diff --git a/lib/isc/include/isc/attributes.h b/lib/isc/include/isc/attributes.h
new file mode 100644
index 0000000..abe6152
--- /dev/null
+++ b/lib/isc/include/isc/attributes.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#ifdef HAVE_STDNORETURN_H
+#include <stdnoreturn.h>
+#elif HAVE_FUNC_ATTRIBUTE_NORETURN
+#define noreturn __attribute__((noreturn))
+#else
+#define noreturn
+#endif
+
+#if HAVE_FUNC_ATTRIBUTE_RETURNS_NONNULL
+#define ISC_ATTR_RETURNS_NONNULL __attribute__((returns_nonnull))
+#else
+#define ISC_ATTR_RETURNS_NONNULL
+#endif
+
+#ifdef HAVE_FUNC_ATTRIBUTE_MALLOC
+/*
+ * Indicates that a function is malloc-like, i.e., that the
+ * pointer P returned by the function cannot alias any other
+ * pointer valid when the function returns.
+ */
+#define ISC_ATTR_MALLOC __attribute__((malloc))
+#if HAVE_MALLOC_EXT_ATTR
+/*
+ * Associates deallocator as a suitable deallocation function
+ * for pointers returned from the function marked with the attribute.
+ */
+#define ISC_ATTR_DEALLOCATOR(deallocator) __attribute__((malloc(deallocator)))
+/*
+ * Similar to ISC_ATTR_DEALLOCATOR, but allows to speficy an index "idx",
+ * which denotes the positional argument to which when the pointer is passed
+ * in calls to deallocator has the effect of deallocating it.
+ */
+#define ISC_ATTR_DEALLOCATOR_IDX(deallocator, idx) \
+ __attribute__((malloc(deallocator, idx)))
+/*
+ * Combines both ISC_ATTR_MALLOC and ISC_ATTR_DEALLOCATOR attributes.
+ */
+#define ISC_ATTR_MALLOC_DEALLOCATOR(deallocator) \
+ __attribute__((malloc, malloc(deallocator)))
+/*
+ * Similar to ISC_ATTR_MALLOC_DEALLOCATOR, but allows to speficy an index "idx",
+ * which denotes the positional argument to which when the pointer is passed
+ * in calls to deallocator has the effect of deallocating it.
+ */
+#define ISC_ATTR_MALLOC_DEALLOCATOR_IDX(deallocator, idx) \
+ __attribute__((malloc, malloc(deallocator, idx)))
+#else /* #ifdef HAVE_MALLOC_EXT_ATTR */
+/*
+ * There is support for malloc attribute but not for
+ * extended attributes, so macros that combine attribute malloc
+ * with a deallocator will only expand to malloc attribute.
+ */
+#define ISC_ATTR_DEALLOCATOR(deallocator)
+#define ISC_ATTR_DEALLOCATOR_IDX(deallocator, idx)
+#define ISC_ATTR_MALLOC_DEALLOCATOR(deallocator) ISC_ATTR_MALLOC
+#define ISC_ATTR_MALLOC_DEALLOCATOR_IDX(deallocator, idx) ISC_ATTR_MALLOC
+#endif
+#else /* #ifdef HAVE_FUNC_ATTRIBUTE_MALLOC */
+/*
+ * There is no support for malloc attribute.
+ */
+#define ISC_ATTR_MALLOC
+#define ISC_ATTR_DEALLOCATOR(deallocator)
+#define ISC_ATTR_DEALLOCATOR_IDX(deallocator, idx)
+#define ISC_ATTR_MALLOC_DEALLOCATOR(deallocator)
+#define ISC_ATTR_MALLOC_DEALLOCATOR_IDX(deallocator, idx)
+#endif /* HAVE_FUNC_ATTRIBUTE_MALLOC */
diff --git a/lib/isc/include/isc/backtrace.h b/lib/isc/include/isc/backtrace.h
new file mode 100644
index 0000000..f818c3b
--- /dev/null
+++ b/lib/isc/include/isc/backtrace.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file isc/backtrace.h
+ * \brief provide a back trace of the running process to help debug problems.
+ *
+ * This module tries to get a back trace of the process using some platform
+ * dependent way when available. It also manages an internal symbol table
+ * that maps function addresses used in the process to their textual symbols.
+ * This module is expected to be used to help debug when some fatal error
+ * happens.
+ *
+ * IMPORTANT NOTE: since the (major) intended use case of this module is
+ * dumping a back trace on a fatal error, normally followed by self termination,
+ * functions defined in this module generally doesn't employ assertion checks
+ * (if it did, a program bug could cause infinite recursive calls to a
+ * backtrace function).
+ */
+
+#pragma once
+
+/***
+ *** Imports
+ ***/
+#include <isc/types.h>
+
+/***
+ *** Functions
+ ***/
+
+ISC_LANG_BEGINDECLS
+int
+isc_backtrace(void **addrs, int maxaddrs);
+/*%<
+ * Get a back trace of the running process above this function itself. On
+ * success, addrs[i] will store the address of the call point of the i-th
+ * stack frame (addrs[0] is the caller of this function). *nframes will store
+ * the total number of frames.
+ *
+ * Requires (note that these are not ensured by assertion checks, see above):
+ *
+ *\li 'addrs' is a valid array containing at least 'maxaddrs' void * entries.
+ *
+ *\li 'nframes' must be non NULL.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_FAILURE
+ *\li #ISC_R_NOTFOUND
+ *\li #ISC_R_NOTIMPLEMENTED
+ */
+
+char **
+isc_backtrace_symbols(void *const *buffer, int size);
+/*
+ * isc_backtrace_symbols() attempts to transform a call stack obtained by
+ * backtrace() into an array of human-readable strings using dladdr(). The
+ * array of strings returned has size elements. It is allocated using
+ * malloc() and should be released using free(). There is no need to free
+ * the individual strings in the array.
+ *
+ * Notes:
+ *
+ *\li On Windows, this is shim implementation using SymFromAddr()
+ *\li On systems with backtrace_symbols(), it's just a thin wrapper
+ *\li Otherwise, it returns NULL
+ *\li See platform NOTES for backtrace_symbols
+ *
+ * Returns:
+ *
+ *\li On success, backtrace_symbols() returns a pointer to the array
+ *\li On error, NULL is returned.
+ */
+
+void
+isc_backtrace_symbols_fd(void *const *buffer, int size, int fd);
+/*
+ * isc_backtrace_symbols_fd() performs the same operation as
+ * isc_backtrace_symbols(), but the resulting strings are immediately written to
+ * the file descriptor fd, and are not returned. isc_backtrace_symbols_fd()
+ * does not call malloc(3), and so can be employed in situations where the
+ * latter function might fail.
+ *
+ * Notes:
+ *
+ *\li See isc_backtrace_symbols() notes
+ *\li See platform NOTES for backtrace_symbols_fd for caveats
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/barrier.h b/lib/isc/include/isc/barrier.h
new file mode 100644
index 0000000..a60472f
--- /dev/null
+++ b/lib/isc/include/isc/barrier.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <isc/util.h>
+
+#if HAVE_PTHREAD_BARRIER_INIT
+
+#include <pthread.h>
+
+#define isc_barrier_t pthread_barrier_t
+
+#define isc_barrier_init(barrier, count) \
+ pthread_barrier_init(barrier, NULL, count)
+#define isc_barrier_destroy(barrier) pthread_barrier_destroy(barrier)
+#define isc_barrier_wait(barrier) pthread_barrier_wait(barrier)
+
+#else
+
+#include <uv.h>
+
+#define isc_barrier_t uv_barrier_t
+
+#define isc_barrier_init(barrier, count) uv_barrier_init(barrier, count)
+#define isc_barrier_destroy(barrier) uv_barrier_destroy(barrier)
+#define isc_barrier_wait(barrier) uv_barrier_wait(barrier)
+
+#endif /* __SANITIZE_THREAD__ */
diff --git a/lib/isc/include/isc/base32.h b/lib/isc/include/isc/base32.h
new file mode 100644
index 0000000..1faa629
--- /dev/null
+++ b/lib/isc/include/isc/base32.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file */
+
+/*
+ * Routines for manipulating base 32 and base 32 hex encoded data.
+ * Based on RFC 4648.
+ *
+ * Base 32 hex preserves the sort order of data when it is encoded /
+ * decoded.
+ *
+ * Base 32 hex "np" is base 32 hex but no padding is produced or accepted.
+ */
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Functions
+ ***/
+
+isc_result_t
+isc_base32_totext(isc_region_t *source, int wordlength, const char *wordbreak,
+ isc_buffer_t *target);
+isc_result_t
+isc_base32hex_totext(isc_region_t *source, int wordlength,
+ const char *wordbreak, isc_buffer_t *target);
+isc_result_t
+isc_base32hexnp_totext(isc_region_t *source, int wordlength,
+ const char *wordbreak, isc_buffer_t *target);
+/*!<
+ * \brief Convert data into base32 encoded text.
+ *
+ * Notes:
+ *\li The base32 encoded text in 'target' will be divided into
+ * words of at most 'wordlength' characters, separated by
+ * the 'wordbreak' string. No parentheses will surround
+ * the text.
+ *
+ * Requires:
+ *\li 'source' is a region containing binary data
+ *\li 'target' is a text buffer containing available space
+ *\li 'wordbreak' points to a null-terminated string of
+ * zero or more whitespace characters
+ *
+ * Ensures:
+ *\li target will contain the base32 encoded version of the data
+ * in source. The 'used' pointer in target will be advanced as
+ * necessary.
+ */
+
+isc_result_t
+isc_base32_decodestring(const char *cstr, isc_buffer_t *target);
+isc_result_t
+isc_base32hex_decodestring(const char *cstr, isc_buffer_t *target);
+isc_result_t
+isc_base32hexnp_decodestring(const char *cstr, isc_buffer_t *target);
+/*!<
+ * \brief Decode a null-terminated string in base32, base32hex, or
+ * base32hex non-padded.
+ *
+ * Requires:
+ *\li 'cstr' is non-null.
+ *\li 'target' is a valid buffer.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS -- the entire decoded representation of 'cstring'
+ * fit in 'target'.
+ *\li #ISC_R_BADBASE32 -- 'cstr' is not a valid base32 encoding.
+ *
+ * Other error returns are any possible error code from:
+ *\li isc_lex_create(),
+ *\li isc_lex_openbuffer(),
+ *\li isc_base32_tobuffer().
+ */
+
+isc_result_t
+isc_base32_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length);
+isc_result_t
+isc_base32hex_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length);
+isc_result_t
+isc_base32hexnp_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length);
+/*!<
+ * \brief Convert text encoded in base32, base32hex, or base32hex
+ * non-padded from a lexer context into `target`. If 'length' is
+ * non-negative, it is the expected number of encoded octets to convert.
+ *
+ * If 'length' is -1 then 0 or more encoded octets are expected.
+ * If 'length' is -2 then 1 or more encoded octets are expected.
+ *
+ * Returns:
+ *\li #ISC_R_BADBASE32 -- invalid base32 encoding.
+ *\li #ISC_R_UNEXPECTEDEND: the text does not contain the expected
+ * number of encoded octets.
+ *
+ * Requires:
+ *\li 'lexer' is a valid lexer context
+ *\li 'target' is a buffer containing binary data
+ *\li 'length' is -2, -1, or non-negative
+ *
+ * Ensures:
+ *\li target will contain the data represented by the base32 encoded
+ * string parsed by the lexer. No more than `length` octets will
+ * be read, if `length` is non-negative. The 'used' pointer in
+ * 'target' will be advanced as necessary.
+ */
+
+isc_result_t
+isc_base32_decoderegion(isc_region_t *source, isc_buffer_t *target);
+isc_result_t
+isc_base32hex_decoderegion(isc_region_t *source, isc_buffer_t *target);
+isc_result_t
+isc_base32hexnp_decoderegion(isc_region_t *source, isc_buffer_t *target);
+/*!<
+ * \brief Decode a packed (no white space permitted) region in
+ * base32, base32hex or base32hex non-padded.
+ *
+ * Requires:
+ *\li 'source' is a valid region.
+ *\li 'target' is a valid buffer.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS -- the entire decoded representation of 'cstring'
+ * fit in 'target'.
+ *\li #ISC_R_BADBASE32 -- 'source' is not a valid base32 encoding.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/base64.h b/lib/isc/include/isc/base64.h
new file mode 100644
index 0000000..b1bc089
--- /dev/null
+++ b/lib/isc/include/isc/base64.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/base64.h */
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Functions
+ ***/
+
+isc_result_t
+isc_base64_totext(isc_region_t *source, int wordlength, const char *wordbreak,
+ isc_buffer_t *target);
+/*!<
+ * \brief Convert data into base64 encoded text.
+ *
+ * Notes:
+ *\li The base64 encoded text in 'target' will be divided into
+ * words of at most 'wordlength' characters, separated by
+ * the 'wordbreak' string. No parentheses will surround
+ * the text.
+ *
+ * Requires:
+ *\li 'source' is a region containing binary data
+ *\li 'target' is a text buffer containing available space
+ *\li 'wordbreak' points to a null-terminated string of
+ * zero or more whitespace characters
+ *
+ * Ensures:
+ *\li target will contain the base64 encoded version of the data
+ * in source. The 'used' pointer in target will be advanced as
+ * necessary.
+ */
+
+isc_result_t
+isc_base64_decodestring(const char *cstr, isc_buffer_t *target);
+/*!<
+ * \brief Decode a null-terminated base64 string.
+ *
+ * Requires:
+ *\li 'cstr' is non-null.
+ *\li 'target' is a valid buffer.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS -- the entire decoded representation of 'cstring'
+ * fit in 'target'.
+ *\li #ISC_R_BADBASE64 -- 'cstr' is not a valid base64 encoding.
+ *
+ * Other error returns are any possible error code from:
+ *\li isc_lex_create(),
+ *\li isc_lex_openbuffer(),
+ *\li isc_base64_tobuffer().
+ */
+
+isc_result_t
+isc_base64_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length);
+/*!<
+ * \brief Convert base64 encoded text from a lexer context into
+ * `target`. If 'length' is non-negative, it is the expected number of
+ * encoded octets to convert.
+ *
+ * If 'length' is -1 then 0 or more encoded octets are expected.
+ * If 'length' is -2 then 1 or more encoded octets are expected.
+ *
+ * Returns:
+ *\li #ISC_R_BADBASE64 -- invalid base64 encoding.
+ *\li #ISC_R_UNEXPECTEDEND: the text does not contain the expected
+ * number of encoded octets.
+ *
+ * Requires:
+ *\li 'lexer' is a valid lexer context
+ *\li 'target' is a buffer containing binary data
+ *\li 'length' is -2, -1, or non-negative
+ *
+ * Ensures:
+ *\li target will contain the data represented by the base64 encoded
+ * string parsed by the lexer. No more than `length` octets will
+ * be read, if `length` is non-negative. The 'used' pointer in
+ * 'target' will be advanced as necessary.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/buffer.h b/lib/isc/include/isc/buffer.h
new file mode 100644
index 0000000..1be0081
--- /dev/null
+++ b/lib/isc/include/isc/buffer.h
@@ -0,0 +1,1023 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*****
+***** Module Info
+*****/
+
+/*! \file isc/buffer.h
+ *
+ * \brief A buffer is a region of memory, together with a set of related
+ * subregions. Buffers are used for parsing and I/O operations.
+ *
+ * The 'used region' and the 'available region' are disjoint, and their
+ * union is the buffer's region. The used region extends from the beginning
+ * of the buffer region to the last used byte. The available region
+ * extends from one byte greater than the last used byte to the end of the
+ * buffer's region. The size of the used region can be changed using various
+ * buffer commands. Initially, the used region is empty.
+ *
+ * The used region is further subdivided into two disjoint regions: the
+ * 'consumed region' and the 'remaining region'. The union of these two
+ * regions is the used region. The consumed region extends from the beginning
+ * of the used region to the byte before the 'current' offset (if any). The
+ * 'remaining' region extends from the current offset to the end of the used
+ * region. The size of the consumed region can be changed using various
+ * buffer commands. Initially, the consumed region is empty.
+ *
+ * The 'active region' is an (optional) subregion of the remaining region.
+ * It extends from the current offset to an offset in the remaining region
+ * that is selected with isc_buffer_setactive(). Initially, the active region
+ * is empty. If the current offset advances beyond the chosen offset, the
+ * active region will also be empty.
+ *
+ * \verbatim
+ * /------------entire length---------------\
+ * /----- used region -----\/-- available --\
+ * +----------------------------------------+
+ * | consumed | remaining | |
+ * +----------------------------------------+
+ * a b c d e
+ *
+ * a == base of buffer.
+ * b == current pointer. Can be anywhere between a and d.
+ * c == active pointer. Meaningful between b and d.
+ * d == used pointer.
+ * e == length of buffer.
+ *
+ * a-e == entire length of buffer.
+ * a-d == used region.
+ * a-b == consumed region.
+ * b-d == remaining region.
+ * b-c == optional active region.
+ *\endverbatim
+ *
+ * The following invariants are maintained by all routines:
+ *
+ *\code
+ * length > 0
+ *
+ * base is a valid pointer to length bytes of memory
+ *
+ * 0 <= used <= length
+ *
+ * 0 <= current <= used
+ *
+ * 0 <= active <= used
+ * (although active < current implies empty active region)
+ *\endcode
+ *
+ * \li MP:
+ * Buffers have no synchronization. Clients must ensure exclusive
+ * access.
+ *
+ * \li Reliability:
+ * No anticipated impact.
+ *
+ * \li Resources:
+ * Memory: 1 pointer + 6 unsigned integers per buffer.
+ *
+ * \li Security:
+ * No anticipated impact.
+ *
+ * \li Standards:
+ * None.
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/assertions.h>
+#include <isc/formatcheck.h>
+#include <isc/lang.h>
+#include <isc/list.h>
+#include <isc/magic.h>
+#include <isc/region.h>
+#include <isc/string.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/*@{*/
+/*!
+ *** Magic numbers
+ ***/
+#define ISC_BUFFER_MAGIC 0x42756621U /* Buf!. */
+#define ISC_BUFFER_VALID(b) ISC_MAGIC_VALID(b, ISC_BUFFER_MAGIC)
+/*@}*/
+
+/*!
+ * Size granularity for dynamically resizable buffers; when reserving
+ * space in a buffer, we round the allocated buffer length up to the
+ * nearest * multiple of this value.
+ */
+#define ISC_BUFFER_INCR 2048
+
+/*
+ * The following macros MUST be used only on valid buffers. It is the
+ * caller's responsibility to ensure this by using the ISC_BUFFER_VALID
+ * check above, or by calling another isc_buffer_*() function (rather than
+ * another macro.)
+ */
+
+/*@{*/
+/*!
+ * Fundamental buffer elements. (A through E in the introductory comment.)
+ */
+#define isc_buffer_base(b) ((void *)(b)->base) /*a*/
+#define isc_buffer_current(b) \
+ ((void *)((unsigned char *)(b)->base + (b)->current)) /*b*/
+#define isc_buffer_active(b) \
+ ((void *)((unsigned char *)(b)->base + (b)->active)) /*c*/
+#define isc_buffer_used(b) \
+ ((void *)((unsigned char *)(b)->base + (b)->used)) /*d*/
+#define isc_buffer_length(b) ((b)->length) /*e*/
+/*@}*/
+
+/*@{*/
+/*!
+ * Derived lengths. (Described in the introductory comment.)
+ */
+#define isc_buffer_usedlength(b) ((b)->used) /* d-a */
+#define isc_buffer_consumedlength(b) ((b)->current) /* b-a */
+#define isc_buffer_remaininglength(b) ((b)->used - (b)->current) /* d-b */
+#define isc_buffer_activelength(b) ((b)->active - (b)->current) /* c-b */
+#define isc_buffer_availablelength(b) ((b)->length - (b)->used) /* e-d */
+/*@}*/
+
+/*!
+ * Note that the buffer structure is public. This is principally so buffer
+ * operations can be implemented using macros. Applications are strongly
+ * discouraged from directly manipulating the structure.
+ */
+
+struct isc_buffer {
+ unsigned int magic;
+ void *base;
+ /*@{*/
+ /*! The following integers are byte offsets from 'base'. */
+ unsigned int length;
+ unsigned int used;
+ unsigned int current;
+ unsigned int active;
+ /*@}*/
+ /*! linkable */
+ ISC_LINK(isc_buffer_t) link;
+ /*! private internal elements */
+ isc_mem_t *mctx;
+ /* automatically realloc buffer at put* */
+ bool autore;
+};
+
+/***
+ *** Functions
+ ***/
+
+void
+isc_buffer_allocate(isc_mem_t *mctx, isc_buffer_t **dynbuffer,
+ unsigned int length);
+/*!<
+ * \brief Allocate a dynamic linkable buffer which has "length" bytes in the
+ * data region.
+ *
+ * Requires:
+ *\li "mctx" is valid.
+ *
+ *\li "dynbuffer" is non-NULL, and "*dynbuffer" is NULL.
+ *
+ * Note:
+ *\li Changing the buffer's length field is not permitted.
+ */
+
+isc_result_t
+isc_buffer_reserve(isc_buffer_t **dynbuffer, unsigned int size);
+/*!<
+ * \brief Make "size" bytes of space available in the buffer. The buffer
+ * pointer may move when you call this function.
+ *
+ * Requires:
+ *\li "dynbuffer" is not NULL.
+ *
+ *\li "*dynbuffer" is a valid dynamic buffer.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS - success
+ *\li ISC_R_NOMEMORY - no memory available
+ *
+ * Ensures:
+ *\li "*dynbuffer" will be valid on return and will contain all the
+ * original data. However, the buffer pointer may be moved during
+ * reallocation.
+ */
+
+void
+isc_buffer_free(isc_buffer_t **dynbuffer);
+/*!<
+ * \brief Release resources allocated for a dynamic buffer.
+ *
+ * Requires:
+ *\li "dynbuffer" is not NULL.
+ *
+ *\li "*dynbuffer" is a valid dynamic buffer.
+ *
+ * Ensures:
+ *\li "*dynbuffer" will be NULL on return, and all memory associated with
+ * the dynamic buffer is returned to the memory context used in
+ * isc_buffer_allocate().
+ */
+
+void
+isc__buffer_initnull(isc_buffer_t *b);
+
+void
+isc_buffer_reinit(isc_buffer_t *b, void *base, unsigned int length);
+/*!<
+ * \brief Make 'b' refer to the 'length'-byte region starting at base.
+ * Any existing data will be copied.
+ *
+ * Requires:
+ *
+ *\li 'length' > 0 AND length >= previous length
+ *
+ *\li 'base' is a pointer to a sequence of 'length' bytes.
+ *
+ */
+
+void
+isc_buffer_setautorealloc(isc_buffer_t *b, bool enable);
+/*!<
+ * \brief Enable or disable autoreallocation on 'b'.
+ *
+ * Requires:
+ *\li 'b' is a valid dynamic buffer (b->mctx != NULL).
+ *
+ */
+
+void
+isc_buffer_compact(isc_buffer_t *b);
+/*!<
+ * \brief Compact the used region by moving the remaining region so it occurs
+ * at the start of the buffer. The used region is shrunk by the size of
+ * the consumed region, and the consumed region is then made empty.
+ *
+ * Requires:
+ *
+ *\li 'b' is a valid buffer
+ *
+ * Ensures:
+ *
+ *\li current == 0
+ *
+ *\li The size of the used region is now equal to the size of the remaining
+ * region (as it was before the call). The contents of the used region
+ * are those of the remaining region (as it was before the call).
+ */
+
+uint8_t
+isc_buffer_getuint8(isc_buffer_t *b);
+/*!<
+ * \brief Read an unsigned 8-bit integer from 'b' and return it.
+ *
+ * Requires:
+ *
+ *\li 'b' is a valid buffer.
+ *
+ *\li The length of the remaining region of 'b' is at least 1.
+ *
+ * Ensures:
+ *
+ *\li The current pointer in 'b' is advanced by 1.
+ *
+ * Returns:
+ *
+ *\li A 8-bit unsigned integer.
+ */
+
+uint16_t
+isc_buffer_getuint16(isc_buffer_t *b);
+/*!<
+ * \brief Read an unsigned 16-bit integer in network byte order from 'b',
+ * convert it to host byte order, and return it.
+ *
+ * Requires:
+ *
+ *\li 'b' is a valid buffer.
+ *
+ *\li The length of the remaining region of 'b' is at least 2.
+ *
+ * Ensures:
+ *
+ *\li The current pointer in 'b' is advanced by 2.
+ *
+ * Returns:
+ *
+ *\li A 16-bit unsigned integer.
+ */
+
+uint32_t
+isc_buffer_getuint32(isc_buffer_t *b);
+/*!<
+ * \brief Read an unsigned 32-bit integer in network byte order from 'b',
+ * convert it to host byte order, and return it.
+ *
+ * Requires:
+ *
+ *\li 'b' is a valid buffer.
+ *
+ *\li The length of the remaining region of 'b' is at least 4.
+ *
+ * Ensures:
+ *
+ *\li The current pointer in 'b' is advanced by 4.
+ *
+ * Returns:
+ *
+ *\li A 32-bit unsigned integer.
+ */
+
+uint64_t
+isc_buffer_getuint48(isc_buffer_t *b);
+/*!<
+ * \brief Read an unsigned 48-bit integer in network byte order from 'b',
+ * convert it to host byte order, and return it.
+ *
+ * Requires:
+ *
+ *\li 'b' is a valid buffer.
+ *
+ *\li The length of the remaining region of 'b' is at least 6.
+ *
+ * Ensures:
+ *
+ *\li The current pointer in 'b' is advanced by 6.
+ *
+ * Returns:
+ *
+ *\li A 48-bit unsigned integer (stored in a 64-bit integer).
+ */
+
+void
+isc_buffer_putdecint(isc_buffer_t *b, int64_t v);
+/*!<
+ * \brief Put decimal representation of 'v' in b
+ *
+ * Requires:
+ *\li 'b' is a valid buffer.
+ *
+ *\li The length of the available region of 'b' is at least strlen(dec('v'))
+ * or the buffer has autoreallocation enabled.
+ *
+ * Ensures:
+ *\li The used pointer in 'b' is advanced by strlen(dec('v')).
+ */
+
+isc_result_t
+isc_buffer_copyregion(isc_buffer_t *b, const isc_region_t *r);
+/*!<
+ * \brief Copy the contents of 'r' into 'b'.
+ *
+ * Notes:
+ *\li If 'b' has autoreallocation enabled, and the length of 'r' is greater
+ * than the length of the available region of 'b', 'b' is reallocated.
+ *
+ * Requires:
+ *\li 'b' is a valid buffer.
+ *
+ *\li 'r' is a valid region.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ *\li ISC_R_NOSPACE The available region of 'b' is not
+ * big enough.
+ */
+
+isc_result_t
+isc_buffer_dup(isc_mem_t *mctx, isc_buffer_t **dstp, const isc_buffer_t *src);
+/*!<
+ * \brief Allocate 'dst' and copy used contents of 'src' into it.
+ *
+ * Requires:
+ *\li 'dstp' is not NULL and *dst is NULL.
+ *\li 'src' is a valid buffer.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ */
+
+isc_result_t
+isc_buffer_printf(isc_buffer_t *b, const char *format, ...)
+ ISC_FORMAT_PRINTF(2, 3);
+/*!<
+ * \brief Append a formatted string to the used region of 'b'.
+ *
+ * Notes:
+ *
+ *\li The 'format' argument is a printf(3) string, with additional arguments
+ * as necessary.
+ *
+ *\li If 'b' has autoreallocation enabled, and the length of the formatted
+ * string is greater than the length of the available region of 'b', 'b'
+ * is reallocated.
+ *
+ * Requires:
+ *
+ *\li 'b' is a valid buffer.
+ *
+ * Ensures:
+ *
+ *\li The used pointer in 'b' is advanced by the number of bytes appended
+ * (excluding the terminating NULL byte).
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS Operation succeeded.
+ *\li #ISC_R_NOSPACE 'b' does not allow reallocation and appending the
+ * formatted string to it would cause it to overflow.
+ *\li #ISC_R_NOMEMORY Reallocation failed.
+ *\li #ISC_R_FAILURE Other error occurred.
+ */
+
+/*
+ * Buffer functions implemented as inline.
+ */
+
+/*! \note
+ * XXXDCL Something more could be done with initializing buffers that
+ * point to const data. For example, isc_buffer_constinit() could
+ * set a new boolean flag in the buffer structure indicating whether
+ * the buffer was initialized with that function. Then if the
+ * boolean were true, the isc_buffer_put* functions could assert a
+ * contractual requirement for a non-const buffer.
+ *
+ * One drawback is that the isc_buffer_* functions that return
+ * pointers would still need to return non-const pointers to avoid compiler
+ * warnings, so it would be up to code that uses them to have to deal
+ * with the possibility that the buffer was initialized as const --
+ * a problem that they *already* have to deal with but have absolutely
+ * no ability to. With a new isc_buffer_isconst() function returning
+ * true/false, they could at least assert a contractual requirement for
+ * non-const buffers when needed.
+ */
+
+/*!
+ * \brief Make 'b' refer to the 'length'-byte region starting at 'base'.
+ *
+ * Requires:
+ *
+ *\li 'length' > 0
+ *
+ *\li 'base' is a pointer to a sequence of 'length' bytes.
+ */
+static inline void
+isc_buffer_init(isc_buffer_t *b, void *base, unsigned int length) {
+ ISC_REQUIRE(b != NULL);
+
+ *b = (isc_buffer_t){ .base = base,
+ .length = length,
+ .magic = ISC_BUFFER_MAGIC };
+ ISC_LINK_INIT(b, link);
+}
+
+/*!
+ *\brief Initialize a buffer 'b' with a null data field and zero length.
+ * This can later be grown as needed and swapped in place.
+ */
+static inline void
+isc_buffer_initnull(isc_buffer_t *b) {
+ *b = (isc_buffer_t){ .magic = ISC_BUFFER_MAGIC };
+ ISC_LINK_INIT(b, link);
+}
+
+/*!
+ * \brief Make 'b' refer to the 'length'-byte constant region starting
+ * at 'base'.
+ *
+ * Requires:
+ *
+ *\li 'length' > 0
+ *\li 'base' is a pointer to a sequence of 'length' bytes.
+ */
+#define isc_buffer_constinit(_b, _d, _l) \
+ do { \
+ union { \
+ void *_var; \
+ const void *_const; \
+ } _deconst; \
+ _deconst._const = (_d); \
+ isc_buffer_init((_b), _deconst._var, (_l)); \
+ } while (0)
+
+/*!
+ * \brief Make 'b' an invalid buffer.
+ *
+ * Requires:
+ *\li 'b' is a valid buffer.
+ *
+ * Ensures:
+ *\li Future attempts to use 'b' without calling isc_buffer_init() on
+ * it will cause an assertion failure.
+ */
+static inline void
+isc_buffer_invalidate(isc_buffer_t *b) {
+ ISC_REQUIRE(ISC_BUFFER_VALID(b));
+ ISC_REQUIRE(!ISC_LINK_LINKED(b, link));
+ ISC_REQUIRE(b->mctx == NULL);
+
+ b->magic = 0;
+ b->base = NULL;
+ b->length = 0;
+ b->used = 0;
+ b->current = 0;
+ b->active = 0;
+}
+
+/*!
+ * \brief Make 'r' refer to the region of 'b'.
+ *
+ * Requires:
+ *
+ *\li 'b' is a valid buffer.
+ *
+ *\li 'r' points to a region structure.
+ */
+static inline void
+isc_buffer_region(isc_buffer_t *b, isc_region_t *r) {
+ ISC_REQUIRE(ISC_BUFFER_VALID(b));
+ ISC_REQUIRE(r != NULL);
+
+ r->base = b->base;
+ r->length = b->length;
+}
+
+/*!
+ * \brief Make 'r' refer to the used region of 'b'.
+ *
+ * Requires:
+ *
+ *\li 'b' is a valid buffer.
+ *
+ *\li 'r' points to a region structure.
+ */
+static inline void
+isc_buffer_usedregion(const isc_buffer_t *b, isc_region_t *r) {
+ ISC_REQUIRE(ISC_BUFFER_VALID(b));
+ ISC_REQUIRE(r != NULL);
+
+ r->base = b->base;
+ r->length = b->used;
+}
+
+/*!
+ * \brief Make 'r' refer to the available region of 'b'.
+ *
+ * Requires:
+ *
+ *\li 'b' is a valid buffer.
+ *
+ *\li 'r' points to a region structure.
+ */
+static inline void
+isc_buffer_availableregion(isc_buffer_t *b, isc_region_t *r) {
+ ISC_REQUIRE(ISC_BUFFER_VALID(b));
+ ISC_REQUIRE(r != NULL);
+
+ r->base = isc_buffer_used(b);
+ r->length = isc_buffer_availablelength(b);
+}
+
+/*!
+ * \brief Increase the 'used' region of 'b' by 'n' bytes.
+ *
+ * Requires:
+ *
+ *\li 'b' is a valid buffer
+ *
+ *\li used + n <= length
+ */
+static inline void
+isc_buffer_add(isc_buffer_t *b, unsigned int n) {
+ ISC_REQUIRE(ISC_BUFFER_VALID(b));
+ ISC_REQUIRE(b->used + n <= b->length);
+
+ b->used += n;
+}
+
+/*!
+ * \brief Decrease the 'used' region of 'b' by 'n' bytes.
+ *
+ * Requires:
+ *
+ *\li 'b' is a valid buffer
+ *
+ *\li used >= n
+ */
+static inline void
+isc_buffer_subtract(isc_buffer_t *b, unsigned int n) {
+ ISC_REQUIRE(ISC_BUFFER_VALID(b));
+ ISC_REQUIRE(b->used >= n);
+
+ b->used -= n;
+ if (b->current > b->used) {
+ b->current = b->used;
+ }
+ if (b->active > b->used) {
+ b->active = b->used;
+ }
+}
+
+/*!<
+ * \brief Make the used region empty.
+ *
+ * Requires:
+ *
+ *\li 'b' is a valid buffer
+ *
+ * Ensures:
+ *
+ *\li used = 0
+ */
+static inline void
+isc_buffer_clear(isc_buffer_t *b) {
+ ISC_REQUIRE(ISC_BUFFER_VALID(b));
+
+ b->used = 0;
+ b->current = 0;
+ b->active = 0;
+}
+
+/*!
+ * \brief Make 'r' refer to the consumed region of 'b'.
+ *
+ * Requires:
+ *
+ *\li 'b' is a valid buffer.
+ *
+ *\li 'r' points to a region structure.
+ */
+static inline void
+isc_buffer_consumedregion(isc_buffer_t *b, isc_region_t *r) {
+ ISC_REQUIRE(ISC_BUFFER_VALID(b));
+ ISC_REQUIRE(r != NULL);
+
+ r->base = b->base;
+ r->length = b->current;
+}
+
+/*!
+ * \brief Make 'r' refer to the remaining region of 'b'.
+ *
+ * Requires:
+ *
+ *\li 'b' is a valid buffer.
+ *
+ *\li 'r' points to a region structure.
+ */
+static inline void
+isc_buffer_remainingregion(isc_buffer_t *b, isc_region_t *r) {
+ ISC_REQUIRE(ISC_BUFFER_VALID(b));
+ ISC_REQUIRE(r != NULL);
+
+ r->base = isc_buffer_current(b);
+ r->length = isc_buffer_remaininglength(b);
+}
+
+/*!
+ * \brief Make 'r' refer to the active region of 'b'.
+ *
+ * Requires:
+ *
+ *\li 'b' is a valid buffer.
+ *
+ *\li 'r' points to a region structure.
+ */
+static inline void
+isc_buffer_activeregion(isc_buffer_t *b, isc_region_t *r) {
+ ISC_REQUIRE(ISC_BUFFER_VALID(b));
+ ISC_REQUIRE(r != NULL);
+
+ if (b->current < b->active) {
+ r->base = isc_buffer_current(b);
+ r->length = isc_buffer_activelength(b);
+ } else {
+ r->base = NULL;
+ r->length = 0;
+ }
+}
+
+/*!
+ * \brief Sets the end of the active region 'n' bytes after current.
+ *
+ * Requires:
+ *
+ *\li 'b' is a valid buffer.
+ *
+ *\li current + n <= used
+ */
+static inline void
+isc_buffer_setactive(isc_buffer_t *b, unsigned int n) {
+ ISC_REQUIRE(ISC_BUFFER_VALID(b));
+ ISC_REQUIRE(b->current + n <= b->used);
+
+ b->active = b->current + n;
+}
+
+/*!<
+ * \brief Make the consumed region empty.
+ *
+ * Requires:
+ *
+ *\li 'b' is a valid buffer
+ *
+ * Ensures:
+ *
+ *\li current == 0
+ */
+static inline void
+isc_buffer_first(isc_buffer_t *b) {
+ ISC_REQUIRE(ISC_BUFFER_VALID(b));
+
+ b->current = 0;
+}
+
+/*!
+ * \brief Increase the 'consumed' region of 'b' by 'n' bytes.
+ *
+ * Requires:
+ *
+ *\li 'b' is a valid buffer
+ *
+ *\li current + n <= used
+ */
+static inline void
+isc_buffer_forward(isc_buffer_t *b, unsigned int n) {
+ ISC_REQUIRE(ISC_BUFFER_VALID(b));
+ ISC_REQUIRE(b->current + n <= b->used);
+
+ b->current += n;
+}
+
+/*!
+ * \brief Decrease the 'consumed' region of 'b' by 'n' bytes.
+ *
+ * Requires:
+ *
+ *\li 'b' is a valid buffer
+ *
+ *\li n <= current
+ */
+static inline void
+isc_buffer_back(isc_buffer_t *b, unsigned int n) {
+ ISC_REQUIRE(ISC_BUFFER_VALID(b));
+ ISC_REQUIRE(n <= b->current);
+
+ b->current -= n;
+}
+
+/*!
+ * \brief Store an unsigned 8-bit integer from 'val' into 'b'.
+ *
+ * Requires:
+ *\li 'b' is a valid buffer.
+ *
+ *\li The length of the available region of 'b' is at least 1
+ * or the buffer has autoreallocation enabled.
+ *
+ * Ensures:
+ *\li The used pointer in 'b' is advanced by 1.
+ */
+static inline void
+isc_buffer_putuint8(isc_buffer_t *b, uint8_t val) {
+ unsigned char *cp;
+
+ ISC_REQUIRE(ISC_BUFFER_VALID(b));
+
+ if (b->autore) {
+ isc_buffer_t *tmp = b;
+ ISC_REQUIRE(isc_buffer_reserve(&tmp, 1) == ISC_R_SUCCESS);
+ }
+
+ ISC_REQUIRE(isc_buffer_availablelength(b) >= 1U);
+
+ cp = isc_buffer_used(b);
+ b->used++;
+ cp[0] = val;
+}
+
+/*!
+ * \brief Store an unsigned 16-bit integer in host byte order from 'val'
+ * into 'b' in network byte order.
+ *
+ * Requires:
+ *\li 'b' is a valid buffer.
+ *
+ *\li The length of the available region of 'b' is at least 2
+ * or the buffer has autoreallocation enabled.
+ *
+ * Ensures:
+ *\li The used pointer in 'b' is advanced by 2.
+ */
+static inline void
+isc_buffer_putuint16(isc_buffer_t *b, uint16_t val) {
+ unsigned char *cp;
+
+ ISC_REQUIRE(ISC_BUFFER_VALID(b));
+
+ if (b->autore) {
+ isc_buffer_t *tmp = b;
+ ISC_REQUIRE(isc_buffer_reserve(&tmp, 2) == ISC_R_SUCCESS);
+ }
+
+ ISC_REQUIRE(isc_buffer_availablelength(b) >= 2U);
+
+ cp = isc_buffer_used(b);
+ b->used += 2;
+ cp[0] = (unsigned char)(val >> 8);
+ cp[1] = (unsigned char)val;
+}
+
+/*!
+ * Store an unsigned 24-bit integer in host byte order from 'val'
+ * into 'b' in network byte order.
+ *
+ * Requires:
+ *\li 'b' is a valid buffer.
+ *
+ *\li The length of the available region of 'b' is at least 3
+ * or the buffer has autoreallocation enabled.
+ *
+ * Ensures:
+ *\li The used pointer in 'b' is advanced by 3.
+ */
+static inline void
+isc_buffer_putuint24(isc_buffer_t *b, uint32_t val) {
+ unsigned char *cp;
+
+ ISC_REQUIRE(ISC_BUFFER_VALID(b));
+
+ if (b->autore) {
+ isc_buffer_t *tmp = b;
+ ISC_REQUIRE(isc_buffer_reserve(&tmp, 3) == ISC_R_SUCCESS);
+ }
+
+ ISC_REQUIRE(isc_buffer_availablelength(b) >= 3U);
+
+ cp = isc_buffer_used(b);
+ b->used += 3;
+ cp[0] = (unsigned char)(val >> 16);
+ cp[1] = (unsigned char)(val >> 8);
+ cp[2] = (unsigned char)val;
+}
+
+/*!
+ * \brief Store an unsigned 32-bit integer in host byte order from 'val'
+ * into 'b' in network byte order.
+ *
+ * Requires:
+ *\li 'b' is a valid buffer.
+ *
+ *\li The length of the available region of 'b' is at least 4
+ * or the buffer has autoreallocation enabled.
+ *
+ * Ensures:
+ *\li The used pointer in 'b' is advanced by 4.
+ */
+static inline void
+isc_buffer_putuint32(isc_buffer_t *b, uint32_t val) {
+ unsigned char *cp;
+
+ ISC_REQUIRE(ISC_BUFFER_VALID(b));
+
+ if (b->autore) {
+ isc_buffer_t *tmp = b;
+ ISC_REQUIRE(isc_buffer_reserve(&tmp, 4) == ISC_R_SUCCESS);
+ }
+
+ ISC_REQUIRE(isc_buffer_availablelength(b) >= 4U);
+
+ cp = isc_buffer_used(b);
+ b->used += 4;
+ cp[0] = (unsigned char)(val >> 24);
+ cp[1] = (unsigned char)(val >> 16);
+ cp[2] = (unsigned char)(val >> 8);
+ cp[3] = (unsigned char)val;
+}
+
+/*!
+ * \brief Store an unsigned 48-bit integer in host byte order from 'val'
+ * into 'b' in network byte order.
+ *
+ * Requires:
+ *\li 'b' is a valid buffer.
+ *
+ *\li The length of the available region of 'b' is at least 6
+ * or the buffer has autoreallocation enabled.
+ *
+ * Ensures:
+ *\li The used pointer in 'b' is advanced by 6.
+ */
+static inline void
+isc_buffer_putuint48(isc_buffer_t *b, uint64_t val) {
+ unsigned char *cp = NULL;
+
+ ISC_REQUIRE(ISC_BUFFER_VALID(b));
+
+ if (b->autore) {
+ isc_buffer_t *tmp = b;
+ ISC_REQUIRE(isc_buffer_reserve(&tmp, 6) == ISC_R_SUCCESS);
+ }
+
+ ISC_REQUIRE(isc_buffer_availablelength(b) >= 6U);
+
+ cp = isc_buffer_used(b);
+ b->used += 6;
+ cp[0] = (unsigned char)(val >> 40);
+ cp[1] = (unsigned char)(val >> 32);
+ cp[2] = (unsigned char)(val >> 24);
+ cp[3] = (unsigned char)(val >> 16);
+ cp[4] = (unsigned char)(val >> 8);
+ cp[5] = (unsigned char)val;
+}
+
+/*!
+ * \brief Copy 'length' bytes of memory at 'base' into 'b'.
+ *
+ * Requires:
+ *\li 'b' is a valid buffer.
+ *
+ *\li 'base' points to 'length' bytes of valid memory.
+ *
+ *\li The length of the available region of 'b' is at least 'length'
+ * or the buffer has autoreallocation enabled.
+ *
+ * Ensures:
+ *\li The used pointer in 'b' is advanced by 'length'.
+ */
+static inline void
+isc_buffer_putmem(isc_buffer_t *b, const unsigned char *base,
+ unsigned int length) {
+ ISC_REQUIRE(ISC_BUFFER_VALID(b));
+
+ if (b->autore) {
+ isc_buffer_t *tmp = b;
+ ISC_REQUIRE(isc_buffer_reserve(&tmp, length) == ISC_R_SUCCESS);
+ }
+
+ ISC_REQUIRE(isc_buffer_availablelength(b) >= (unsigned int)length);
+
+ if (length > 0U) {
+ memmove(isc_buffer_used(b), base, length);
+ b->used += length;
+ }
+}
+
+/*!
+ * \brief Copy 'source' into 'b', not including terminating NUL.
+ *
+ * Requires:
+ *\li 'b' is a valid buffer.
+ *
+ *\li 'source' is a valid NULL terminated string.
+ *
+ *\li The length of the available region of 'b' is at least strlen('source')
+ * or the buffer has autoreallocation enabled.
+ *
+ * Ensures:
+ *\li The used pointer in 'b' is advanced by strlen('source').
+ */
+static inline void
+isc_buffer_putstr(isc_buffer_t *b, const char *source) {
+ unsigned int length;
+ unsigned char *cp;
+
+ ISC_REQUIRE(ISC_BUFFER_VALID(b));
+ ISC_REQUIRE(source != NULL);
+
+ length = (unsigned int)strlen(source);
+ if (b->autore) {
+ isc_buffer_t *tmp = b;
+ ISC_REQUIRE(isc_buffer_reserve(&tmp, length) == ISC_R_SUCCESS);
+ }
+
+ ISC_REQUIRE(isc_buffer_availablelength(b) >= length);
+
+ cp = isc_buffer_used(b);
+ memmove(cp, source, length);
+ b->used += length;
+}
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/cmocka.h b/lib/isc/include/isc/cmocka.h
new file mode 100644
index 0000000..de86d5a
--- /dev/null
+++ b/lib/isc/include/isc/cmocka.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file isc/cmocka.h */
+
+#pragma once
+
+#include <cmocka.h>
+
+#include <isc/lang.h>
+
+ISC_LANG_BEGINDECLS
+
+/*
+ * Copy the test identified by 'name' from 'tests' to 'selected'.
+ */
+#define cmocka_add_test_byname(tests, name, selected) \
+ _cmocka_add_test_byname(tests, sizeof(tests) / sizeof(tests[0]), name, \
+ selected, \
+ sizeof(selected) / sizeof(selected[0]))
+
+static inline bool
+_cmocka_add_test_byname(const struct CMUnitTest *tests, size_t ntests,
+ const char *name, struct CMUnitTest *selected,
+ size_t nselected) {
+ size_t i, j;
+
+ for (i = 0; i < ntests && tests[i].name != NULL; i++) {
+ if (strcmp(tests[i].name, name) != 0) {
+ continue;
+ }
+ for (j = 0; j < nselected && selected[j].name != NULL; j++) {
+ if (strcmp(tests[j].name, name) == 0) {
+ break;
+ }
+ }
+ if (j < nselected && selected[j].name == NULL) {
+ selected[j] = tests[i];
+ }
+ return (true);
+ }
+ return (false);
+}
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/commandline.h b/lib/isc/include/isc/commandline.h
new file mode 100644
index 0000000..ac72135
--- /dev/null
+++ b/lib/isc/include/isc/commandline.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/commandline.h */
+
+#include <stdbool.h>
+
+#include <isc/lang.h>
+#include <isc/result.h>
+#include <isc/types.h>
+
+/*% Index into parent argv vector. */
+extern int isc_commandline_index;
+/*% Character checked for validity. */
+extern int isc_commandline_option;
+/*% Argument associated with option. */
+extern char *isc_commandline_argument;
+/*% For printing error messages. */
+extern char *isc_commandline_progname;
+/*% Print error message. */
+extern bool isc_commandline_errprint;
+/*% Reset getopt. */
+extern bool isc_commandline_reset;
+
+ISC_LANG_BEGINDECLS
+
+int
+isc_commandline_parse(int argc, char *const *argv, const char *options);
+/*%<
+ * Parse a command line (similar to getopt())
+ */
+
+isc_result_t
+isc_commandline_strtoargv(isc_mem_t *mctx, char *s, unsigned int *argcp,
+ char ***argvp, unsigned int n);
+/*%<
+ * Tokenize the string "s" into whitespace-separated words,
+ * returning the number of words in '*argcp' and an array
+ * of pointers to the words in '*argvp'. The caller
+ * must free the array using isc_mem_free(). The string
+ * is modified in-place.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/condition.h b/lib/isc/include/isc/condition.h
new file mode 100644
index 0000000..0e1dcf1
--- /dev/null
+++ b/lib/isc/include/isc/condition.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file */
+
+#include <errno.h>
+
+#include <isc/error.h>
+#include <isc/lang.h>
+#include <isc/mutex.h>
+#include <isc/result.h>
+#include <isc/strerr.h>
+#include <isc/string.h>
+#include <isc/types.h>
+
+typedef pthread_cond_t isc_condition_t;
+
+#define isc_condition_init(cond) \
+ if (pthread_cond_init(cond, NULL) != 0) { \
+ FATAL_SYSERROR(errno, "pthread_cond_init()"); \
+ }
+
+#define isc_condition_wait(cp, mp) \
+ ((pthread_cond_wait((cp), (mp)) == 0) ? ISC_R_SUCCESS \
+ : ISC_R_UNEXPECTED)
+
+#define isc_condition_signal(cp) \
+ ((pthread_cond_signal((cp)) == 0) ? ISC_R_SUCCESS : ISC_R_UNEXPECTED)
+
+#define isc_condition_broadcast(cp) \
+ ((pthread_cond_broadcast((cp)) == 0) ? ISC_R_SUCCESS : ISC_R_UNEXPECTED)
+
+#define isc_condition_destroy(cp) \
+ ((pthread_cond_destroy((cp)) == 0) ? ISC_R_SUCCESS : ISC_R_UNEXPECTED)
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+isc_condition_waituntil(isc_condition_t *, isc_mutex_t *, isc_time_t *);
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/counter.h b/lib/isc/include/isc/counter.h
new file mode 100644
index 0000000..820e4a2
--- /dev/null
+++ b/lib/isc/include/isc/counter.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*****
+***** Module Info
+*****/
+
+/*! \file isc/counter.h
+ *
+ * \brief The isc_counter_t object is a simplified version of the
+ * isc_quota_t object; it tracks the consumption of limited
+ * resources, returning an error condition when the quota is
+ * exceeded. However, unlike isc_quota_t, attaching and detaching
+ * from a counter object does not increment or decrement the counter.
+ */
+
+/***
+ *** Imports.
+ ***/
+
+#include <isc/lang.h>
+#include <isc/mutex.h>
+#include <isc/types.h>
+
+/*****
+***** Types.
+*****/
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+isc_counter_create(isc_mem_t *mctx, int limit, isc_counter_t **counterp);
+/*%<
+ * Allocate and initialize a counter object.
+ */
+
+isc_result_t
+isc_counter_increment(isc_counter_t *counter);
+/*%<
+ * Increment the counter.
+ *
+ * If the counter limit is nonzero and has been reached, then
+ * return ISC_R_QUOTA, otherwise ISC_R_SUCCESS. (The counter is
+ * incremented regardless of return value.)
+ */
+
+unsigned int
+isc_counter_used(isc_counter_t *counter);
+/*%<
+ * Return the current counter value.
+ */
+
+void
+isc_counter_setlimit(isc_counter_t *counter, int limit);
+/*%<
+ * Set the counter limit.
+ */
+
+void
+isc_counter_attach(isc_counter_t *source, isc_counter_t **targetp);
+/*%<
+ * Attach to a counter object, increasing its reference counter.
+ */
+
+void
+isc_counter_detach(isc_counter_t **counterp);
+/*%<
+ * Detach (and destroy if reference counter has dropped to zero)
+ * a counter object.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/crc64.h b/lib/isc/include/isc/crc64.h
new file mode 100644
index 0000000..4147672
--- /dev/null
+++ b/lib/isc/include/isc/crc64.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/crc64.h
+ * \brief CRC64 in C
+ */
+
+#include <inttypes.h>
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+void
+isc_crc64_init(uint64_t *crc);
+/*%
+ * Initialize a new CRC.
+ *
+ * Requires:
+ * * 'crc' is not NULL.
+ */
+
+void
+isc_crc64_update(uint64_t *crc, const void *data, size_t len);
+/*%
+ * Add data to the CRC.
+ *
+ * Requires:
+ * * 'crc' is not NULL.
+ * * 'data' is not NULL.
+ */
+
+void
+isc_crc64_final(uint64_t *crc);
+/*%
+ * Finalize the CRC.
+ *
+ * Requires:
+ * * 'crc' is not NULL.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/deprecated.h b/lib/isc/include/isc/deprecated.h
new file mode 100644
index 0000000..c83508d
--- /dev/null
+++ b/lib/isc/include/isc/deprecated.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#if (__GNUC__ + 0) > 3
+#define ISC_DEPRECATED __attribute__((deprecated))
+#else /* if (__GNUC__ + 0) > 3 */
+#define ISC_DEPRECATED /* none */
+#endif /* __GNUC__ > 3*/
diff --git a/lib/isc/include/isc/dir.h b/lib/isc/include/isc/dir.h
new file mode 100644
index 0000000..c1ff2c7
--- /dev/null
+++ b/lib/isc/include/isc/dir.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file */
+
+#include <dirent.h>
+#include <limits.h>
+
+#include <isc/lang.h>
+#include <isc/result.h>
+
+#include <sys/types.h> /* Required on some systems. */
+
+#ifndef NAME_MAX
+#define NAME_MAX 256
+#endif
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+/*% Directory Entry */
+typedef struct isc_direntry {
+ char name[NAME_MAX];
+ unsigned int length;
+} isc_direntry_t;
+
+/*% Directory */
+typedef struct isc_dir {
+ unsigned int magic;
+ char dirname[PATH_MAX];
+ isc_direntry_t entry;
+ DIR *handle;
+} isc_dir_t;
+
+ISC_LANG_BEGINDECLS
+
+void
+isc_dir_init(isc_dir_t *dir);
+
+isc_result_t
+isc_dir_open(isc_dir_t *dir, const char *dirname);
+
+isc_result_t
+isc_dir_read(isc_dir_t *dir);
+
+isc_result_t
+isc_dir_reset(isc_dir_t *dir);
+
+void
+isc_dir_close(isc_dir_t *dir);
+
+isc_result_t
+isc_dir_chdir(const char *dirname);
+
+isc_result_t
+isc_dir_chroot(const char *dirname);
+
+isc_result_t
+isc_dir_createunique(char *templet);
+/*!<
+ * Use a templet (such as from isc_file_mktemplate()) to create a uniquely
+ * named, empty directory. The templet string is modified in place.
+ * If result == ISC_R_SUCCESS, it is the name of the directory that was
+ * created.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/endian.h b/lib/isc/include/isc/endian.h
new file mode 100644
index 0000000..be91b1d
--- /dev/null
+++ b/lib/isc/include/isc/endian.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
+ defined(__OpenBSD__) || defined(__bsdi__)
+
+#include <sys/endian.h>
+
+/*
+ * Recent BSDs should have [bl]e{16,32,64}toh() defined in <sys/endian.h>.
+ * Older ones might not, but these should have the alternatively named
+ * [bl]etoh{16,32,64}() functions defined.
+ */
+#ifndef be16toh
+#define be16toh(x) betoh16(x)
+#define le16toh(x) letoh16(x)
+#define be32toh(x) betoh32(x)
+#define le32toh(x) letoh32(x)
+#define be64toh(x) betoh64(x)
+#define le64toh(x) letoh64(x)
+#endif /* !be16toh */
+
+#elif defined __APPLE__
+
+/*
+ * macOS has its own byte-swapping routines, so use these.
+ */
+
+#include <libkern/OSByteOrder.h>
+
+#define htobe16(x) OSSwapHostToBigInt16(x)
+#define htole16(x) OSSwapHostToLittleInt16(x)
+#define be16toh(x) OSSwapBigToHostInt16(x)
+#define le16toh(x) OSSwapLittleToHostInt16(x)
+
+#define htobe32(x) OSSwapHostToBigInt32(x)
+#define htole32(x) OSSwapHostToLittleInt32(x)
+#define be32toh(x) OSSwapBigToHostInt32(x)
+#define le32toh(x) OSSwapLittleToHostInt32(x)
+
+#define htobe64(x) OSSwapHostToBigInt64(x)
+#define htole64(x) OSSwapHostToLittleInt64(x)
+#define be64toh(x) OSSwapBigToHostInt64(x)
+#define le64toh(x) OSSwapLittleToHostInt64(x)
+
+#elif defined(sun) || defined(__sun) || defined(__SVR4)
+
+/*
+ * For Solaris, rely on the fallback definitions below, though use
+ * Solaris-specific versions of bswap_{16,32,64}().
+ */
+
+#include <sys/byteorder.h>
+
+#define bswap_16(x) BSWAP_16(x)
+#define bswap_32(x) BSWAP_32(x)
+#define bswap_64(x) BSWAP_64(x)
+
+#elif defined(__ANDROID__) || defined(__CYGWIN__) || defined(__GNUC__) || \
+ defined(__GNU__)
+
+#include <byteswap.h>
+#include <endian.h>
+
+#else /* if defined(__DragonFly__) || defined(__FreeBSD__) || \
+ * defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) */
+
+#endif /* Specific platform support */
+
+/*
+ * Fallback definitions.
+ */
+
+#include <inttypes.h>
+
+#ifndef bswap_16
+#define bswap_16(x) \
+ ((uint16_t)((((uint16_t)(x)&0xff00) >> 8) | \
+ (((uint16_t)(x)&0x00ff) << 8)))
+#endif /* !bswap_16 */
+
+#ifndef bswap_32
+#define bswap_32(x) \
+ ((uint32_t)((((uint32_t)(x)&0xff000000) >> 24) | \
+ (((uint32_t)(x)&0x00ff0000) >> 8) | \
+ (((uint32_t)(x)&0x0000ff00) << 8) | \
+ (((uint32_t)(x)&0x000000ff) << 24)))
+#endif /* !bswap_32 */
+
+#ifndef bswap_64
+#define bswap_64(x) \
+ ((uint64_t)((((uint64_t)(x)&0xff00000000000000ULL) >> 56) | \
+ (((uint64_t)(x)&0x00ff000000000000ULL) >> 40) | \
+ (((uint64_t)(x)&0x0000ff0000000000ULL) >> 24) | \
+ (((uint64_t)(x)&0x000000ff00000000ULL) >> 8) | \
+ (((uint64_t)(x)&0x00000000ff000000ULL) << 8) | \
+ (((uint64_t)(x)&0x0000000000ff0000ULL) << 24) | \
+ (((uint64_t)(x)&0x000000000000ff00ULL) << 40) | \
+ (((uint64_t)(x)&0x00000000000000ffULL) << 56)))
+#endif /* !bswap_64 */
+
+#ifndef htobe16
+#if WORDS_BIGENDIAN
+
+#define htobe16(x) (x)
+#define htole16(x) bswap_16(x)
+#define be16toh(x) (x)
+#define le16toh(x) bswap_16(x)
+
+#else /* WORDS_BIGENDIAN */
+
+#define htobe16(x) bswap_16(x)
+#define htole16(x) (x)
+#define be16toh(x) bswap_16(x)
+#define le16toh(x) (x)
+
+#endif /* WORDS_BIGENDIAN */
+#endif /* !htobe16 */
+
+#ifndef htobe32
+#if WORDS_BIGENDIAN
+
+#define htobe32(x) (x)
+#define htole32(x) bswap_32(x)
+#define be32toh(x) (x)
+#define le32toh(x) bswap_32(x)
+
+#else /* WORDS_BIGENDIAN */
+
+#define htobe32(x) bswap_32(x)
+#define htole32(x) (x)
+#define be32toh(x) bswap_32(x)
+#define le32toh(x) (x)
+
+#endif /* WORDS_BIGENDIAN */
+#endif /* !htobe32 */
+
+#ifndef htobe64
+#if WORDS_BIGENDIAN
+
+#define htobe64(x) (x)
+#define htole64(x) bswap_64(x)
+#define be64toh(x) (x)
+#define le64toh(x) bswap_64(x)
+
+#else /* WORDS_BIGENDIAN */
+
+#define htobe64(x) bswap_64(x)
+#define htole64(x) (x)
+#define be64toh(x) bswap_64(x)
+#define le64toh(x) (x)
+
+#endif /* WORDS_BIGENDIAN */
+#endif /* !htobe64 */
diff --git a/lib/isc/include/isc/errno.h b/lib/isc/include/isc/errno.h
new file mode 100644
index 0000000..0151df3
--- /dev/null
+++ b/lib/isc/include/isc/errno.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/file.h */
+
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+isc_errno_toresult(int err);
+/*!<
+ * \brief Convert a POSIX errno value to an ISC result code.
+ */
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/error.h b/lib/isc/include/isc/error.h
new file mode 100644
index 0000000..f4f6682
--- /dev/null
+++ b/lib/isc/include/isc/error.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/error.h */
+
+#include <stdarg.h>
+
+#include <isc/attributes.h>
+#include <isc/formatcheck.h>
+#include <isc/lang.h>
+
+ISC_LANG_BEGINDECLS
+
+typedef void (*isc_errorcallback_t)(const char *, int, const char *,
+ const char *, va_list);
+
+/*% set unexpected error */
+void isc_error_setunexpected(isc_errorcallback_t);
+
+/*% set fatal error */
+void isc_error_setfatal(isc_errorcallback_t);
+
+/*% unexpected error */
+void
+isc_error_unexpected(const char *, int, const char *, const char *, ...)
+ ISC_FORMAT_PRINTF(4, 5);
+
+/*% fatal error */
+noreturn void
+isc_error_fatal(const char *, int, const char *, const char *, ...)
+ ISC_FORMAT_PRINTF(4, 5);
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/event.h b/lib/isc/include/isc/event.h
new file mode 100644
index 0000000..811bcd8
--- /dev/null
+++ b/lib/isc/include/isc/event.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/event.h */
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+/*****
+***** Events.
+*****/
+
+typedef void (*isc_eventdestructor_t)(isc_event_t *);
+
+#define ISC_EVENT_COMMON(ltype) \
+ size_t ev_size; \
+ unsigned int ev_attributes; \
+ void *ev_tag; \
+ isc_eventtype_t ev_type; \
+ isc_taskaction_t ev_action; \
+ void *ev_arg; \
+ void *ev_sender; \
+ isc_eventdestructor_t ev_destroy; \
+ void *ev_destroy_arg; \
+ ISC_LINK(ltype) ev_link; \
+ ISC_LINK(ltype) ev_ratelink
+
+/*%
+ * Attributes matching a mask of 0x000000ff are reserved for the task library's
+ * definition. Attributes of 0xffffff00 may be used by the application
+ * or non-ISC libraries.
+ */
+#define ISC_EVENTATTR_NOPURGE 0x00000001
+
+/*%
+ * The ISC_EVENTATTR_CANCELED attribute is intended to indicate
+ * that an event is delivered as a result of a canceled operation
+ * rather than successful completion, by mutual agreement
+ * between the sender and receiver. It is not set or used by
+ * the task system.
+ */
+#define ISC_EVENTATTR_CANCELED 0x00000002
+
+#define ISC_EVENT_INIT(event, sz, at, ta, ty, ac, ar, sn, df, da) \
+ do { \
+ (event)->ev_size = (sz); \
+ (event)->ev_attributes = (at); \
+ (event)->ev_tag = (ta); \
+ (event)->ev_type = (ty); \
+ (event)->ev_action = (ac); \
+ (event)->ev_arg = (ar); \
+ (event)->ev_sender = (sn); \
+ (event)->ev_destroy = (df); \
+ (event)->ev_destroy_arg = (da); \
+ ISC_LINK_INIT((event), ev_link); \
+ ISC_LINK_INIT((event), ev_ratelink); \
+ } while (0)
+
+/*%
+ * This structure is public because "subclassing" it may be useful when
+ * defining new event types.
+ */
+struct isc_event {
+ ISC_EVENT_COMMON(struct isc_event);
+};
+
+#define ISC_EVENTTYPE_FIRSTEVENT 0x00000000
+#define ISC_EVENTTYPE_LASTEVENT 0xffffffff
+
+#define ISC_EVENT_PTR(p) ((isc_event_t **)(void *)(p))
+
+ISC_LANG_BEGINDECLS
+
+isc_event_t *
+isc_event_allocate(isc_mem_t *mctx, void *sender, isc_eventtype_t type,
+ isc_taskaction_t action, void *arg, size_t size);
+isc_event_t *
+isc_event_constallocate(isc_mem_t *mctx, void *sender, isc_eventtype_t type,
+ isc_taskaction_t action, const void *arg, size_t size);
+/*%<
+ * Allocate an event structure.
+ *
+ * Allocate and initialize in a structure with initial elements
+ * defined by:
+ *
+ * \code
+ * struct {
+ * ISC_EVENT_COMMON(struct isc_event);
+ * ...
+ * };
+ * \endcode
+ *
+ * Requires:
+ *\li 'size' >= sizeof(struct isc_event)
+ *\li 'action' to be non NULL
+ *
+ * Returns:
+ *\li a pointer to a initialized structure of the requested size.
+ *\li NULL if unable to allocate memory.
+ */
+
+void
+isc_event_free(isc_event_t **);
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/eventclass.h b/lib/isc/include/isc/eventclass.h
new file mode 100644
index 0000000..b8be2ff
--- /dev/null
+++ b/lib/isc/include/isc/eventclass.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/eventclass.h
+ ***** Registry of Predefined Event Type Classes
+ *****/
+
+/*%
+ * An event class is an unsigned 16 bit number. Each class may contain up
+ * to 65536 events. An event type is formed by adding the event number
+ * within the class to the class number.
+ *
+ */
+
+#define ISC_EVENTCLASS(eclass) ((eclass) << 16)
+
+/*@{*/
+/*!
+ * Classes < 1024 are reserved for ISC use.
+ * Event classes >= 1024 and <= 65535 are reserved for application use.
+ */
+
+#define ISC_EVENTCLASS_TASK ISC_EVENTCLASS(0)
+#define ISC_EVENTCLASS_TIMER ISC_EVENTCLASS(1)
+#define ISC_EVENTCLASS_SOCKET ISC_EVENTCLASS(2)
+#define ISC_EVENTCLASS_FILE ISC_EVENTCLASS(3)
+#define ISC_EVENTCLASS_DNS ISC_EVENTCLASS(4)
+#define ISC_EVENTCLASS_APP ISC_EVENTCLASS(5)
+#define ISC_EVENTCLASS_OMAPI ISC_EVENTCLASS(6)
+#define ISC_EVENTCLASS_RATELIMITER ISC_EVENTCLASS(7)
+#define ISC_EVENTCLASS_ISCCC ISC_EVENTCLASS(8)
+#define ISC_EVENTCLASS_NS ISC_EVENTCLASS(9)
+/*@}*/
diff --git a/lib/isc/include/isc/file.h b/lib/isc/include/isc/file.h
new file mode 100644
index 0000000..f78c5c5
--- /dev/null
+++ b/lib/isc/include/isc/file.h
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/file.h */
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <isc/lang.h>
+#include <isc/stat.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+isc_file_settime(const char *file, isc_time_t *time);
+
+isc_result_t
+isc_file_mode(const char *file, mode_t *modep);
+
+isc_result_t
+isc_file_getmodtime(const char *file, isc_time_t *time);
+/*!<
+ * \brief Get the time of last modification of a file.
+ *
+ * Notes:
+ *\li The time that is set is relative to the (OS-specific) epoch, as are
+ * all isc_time_t structures.
+ *
+ * Requires:
+ *\li file != NULL.
+ *\li time != NULL.
+ *
+ * Ensures:
+ *\li If the file could not be accessed, 'time' is unchanged.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ * Success.
+ *\li #ISC_R_NOTFOUND
+ * No such file exists.
+ *\li #ISC_R_INVALIDFILE
+ * The path specified was not usable by the operating system.
+ *\li #ISC_R_NOPERM
+ * The file's metainformation could not be retrieved because
+ * permission was denied to some part of the file's path.
+ *\li #ISC_R_IOERROR
+ * Hardware error interacting with the filesystem.
+ *\li #ISC_R_UNEXPECTED
+ * Something totally unexpected happened.
+ *
+ */
+
+isc_result_t
+isc_file_mktemplate(const char *path, char *buf, size_t buflen);
+/*!<
+ * \brief Generate a template string suitable for use with
+ * isc_file_openunique().
+ *
+ * Notes:
+ *\li This function is intended to make creating temporary files
+ * portable between different operating systems.
+ *
+ *\li The path is prepended to an implementation-defined string and
+ * placed into buf. The string has no path characters in it,
+ * and its maximum length is 14 characters plus a NUL. Thus
+ * buflen should be at least strlen(path) + 15 characters or
+ * an error will be returned.
+ *
+ * Requires:
+ *\li buf != NULL.
+ *
+ * Ensures:
+ *\li If result == #ISC_R_SUCCESS:
+ * buf contains a string suitable for use as the template argument
+ * to isc_file_openunique().
+ *
+ *\li If result != #ISC_R_SUCCESS:
+ * buf is unchanged.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS Success.
+ *\li #ISC_R_NOSPACE buflen indicates buf is too small for the catenation
+ * of the path with the internal template string.
+ */
+
+isc_result_t
+isc_file_openunique(char *templet, FILE **fp);
+isc_result_t
+isc_file_openuniqueprivate(char *templet, FILE **fp);
+isc_result_t
+isc_file_openuniquemode(char *templet, int mode, FILE **fp);
+isc_result_t
+isc_file_bopenunique(char *templet, FILE **fp);
+isc_result_t
+isc_file_bopenuniqueprivate(char *templet, FILE **fp);
+isc_result_t
+isc_file_bopenuniquemode(char *templet, int mode, FILE **fp);
+/*!<
+ * \brief Create and open a file with a unique name based on 'templet'.
+ * isc_file_bopen*() open the file in binary mode in Windows.
+ * isc_file_open*() open the file in text mode in Windows.
+ *
+ * Notes:
+ *\li 'template' is a reserved work in C++. If you want to complain
+ * about the spelling of 'templet', first look it up in the
+ * Merriam-Webster English dictionary. (http://www.m-w.com/)
+ *
+ *\li This function works by using the template to generate file names.
+ * The template must be a writable string, as it is modified in place.
+ * Trailing X characters in the file name (full file name on Unix,
+ * basename on Win32 -- eg, tmp-XXXXXX vs XXXXXX.tmp, respectively)
+ * are replaced with ASCII characters until a non-existent filename
+ * is found. If the template does not include pathname information,
+ * the files in the working directory of the program are searched.
+ *
+ *\li isc_file_mktemplate is a good, portable way to get a template.
+ *
+ * Requires:
+ *\li 'fp' is non-NULL and '*fp' is NULL.
+ *
+ *\li 'template' is non-NULL, and of a form suitable for use by
+ * the system as described above.
+ *
+ * Ensures:
+ *\li If result is #ISC_R_SUCCESS:
+ * *fp points to an stream opening in stdio's "w+" mode.
+ *
+ *\li If result is not #ISC_R_SUCCESS:
+ * *fp is NULL.
+ *
+ * No file is open. Even if one was created (but unable
+ * to be reopened as a stdio FILE pointer) then it has been
+ * removed.
+ *
+ *\li This function does *not* ensure that the template string has not been
+ * modified, even if the operation was unsuccessful.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ * Success.
+ *\li #ISC_R_EXISTS
+ * No file with a unique name could be created based on the
+ * template.
+ *\li #ISC_R_INVALIDFILE
+ * The path specified was not usable by the operating system.
+ *\li #ISC_R_NOPERM
+ * The file could not be created because permission was denied
+ * to some part of the file's path.
+ *\li #ISC_R_IOERROR
+ * Hardware error interacting with the filesystem.
+ *\li #ISC_R_UNEXPECTED
+ * Something totally unexpected happened.
+ */
+
+isc_result_t
+isc_file_remove(const char *filename);
+/*!<
+ * \brief Remove the file named by 'filename'.
+ */
+
+isc_result_t
+isc_file_rename(const char *oldname, const char *newname);
+/*!<
+ * \brief Rename the file 'oldname' to 'newname'.
+ */
+
+bool
+isc_file_exists(const char *pathname);
+/*!<
+ * \brief Return #true if the calling process can tell that the given file
+ * exists. Will not return true if the calling process has insufficient
+ * privileges to search the entire path.
+ */
+
+bool
+isc_file_isabsolute(const char *filename);
+/*!<
+ * \brief Return #true if the given file name is absolute.
+ */
+
+isc_result_t
+isc_file_isplainfile(const char *name);
+
+isc_result_t
+isc_file_isplainfilefd(int fd);
+/*!<
+ * \brief Check that the file is a plain file
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ * Success. The file is a plain file.
+ *\li #ISC_R_INVALIDFILE
+ * The path specified was not usable by the operating system.
+ *\li #ISC_R_FILENOTFOUND
+ * The file does not exist. This return code comes from
+ * errno=ENOENT when stat returns -1. This code is mentioned
+ * here, because in logconf.c, it is the one rcode that is
+ * permitted in addition to ISC_R_SUCCESS. This is done since
+ * the next call in logconf.c is to isc_stdio_open(), which
+ * will create the file if it can.
+ *\li other ISC_R_* errors translated from errno
+ * These occur when stat returns -1 and an errno.
+ */
+
+isc_result_t
+isc_file_isdirectory(const char *name);
+/*!<
+ * \brief Check that 'name' exists and is a directory.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ * Success, file is a directory.
+ *\li #ISC_R_INVALIDFILE
+ * File is not a directory.
+ *\li #ISC_R_FILENOTFOUND
+ * File does not exist.
+ *\li other ISC_R_* errors translated from errno
+ * These occur when stat returns -1 and an errno.
+ */
+
+bool
+isc_file_iscurrentdir(const char *filename);
+/*!<
+ * \brief Return #true if the given file name is the current directory (".").
+ */
+
+bool
+isc_file_ischdiridempotent(const char *filename);
+/*%<
+ * Return #true if calling chdir(filename) multiple times will give
+ * the same result as calling it once.
+ */
+
+const char *
+isc_file_basename(const char *filename);
+/*%<
+ * Return the final component of the path in the file name.
+ */
+
+isc_result_t
+isc_file_progname(const char *filename, char *buf, size_t buflen);
+/*!<
+ * \brief Given an operating system specific file name "filename"
+ * referring to a program, return the canonical program name.
+ *
+ * Any directory prefix or executable file name extension (if
+ * used on the OS in case) is stripped. On systems where program
+ * names are case insensitive, the name is canonicalized to all
+ * lower case. The name is written to 'buf', an array of 'buflen'
+ * chars, and null terminated.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOSPACE The name did not fit in 'buf'.
+ */
+
+isc_result_t
+isc_file_template(const char *path, const char *templet, char *buf,
+ size_t buflen);
+/*%<
+ * Create an OS specific template using 'path' to define the directory
+ * 'templet' to describe the filename and store the result in 'buf'
+ * such that path can be renamed to buf atomically.
+ */
+
+isc_result_t
+isc_file_renameunique(const char *file, char *templet);
+/*%<
+ * Rename 'file' using 'templet' as a template for the new file name.
+ */
+
+isc_result_t
+isc_file_absolutepath(const char *filename, char *path, size_t pathlen);
+/*%<
+ * Given a file name, return the fully qualified path to the file.
+ */
+
+/*
+ * XXX We should also have a isc_file_writeeopen() function
+ * for safely open a file in a publicly writable directory
+ * (see write_open() in BIND 8's ns_config.c).
+ */
+
+isc_result_t
+isc_file_truncate(const char *filename, isc_offset_t size);
+/*%<
+ * Truncate/extend the file specified to 'size' bytes.
+ */
+
+isc_result_t
+isc_file_safecreate(const char *filename, FILE **fp);
+/*%<
+ * Open 'filename' for writing, truncating if necessary. Ensure that
+ * if it existed it was a normal file. If creating the file, ensure
+ * that only the owner can read/write it.
+ */
+
+isc_result_t
+isc_file_splitpath(isc_mem_t *mctx, const char *path, char **dirname,
+ char const **basename);
+/*%<
+ * Split a path into dirname and basename. If 'path' contains no slash
+ * (or, on windows, backslash), then '*dirname' is set to ".".
+ *
+ * Allocates memory for '*dirname', which can be freed with isc_mem_free().
+ *
+ * Returns:
+ * - ISC_R_SUCCESS on success
+ * - ISC_R_INVALIDFILE if 'path' is empty or ends with '/'
+ * - ISC_R_NOMEMORY if unable to allocate memory
+ */
+
+isc_result_t
+isc_file_getsize(const char *file, off_t *size);
+/*%<
+ * Return the size of the file (stored in the parameter pointed
+ * to by 'size') in bytes.
+ *
+ * Returns:
+ * - ISC_R_SUCCESS on success
+ */
+
+isc_result_t
+isc_file_getsizefd(int fd, off_t *size);
+/*%<
+ * Return the size of the file (stored in the parameter pointed
+ * to by 'size') in bytes.
+ *
+ * Returns:
+ * - ISC_R_SUCCESS on success
+ */
+
+isc_result_t
+isc_file_sanitize(const char *dir, const char *base, const char *ext,
+ char *path, size_t length);
+/*%<
+ * Generate a sanitized filename, such as for MKEYS or NZF files.
+ *
+ * Historically, MKEYS and NZF files used SHA256 hashes of the view
+ * name for the filename; this was to deal with the possibility of
+ * forbidden characters such as "/" being in a view name, and to
+ * avoid problems with case-insensitive file systems.
+ *
+ * Given a basename 'base' and an extension 'ext', this function checks
+ * for the existence of file using the old-style name format in directory
+ * 'dir'. If found, it returns the path to that file. If there is no
+ * file already in place, a new pathname is generated; if the basename
+ * contains any excluded characters, then a truncated SHA256 hash is
+ * used, otherwise the basename is used. The path name is copied
+ * into 'path', which must point to a buffer of at least 'length'
+ * bytes.
+ *
+ * Requires:
+ * - base != NULL
+ * - path != NULL
+ *
+ * Returns:
+ * - ISC_R_SUCCESS on success
+ * - ISC_R_NOSPACE if the resulting path would be longer than 'length'
+ */
+
+bool
+isc_file_isdirwritable(const char *path);
+/*%<
+ * Return true if the path is a directory and is writable
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/formatcheck.h b/lib/isc/include/isc/formatcheck.h
new file mode 100644
index 0000000..e3a37b0
--- /dev/null
+++ b/lib/isc/include/isc/formatcheck.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/formatcheck.h */
+
+/*%
+ * ISC_FORMAT_PRINTF().
+ *
+ * \li fmt is the location of the format string parameter.
+ * \li args is the location of the first argument (or 0 for no argument
+ * checking).
+ *
+ * Note:
+ * \li The first parameter is 1, not 0.
+ */
+#ifdef __GNUC__
+#define ISC_FORMAT_PRINTF(fmt, args) \
+ __attribute__((__format__(__printf__, fmt, args)))
+#else /* ifdef __GNUC__ */
+#define ISC_FORMAT_PRINTF(fmt, args)
+#endif /* ifdef __GNUC__ */
diff --git a/lib/isc/include/isc/fuzz.h b/lib/isc/include/isc/fuzz.h
new file mode 100644
index 0000000..ce322ea
--- /dev/null
+++ b/lib/isc/include/isc/fuzz.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+typedef enum {
+ isc_fuzz_none,
+ isc_fuzz_client,
+ isc_fuzz_tcpclient,
+ isc_fuzz_resolver,
+ isc_fuzz_http,
+ isc_fuzz_rndc
+} isc_fuzztype_t;
diff --git a/lib/isc/include/isc/glob.h b/lib/isc/include/isc/glob.h
new file mode 100644
index 0000000..c9e8d19
--- /dev/null
+++ b/lib/isc/include/isc/glob.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <isc/lang.h>
+#include <isc/result.h>
+
+#if HAVE_GLOB_H
+#include <glob.h>
+#else
+#include <stddef.h>
+
+#include <isc/mem.h>
+
+typedef struct {
+ size_t gl_pathc;
+ char **gl_pathv;
+ isc_mem_t *mctx;
+ void *reserved;
+} glob_t;
+
+#endif
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+isc_glob(const char *pattern, glob_t *pglob);
+
+void
+isc_globfree(glob_t *pglob);
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/hash.h b/lib/isc/include/isc/hash.h
new file mode 100644
index 0000000..ee9e74f
--- /dev/null
+++ b/lib/isc/include/isc/hash.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include "isc/lang.h"
+#include "isc/types.h"
+
+/***
+ *** Functions
+ ***/
+ISC_LANG_BEGINDECLS
+
+const void *
+isc_hash_get_initializer(void);
+
+void
+isc_hash_set_initializer(const void *initializer);
+
+#define isc_hash_function isc_hash64
+
+uint32_t
+isc_hash32(const void *data, const size_t length, const bool case_sensitive);
+uint64_t
+isc_hash64(const void *data, const size_t length, const bool case_sensitive);
+/*!<
+ * \brief Calculate a hash over data.
+ *
+ * This hash function is useful for hashtables. The hash function is
+ * opaque and not important to the caller. The returned hash values are
+ * non-deterministic and will have different mapping every time a
+ * process using this library is run, but will have uniform
+ * distribution.
+ *
+ * isc_hash_32/64() calculates the hash from start to end over the
+ * input data.
+ *
+ * 'data' is the data to be hashed.
+ *
+ * 'length' is the size of the data to be hashed.
+ *
+ * 'case_sensitive' specifies whether the hash key should be treated as
+ * case_sensitive values. It should typically be false if the hash key
+ * is a DNS name.
+ *
+ * Returns:
+ * \li 32 or 64-bit hash value
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/heap.h b/lib/isc/include/isc/heap.h
new file mode 100644
index 0000000..98ae74c
--- /dev/null
+++ b/lib/isc/include/isc/heap.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/heap.h */
+
+#include <stdbool.h>
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/*%
+ * The comparison function returns true if the first argument has
+ * higher priority than the second argument, and false otherwise.
+ */
+typedef bool (*isc_heapcompare_t)(void *, void *);
+
+/*%
+ * The index function allows the client of the heap to receive a callback
+ * when an item's index number changes. This allows it to maintain
+ * sync with its external state, but still delete itself, since deletions
+ * from the heap require the index be provided.
+ */
+typedef void (*isc_heapindex_t)(void *, unsigned int);
+
+/*%
+ * The heapaction function is used when iterating over the heap.
+ *
+ * NOTE: The heap structure CANNOT BE MODIFIED during the call to
+ * isc_heap_foreach().
+ */
+typedef void (*isc_heapaction_t)(void *, void *);
+
+typedef struct isc_heap isc_heap_t;
+
+void
+isc_heap_create(isc_mem_t *mctx, isc_heapcompare_t compare,
+ isc_heapindex_t index, unsigned int size_increment,
+ isc_heap_t **heapp);
+/*!<
+ * \brief Create a new heap. The heap is implemented using a space-efficient
+ * storage method. When the heap elements are deleted space is not freed
+ * but will be reused when new elements are inserted.
+ *
+ * Heap elements are indexed from 1.
+ *
+ * Requires:
+ *\li "mctx" is valid.
+ *\li "compare" is a function which takes two void * arguments and
+ * returns true if the first argument has a higher priority than
+ * the second, and false otherwise.
+ *\li "index" is a function which takes a void *, and an unsigned int
+ * argument. This function will be called whenever an element's
+ * index value changes, so it may continue to delete itself from the
+ * heap. This option may be NULL if this functionality is unneeded.
+ *\li "size_increment" is a hint about how large the heap should grow
+ * when resizing is needed. If this is 0, a default size will be
+ * used, which is currently 1024, allowing space for an additional 1024
+ * heap elements to be inserted before adding more space.
+ *\li "heapp" is not NULL, and "*heap" is NULL.
+ */
+
+void
+isc_heap_destroy(isc_heap_t **heapp);
+/*!<
+ * \brief Destroys a heap.
+ *
+ * Requires:
+ *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t.
+ */
+
+void
+isc_heap_insert(isc_heap_t *heap, void *elt);
+/*!<
+ * \brief Inserts a new element into a heap.
+ *
+ * Requires:
+ *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t.
+ */
+
+void
+isc_heap_delete(isc_heap_t *heap, unsigned int index);
+/*!<
+ * \brief Deletes an element from a heap, by element index.
+ *
+ * Requires:
+ *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t.
+ *\li "index" is a valid element index, as provided by the "index" callback
+ * provided during heap creation.
+ */
+
+void
+isc_heap_increased(isc_heap_t *heap, unsigned int index);
+/*!<
+ * \brief Indicates to the heap that an element's priority has increased.
+ * This function MUST be called whenever an element has increased in priority.
+ *
+ * Requires:
+ *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t.
+ *\li "index" is a valid element index, as provided by the "index" callback
+ * provided during heap creation.
+ */
+
+void
+isc_heap_decreased(isc_heap_t *heap, unsigned int index);
+/*!<
+ * \brief Indicates to the heap that an element's priority has decreased.
+ * This function MUST be called whenever an element has decreased in priority.
+ *
+ * Requires:
+ *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t.
+ *\li "index" is a valid element index, as provided by the "index" callback
+ * provided during heap creation.
+ */
+
+void *
+isc_heap_element(isc_heap_t *heap, unsigned int index);
+/*!<
+ * \brief Returns the element for a specific element index.
+ *
+ * Requires:
+ *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t.
+ *\li "index" is a valid element index, as provided by the "index" callback
+ * provided during heap creation.
+ *
+ * Returns:
+ *\li A pointer to the element for the element index.
+ */
+
+void
+isc_heap_foreach(isc_heap_t *heap, isc_heapaction_t action, void *uap);
+/*!<
+ * \brief Iterate over the heap, calling an action for each element. The
+ * order of iteration is not sorted.
+ *
+ * Requires:
+ *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t.
+ *\li "action" is not NULL, and is a function which takes two arguments.
+ * The first is a void *, representing the element, and the second is
+ * "uap" as provided to isc_heap_foreach.
+ *\li "uap" is a caller-provided argument, and may be NULL.
+ *
+ * Note:
+ *\li The heap structure CANNOT be modified during this iteration. The only
+ * safe function to call while iterating the heap is isc_heap_element().
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/hex.h b/lib/isc/include/isc/hex.h
new file mode 100644
index 0000000..d5f714e
--- /dev/null
+++ b/lib/isc/include/isc/hex.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/hex.h */
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Functions
+ ***/
+
+isc_result_t
+isc_hex_totext(isc_region_t *source, int wordlength, const char *wordbreak,
+ isc_buffer_t *target);
+/*!<
+ * \brief Convert data into hex encoded text.
+ *
+ * Notes:
+ *\li The hex encoded text in 'target' will be divided into
+ * words of at most 'wordlength' characters, separated by
+ * the 'wordbreak' string. No parentheses will surround
+ * the text.
+ *
+ * Requires:
+ *\li 'source' is a region containing binary data
+ *\li 'target' is a text buffer containing available space
+ *\li 'wordbreak' points to a null-terminated string of
+ * zero or more whitespace characters
+ *
+ * Ensures:
+ *\li target will contain the hex encoded version of the data
+ * in source. The 'used' pointer in target will be advanced as
+ * necessary.
+ */
+
+isc_result_t
+isc_hex_decodestring(const char *cstr, isc_buffer_t *target);
+/*!<
+ * \brief Decode a null-terminated hex string.
+ *
+ * Requires:
+ *\li 'cstr' is non-null.
+ *\li 'target' is a valid buffer.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS -- the entire decoded representation of 'cstring'
+ * fit in 'target'.
+ *\li #ISC_R_BADHEX -- 'cstr' is not a valid hex encoding.
+ *
+ * Other error returns are any possible error code from:
+ * isc_lex_create(),
+ * isc_lex_openbuffer(),
+ * isc_hex_tobuffer().
+ */
+
+isc_result_t
+isc_hex_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length);
+/*!<
+ * \brief Convert hex-encoded text from a lexer context into
+ * `target`. If 'length' is non-negative, it is the expected number of
+ * encoded octets to convert.
+ *
+ * If 'length' is -1 then 0 or more encoded octets are expected.
+ * If 'length' is -2 then 1 or more encoded octets are expected.
+ *
+ * Returns:
+ *\li #ISC_R_BADHEX -- invalid hex encoding
+ *\li #ISC_R_UNEXPECTEDEND: the text does not contain the expected
+ * number of encoded octets.
+ *
+ * Requires:
+ *\li 'lexer' is a valid lexer context
+ *\li 'target' is a buffer containing binary data
+ *\li 'length' is -2, -1, or non-negative
+ *
+ * Ensures:
+ *\li target will contain the data represented by the hex encoded
+ * string parsed by the lexer. No more than `length` octets will
+ * be read, if `length` is non-negative. The 'used' pointer in
+ * 'target' will be advanced as necessary.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/hmac.h b/lib/isc/include/isc/hmac.h
new file mode 100644
index 0000000..68b63d4
--- /dev/null
+++ b/lib/isc/include/isc/hmac.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*!
+ * \file isc/hmac.h
+ * \brief This is the header for for message authentication code.
+ */
+
+#pragma once
+
+#include <isc/lang.h>
+#include <isc/md.h>
+#include <isc/result.h>
+#include <isc/types.h>
+
+typedef void isc_hmac_t;
+
+/**
+ * isc_hmac:
+ * @type: the digest type
+ * @key: the key
+ * @keylen: the length of the key
+ * @buf: data to hash
+ * @len: length of the data to hash
+ * @digest: the output buffer
+ * @digestlen: in: the length of @digest
+ * out: the length of the data written to @digest
+ *
+ * This function computes the message authentication code using a digest type
+ * @type with key @key which is @keylen bytes long from data in @buf which is
+ * @len bytes long, and places the output into @digest, which must have space
+ * for the hash function output (use ISC_MAX_MD_SIZE if unsure). @digestlen
+ * is used to pass in the length of the digest buffer and returns the length
+ * of digest written to @digest.
+ */
+isc_result_t
+isc_hmac(const isc_md_type_t *type, const void *key, const size_t keylen,
+ const unsigned char *buf, const size_t len, unsigned char *digest,
+ unsigned int *digestlen);
+
+/**
+ * isc_hmac_new:
+ *
+ * This function allocates, initializes and returns HMAC context.
+ */
+isc_hmac_t *
+isc_hmac_new(void);
+
+/**
+ * isc_hmac_free:
+ * @md: HMAC context
+ *
+ * This function cleans up HMAC context and frees up the space allocated to it.
+ */
+void
+isc_hmac_free(isc_hmac_t *hmac);
+
+/**
+ * isc_hmac_init:
+ * @md: HMAC context
+ * @key: HMAC key
+ * @keylen: HMAC key length
+ * @type: digest type
+ *
+ * This function sets up HMAC context to use a hash function of @type and key
+ * @key which is @keylen bytes long.
+ */
+
+isc_result_t
+isc_hmac_init(isc_hmac_t *hmac, const void *key, const size_t keylen,
+ const isc_md_type_t *type);
+
+/**
+ * isc_hmac_reset:
+ * @hmac: HMAC context
+ *
+ * This function resets the HMAC context. This can be used to reuse an already
+ * existing context.
+ */
+isc_result_t
+isc_hmac_reset(isc_hmac_t *hmac);
+
+/**
+ * isc_hmac_update:
+ * @hmac: HMAC context
+ * @buf: data to hash
+ * @len: length of the data to hash
+ *
+ * This function can be called repeatedly with chunks of the message @buf to be
+ * authenticated which is @len bytes long.
+ */
+isc_result_t
+isc_hmac_update(isc_hmac_t *hmac, const unsigned char *buf, const size_t len);
+
+/**
+ * isc_hmac_final:
+ * @hmac: HMAC context
+ * @digest: the output buffer
+ * @digestlen: in: the length of @digest
+ * out: the length of the data written to @digest
+ *
+ * This function retrieves the message authentication code from @hmac and places
+ * it in @digest, which must have space for the hash function output. @digestlen
+ * is used to pass in the length of the digest buffer and returns the length
+ * of digest written to @digest. After calling this function no additional
+ * calls to isc_hmac_update() can be made.
+ */
+isc_result_t
+isc_hmac_final(isc_hmac_t *hmac, unsigned char *digest,
+ unsigned int *digestlen);
+
+/**
+ * isc_hmac_md_type:
+ * @hmac: HMAC context
+ *
+ * This function return the isc_md_type_t previously set for the supplied
+ * HMAC context or NULL if no isc_md_type_t has been set.
+ */
+const isc_md_type_t *
+isc_hmac_get_md_type(isc_hmac_t *hmac);
+
+/**
+ * isc_hmac_get_size:
+ *
+ * This function return the size of the message digest when passed an isc_hmac_t
+ * structure, i.e. the size of the hash.
+ */
+size_t
+isc_hmac_get_size(isc_hmac_t *hmac);
+
+/**
+ * isc_hmac_get_block_size:
+ *
+ * This function return the block size of the message digest when passed an
+ * isc_hmac_t structure.
+ */
+int
+isc_hmac_get_block_size(isc_hmac_t *hmac);
diff --git a/lib/isc/include/isc/ht.h b/lib/isc/include/isc/ht.h
new file mode 100644
index 0000000..163fbef
--- /dev/null
+++ b/lib/isc/include/isc/ht.h
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* ! \file */
+
+#pragma once
+
+#include <inttypes.h>
+#include <string.h>
+
+#include <isc/result.h>
+#include <isc/types.h>
+
+typedef struct isc_ht isc_ht_t;
+typedef struct isc_ht_iter isc_ht_iter_t;
+
+enum { ISC_HT_CASE_SENSITIVE = 0x00, ISC_HT_CASE_INSENSITIVE = 0x01 };
+
+/*%
+ * Initialize hashtable at *htp, using memory context and size of (1<<bits)
+ *
+ * If 'options' contains ISC_HT_CASE_INSENSITIVE, then upper- and lower-case
+ * letters in key values will generate the same hash values; this can be used
+ * when the key for a hash table is a DNS name.
+ *
+ * Requires:
+ *\li 'htp' is not NULL and '*htp' is NULL.
+ *\li 'mctx' is a valid memory context.
+ *\li 'bits' >=1 and 'bits' <=32
+ *
+ */
+void
+isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits,
+ unsigned int options);
+
+/*%
+ * Destroy hashtable, freeing everything
+ *
+ * Requires:
+ * \li '*htp' is valid hashtable
+ */
+void
+isc_ht_destroy(isc_ht_t **htp);
+
+/*%
+ * Add a node to hashtable, pointed by binary key 'key' of size 'keysize';
+ * set its value to 'value'
+ *
+ * Requires:
+ *\li 'ht' is a valid hashtable
+ *\li write-lock
+ *
+ * Returns:
+ *\li #ISC_R_NOMEMORY -- not enough memory to create pool
+ *\li #ISC_R_EXISTS -- node of the same key already exists
+ *\li #ISC_R_SUCCESS -- all is well.
+ */
+isc_result_t
+isc_ht_add(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize,
+ void *value);
+
+/*%
+ * Find a node matching 'key'/'keysize' in hashtable 'ht';
+ * if found, set '*valuep' to its value. (If 'valuep' is NULL,
+ * then simply return SUCCESS or NOTFOUND to indicate whether the
+ * key exists in the hashtable.)
+ *
+ * Requires:
+ * \li 'ht' is a valid hashtable
+ * \li read-lock
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS -- success
+ * \li #ISC_R_NOTFOUND -- key not found
+ */
+isc_result_t
+isc_ht_find(const isc_ht_t *ht, const unsigned char *key,
+ const uint32_t keysize, void **valuep);
+
+/*%
+ * Delete node from hashtable
+ *
+ * Requires:
+ *\li ht is a valid hashtable
+ *\li write-lock
+ *
+ * Returns:
+ *\li #ISC_R_NOTFOUND -- key not found
+ *\li #ISC_R_SUCCESS -- all is well
+ */
+isc_result_t
+isc_ht_delete(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize);
+
+/*%
+ * Create an iterator for the hashtable; point '*itp' to it.
+ *
+ * Requires:
+ *\li 'ht' is a valid hashtable
+ *\li 'itp' is non NULL and '*itp' is NULL.
+ */
+void
+isc_ht_iter_create(isc_ht_t *ht, isc_ht_iter_t **itp);
+
+/*%
+ * Destroy the iterator '*itp', set it to NULL
+ *
+ * Requires:
+ *\li 'itp' is non NULL and '*itp' is non NULL.
+ */
+void
+isc_ht_iter_destroy(isc_ht_iter_t **itp);
+
+/*%
+ * Set an iterator to the first entry.
+ *
+ * Requires:
+ *\li 'it' is non NULL.
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS -- success
+ * \li #ISC_R_NOMORE -- no data in the hashtable
+ */
+isc_result_t
+isc_ht_iter_first(isc_ht_iter_t *it);
+
+/*%
+ * Set an iterator to the next entry.
+ *
+ * Requires:
+ *\li 'it' is non NULL.
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS -- success
+ * \li #ISC_R_NOMORE -- end of hashtable reached
+ */
+isc_result_t
+isc_ht_iter_next(isc_ht_iter_t *it);
+
+/*%
+ * Delete current entry and set an iterator to the next entry.
+ *
+ * Requires:
+ *\li 'it' is non NULL.
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS -- success
+ * \li #ISC_R_NOMORE -- end of hashtable reached
+ */
+isc_result_t
+isc_ht_iter_delcurrent_next(isc_ht_iter_t *it);
+
+/*%
+ * Set 'value' to the current value under the iterator
+ *
+ * Requires:
+ *\li 'it' is non NULL.
+ *\li 'valuep' is non NULL and '*valuep' is NULL.
+ */
+void
+isc_ht_iter_current(isc_ht_iter_t *it, void **valuep);
+
+/*%
+ * Set 'key' and 'keysize to the current key and keysize for the value
+ * under the iterator
+ *
+ * Requires:
+ *\li 'it' is non NULL.
+ *\li 'key' is non NULL and '*key' is NULL.
+ *\li 'keysize' is non NULL.
+ */
+void
+isc_ht_iter_currentkey(isc_ht_iter_t *it, unsigned char **key, size_t *keysize);
+
+/*%
+ * Returns the number of items in the hashtable.
+ *
+ * Requires:
+ *\li 'ht' is a valid hashtable
+ */
+size_t
+isc_ht_count(const isc_ht_t *ht);
diff --git a/lib/isc/include/isc/httpd.h b/lib/isc/include/isc/httpd.h
new file mode 100644
index 0000000..1f69a4c
--- /dev/null
+++ b/lib/isc/include/isc/httpd.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file */
+
+#include <stdbool.h>
+
+#include <isc/event.h>
+#include <isc/eventclass.h>
+#include <isc/mutex.h>
+#include <isc/task.h>
+#include <isc/time.h>
+#include <isc/types.h>
+#include <isc/url.h>
+
+#define HTTPD_EVENTCLASS ISC_EVENTCLASS(4300)
+#define HTTPD_SHUTDOWN (HTTPD_EVENTCLASS + 0x0001)
+
+#define ISC_HTTPDMGR_SHUTTINGDOWN 0x00000001
+
+typedef isc_result_t(isc_httpdaction_t)(
+ const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, void *arg,
+ unsigned int *retcode, const char **retmsg, const char **mimetype,
+ isc_buffer_t *body, isc_httpdfree_t **freecb, void **freecb_args);
+
+typedef bool(isc_httpdclientok_t)(const isc_sockaddr_t *, void *);
+
+isc_result_t
+isc_httpdmgr_create(isc_nm_t *nm, isc_mem_t *mctx, isc_sockaddr_t *addr,
+ isc_httpdclientok_t *client_ok,
+ isc_httpdondestroy_t *ondestroy, void *cb_arg,
+ isc_httpdmgr_t **httpdmgrp);
+
+void
+isc_httpdmgr_shutdown(isc_httpdmgr_t **httpdp);
+
+isc_result_t
+isc_httpdmgr_addurl(isc_httpdmgr_t *httpdmgr, const char *url, bool isstatic,
+ isc_httpdaction_t *func, void *arg);
+
+void
+isc_httpd_setfinishhook(void (*fn)(void));
+
+bool
+isc_httpdurl_isstatic(const isc_httpdurl_t *url);
+
+const isc_time_t *
+isc_httpdurl_loadtime(const isc_httpdurl_t *url);
+
+const isc_time_t *
+isc_httpd_if_modified_since(const isc_httpd_t *httpd);
diff --git a/lib/isc/include/isc/interfaceiter.h b/lib/isc/include/isc/interfaceiter.h
new file mode 100644
index 0000000..c696091
--- /dev/null
+++ b/lib/isc/include/isc/interfaceiter.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*****
+***** Module Info
+*****/
+
+/*! \file isc/interfaceiter.h
+ * \brief Iterates over the list of network interfaces.
+ *
+ * Interfaces whose address family is not supported are ignored and never
+ * returned by the iterator. Interfaces whose netmask, interface flags,
+ * or similar cannot be obtained are also ignored, and the failure is logged.
+ *
+ * Standards:
+ * The API for scanning varies greatly among operating systems.
+ * This module attempts to hide the differences.
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <inttypes.h>
+
+#include <isc/lang.h>
+#include <isc/netaddr.h>
+#include <isc/types.h>
+
+/*!
+ * \brief Public structure describing a network interface.
+ */
+
+struct isc_interface {
+ char name[32]; /*%< Interface name, null-terminated. */
+ unsigned int af; /*%< Address family. */
+ isc_netaddr_t address; /*%< Local address. */
+ isc_netaddr_t netmask; /*%< Network mask. */
+ isc_netaddr_t dstaddress; /*%< Destination address
+ * (point-to-point
+ * only). */
+ uint32_t flags; /*%< Flags; see INTERFACE flags. */
+};
+
+/*@{*/
+/*! Interface flags. */
+
+#define INTERFACE_F_UP 0x00000001U
+#define INTERFACE_F_POINTTOPOINT 0x00000002U
+#define INTERFACE_F_LOOPBACK 0x00000004U
+/*@}*/
+
+/***
+ *** Functions
+ ***/
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+isc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp);
+/*!<
+ * \brief Create an iterator for traversing the operating system's list
+ * of network interfaces.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ * \li #ISC_R_NOMEMORY
+ *\li Various network-related errors
+ */
+
+isc_result_t
+isc_interfaceiter_first(isc_interfaceiter_t *iter);
+/*!<
+ * \brief Position the iterator on the first interface.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS Success.
+ *\li #ISC_R_NOMORE There are no interfaces.
+ */
+
+isc_result_t
+isc_interfaceiter_current(isc_interfaceiter_t *iter, isc_interface_t *ifdata);
+/*!<
+ * \brief Get information about the interface the iterator is currently
+ * positioned at and store it at *ifdata.
+ *
+ * Requires:
+ *\li The iterator has been successfully positioned using
+ * isc_interface_iter_first() / isc_interface_iter_next().
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS Success.
+ */
+
+isc_result_t
+isc_interfaceiter_next(isc_interfaceiter_t *iter);
+/*!<
+ * \brief Position the iterator on the next interface.
+ *
+ * Requires:
+ * \li The iterator has been successfully positioned using
+ * isc_interface_iter_first() / isc_interface_iter_next().
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS Success.
+ *\li #ISC_R_NOMORE There are no more interfaces.
+ */
+
+void
+isc_interfaceiter_destroy(isc_interfaceiter_t **iterp);
+/*!<
+ * \brief Destroy the iterator.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/iterated_hash.h b/lib/isc/include/isc/iterated_hash.h
new file mode 100644
index 0000000..b5d6ab6
--- /dev/null
+++ b/lib/isc/include/isc/iterated_hash.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <isc/lang.h>
+
+/*
+ * The maximal hash length that can be encoded in a name
+ * using base32hex. floor(255/8)*5
+ */
+#define NSEC3_MAX_HASH_LENGTH 155
+
+/*
+ * The maximum has that can be encoded in a single label using
+ * base32hex. floor(63/8)*5
+ */
+#define NSEC3_MAX_LABEL_HASH 35
+
+ISC_LANG_BEGINDECLS
+
+int
+isc_iterated_hash(unsigned char *out, const unsigned int hashalg,
+ const int iterations, const unsigned char *salt,
+ const int saltlength, const unsigned char *in,
+ const int inlength);
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/lang.h b/lib/isc/include/isc/lang.h
new file mode 100644
index 0000000..b7e88a5
--- /dev/null
+++ b/lib/isc/include/isc/lang.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/lang.h */
+
+#ifdef __cplusplus
+#define ISC_LANG_BEGINDECLS extern "C" {
+#define ISC_LANG_ENDDECLS }
+#else /* ifdef __cplusplus */
+#define ISC_LANG_BEGINDECLS
+#define ISC_LANG_ENDDECLS
+#endif /* ifdef __cplusplus */
diff --git a/lib/isc/include/isc/lex.h b/lib/isc/include/isc/lex.h
new file mode 100644
index 0000000..14b29cf
--- /dev/null
+++ b/lib/isc/include/isc/lex.h
@@ -0,0 +1,444 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*****
+***** Module Info
+*****/
+
+/*! \file isc/lex.h
+ * \brief The "lex" module provides a lightweight tokenizer. It can operate
+ * on files or buffers, and can handle "include". It is designed for
+ * parsing of DNS master files and the BIND configuration file, but
+ * should be general enough to tokenize other things, e.g. HTTP.
+ *
+ * \li MP:
+ * No synchronization is provided. Clients must ensure exclusive
+ * access.
+ *
+ * \li Reliability:
+ * No anticipated impact.
+ *
+ * \li Resources:
+ * TBS
+ *
+ * \li Security:
+ * No anticipated impact.
+ *
+ * \li Standards:
+ * None.
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <isc/lang.h>
+#include <isc/region.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Options
+ ***/
+
+/*@{*/
+/*!
+ * Various options for isc_lex_gettoken().
+ */
+
+#define ISC_LEXOPT_EOL 0x0001 /*%< Want end-of-line token. */
+#define ISC_LEXOPT_EOF 0x0002 /*%< Want end-of-file token. */
+#define ISC_LEXOPT_INITIALWS 0x0004 /*%< Want initial whitespace. */
+#define ISC_LEXOPT_NUMBER 0x0008 /*%< Recognize numbers. */
+#define ISC_LEXOPT_QSTRING 0x0010 /*%< Recognize qstrings. */
+/*@}*/
+
+/*@{*/
+/*!
+ * The ISC_LEXOPT_DNSMULTILINE option handles the processing of '(' and ')' in
+ * the DNS master file format. If this option is set, then the
+ * ISC_LEXOPT_INITIALWS and ISC_LEXOPT_EOL options will be ignored when
+ * the paren count is > 0. To use this option, '(' and ')' must be special
+ * characters.
+ */
+#define ISC_LEXOPT_DNSMULTILINE 0x0020 /*%< Handle '(' and ')'. */
+#define ISC_LEXOPT_NOMORE 0x0040 /*%< Want "no more" token. */
+
+#define ISC_LEXOPT_CNUMBER 0x0080 /*%< Recognize octal and hex. */
+#define ISC_LEXOPT_ESCAPE 0x0100 /*%< Recognize escapes. */
+#define ISC_LEXOPT_QSTRINGMULTILINE 0x0200 /*%< Allow multiline "" strings */
+#define ISC_LEXOPT_OCTAL 0x0400 /*%< Expect a octal number. */
+#define ISC_LEXOPT_BTEXT 0x0800 /*%< Bracketed text. */
+#define ISC_LEXOPT_VPAIR 0x1000 /*%< Recognize value pair. */
+#define ISC_LEXOPT_QVPAIR 0x2000 /*%< Recognize quoted value pair. */
+/*@}*/
+/*@{*/
+/*!
+ * Various commenting styles, which may be changed at any time with
+ * isc_lex_setcomments().
+ */
+
+#define ISC_LEXCOMMENT_C 0x01
+#define ISC_LEXCOMMENT_CPLUSPLUS 0x02
+#define ISC_LEXCOMMENT_SHELL 0x04
+#define ISC_LEXCOMMENT_DNSMASTERFILE 0x08
+/*@}*/
+
+/***
+ *** Types
+ ***/
+
+/*! Lex */
+
+typedef char isc_lexspecials_t[256];
+
+/* Tokens */
+
+typedef enum {
+ isc_tokentype_unknown = 0,
+ isc_tokentype_string = 1,
+ isc_tokentype_number = 2,
+ isc_tokentype_qstring = 3,
+ isc_tokentype_eol = 4,
+ isc_tokentype_eof = 5,
+ isc_tokentype_initialws = 6,
+ isc_tokentype_special = 7,
+ isc_tokentype_nomore = 8,
+ isc_tokentype_btext = 9,
+ isc_tokentype_vpair = 10,
+ isc_tokentype_qvpair = 11,
+} isc_tokentype_t;
+
+typedef union {
+ char as_char;
+ unsigned long as_ulong;
+ isc_region_t as_region;
+ isc_textregion_t as_textregion;
+ void *as_pointer;
+} isc_tokenvalue_t;
+
+typedef struct isc_token {
+ isc_tokentype_t type;
+ isc_tokenvalue_t value;
+} isc_token_t;
+
+/***
+ *** Functions
+ ***/
+
+isc_result_t
+isc_lex_create(isc_mem_t *mctx, size_t max_token, isc_lex_t **lexp);
+/*%<
+ * Create a lexer.
+ *
+ * 'max_token' is a hint of the number of bytes in the largest token.
+ *
+ * Requires:
+ *\li '*lexp' is a valid lexer.
+ *
+ * Ensures:
+ *\li On success, *lexp is attached to the newly created lexer.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ */
+
+void
+isc_lex_destroy(isc_lex_t **lexp);
+/*%<
+ * Destroy the lexer.
+ *
+ * Requires:
+ *\li '*lexp' is a valid lexer.
+ *
+ * Ensures:
+ *\li *lexp == NULL
+ */
+
+unsigned int
+isc_lex_getcomments(isc_lex_t *lex);
+/*%<
+ * Return the current lexer commenting styles.
+ *
+ * Requires:
+ *\li 'lex' is a valid lexer.
+ *
+ * Returns:
+ *\li The commenting styles which are currently allowed.
+ */
+
+void
+isc_lex_setcomments(isc_lex_t *lex, unsigned int comments);
+/*%<
+ * Set allowed lexer commenting styles.
+ *
+ * Requires:
+ *\li 'lex' is a valid lexer.
+ *
+ *\li 'comments' has meaningful values.
+ */
+
+void
+isc_lex_getspecials(isc_lex_t *lex, isc_lexspecials_t specials);
+/*%<
+ * Put the current list of specials into 'specials'.
+ *
+ * Requires:
+ *\li 'lex' is a valid lexer.
+ */
+
+void
+isc_lex_setspecials(isc_lex_t *lex, isc_lexspecials_t specials);
+/*!<
+ * The characters in 'specials' are returned as tokens. Along with
+ * whitespace, they delimit strings and numbers.
+ *
+ * Note:
+ *\li Comment processing takes precedence over special character
+ * recognition.
+ *
+ * Requires:
+ *\li 'lex' is a valid lexer.
+ */
+
+isc_result_t
+isc_lex_openfile(isc_lex_t *lex, const char *filename);
+/*%<
+ * Open 'filename' and make it the current input source for 'lex'.
+ *
+ * Requires:
+ *\li 'lex' is a valid lexer.
+ *
+ *\li filename is a valid C string.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY Out of memory
+ *\li #ISC_R_NOTFOUND File not found
+ *\li #ISC_R_NOPERM No permission to open file
+ *\li #ISC_R_FAILURE Couldn't open file, not sure why
+ *\li #ISC_R_UNEXPECTED
+ */
+
+isc_result_t
+isc_lex_openstream(isc_lex_t *lex, FILE *stream);
+/*%<
+ * Make 'stream' the current input source for 'lex'.
+ *
+ * Requires:
+ *\li 'lex' is a valid lexer.
+ *
+ *\li 'stream' is a valid C stream.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY Out of memory
+ */
+
+isc_result_t
+isc_lex_openbuffer(isc_lex_t *lex, isc_buffer_t *buffer);
+/*%<
+ * Make 'buffer' the current input source for 'lex'.
+ *
+ * Requires:
+ *\li 'lex' is a valid lexer.
+ *
+ *\li 'buffer' is a valid buffer.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY Out of memory
+ */
+
+isc_result_t
+isc_lex_close(isc_lex_t *lex);
+/*%<
+ * Close the most recently opened object (i.e. file or buffer).
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMORE No more input sources
+ */
+
+isc_result_t
+isc_lex_gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *tokenp);
+/*%<
+ * Get the next token.
+ *
+ * Requires:
+ *\li 'lex' is a valid lexer.
+ *
+ *\li 'lex' has an input source.
+ *
+ *\li 'options' contains valid options.
+ *
+ *\li '*tokenp' is a valid pointer.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_UNEXPECTEDEND
+ *\li #ISC_R_NOMEMORY
+ *
+ * These two results are returned only if their corresponding lexer
+ * options are not set.
+ *
+ *\li #ISC_R_EOF End of input source
+ *\li #ISC_R_NOMORE No more input sources
+ */
+
+isc_result_t
+isc_lex_getmastertoken(isc_lex_t *lex, isc_token_t *token,
+ isc_tokentype_t expect, bool eol);
+/*%<
+ * Get the next token from a DNS master file type stream. This is a
+ * convenience function that sets appropriate options and handles quoted
+ * strings and end of line correctly for master files. It also ungets
+ * unexpected tokens. If `eol` is set then expect end-of-line otherwise
+ * eol is a error.
+ *
+ * Requires:
+ *\li 'lex' is a valid lexer.
+ *
+ *\li 'token' is a valid pointer
+ *
+ * Returns:
+ *
+ * \li any return code from isc_lex_gettoken().
+ */
+
+isc_result_t
+isc_lex_getoctaltoken(isc_lex_t *lex, isc_token_t *token, bool eol);
+/*%<
+ * Get the next token from a DNS master file type stream. This is a
+ * convenience function that sets appropriate options and handles end
+ * of line correctly for master files. It also ungets unexpected tokens.
+ * If `eol` is set then expect end-of-line otherwise eol is a error.
+ *
+ * Requires:
+ *\li 'lex' is a valid lexer.
+ *
+ *\li 'token' is a valid pointer
+ *
+ * Returns:
+ *
+ * \li any return code from isc_lex_gettoken().
+ */
+
+void
+isc_lex_ungettoken(isc_lex_t *lex, isc_token_t *tokenp);
+/*%<
+ * Unget the current token.
+ *
+ * Requires:
+ *\li 'lex' is a valid lexer.
+ *
+ *\li 'lex' has an input source.
+ *
+ *\li 'tokenp' points to a valid token.
+ *
+ *\li There is no ungotten token already.
+ */
+
+void
+isc_lex_getlasttokentext(isc_lex_t *lex, isc_token_t *tokenp, isc_region_t *r);
+/*%<
+ * Returns a region containing the text of the last token returned.
+ *
+ * Requires:
+ *\li 'lex' is a valid lexer.
+ *
+ *\li 'lex' has an input source.
+ *
+ *\li 'tokenp' points to a valid token.
+ *
+ *\li A token has been gotten and not ungotten.
+ */
+
+char *
+isc_lex_getsourcename(isc_lex_t *lex);
+/*%<
+ * Return the input source name.
+ *
+ * Requires:
+ *\li 'lex' is a valid lexer.
+ *
+ * Returns:
+ * \li source name or NULL if no current source.
+ *\li result valid while current input source exists.
+ */
+
+unsigned long
+isc_lex_getsourceline(isc_lex_t *lex);
+/*%<
+ * Return the input source line number.
+ *
+ * Requires:
+ *\li 'lex' is a valid lexer.
+ *
+ * Returns:
+ *\li Current line number or 0 if no current source.
+ */
+
+isc_result_t
+isc_lex_setsourcename(isc_lex_t *lex, const char *name);
+/*%<
+ * Assigns a new name to the input source.
+ *
+ * Requires:
+ *
+ * \li 'lex' is a valid lexer.
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOMEMORY
+ * \li #ISC_R_NOTFOUND - there are no sources.
+ */
+
+isc_result_t
+isc_lex_setsourceline(isc_lex_t *lex, unsigned long line);
+/*%<
+ * Assigns a new line number to the input source. This can be used
+ * when parsing a buffer that's been excerpted from the middle a file,
+ * allowing logged messages to display the correct line number,
+ * rather than the line number within the buffer.
+ *
+ * Requires:
+ *
+ * \li 'lex' is a valid lexer.
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOTFOUND - there are no sources.
+ */
+
+bool
+isc_lex_isfile(isc_lex_t *lex);
+/*%<
+ * Return whether the current input source is a file.
+ *
+ * Requires:
+ *\li 'lex' is a valid lexer.
+ *
+ * Returns:
+ * \li #true if the current input is a file,
+ *\li #false otherwise.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/list.h b/lib/isc/include/isc/list.h
new file mode 100644
index 0000000..2cf4437
--- /dev/null
+++ b/lib/isc/include/isc/list.h
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <isc/assertions.h>
+
+#define ISC_LINK_TOMBSTONE(type) ((type *)-1)
+
+#define ISC_LIST_INITIALIZER \
+ { \
+ .head = NULL, .tail = NULL, \
+ }
+#define ISC_LINK_INITIALIZER_TYPE(type) \
+ { \
+ .prev = ISC_LINK_TOMBSTONE(type), \
+ .next = ISC_LINK_TOMBSTONE(type), \
+ }
+#define ISC_LINK_INITIALIZER ISC_LINK_INITIALIZER_TYPE(void)
+
+#ifdef ISC_LIST_CHECKINIT
+#define ISC_LINK_INSIST(x) ISC_INSIST(x)
+#else /* ifdef ISC_LIST_CHECKINIT */
+#define ISC_LINK_INSIST(x)
+#endif /* ifdef ISC_LIST_CHECKINIT */
+
+#define ISC_LIST(type) \
+ struct { \
+ type *head, *tail; \
+ }
+#define ISC_LIST_INIT(list) \
+ do { \
+ (list).head = NULL; \
+ (list).tail = NULL; \
+ } while (0)
+
+#define ISC_LINK(type) \
+ struct { \
+ type *prev, *next; \
+ }
+#define ISC_LINK_INIT_TYPE(elt, link, type) \
+ do { \
+ (elt)->link.prev = ISC_LINK_TOMBSTONE(type); \
+ (elt)->link.next = ISC_LINK_TOMBSTONE(type); \
+ } while (0)
+#define ISC_LINK_INIT(elt, link) ISC_LINK_INIT_TYPE(elt, link, void)
+#define ISC_LINK_LINKED_TYPE(elt, link, type) \
+ ((type *)((elt)->link.prev) != ISC_LINK_TOMBSTONE(type))
+#define ISC_LINK_LINKED(elt, link) ISC_LINK_LINKED_TYPE(elt, link, void)
+
+#define ISC_LIST_HEAD(list) ((list).head)
+#define ISC_LIST_TAIL(list) ((list).tail)
+#define ISC_LIST_EMPTY(list) ((list).head == NULL)
+
+#define __ISC_LIST_PREPENDUNSAFE(list, elt, link) \
+ do { \
+ if ((list).head != NULL) { \
+ (list).head->link.prev = (elt); \
+ } else { \
+ (list).tail = (elt); \
+ } \
+ (elt)->link.prev = NULL; \
+ (elt)->link.next = (list).head; \
+ (list).head = (elt); \
+ } while (0)
+
+#define ISC_LIST_PREPEND(list, elt, link) \
+ do { \
+ ISC_LINK_INSIST(!ISC_LINK_LINKED(elt, link)); \
+ __ISC_LIST_PREPENDUNSAFE(list, elt, link); \
+ } while (0)
+
+#define ISC_LIST_INITANDPREPEND(list, elt, link) \
+ __ISC_LIST_PREPENDUNSAFE(list, elt, link)
+
+#define __ISC_LIST_APPENDUNSAFE(list, elt, link) \
+ do { \
+ if ((list).tail != NULL) { \
+ (list).tail->link.next = (elt); \
+ } else { \
+ (list).head = (elt); \
+ } \
+ (elt)->link.prev = (list).tail; \
+ (elt)->link.next = NULL; \
+ (list).tail = (elt); \
+ } while (0)
+
+#define ISC_LIST_APPEND(list, elt, link) \
+ do { \
+ ISC_LINK_INSIST(!ISC_LINK_LINKED(elt, link)); \
+ __ISC_LIST_APPENDUNSAFE(list, elt, link); \
+ } while (0)
+
+#define ISC_LIST_INITANDAPPEND(list, elt, link) \
+ __ISC_LIST_APPENDUNSAFE(list, elt, link)
+
+#define __ISC_LIST_UNLINKUNSAFE_TYPE(list, elt, link, type) \
+ do { \
+ if ((elt)->link.next != NULL) { \
+ (elt)->link.next->link.prev = (elt)->link.prev; \
+ } else { \
+ ISC_INSIST((list).tail == (elt)); \
+ (list).tail = (elt)->link.prev; \
+ } \
+ if ((elt)->link.prev != NULL) { \
+ (elt)->link.prev->link.next = (elt)->link.next; \
+ } else { \
+ ISC_INSIST((list).head == (elt)); \
+ (list).head = (elt)->link.next; \
+ } \
+ (elt)->link.prev = ISC_LINK_TOMBSTONE(type); \
+ (elt)->link.next = ISC_LINK_TOMBSTONE(type); \
+ ISC_INSIST((list).head != (elt)); \
+ ISC_INSIST((list).tail != (elt)); \
+ } while (0)
+
+#define __ISC_LIST_UNLINKUNSAFE(list, elt, link) \
+ __ISC_LIST_UNLINKUNSAFE_TYPE(list, elt, link, void)
+
+#define ISC_LIST_UNLINK_TYPE(list, elt, link, type) \
+ do { \
+ ISC_LINK_INSIST(ISC_LINK_LINKED(elt, link)); \
+ __ISC_LIST_UNLINKUNSAFE_TYPE(list, elt, link, type); \
+ } while (0)
+#define ISC_LIST_UNLINK(list, elt, link) \
+ ISC_LIST_UNLINK_TYPE(list, elt, link, void)
+
+#define ISC_LIST_PREV(elt, link) ((elt)->link.prev)
+#define ISC_LIST_NEXT(elt, link) ((elt)->link.next)
+
+#define __ISC_LIST_INSERTBEFOREUNSAFE(list, before, elt, link) \
+ do { \
+ if ((before)->link.prev == NULL) { \
+ ISC_LIST_PREPEND(list, elt, link); \
+ } else { \
+ (elt)->link.prev = (before)->link.prev; \
+ (before)->link.prev = (elt); \
+ (elt)->link.prev->link.next = (elt); \
+ (elt)->link.next = (before); \
+ } \
+ } while (0)
+
+#define ISC_LIST_INSERTBEFORE(list, before, elt, link) \
+ do { \
+ ISC_LINK_INSIST(ISC_LINK_LINKED(before, link)); \
+ ISC_LINK_INSIST(!ISC_LINK_LINKED(elt, link)); \
+ __ISC_LIST_INSERTBEFOREUNSAFE(list, before, elt, link); \
+ } while (0)
+
+#define __ISC_LIST_INSERTAFTERUNSAFE(list, after, elt, link) \
+ do { \
+ if ((after)->link.next == NULL) { \
+ ISC_LIST_APPEND(list, elt, link); \
+ } else { \
+ (elt)->link.next = (after)->link.next; \
+ (after)->link.next = (elt); \
+ (elt)->link.next->link.prev = (elt); \
+ (elt)->link.prev = (after); \
+ } \
+ } while (0)
+
+#define ISC_LIST_INSERTAFTER(list, after, elt, link) \
+ do { \
+ ISC_LINK_INSIST(ISC_LINK_LINKED(after, link)); \
+ ISC_LINK_INSIST(!ISC_LINK_LINKED(elt, link)); \
+ __ISC_LIST_INSERTAFTERUNSAFE(list, after, elt, link); \
+ } while (0)
+
+#define ISC_LIST_APPENDLIST(list1, list2, link) \
+ do { \
+ if (ISC_LIST_EMPTY(list1)) { \
+ (list1) = (list2); \
+ } else if (!ISC_LIST_EMPTY(list2)) { \
+ (list1).tail->link.next = (list2).head; \
+ (list2).head->link.prev = (list1).tail; \
+ (list1).tail = (list2).tail; \
+ } \
+ (list2).head = NULL; \
+ (list2).tail = NULL; \
+ } while (0)
+
+#define ISC_LIST_PREPENDLIST(list1, list2, link) \
+ do { \
+ if (ISC_LIST_EMPTY(list1)) { \
+ (list1) = (list2); \
+ } else if (!ISC_LIST_EMPTY(list2)) { \
+ (list2).tail->link.next = (list1).head; \
+ (list1).head->link.prev = (list2).tail; \
+ (list1).head = (list2).head; \
+ } \
+ (list2).head = NULL; \
+ (list2).tail = NULL; \
+ } while (0)
+
+#define ISC_LIST_ENQUEUE(list, elt, link) ISC_LIST_APPEND(list, elt, link)
+#define __ISC_LIST_ENQUEUEUNSAFE(list, elt, link) \
+ __ISC_LIST_APPENDUNSAFE(list, elt, link)
+#define ISC_LIST_DEQUEUE(list, elt, link) \
+ ISC_LIST_UNLINK_TYPE(list, elt, link, void)
+#define ISC_LIST_DEQUEUE_TYPE(list, elt, link, type) \
+ ISC_LIST_UNLINK_TYPE(list, elt, link, type)
+#define __ISC_LIST_DEQUEUEUNSAFE(list, elt, link) \
+ __ISC_LIST_UNLINKUNSAFE_TYPE(list, elt, link, void)
+#define __ISC_LIST_DEQUEUEUNSAFE_TYPE(list, elt, link, type) \
+ __ISC_LIST_UNLINKUNSAFE_TYPE(list, elt, link, type)
+
+#define ISC_LIST_MOVEUNSAFE(dest, src) \
+ { \
+ (dest).head = (src).head; \
+ (dest).tail = (src).tail; \
+ (src).head = NULL; \
+ (src).tail = NULL; \
+ }
+
+#define ISC_LIST_MOVE(dest, src) \
+ { \
+ INSIST(ISC_LIST_EMPTY(dest)); \
+ ISC_LIST_MOVEUNSAFE(dest, src); \
+ }
diff --git a/lib/isc/include/isc/log.h b/lib/isc/include/isc/log.h
new file mode 100644
index 0000000..54b4022
--- /dev/null
+++ b/lib/isc/include/isc/log.h
@@ -0,0 +1,853 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/log.h */
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <syslog.h> /* XXXDCL NT */
+
+#include <isc/formatcheck.h>
+#include <isc/lang.h>
+#include <isc/types.h>
+
+/*@{*/
+/*!
+ * \brief Severity levels, patterned after Unix's syslog levels.
+ *
+ */
+#define ISC_LOG_DEBUG(level) (level)
+/*!
+ * #ISC_LOG_DYNAMIC can only be used for defining channels with
+ * isc_log_createchannel(), not to specify a level in isc_log_write().
+ */
+#define ISC_LOG_DYNAMIC 0
+#define ISC_LOG_INFO (-1)
+#define ISC_LOG_NOTICE (-2)
+#define ISC_LOG_WARNING (-3)
+#define ISC_LOG_ERROR (-4)
+#define ISC_LOG_CRITICAL (-5)
+/*@}*/
+
+/*@{*/
+/*!
+ * \brief Destinations.
+ */
+#define ISC_LOG_TONULL 1
+#define ISC_LOG_TOSYSLOG 2
+#define ISC_LOG_TOFILE 3
+#define ISC_LOG_TOFILEDESC 4
+/*@}*/
+
+/*@{*/
+/*%
+ * Channel flags.
+ */
+#define ISC_LOG_PRINTTIME 0x00001
+#define ISC_LOG_PRINTLEVEL 0x00002
+#define ISC_LOG_PRINTCATEGORY 0x00004
+#define ISC_LOG_PRINTMODULE 0x00008
+#define ISC_LOG_PRINTTAG 0x00010 /* tag and ":" */
+#define ISC_LOG_PRINTPREFIX 0x00020 /* tag only, no colon */
+#define ISC_LOG_PRINTALL 0x0003F
+#define ISC_LOG_BUFFERED 0x00040
+#define ISC_LOG_DEBUGONLY 0x01000
+#define ISC_LOG_OPENERR 0x08000 /* internal */
+#define ISC_LOG_ISO8601 0x10000 /* if PRINTTIME, use ISO8601 */
+#define ISC_LOG_UTC 0x20000 /* if PRINTTIME, use UTC */
+/*@}*/
+
+/*@{*/
+/*!
+ * \brief Other options.
+ *
+ * XXXDCL INFINITE doesn't yet work. Arguably it isn't needed, but
+ * since I am intend to make large number of versions work efficiently,
+ * INFINITE is going to be trivial to add to that.
+ */
+#define ISC_LOG_ROLLINFINITE (-1)
+#define ISC_LOG_ROLLNEVER (-2)
+#define ISC_LOG_MAX_VERSIONS 256
+/*@}*/
+
+/*@{*/
+/*!
+ * \brief Type of suffix used on rolled log files.
+ */
+typedef enum {
+ isc_log_rollsuffix_increment,
+ isc_log_rollsuffix_timestamp
+} isc_log_rollsuffix_t;
+/*@}*/
+
+/*!
+ * \brief Used to name the categories used by a library.
+ *
+ * An array of isc_logcategory
+ * structures names each category, and the id value is initialized by calling
+ * isc_log_registercategories.
+ */
+struct isc_logcategory {
+ const char *name;
+ unsigned int id;
+};
+
+/*%
+ * Similar to isc_logcategory, but for all the modules a library defines.
+ */
+struct isc_logmodule {
+ const char *name;
+ unsigned int id;
+};
+
+/*%
+ * The isc_logfile structure is initialized as part of an isc_logdestination
+ * before calling isc_log_createchannel().
+ *
+ * When defining an #ISC_LOG_TOFILE
+ * channel the name, versions and maximum_size should be set before calling
+ * isc_log_createchannel(). To define an #ISC_LOG_TOFILEDESC channel set only
+ * the stream before the call.
+ *
+ * Setting maximum_size to zero implies no maximum.
+ */
+typedef struct isc_logfile {
+ FILE *stream; /*%< Initialized to NULL for
+ * #ISC_LOG_TOFILE. */
+ const char *name; /*%< NULL for #ISC_LOG_TOFILEDESC. */
+ int versions; /* >= 0, #ISC_LOG_ROLLNEVER,
+ * #ISC_LOG_ROLLINFINITE. */
+ isc_log_rollsuffix_t suffix;
+ /*%
+ * stdio's ftell is standardized to return a long, which may well not
+ * be big enough for the largest file supportable by the operating
+ * system (though it is _probably_ big enough for the largest log
+ * anyone would want). st_size returned by fstat should be typedef'd
+ * to a size large enough for the largest possible file on a system.
+ */
+ isc_offset_t maximum_size;
+ bool maximum_reached; /*%< Private. */
+} isc_logfile_t;
+
+/*%
+ * Passed to isc_log_createchannel to define the attributes of either
+ * a stdio or a syslog log.
+ */
+typedef union isc_logdestination {
+ isc_logfile_t file;
+ int facility; /* XXXDCL NT */
+} isc_logdestination_t;
+
+/*@{*/
+/*%
+ * The built-in categories of libisc.
+ *
+ * Each library registering categories should provide library_LOGCATEGORY_name
+ * definitions with indexes into its isc_logcategory structure corresponding to
+ * the order of the names.
+ */
+extern isc_logcategory_t isc_categories[];
+extern isc_log_t *isc_lctx;
+extern isc_logmodule_t isc_modules[];
+/*@}*/
+
+/*@{*/
+/*%
+ * Do not log directly to DEFAULT. Use another category. When in doubt,
+ * use GENERAL.
+ */
+#define ISC_LOGCATEGORY_DEFAULT (&isc_categories[0])
+#define ISC_LOGCATEGORY_GENERAL (&isc_categories[1])
+#define ISC_LOGCATEGORY_SSLKEYLOG (&isc_categories[2])
+/*@}*/
+
+#define ISC_LOGMODULE_SOCKET (&isc_modules[0])
+#define ISC_LOGMODULE_TIME (&isc_modules[1])
+#define ISC_LOGMODULE_INTERFACE (&isc_modules[2])
+#define ISC_LOGMODULE_TIMER (&isc_modules[3])
+#define ISC_LOGMODULE_FILE (&isc_modules[4])
+#define ISC_LOGMODULE_NETMGR (&isc_modules[5])
+#define ISC_LOGMODULE_OTHER (&isc_modules[6])
+
+ISC_LANG_BEGINDECLS
+
+void
+isc_log_create(isc_mem_t *mctx, isc_log_t **lctxp, isc_logconfig_t **lcfgp);
+/*%<
+ * Establish a new logging context, with default channels.
+ *
+ * Notes:
+ *\li isc_log_create() calls isc_logconfig_create(), so see its comment
+ * below for more information.
+ *
+ * Requires:
+ *\li mctx is a valid memory context.
+ *\li lctxp is not null and *lctxp is null.
+ *\li lcfgp is null or lcfgp is not null and *lcfgp is null.
+ *
+ * Ensures:
+ *\li *lctxp will point to a valid logging context if all of the necessary
+ * memory was allocated, or NULL otherwise.
+ *\li *lcfgp will point to a valid logging configuration if all of the
+ * necessary memory was allocated, or NULL otherwise.
+ *\li On failure, no additional memory is allocated.
+ */
+
+void
+isc_logconfig_create(isc_log_t *lctx, isc_logconfig_t **lcfgp);
+/*%<
+ * Create the data structure that holds all of the configurable information
+ * about where messages are actually supposed to be sent -- the information
+ * that could changed based on some configuration file, as opposed to the
+ * the category/module specification of isc_log_[v]write[1] that is compiled
+ * into a program, or the debug_level which is dynamic state information.
+ *
+ * Notes:
+ *\li It is necessary to specify the logging context the configuration
+ * will be used with because the number of categories and modules
+ * needs to be known in order to set the configuration. However,
+ * the configuration is not used by the logging context until the
+ * isc_logconfig_use function is called.
+ *
+ *\li The memory context used for operations that allocate memory for
+ * the configuration is that of the logging context, as specified
+ * in the isc_log_create call.
+ *
+ *\li Four default channels are established:
+ *\verbatim
+ * default_syslog
+ * - log to syslog's daemon facility #ISC_LOG_INFO or higher
+ * default_stderr
+ * - log to stderr #ISC_LOG_INFO or higher
+ * default_debug
+ * - log to stderr #ISC_LOG_DEBUG dynamically
+ * null
+ * - log nothing
+ *\endverbatim
+ *
+ * Requires:
+ *\li lctx is a valid logging context.
+ *\li lcftp is not null and *lcfgp is null.
+ *
+ * Ensures:
+ *\li *lcfgp will point to a valid logging context if all of the necessary
+ * memory was allocated, or NULL otherwise.
+ *\li On failure, no additional memory is allocated.
+ */
+
+void
+isc_logconfig_use(isc_log_t *lctx, isc_logconfig_t *lcfg);
+/*%<
+ * Associate a new configuration with a logging context.
+ *
+ * Notes:
+ *\li This is thread safe. The logging context will lock a mutex
+ * before attempting to swap in the new configuration, and isc_log_doit
+ * (the internal function used by all of isc_log_[v]write[1]) locks
+ * the same lock for the duration of its use of the configuration.
+ *
+ * Requires:
+ *\li lctx is a valid logging context.
+ *\li lcfg is a valid logging configuration.
+ *\li lctx is the same configuration given to isc_logconfig_create
+ * when the configuration was created.
+ *
+ * Ensures:
+ *\li Future calls to isc_log_write will use the new configuration.
+ */
+
+void
+isc_log_destroy(isc_log_t **lctxp);
+/*%<
+ * Deallocate the memory associated with a logging context.
+ *
+ * Requires:
+ *\li *lctx is a valid logging context.
+ *
+ * Ensures:
+ *\li All of the memory associated with the logging context is returned
+ * to the free memory pool.
+ *
+ *\li Any open files are closed.
+ *
+ *\li The logging context is marked as invalid.
+ */
+
+void
+isc_logconfig_destroy(isc_logconfig_t **lcfgp);
+/*%<
+ * Destroy a logging configuration.
+ *
+ * Requires:
+ *\li lcfgp is not null and *lcfgp is a valid logging configuration.
+ *\li The logging configuration is not in use by an existing logging context.
+ *
+ * Ensures:
+ *\li All memory allocated for the configuration is freed.
+ *
+ *\li The configuration is marked as invalid.
+ */
+
+void
+isc_log_registercategories(isc_log_t *lctx, isc_logcategory_t categories[]);
+/*%<
+ * Identify logging categories a library will use.
+ *
+ * Notes:
+ *\li A category should only be registered once, but no mechanism enforces
+ * this rule.
+ *
+ *\li The end of the categories array is identified by a NULL name.
+ *
+ *\li Because the name is used by #ISC_LOG_PRINTCATEGORY, it should not
+ * be altered or destroyed after isc_log_registercategories().
+ *
+ *\li Because each element of the categories array is used by
+ * isc_log_categorybyname, it should not be altered or destroyed
+ * after registration.
+ *
+ *\li The value of the id integer in each structure is overwritten
+ * by this function, and so id need not be initialized to any particular
+ * value prior to the function call.
+ *
+ *\li A subsequent call to isc_log_registercategories with the same
+ * logging context (but new categories) will cause the last
+ * element of the categories array from the prior call to have
+ * its "name" member changed from NULL to point to the new
+ * categories array, and its "id" member set to UINT_MAX.
+ *
+ * Requires:
+ *\li lctx is a valid logging context.
+ *\li categories != NULL.
+ *\li categories[0].name != NULL.
+ *
+ * Ensures:
+ * \li There are references to each category in the logging context,
+ * so they can be used with isc_log_usechannel() and isc_log_write().
+ */
+
+void
+isc_log_registermodules(isc_log_t *lctx, isc_logmodule_t modules[]);
+/*%<
+ * Identify logging categories a library will use.
+ *
+ * Notes:
+ *\li A module should only be registered once, but no mechanism enforces
+ * this rule.
+ *
+ *\li The end of the modules array is identified by a NULL name.
+ *
+ *\li Because the name is used by #ISC_LOG_PRINTMODULE, it should not
+ * be altered or destroyed after isc_log_registermodules().
+ *
+ *\li Because each element of the modules array is used by
+ * isc_log_modulebyname, it should not be altered or destroyed
+ * after registration.
+ *
+ *\li The value of the id integer in each structure is overwritten
+ * by this function, and so id need not be initialized to any particular
+ * value prior to the function call.
+ *
+ *\li A subsequent call to isc_log_registermodules with the same
+ * logging context (but new modules) will cause the last
+ * element of the modules array from the prior call to have
+ * its "name" member changed from NULL to point to the new
+ * modules array, and its "id" member set to UINT_MAX.
+ *
+ * Requires:
+ *\li lctx is a valid logging context.
+ *\li modules != NULL.
+ *\li modules[0].name != NULL;
+ *
+ * Ensures:
+ *\li Each module has a reference in the logging context, so they can be
+ * used with isc_log_usechannel() and isc_log_write().
+ */
+
+void
+isc_log_createchannel(isc_logconfig_t *lcfg, const char *name,
+ unsigned int type, int level,
+ const isc_logdestination_t *destination,
+ unsigned int flags);
+/*%<
+ * Specify the parameters of a logging channel.
+ *
+ * Notes:
+ *\li The name argument is copied to memory in the logging context, so
+ * it can be altered or destroyed after isc_log_createchannel().
+ *
+ *\li Defining a very large number of channels will have a performance
+ * impact on isc_log_usechannel(), since the names are searched
+ * linearly until a match is made. This same issue does not affect
+ * isc_log_write, however.
+ *
+ *\li Channel names can be redefined; this is primarily useful for programs
+ * that want their own definition of default_syslog, default_debug
+ * and default_stderr.
+ *
+ *\li Any channel that is redefined will not affect logging that was
+ * already directed to its original definition, _except_ for the
+ * default_stderr channel. This case is handled specially so that
+ * the default logging category can be changed by redefining
+ * default_stderr. (XXXDCL Though now that I think of it, the default
+ * logging category can be changed with only one additional function
+ * call by defining a new channel and then calling isc_log_usechannel()
+ * for #ISC_LOGCATEGORY_DEFAULT.)
+ *
+ *\li Specifying #ISC_LOG_PRINTTIME or #ISC_LOG_PRINTTAG for syslog is
+ * allowed, but probably not what you wanted to do.
+ *
+ * #ISC_LOG_DEBUGONLY will mark the channel as usable only when the
+ * debug level of the logging context (see isc_log_setdebuglevel)
+ * is non-zero.
+ *
+ * Requires:
+ *\li lcfg is a valid logging configuration.
+ *
+ *\li name is not NULL.
+ *
+ *\li type is #ISC_LOG_TOSYSLOG, #ISC_LOG_TOFILE, #ISC_LOG_TOFILEDESC or
+ * #ISC_LOG_TONULL.
+ *
+ *\li destination is not NULL unless type is #ISC_LOG_TONULL.
+ *
+ *\li level is >= #ISC_LOG_CRITICAL (the most negative logging level).
+ *
+ *\li flags does not include any bits aside from the ISC_LOG_PRINT* bits,
+ * #ISC_LOG_DEBUGONLY or #ISC_LOG_BUFFERED.
+ *
+ * Ensures:
+ *\li #ISC_R_SUCCESS
+ * A channel with the given name is usable with
+ * isc_log_usechannel().
+ *
+ *\li #ISC_R_NOMEMORY or #ISC_R_UNEXPECTED
+ * No additional memory is being used by the logging context.
+ * Any channel that previously existed with the given name
+ * is not redefined.
+ */
+
+isc_result_t
+isc_log_usechannel(isc_logconfig_t *lcfg, const char *name,
+ const isc_logcategory_t *category,
+ const isc_logmodule_t *module);
+/*%<
+ * Associate a named logging channel with a category and module that
+ * will use it.
+ *
+ * Notes:
+ *\li The name is searched for linearly in the set of known channel names
+ * until a match is found. (Note the performance impact of a very large
+ * number of named channels.) When multiple channels of the same
+ * name are defined, the most recent definition is found.
+ *
+ *\li Specifying a very large number of channels for a category will have
+ * a moderate impact on performance in isc_log_write(), as each
+ * call looks up the category for the start of a linked list, which
+ * it follows all the way to the end to find matching modules. The
+ * test for matching modules is integral, though.
+ *
+ *\li If category is NULL, then the channel is associated with the indicated
+ * module for all known categories (including the "default" category).
+ *
+ *\li If module is NULL, then the channel is associated with every module
+ * that uses that category.
+ *
+ *\li Passing both category and module as NULL would make every log message
+ * use the indicated channel.
+ *
+ * \li Specifying a channel that is #ISC_LOG_TONULL for a category/module pair
+ * has no effect on any other channels associated with that pair,
+ * regardless of ordering. Thus you cannot use it to "mask out" one
+ * category/module pair when you have specified some other channel that
+ * is also used by that category/module pair.
+ *
+ * Requires:
+ *\li lcfg is a valid logging configuration.
+ *
+ *\li category is NULL or has an id that is in the range of known ids.
+ *
+ * module is NULL or has an id that is in the range of known ids.
+ *
+ * Ensures:
+ *\li #ISC_R_SUCCESS
+ * The channel will be used by the indicated category/module
+ * arguments.
+ *
+ *\li #ISC_R_NOMEMORY
+ * If assignment for a specific category has been requested,
+ * the channel has not been associated with the indicated
+ * category/module arguments and no additional memory is
+ * used by the logging context.
+ * If assignment for all categories has been requested
+ * then _some_ may have succeeded (starting with category
+ * "default" and progressing through the order of categories
+ * passed to isc_log_registercategories()) and additional memory
+ * is being used by whatever assignments succeeded.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS Success
+ *\li #ISC_R_NOMEMORY Resource limit: Out of memory
+ */
+
+/* Attention: next four comments PRECEDE code */
+/*!
+ * \brief
+ * Write a message to the log channels.
+ *
+ * Notes:
+ *\li lctx can be NULL; this is allowed so that programs which use
+ * libraries that use the ISC logging system are not required to
+ * also use it.
+ *
+ *\li The format argument is a printf(3) string, with additional arguments
+ * as necessary.
+ *
+ * Requires:
+ *\li lctx is a valid logging context.
+ *
+ *\li The category and module arguments must have ids that are in the
+ * range of known ids, as established by isc_log_registercategories()
+ * and isc_log_registermodules().
+ *
+ *\li level != #ISC_LOG_DYNAMIC. ISC_LOG_DYNAMIC is used only to define
+ * channels, and explicit debugging level must be identified for
+ * isc_log_write() via ISC_LOG_DEBUG(level).
+ *
+ *\li format != NULL.
+ *
+ * Ensures:
+ *\li The log message is written to every channel associated with the
+ * indicated category/module pair.
+ *
+ * Returns:
+ *\li Nothing. Failure to log a message is not construed as a
+ * meaningful error.
+ */
+void
+isc_log_write(isc_log_t *lctx, isc_logcategory_t *category,
+ isc_logmodule_t *module, int level, const char *format, ...)
+
+ ISC_FORMAT_PRINTF(5, 6);
+
+/*%
+ * Write a message to the log channels.
+ *
+ * Notes:
+ *\li lctx can be NULL; this is allowed so that programs which use
+ * libraries that use the ISC logging system are not required to
+ * also use it.
+ *
+ *\li The format argument is a printf(3) string, with additional arguments
+ * as necessary.
+ *
+ * Requires:
+ *\li lctx is a valid logging context.
+ *
+ *\li The category and module arguments must have ids that are in the
+ * range of known ids, as established by isc_log_registercategories()
+ * and isc_log_registermodules().
+ *
+ *\li level != #ISC_LOG_DYNAMIC. ISC_LOG_DYNAMIC is used only to define
+ * channels, and explicit debugging level must be identified for
+ * isc_log_write() via ISC_LOG_DEBUG(level).
+ *
+ *\li format != NULL.
+ *
+ * Ensures:
+ *\li The log message is written to every channel associated with the
+ * indicated category/module pair.
+ *
+ * Returns:
+ *\li Nothing. Failure to log a message is not construed as a
+ * meaningful error.
+ */
+void
+isc_log_vwrite(isc_log_t *lctx, isc_logcategory_t *category,
+ isc_logmodule_t *module, int level, const char *format,
+ va_list args)
+
+ ISC_FORMAT_PRINTF(5, 0);
+
+/*%
+ * Write a message to the log channels, pruning duplicates that occur within
+ * a configurable amount of seconds (see isc_log_[sg]etduplicateinterval).
+ * This function is otherwise identical to isc_log_write().
+ */
+void
+isc_log_write1(isc_log_t *lctx, isc_logcategory_t *category,
+ isc_logmodule_t *module, int level, const char *format, ...)
+
+ ISC_FORMAT_PRINTF(5, 6);
+
+/*%
+ * Write a message to the log channels, pruning duplicates that occur within
+ * a configurable amount of seconds (see isc_log_[sg]etduplicateinterval).
+ * This function is otherwise identical to isc_log_vwrite().
+ */
+void
+isc_log_vwrite1(isc_log_t *lctx, isc_logcategory_t *category,
+ isc_logmodule_t *module, int level, const char *format,
+ va_list args)
+
+ ISC_FORMAT_PRINTF(5, 0);
+
+void
+isc_log_setdebuglevel(isc_log_t *lctx, unsigned int level);
+/*%<
+ * Set the debugging level used for logging.
+ *
+ * Notes:
+ *\li Setting the debugging level to 0 disables debugging log messages.
+ *
+ * Requires:
+ *\li lctx is a valid logging context.
+ *
+ * Ensures:
+ *\li The debugging level is set to the requested value.
+ */
+
+unsigned int
+isc_log_getdebuglevel(isc_log_t *lctx);
+/*%<
+ * Get the current debugging level.
+ *
+ * Notes:
+ *\li This is provided so that a program can have a notion of
+ * "increment debugging level" or "decrement debugging level"
+ * without needing to keep track of what the current level is.
+ *
+ *\li A return value of 0 indicates that debugging messages are disabled.
+ *
+ * Requires:
+ *\li lctx is a valid logging context.
+ *
+ * Ensures:
+ *\li The current logging debugging level is returned.
+ */
+
+bool
+isc_log_wouldlog(isc_log_t *lctx, int level);
+/*%<
+ * Determine whether logging something to 'lctx' at 'level' would
+ * actually cause something to be logged somewhere.
+ *
+ * If #false is returned, it is guaranteed that nothing would
+ * be logged, allowing the caller to omit unnecessary
+ * isc_log_write() calls and possible message preformatting.
+ */
+
+void
+isc_log_setduplicateinterval(isc_logconfig_t *lcfg, unsigned int interval);
+/*%<
+ * Set the interval over which duplicate log messages will be ignored
+ * by isc_log_[v]write1(), in seconds.
+ *
+ * Notes:
+ *\li Increasing the duplicate interval from X to Y will not necessarily
+ * filter out duplicates of messages logged in Y - X seconds since the
+ * increase. (Example: Message1 is logged at midnight. Message2
+ * is logged at 00:01:00, when the interval is only 30 seconds, causing
+ * Message1 to be expired from the log message history. Then the interval
+ * is increased to 3000 (five minutes) and at 00:04:00 Message1 is logged
+ * again. It will appear the second time even though less than five
+ * passed since the first occurrence.
+ *
+ * Requires:
+ *\li lctx is a valid logging context.
+ */
+
+unsigned int
+isc_log_getduplicateinterval(isc_logconfig_t *lcfg);
+/*%<
+ * Get the current duplicate filtering interval.
+ *
+ * Requires:
+ *\li lctx is a valid logging context.
+ *
+ * Returns:
+ *\li The current duplicate filtering interval.
+ */
+
+void
+isc_log_settag(isc_logconfig_t *lcfg, const char *tag);
+/*%<
+ * Set the program name or other identifier for #ISC_LOG_PRINTTAG.
+ *
+ * Requires:
+ *\li lcfg is a valid logging configuration.
+ *
+ * Notes:
+ *\li If this function has not set the tag to a non-NULL, non-empty value,
+ * then the #ISC_LOG_PRINTTAG channel flag will not print anything.
+ * Unlike some implementations of syslog on Unix systems, you *must* set
+ * the tag in order to get it logged. It is not implicitly derived from
+ * the program name (which is pretty impossible to infer portably).
+ *
+ *\li Setting the tag to NULL or the empty string will also cause the
+ * #ISC_LOG_PRINTTAG channel flag to not print anything. If tag equals the
+ * empty string, calls to isc_log_gettag will return NULL.
+ *
+ * XXXDCL when creating a new isc_logconfig_t, it might be nice if the tag
+ * of the currently active isc_logconfig_t was inherited. this does not
+ * currently happen.
+ */
+
+char *
+isc_log_gettag(isc_logconfig_t *lcfg);
+/*%<
+ * Get the current identifier printed with #ISC_LOG_PRINTTAG.
+ *
+ * Requires:
+ *\li lcfg is a valid logging configuration.
+ *
+ * Notes:
+ *\li Since isc_log_settag() will not associate a zero-length string
+ * with the logging configuration, attempts to do so will cause
+ * this function to return NULL. However, a determined programmer
+ * will observe that (currently) a tag of length greater than zero
+ * could be set, and then modified to be zero length.
+ *
+ * Returns:
+ *\li A pointer to the current identifier, or NULL if none has been set.
+ */
+
+void
+isc_log_opensyslog(const char *tag, int options, int facility);
+/*%<
+ * Initialize syslog logging.
+ *
+ * Notes:
+ *\li XXXDCL NT
+ * This is currently equivalent to openlog(), but is not going to remain
+ * that way. In the meantime, the arguments are all identical to
+ * those used by openlog(3), as follows:
+ *
+ * \code
+ * tag: The string to use in the position of the program
+ * name in syslog messages. Most (all?) syslogs
+ * will use basename(argv[0]) if tag is NULL.
+ *
+ * options: LOG_CONS, LOG_PID, LOG_NDELAY ... whatever your
+ * syslog supports.
+ *
+ * facility: The default syslog facility. This is irrelevant
+ * since isc_log_write will ALWAYS use the channel's
+ * declared facility.
+ * \endcode
+ *
+ *\li Zero effort has been made (yet) to accommodate systems with openlog()
+ * that only takes two arguments, or to identify valid syslog
+ * facilities or options for any given architecture.
+ *
+ *\li It is necessary to call isc_log_opensyslog() to initialize
+ * syslogging on machines which do not support network connections to
+ * syslogd because they require a Unix domain socket to be used. Since
+ * this is a chore to determine at run-time, it is suggested that it
+ * always be called by programs using the ISC logging system.
+ *
+ * Requires:
+ *\li Nothing.
+ *
+ * Ensures:
+ *\li openlog() is called to initialize the syslog system.
+ */
+
+void
+isc_log_closefilelogs(isc_log_t *lctx);
+/*%<
+ * Close all open files used by #ISC_LOG_TOFILE channels.
+ *
+ * Notes:
+ *\li This function is provided for programs that want to use their own
+ * log rolling mechanism rather than the one provided internally.
+ * For example, a program that wanted to keep daily logs would define
+ * a channel which used #ISC_LOG_ROLLNEVER, then once a day would
+ * rename the log file and call isc_log_closefilelogs().
+ *
+ *\li #ISC_LOG_TOFILEDESC channels are unaffected.
+ *
+ * Requires:
+ *\li lctx is a valid context.
+ *
+ * Ensures:
+ *\li The open files are closed and will be reopened when they are
+ * next needed.
+ */
+
+isc_logcategory_t *
+isc_log_categorybyname(isc_log_t *lctx, const char *name);
+/*%<
+ * Find a category by its name.
+ *
+ * Notes:
+ *\li The string name of a category is not required to be unique.
+ *
+ * Requires:
+ *\li lctx is a valid context.
+ *\li name is not NULL.
+ *
+ * Returns:
+ *\li A pointer to the _first_ isc_logcategory_t structure used by "name".
+ *
+ *\li NULL if no category exists by that name.
+ */
+
+isc_logmodule_t *
+isc_log_modulebyname(isc_log_t *lctx, const char *name);
+/*%<
+ * Find a module by its name.
+ *
+ * Notes:
+ *\li The string name of a module is not required to be unique.
+ *
+ * Requires:
+ *\li lctx is a valid context.
+ *\li name is not NULL.
+ *
+ * Returns:
+ *\li A pointer to the _first_ isc_logmodule_t structure used by "name".
+ *
+ *\li NULL if no module exists by that name.
+ */
+
+void
+isc_log_setcontext(isc_log_t *lctx);
+/*%<
+ * Sets the context used by the libisc for logging.
+ *
+ * Requires:
+ *\li lctx be a valid context.
+ */
+
+isc_result_t
+isc_logfile_roll(isc_logfile_t *file);
+/*%<
+ * Roll a logfile.
+ *
+ * Requires:
+ *\li file is not NULL.
+ */
+
+void
+isc_log_setforcelog(bool v);
+/*%<
+ * Turn forced logging on/off for the current thread. This can be used to
+ * temporarily increase the debug level to maximum for the duration of
+ * a single task event.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/magic.h b/lib/isc/include/isc/magic.h
new file mode 100644
index 0000000..5e2f184
--- /dev/null
+++ b/lib/isc/include/isc/magic.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/magic.h */
+
+typedef struct {
+ unsigned int magic;
+} isc__magic_t;
+
+/*%
+ * To use this macro the magic number MUST be the first thing in the
+ * structure, and MUST be of type "unsigned int".
+ * The intent of this is to allow magic numbers to be checked even though
+ * the object is otherwise opaque.
+ */
+#define ISC_MAGIC_VALID(a, b) \
+ ((a) != NULL && ((const isc__magic_t *)(a))->magic == (b))
+
+#define ISC_MAGIC(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d))
diff --git a/lib/isc/include/isc/managers.h b/lib/isc/include/isc/managers.h
new file mode 100644
index 0000000..774ed30
--- /dev/null
+++ b/lib/isc/include/isc/managers.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <isc/netmgr.h>
+#include <isc/result.h>
+#include <isc/task.h>
+#include <isc/timer.h>
+
+typedef struct isc_managers isc_managers_t;
+
+isc_result_t
+isc_managers_create(isc_mem_t *mctx, size_t workers, size_t quantum,
+ isc_nm_t **netmgrp, isc_taskmgr_t **taskmgrp,
+ isc_timermgr_t **timermgrp);
+
+void
+isc_managers_destroy(isc_nm_t **netmgrp, isc_taskmgr_t **taskmgrp,
+ isc_timermgr_t **timermgrp);
diff --git a/lib/isc/include/isc/md.h b/lib/isc/include/isc/md.h
new file mode 100644
index 0000000..f52424b
--- /dev/null
+++ b/lib/isc/include/isc/md.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*!
+ * \file isc/md.h
+ * \brief This is the header file for message digest algorithms.
+ */
+
+#pragma once
+
+#include <isc/lang.h>
+#include <isc/result.h>
+#include <isc/types.h>
+
+typedef void isc_md_t;
+
+/**
+ * isc_md_type_t:
+ * @ISC_MD_MD5: MD5
+ * @ISC_MD_SHA1: SHA-1
+ * @ISC_MD_SHA224: SHA-224
+ * @ISC_MD_SHA256: SHA-256
+ * @ISC_MD_SHA384: SHA-384
+ * @ISC_MD_SHA512: SHA-512
+ *
+ * Enumeration of supported message digest algorithms.
+ */
+typedef void isc_md_type_t;
+
+#define ISC_MD_MD5 isc__md_md5()
+#define ISC_MD_SHA1 isc__md_sha1()
+#define ISC_MD_SHA224 isc__md_sha224()
+#define ISC_MD_SHA256 isc__md_sha256()
+#define ISC_MD_SHA384 isc__md_sha384()
+#define ISC_MD_SHA512 isc__md_sha512()
+
+const isc_md_type_t *
+isc__md_md5(void);
+const isc_md_type_t *
+isc__md_sha1(void);
+const isc_md_type_t *
+isc__md_sha224(void);
+const isc_md_type_t *
+isc__md_sha256(void);
+const isc_md_type_t *
+isc__md_sha384(void);
+const isc_md_type_t *
+isc__md_sha512(void);
+
+#define ISC_MD5_DIGESTLENGTH isc_md_type_get_size(ISC_MD_MD5)
+#define ISC_MD5_BLOCK_LENGTH isc_md_type_get_block_size(ISC_MD_MD5)
+#define ISC_SHA1_DIGESTLENGTH isc_md_type_get_size(ISC_MD_SHA1)
+#define ISC_SHA1_BLOCK_LENGTH isc_md_type_get_block_size(ISC_MD_SHA1)
+#define ISC_SHA224_DIGESTLENGTH isc_md_type_get_size(ISC_MD_SHA224)
+#define ISC_SHA224_BLOCK_LENGTH isc_md_type_get_block_size(ISC_MD_SHA224)
+#define ISC_SHA256_DIGESTLENGTH isc_md_type_get_size(ISC_MD_SHA256)
+#define ISC_SHA256_BLOCK_LENGTH isc_md_type_get_block_size(ISC_MD_SHA256)
+#define ISC_SHA384_DIGESTLENGTH isc_md_type_get_size(ISC_MD_SHA384)
+#define ISC_SHA384_BLOCK_LENGTH isc_md_type_get_block_size(ISC_MD_SHA384)
+#define ISC_SHA512_DIGESTLENGTH isc_md_type_get_size(ISC_MD_SHA512)
+#define ISC_SHA512_BLOCK_LENGTH isc_md_type_get_block_size(ISC_MD_SHA512)
+
+#define ISC_MAX_MD_SIZE 64U /* EVP_MAX_MD_SIZE */
+#define ISC_MAX_BLOCK_SIZE 128U /* ISC_SHA512_BLOCK_LENGTH */
+
+/**
+ * isc_md:
+ * @type: the digest type
+ * @buf: the data to hash
+ * @len: the length of the data to hash
+ * @digest: the output buffer
+ * @digestlen: the length of the data written to @digest
+ *
+ * This function hashes @len bytes of data at @buf and places the result in
+ * @digest. If the @digestlen parameter is not NULL then the number of bytes of
+ * data written (i.e. the length of the digest) will be written to the integer
+ * at @digestlen, at most ISC_MAX_MD_SIZE bytes will be written.
+ */
+isc_result_t
+isc_md(const isc_md_type_t *type, const unsigned char *buf, const size_t len,
+ unsigned char *digest, unsigned int *digestlen);
+
+/**
+ * isc_md_new:
+ *
+ * This function allocates, initializes and returns a digest context.
+ */
+isc_md_t *
+isc_md_new(void);
+
+/**
+ * isc_md_free:
+ * @md: message digest context
+ *
+ * This function cleans up digest context ctx and frees up the space allocated
+ * to it.
+ */
+void
+isc_md_free(isc_md_t *);
+
+/**
+ * isc_md_init:
+ * @md: message digest context
+ * @type: digest type
+ *
+ * This function sets up digest context @md to use a digest @type. @md must be
+ * initialized before calling this function.
+ */
+isc_result_t
+isc_md_init(isc_md_t *, const isc_md_type_t *md_type);
+
+/**
+ * isc_md_reset:
+ * @md: message digest context
+ *
+ * This function resets the digest context ctx. This can be used to reuse an
+ * already existing context.
+ */
+isc_result_t
+isc_md_reset(isc_md_t *md);
+
+/**
+ * isc_md_update:
+ * @md: message digest context
+ * @buf: data to hash
+ * @len: length of the data to hash
+ *
+ * This function hashes @len bytes of data at @buf into the digest context @md.
+ * This function can be called several times on the same @md to hash additional
+ * data.
+ */
+isc_result_t
+isc_md_update(isc_md_t *md, const unsigned char *buf, const size_t len);
+
+/**
+ * isc_md_final:
+ * @md: message digest context
+ * @digest: the output buffer
+ * @digestlen: the length of the data written to @digest
+ *
+ * This function retrieves the digest value from @md and places it in @digest.
+ * If the @digestlen parameter is not NULL then the number of bytes of data
+ * written (i.e. the length of the digest) will be written to the integer at
+ * @digestlen, at most ISC_MAX_MD_SIZE bytes will be written. After calling
+ * this function no additional calls to isc_md_update() can be made.
+ */
+isc_result_t
+isc_md_final(isc_md_t *md, unsigned char *digest, unsigned int *digestlen);
+
+/**
+ * isc_md_get_type:
+ * @md: message digest contezt
+ *
+ * This function return the isc_md_type_t previously set for the supplied
+ * message digest context or NULL if no isc_md_type_t has been set.
+ */
+const isc_md_type_t *
+isc_md_get_md_type(isc_md_t *md);
+
+/**
+ * isc_md_size:
+ *
+ * This function return the size of the message digest when passed an isc_md_t
+ * structure, i.e. the size of the hash.
+ */
+size_t
+isc_md_get_size(isc_md_t *md);
+
+/**
+ * isc_md_block_size:
+ *
+ * This function return the block size of the message digest when passed an
+ * isc_md_t structure.
+ */
+size_t
+isc_md_get_block_size(isc_md_t *md);
+
+/**
+ * isc_md_size:
+ *
+ * This function return the size of the message digest when passed an
+ * isc_md_type_t , i.e. the size of the hash.
+ */
+size_t
+isc_md_type_get_size(const isc_md_type_t *md_type);
+
+/**
+ * isc_md_block_size:
+ *
+ * This function return the block size of the message digest when passed an
+ * isc_md_type_t.
+ */
+size_t
+isc_md_type_get_block_size(const isc_md_type_t *md_type);
diff --git a/lib/isc/include/isc/mem.h b/lib/isc/include/isc/mem.h
new file mode 100644
index 0000000..bd59f73
--- /dev/null
+++ b/lib/isc/include/isc/mem.h
@@ -0,0 +1,572 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/mem.h */
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <isc/attributes.h>
+#include <isc/lang.h>
+#include <isc/mutex.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+#define ISC_MEM_LOWATER 0
+#define ISC_MEM_HIWATER 1
+typedef void (*isc_mem_water_t)(void *, int);
+
+/*%
+ * Define ISC_MEM_TRACKLINES=1 to turn on detailed tracing of memory
+ * allocation and freeing by file and line number.
+ */
+#ifndef ISC_MEM_TRACKLINES
+#define ISC_MEM_TRACKLINES 0
+#endif /* ifndef ISC_MEM_TRACKLINES */
+
+extern unsigned int isc_mem_debugging;
+extern unsigned int isc_mem_defaultflags;
+
+/*@{*/
+#define ISC_MEM_DEBUGTRACE 0x00000001U
+#define ISC_MEM_DEBUGRECORD 0x00000002U
+#define ISC_MEM_DEBUGUSAGE 0x00000004U
+#define ISC_MEM_DEBUGALL 0x0000001FU
+/*!<
+ * The variable isc_mem_debugging holds a set of flags for
+ * turning certain memory debugging options on or off at
+ * runtime. It is initialized to the value ISC_MEM_DEGBUGGING,
+ * which is 0 by default but may be overridden at compile time.
+ * The following flags can be specified:
+ *
+ * \li #ISC_MEM_DEBUGTRACE
+ * Log each allocation and free to isc_lctx.
+ *
+ * \li #ISC_MEM_DEBUGRECORD
+ * Remember each allocation, and match them up on free.
+ * Crash if a free doesn't match an allocation.
+ *
+ * \li #ISC_MEM_DEBUGUSAGE
+ * If a hi_water mark is set, print the maximum inuse memory
+ * every time it is raised once it exceeds the hi_water mark.
+ */
+/*@}*/
+
+#if ISC_MEM_TRACKLINES
+#define _ISC_MEM_FILELINE , __FILE__, __LINE__
+#define _ISC_MEM_FLARG , const char *, unsigned int
+#else /* if ISC_MEM_TRACKLINES */
+#define _ISC_MEM_FILELINE
+#define _ISC_MEM_FLARG
+#endif /* if ISC_MEM_TRACKLINES */
+
+/*
+ * Flags for isc_mem_create() calls.
+ */
+#define ISC_MEMFLAG_RESERVED1 0x00000001 /* reserved, obsoleted, don't use */
+#define ISC_MEMFLAG_RESERVED2 0x00000002 /* reserved, obsoleted, don't use */
+#define ISC_MEMFLAG_FILL \
+ 0x00000004 /* fill with pattern after alloc and frees */
+
+/*%
+ * Define ISC_MEM_DEFAULTFILL=1 to turn filling the memory with pattern
+ * after alloc and free.
+ */
+#if ISC_MEM_DEFAULTFILL
+#define ISC_MEMFLAG_DEFAULT ISC_MEMFLAG_FILL
+#else /* if !ISC_MEM_USE_INTERNAL_MALLOC */
+#define ISC_MEMFLAG_DEFAULT 0
+#endif /* if !ISC_MEM_USE_INTERNAL_MALLOC */
+
+/*%
+ * isc_mem_putanddetach() is a convenience function for use where you
+ * have a structure with an attached memory context.
+ *
+ * Given:
+ *
+ * \code
+ * struct {
+ * ...
+ * isc_mem_t *mctx;
+ * ...
+ * } *ptr;
+ *
+ * isc_mem_t *mctx;
+ *
+ * isc_mem_putanddetach(&ptr->mctx, ptr, sizeof(*ptr));
+ * \endcode
+ *
+ * is the equivalent of:
+ *
+ * \code
+ * mctx = NULL;
+ * isc_mem_attach(ptr->mctx, &mctx);
+ * isc_mem_detach(&ptr->mctx);
+ * isc_mem_put(mctx, ptr, sizeof(*ptr));
+ * isc_mem_detach(&mctx);
+ * \endcode
+ */
+
+/*%
+ * These functions are actually implemented in isc__mem_<function>
+ * (two underscores). The single-underscore macros are used to pass
+ * __FILE__ and __LINE__, and in the case of the put functions, to
+ * set the pointer being freed to NULL in the calling function.
+ *
+ * Many of these functions have a further isc___mem_<function>
+ * (three underscores) implementation, which is called indirectly
+ * via the isc_memmethods structure in the mctx so that dynamically
+ * loaded modules can use them even if named is statically linked.
+ */
+
+#define ISCMEMFUNC(sfx) isc__mem_##sfx
+#define ISCMEMPOOLFUNC(sfx) isc__mempool_##sfx
+
+#define isc_mem_get(c, s) ISCMEMFUNC(get)((c), (s), 0 _ISC_MEM_FILELINE)
+#define isc_mem_get_aligned(c, s, a) \
+ ISCMEMFUNC(get)((c), (s), (a)_ISC_MEM_FILELINE)
+#define isc_mem_reget(c, p, o, n) \
+ ISCMEMFUNC(reget)((c), (p), (o), (n), 0 _ISC_MEM_FILELINE)
+#define isc_mem_reget_aligned(c, p, o, n, a) \
+ ISCMEMFUNC(reget)((c), (p), (o), (n), (a)_ISC_MEM_FILELINE)
+#define isc_mem_allocate(c, s) ISCMEMFUNC(allocate)((c), (s)_ISC_MEM_FILELINE)
+#define isc_mem_reallocate(c, p, s) \
+ ISCMEMFUNC(reallocate)((c), (p), (s)_ISC_MEM_FILELINE)
+#define isc_mem_strdup(c, p) ISCMEMFUNC(strdup)((c), (p)_ISC_MEM_FILELINE)
+#define isc_mem_strndup(c, p, l) \
+ ISCMEMFUNC(strndup)((c), (p), (l)_ISC_MEM_FILELINE)
+#define isc_mempool_get(c) ISCMEMPOOLFUNC(get)((c)_ISC_MEM_FILELINE)
+
+#define isc_mem_put(c, p, s) \
+ do { \
+ ISCMEMFUNC(put)((c), (p), (s), 0 _ISC_MEM_FILELINE); \
+ (p) = NULL; \
+ } while (0)
+#define isc_mem_put_aligned(c, p, s, a) \
+ do { \
+ ISCMEMFUNC(put) \
+ ((c), (p), (s), (a)_ISC_MEM_FILELINE); \
+ (p) = NULL; \
+ } while (0)
+#define isc_mem_putanddetach(c, p, s) \
+ do { \
+ ISCMEMFUNC(putanddetach)((c), (p), (s), 0 _ISC_MEM_FILELINE); \
+ (p) = NULL; \
+ } while (0)
+#define isc_mem_putanddetach_aligned(c, p, s, a) \
+ do { \
+ ISCMEMFUNC(putanddetach) \
+ ((c), (p), (s), (a)_ISC_MEM_FILELINE); \
+ (p) = NULL; \
+ } while (0)
+#define isc_mem_free(c, p) \
+ do { \
+ ISCMEMFUNC(free)((c), (p)_ISC_MEM_FILELINE); \
+ (p) = NULL; \
+ } while (0)
+#define isc_mempool_put(c, p) \
+ do { \
+ ISCMEMPOOLFUNC(put)((c), (p)_ISC_MEM_FILELINE); \
+ (p) = NULL; \
+ } while (0)
+
+/*@{*/
+#define isc_mem_create(cp) ISCMEMFUNC(create)((cp)_ISC_MEM_FILELINE)
+void ISCMEMFUNC(create)(isc_mem_t **_ISC_MEM_FLARG);
+
+/*!<
+ * \brief Create a memory context.
+ *
+ * Requires:
+ * mctxp != NULL && *mctxp == NULL */
+/*@}*/
+
+#define isc_mem_create_arena(cp) isc__mem_create_arena((cp)_ISC_MEM_FILELINE)
+void
+isc__mem_create_arena(isc_mem_t **_ISC_MEM_FLARG);
+/*!<
+ * \brief Create a memory context that routs all its operations to a
+ * dedicated jemalloc arena (when available). When jemalloc is not
+ * available, the function is, effectively, an alias to
+ * isc_mem_create().
+ *
+ * Requires:
+ * mctxp != NULL && *mctxp == NULL */
+/*@}*/
+
+isc_result_t
+isc_mem_arena_set_muzzy_decay_ms(isc_mem_t *mctx, const ssize_t decay_ms);
+
+isc_result_t
+isc_mem_arena_set_dirty_decay_ms(isc_mem_t *mctx, const ssize_t decay_ms);
+/*!<
+ * \brief These two functions set the given parameters on the
+ * jemalloc arena associated with the memory context (if there is
+ * one). When jemalloc is not available, these are no-op.
+ *
+ * NOTE: The "muzzy_decay_ms" and "dirty_decay_ms" are the most common
+ * parameters to adjust when the defaults do not work well (per the
+ * official jemalloc tuning guide:
+ * https://github.com/jemalloc/jemalloc/blob/dev/TUNING.md).
+ *
+ * Requires:
+ * mctx - a valid memory context.
+ */
+/*@}*/
+
+void
+isc_mem_attach(isc_mem_t *, isc_mem_t **);
+
+/*@{*/
+void
+isc_mem_attach(isc_mem_t *, isc_mem_t **);
+#define isc_mem_detach(cp) ISCMEMFUNC(detach)((cp)_ISC_MEM_FILELINE)
+void ISCMEMFUNC(detach)(isc_mem_t **_ISC_MEM_FLARG);
+/*!<
+ * \brief Attach to / detach from a memory context.
+ *
+ * This is intended for applications that use multiple memory contexts
+ * in such a way that it is not obvious when the last allocations from
+ * a given context has been freed and destroying the context is safe.
+ *
+ * Most applications do not need to call these functions as they can
+ * simply create a single memory context at the beginning of main()
+ * and destroy it at the end of main(), thereby guaranteeing that it
+ * is not destroyed while there are outstanding allocations.
+ */
+/*@}*/
+
+#define isc_mem_destroy(cp) ISCMEMFUNC(destroy)((cp)_ISC_MEM_FILELINE)
+void ISCMEMFUNC(destroy)(isc_mem_t **_ISC_MEM_FLARG);
+/*%<
+ * Destroy a memory context.
+ */
+
+void
+isc_mem_stats(isc_mem_t *mctx, FILE *out);
+/*%<
+ * Print memory usage statistics for 'mctx' on the stream 'out'.
+ */
+
+void
+isc_mem_setdestroycheck(isc_mem_t *mctx, bool on);
+/*%<
+ * If 'on' is true, 'mctx' will check for memory leaks when
+ * destroyed and abort the program if any are present.
+ */
+
+size_t
+isc_mem_inuse(isc_mem_t *mctx);
+/*%<
+ * Get an estimate of the amount of memory in use in 'mctx', in bytes.
+ * This includes quantization overhead, but does not include memory
+ * allocated from the system but not yet used.
+ */
+
+size_t
+isc_mem_maxinuse(isc_mem_t *mctx);
+/*%<
+ * Get an estimate of the largest amount of memory that has been in
+ * use in 'mctx' at any time.
+ */
+
+size_t
+isc_mem_total(isc_mem_t *mctx);
+/*%<
+ * Get the total amount of memory in 'mctx', in bytes, including memory
+ * not yet used.
+ */
+
+size_t
+isc_mem_malloced(isc_mem_t *ctx);
+/*%<
+ * Get an estimate of the amount of memory allocated in 'mctx', in bytes.
+ */
+
+size_t
+isc_mem_maxmalloced(isc_mem_t *ctx);
+/*%<
+ * Get an estimate of the largest amount of memory that has been
+ * allocated in 'mctx' at any time.
+ */
+
+bool
+isc_mem_isovermem(isc_mem_t *mctx);
+/*%<
+ * Return true iff the memory context is in "over memory" state, i.e.,
+ * a hiwater mark has been set and the used amount of memory has exceeds
+ * the mark.
+ */
+
+void
+isc_mem_clearwater(isc_mem_t *mctx);
+void
+isc_mem_setwater(isc_mem_t *mctx, isc_mem_water_t water, void *water_arg,
+ size_t hiwater, size_t lowater);
+/*%<
+ * Set high and low water marks for this memory context.
+ *
+ * When the memory usage of 'mctx' exceeds 'hiwater',
+ * '(water)(water_arg, #ISC_MEM_HIWATER)' will be called. 'water' needs
+ * to call isc_mem_waterack() with #ISC_MEM_HIWATER to acknowledge the
+ * state change. 'water' may be called multiple times.
+ *
+ * When the usage drops below 'lowater', 'water' will again be called,
+ * this time with #ISC_MEM_LOWATER. 'water' need to calls
+ * isc_mem_waterack() with #ISC_MEM_LOWATER to acknowledge the change.
+ *
+ * static void
+ * water(void *arg, int mark) {
+ * struct foo *foo = arg;
+ *
+ * LOCK(&foo->marklock);
+ * if (foo->mark != mark) {
+ * foo->mark = mark;
+ * ....
+ * isc_mem_waterack(foo->mctx, mark);
+ * }
+ * UNLOCK(&foo->marklock);
+ * }
+ *
+ * if 'water' is set to NULL, the 'hiwater' and 'lowater' must set to 0, and
+ * high- and low-water processing are disabled for this memory context. There's
+ * a convenient function isc_mem_clearwater().
+ *
+ * Requires:
+ *
+ *\li If 'water' is NULL, 'hiwater' and 'lowater' must be set to 0.
+ *\li If 'water' and 'water_arg' have previously been set, they are
+ unchanged.
+ *\li 'hiwater' >= 'lowater'
+ */
+
+void
+isc_mem_waterack(isc_mem_t *ctx, int mark);
+/*%<
+ * Called to acknowledge changes in signaled by calls to 'water'.
+ */
+
+void
+isc_mem_checkdestroyed(FILE *file);
+/*%<
+ * Check that all memory contexts have been destroyed.
+ * Prints out those that have not been.
+ * Fatally fails if there are still active contexts.
+ */
+
+unsigned int
+isc_mem_references(isc_mem_t *ctx);
+/*%<
+ * Return the current reference count.
+ */
+
+void
+isc_mem_setname(isc_mem_t *ctx, const char *name);
+/*%<
+ * Name 'ctx'.
+ *
+ * Notes:
+ *
+ *\li Only the first 15 characters of 'name' will be copied.
+ *
+ * Requires:
+ *
+ *\li 'ctx' is a valid ctx.
+ */
+
+const char *
+isc_mem_getname(isc_mem_t *ctx);
+/*%<
+ * Get the name of 'ctx', as previously set using isc_mem_setname().
+ *
+ * Requires:
+ *\li 'ctx' is a valid ctx.
+ *
+ * Returns:
+ *\li A non-NULL pointer to a null-terminated string.
+ * If the ctx has not been named, the string is
+ * empty.
+ */
+
+#ifdef HAVE_LIBXML2
+int
+isc_mem_renderxml(void *writer0);
+/*%<
+ * Render all contexts' statistics and status in XML for writer.
+ */
+#endif /* HAVE_LIBXML2 */
+
+#ifdef HAVE_JSON_C
+isc_result_t
+isc_mem_renderjson(void *memobj0);
+/*%<
+ * Render all contexts' statistics and status in JSON.
+ */
+#endif /* HAVE_JSON_C */
+
+/*
+ * Memory pools
+ */
+
+#define isc_mempool_create(c, s, mp) \
+ isc__mempool_create((c), (s), (mp)_ISC_MEM_FILELINE)
+void
+isc__mempool_create(isc_mem_t *restrict mctx, const size_t element_size,
+ isc_mempool_t **mpctxp _ISC_MEM_FLARG);
+/*%<
+ * Create a memory pool.
+ *
+ * Requires:
+ *\li mctx is a valid memory context.
+ *\li size > 0
+ *\li mpctxp != NULL and *mpctxp == NULL
+ *
+ * Defaults:
+ *\li freemax = 1
+ *\li fillcount = 1
+ *
+ * Returns:
+ *\li #ISC_R_NOMEMORY -- not enough memory to create pool
+ *\li #ISC_R_SUCCESS -- all is well.
+ */
+
+#define isc_mempool_destroy(mp) isc__mempool_destroy((mp)_ISC_MEM_FILELINE)
+void
+isc__mempool_destroy(isc_mempool_t **restrict mpctxp _ISC_MEM_FLARG);
+/*%<
+ * Destroy a memory pool.
+ *
+ * Requires:
+ *\li mpctxp != NULL && *mpctxp is a valid pool.
+ *\li The pool has no un"put" allocations outstanding
+ */
+
+void
+isc_mempool_setname(isc_mempool_t *restrict mpctx, const char *name);
+/*%<
+ * Associate a name with a memory pool. At most 15 characters may be
+ *used.
+ *
+ * Requires:
+ *\li mpctx is a valid pool.
+ *\li name != NULL;
+ */
+
+/*
+ * The following functions get/set various parameters. Note that due to
+ * the unlocked nature of pools these are potentially random values
+ *unless the imposed externally provided locking protocols are followed.
+ *
+ * Also note that the quota limits will not always take immediate
+ * effect.
+ *
+ * All functions require (in addition to other requirements):
+ * mpctx is a valid memory pool
+ */
+
+unsigned int
+isc_mempool_getfreemax(isc_mempool_t *restrict mpctx);
+/*%<
+ * Returns the maximum allowed size of the free list.
+ */
+
+void
+isc_mempool_setfreemax(isc_mempool_t *restrict mpctx, const unsigned int limit);
+/*%<
+ * Sets the maximum allowed size of the free list.
+ */
+
+unsigned int
+isc_mempool_getfreecount(isc_mempool_t *restrict mpctx);
+/*%<
+ * Returns current size of the free list.
+ */
+
+unsigned int
+isc_mempool_getallocated(isc_mempool_t *restrict mpctx);
+/*%<
+ * Returns the number of items allocated from this pool.
+ */
+
+unsigned int
+isc_mempool_getfillcount(isc_mempool_t *restrict mpctx);
+/*%<
+ * Returns the number of items allocated as a block from the parent
+ * memory context when the free list is empty.
+ */
+
+void
+isc_mempool_setfillcount(isc_mempool_t *restrict mpctx,
+ const unsigned int limit);
+/*%<
+ * Sets the fillcount.
+ *
+ * Additional requirements:
+ *\li limit > 0
+ */
+
+#if defined(UNIT_TESTING) && defined(malloc)
+/*
+ * cmocka.h redefined malloc as a macro, we #undef it
+ * to avoid replacing ISC_ATTR_MALLOC with garbage.
+ */
+#pragma push_macro("malloc")
+#undef malloc
+#define POP_MALLOC_MACRO 1
+#endif
+
+/*
+ * Pseudo-private functions for use via macros. Do not call directly.
+ */
+void ISCMEMFUNC(putanddetach)(isc_mem_t **, void *, size_t,
+ size_t _ISC_MEM_FLARG);
+void ISCMEMFUNC(put)(isc_mem_t *, void *, size_t, size_t _ISC_MEM_FLARG);
+void ISCMEMFUNC(free)(isc_mem_t *, void *_ISC_MEM_FLARG);
+
+ISC_ATTR_MALLOC_DEALLOCATOR_IDX(ISCMEMFUNC(put), 2)
+void *ISCMEMFUNC(get)(isc_mem_t *, size_t, size_t _ISC_MEM_FLARG);
+
+ISC_ATTR_DEALLOCATOR_IDX(ISCMEMFUNC(put), 2)
+void *ISCMEMFUNC(reget)(isc_mem_t *, void *, size_t, size_t,
+ size_t _ISC_MEM_FLARG);
+
+ISC_ATTR_MALLOC_DEALLOCATOR_IDX(ISCMEMFUNC(free), 2)
+void *ISCMEMFUNC(allocate)(isc_mem_t *, size_t _ISC_MEM_FLARG);
+
+ISC_ATTR_DEALLOCATOR_IDX(ISCMEMFUNC(free), 2)
+void *ISCMEMFUNC(reallocate)(isc_mem_t *, void *, size_t _ISC_MEM_FLARG);
+
+ISC_ATTR_RETURNS_NONNULL
+ISC_ATTR_MALLOC_DEALLOCATOR_IDX(ISCMEMFUNC(free), 2)
+char *ISCMEMFUNC(strdup)(isc_mem_t *, const char *_ISC_MEM_FLARG);
+
+ISC_ATTR_RETURNS_NONNULL
+ISC_ATTR_MALLOC_DEALLOCATOR_IDX(ISCMEMFUNC(free), 2)
+char *ISCMEMFUNC(strndup)(isc_mem_t *, const char *, size_t _ISC_MEM_FLARG);
+
+ISC_ATTR_MALLOC_DEALLOCATOR_IDX(ISCMEMPOOLFUNC(put), 2)
+void *ISCMEMPOOLFUNC(get)(isc_mempool_t *_ISC_MEM_FLARG);
+
+void ISCMEMPOOLFUNC(put)(isc_mempool_t *, void *_ISC_MEM_FLARG);
+
+#ifdef POP_MALLOC_MACRO
+/*
+ * Restore cmocka.h macro for malloc.
+ */
+#pragma pop_macro("malloc")
+#endif
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/meminfo.h b/lib/isc/include/isc/meminfo.h
new file mode 100644
index 0000000..fc39e26
--- /dev/null
+++ b/lib/isc/include/isc/meminfo.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+uint64_t
+isc_meminfo_totalphys(void);
+/*%<
+ * Return total available physical memory in bytes, or 0 if this cannot
+ * be determined
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/mutex.h b/lib/isc/include/isc/mutex.h
new file mode 100644
index 0000000..b794216
--- /dev/null
+++ b/lib/isc/include/isc/mutex.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file */
+
+#include <pthread.h>
+#include <stdio.h>
+
+#include <isc/lang.h>
+#include <isc/result.h> /* for ISC_R_ codes */
+
+ISC_LANG_BEGINDECLS
+
+typedef pthread_mutex_t isc_mutex_t;
+
+int
+isc__mutex_init(isc_mutex_t *mp);
+
+#define isc_mutex_init(mp) \
+ do { \
+ int _err = isc__mutex_init((mp)); \
+ if (_err != 0) { \
+ FATAL_SYSERROR(_err, "pthread_mutex_init()"); \
+ } \
+ } while (0)
+
+#define isc_mutex_lock(mp) \
+ ((pthread_mutex_lock((mp)) == 0) ? ISC_R_SUCCESS : ISC_R_UNEXPECTED)
+
+#define isc_mutex_unlock(mp) \
+ ((pthread_mutex_unlock((mp)) == 0) ? ISC_R_SUCCESS : ISC_R_UNEXPECTED)
+
+#define isc_mutex_trylock(mp) \
+ ((pthread_mutex_trylock((mp)) == 0) ? ISC_R_SUCCESS : ISC_R_LOCKBUSY)
+
+#define isc_mutex_destroy(mp) RUNTIME_CHECK(pthread_mutex_destroy((mp)) == 0)
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/mutexblock.h b/lib/isc/include/isc/mutexblock.h
new file mode 100644
index 0000000..f5f2e32
--- /dev/null
+++ b/lib/isc/include/isc/mutexblock.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/mutexblock.h */
+
+#include <isc/lang.h>
+#include <isc/mutex.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+void
+isc_mutexblock_init(isc_mutex_t *block, unsigned int count);
+/*%<
+ * Initialize a block of locks. If an error occurs all initialized locks
+ * will be destroyed, if possible.
+ *
+ * Requires:
+ *
+ *\li block != NULL
+ *
+ *\li count > 0
+ *
+ */
+
+void
+isc_mutexblock_destroy(isc_mutex_t *block, unsigned int count);
+/*%<
+ * Destroy a block of locks.
+ *
+ * Requires:
+ *
+ *\li block != NULL
+ *
+ *\li count > 0
+ *
+ *\li Each lock in the block be initialized via isc_mutex_init() or
+ * the whole block was initialized via isc_mutex_initblock().
+ *
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/net.h b/lib/isc/include/isc/net.h
new file mode 100644
index 0000000..1a3ce63
--- /dev/null
+++ b/lib/isc/include/isc/net.h
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*****
+***** Module Info
+*****/
+
+/*! \file
+ * \brief
+ * Basic Networking Types
+ *
+ * This module is responsible for defining the following basic networking
+ * types:
+ *
+ *\li struct in_addr
+ *\li struct in6_addr
+ *\li struct in6_pktinfo
+ *\li struct sockaddr
+ *\li struct sockaddr_in
+ *\li struct sockaddr_in6
+ *\li struct sockaddr_storage
+ *\li in_port_t
+ *
+ * It ensures that the AF_ and PF_ macros are defined.
+ *
+ * It declares ntoh[sl]() and hton[sl]().
+ *
+ * It declares inet_ntop(), and inet_pton().
+ *
+ * It ensures that #INADDR_LOOPBACK, #INADDR_ANY, #IN6ADDR_ANY_INIT,
+ * IN6ADDR_V4MAPPED_INIT, in6addr_any, and in6addr_loopback are available.
+ *
+ * It ensures that IN_MULTICAST() is available to check for multicast
+ * addresses.
+ *
+ * MP:
+ *\li No impact.
+ *
+ * Reliability:
+ *\li No anticipated impact.
+ *
+ * Resources:
+ *\li N/A.
+ *
+ * Security:
+ *\li No anticipated impact.
+ *
+ * Standards:
+ *\li BSD Socket API
+ *\li RFC2553
+ */
+
+/***
+ *** Imports.
+ ***/
+#include <inttypes.h>
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+#include <arpa/inet.h> /* Contractual promise. */
+#include <net/if.h>
+#include <netinet/in.h> /* Contractual promise. */
+#include <sys/socket.h> /* Contractual promise. */
+#include <sys/types.h>
+
+#ifndef IN6ADDR_LOOPBACK_INIT
+#ifdef s6_addr
+/*% IPv6 address loopback init */
+#define IN6ADDR_LOOPBACK_INIT \
+ { \
+ { \
+ { \
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 \
+ } \
+ } \
+ }
+#else /* ifdef s6_addr */
+#define IN6ADDR_LOOPBACK_INIT \
+ { \
+ { \
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 \
+ } \
+ }
+#endif /* ifdef s6_addr */
+#endif /* ifndef IN6ADDR_LOOPBACK_INIT */
+
+#ifndef IN6ADDR_V4MAPPED_INIT
+#ifdef s6_addr
+/*% IPv6 v4mapped prefix init */
+#define IN6ADDR_V4MAPPED_INIT \
+ { \
+ { \
+ { \
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0, \
+ 0, 0, 0 \
+ } \
+ } \
+ }
+#else /* ifdef s6_addr */
+#define IN6ADDR_V4MAPPED_INIT \
+ { \
+ { \
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 0, 0, 0 \
+ } \
+ }
+#endif /* ifdef s6_addr */
+#endif /* ifndef IN6ADDR_V4MAPPED_INIT */
+
+#ifndef IN6_IS_ADDR_V4MAPPED
+/*% Is IPv6 address V4 mapped? */
+#define IN6_IS_ADDR_V4MAPPED(x) \
+ (memcmp((x)->s6_addr, in6addr_any.s6_addr, 10) == 0 && \
+ (x)->s6_addr[10] == 0xff && (x)->s6_addr[11] == 0xff)
+#endif /* ifndef IN6_IS_ADDR_V4MAPPED */
+
+#ifndef IN6_IS_ADDR_V4COMPAT
+/*% Is IPv6 address V4 compatible? */
+#define IN6_IS_ADDR_V4COMPAT(x) \
+ (memcmp((x)->s6_addr, in6addr_any.s6_addr, 12) == 0 && \
+ ((x)->s6_addr[12] != 0 || (x)->s6_addr[13] != 0 || \
+ (x)->s6_addr[14] != 0 || \
+ ((x)->s6_addr[15] != 0 && (x)->s6_addr[15] != 1)))
+#endif /* ifndef IN6_IS_ADDR_V4COMPAT */
+
+#ifndef IN6_IS_ADDR_MULTICAST
+/*% Is IPv6 address multicast? */
+#define IN6_IS_ADDR_MULTICAST(a) ((a)->s6_addr[0] == 0xff)
+#endif /* ifndef IN6_IS_ADDR_MULTICAST */
+
+#ifndef IN6_IS_ADDR_LINKLOCAL
+/*% Is IPv6 address linklocal? */
+#define IN6_IS_ADDR_LINKLOCAL(a) \
+ (((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0x80))
+#endif /* ifndef IN6_IS_ADDR_LINKLOCAL */
+
+#ifndef IN6_IS_ADDR_SITELOCAL
+/*% is IPv6 address sitelocal? */
+#define IN6_IS_ADDR_SITELOCAL(a) \
+ (((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0xc0))
+#endif /* ifndef IN6_IS_ADDR_SITELOCAL */
+
+#ifndef IN6_IS_ADDR_LOOPBACK
+/*% is IPv6 address loopback? */
+#define IN6_IS_ADDR_LOOPBACK(x) \
+ (memcmp((x)->s6_addr, in6addr_loopback.s6_addr, 16) == 0)
+#endif /* ifndef IN6_IS_ADDR_LOOPBACK */
+
+#ifndef AF_INET6
+/*% IPv6 */
+#define AF_INET6 99
+#endif /* ifndef AF_INET6 */
+
+#ifndef PF_INET6
+/*% IPv6 */
+#define PF_INET6 AF_INET6
+#endif /* ifndef PF_INET6 */
+
+#ifndef INADDR_ANY
+/*% inaddr any */
+#define INADDR_ANY 0x00000000UL
+#endif /* ifndef INADDR_ANY */
+
+#ifndef INADDR_LOOPBACK
+/*% inaddr loopback */
+#define INADDR_LOOPBACK 0x7f000001UL
+#endif /* ifndef INADDR_LOOPBACK */
+
+#ifndef MSG_TRUNC
+/*%
+ * If this system does not have MSG_TRUNC (as returned from recvmsg())
+ * ISC_PLATFORM_RECVOVERFLOW will be defined. This will enable the MSG_TRUNC
+ * faking code in socket.c.
+ */
+#define ISC_PLATFORM_RECVOVERFLOW
+#endif /* ifndef MSG_TRUNC */
+
+/*% IP address. */
+#define ISC__IPADDR(x) ((uint32_t)htonl((uint32_t)(x)))
+
+/*% Is IP address multicast? */
+#define ISC_IPADDR_ISMULTICAST(i) \
+ (((uint32_t)(i)&ISC__IPADDR(0xf0000000)) == ISC__IPADDR(0xe0000000))
+
+#define ISC_IPADDR_ISEXPERIMENTAL(i) \
+ (((uint32_t)(i)&ISC__IPADDR(0xf0000000)) == ISC__IPADDR(0xf0000000))
+
+/***
+ *** Functions.
+ ***/
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+isc_net_probeipv4(void);
+/*%<
+ * Check if the system's kernel supports IPv4.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS IPv4 is supported.
+ *\li #ISC_R_NOTFOUND IPv4 is not supported.
+ *\li #ISC_R_DISABLED IPv4 is disabled.
+ *\li #ISC_R_UNEXPECTED
+ */
+
+isc_result_t
+isc_net_probeipv6(void);
+/*%<
+ * Check if the system's kernel supports IPv6.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS IPv6 is supported.
+ *\li #ISC_R_NOTFOUND IPv6 is not supported.
+ *\li #ISC_R_DISABLED IPv6 is disabled.
+ *\li #ISC_R_UNEXPECTED
+ */
+
+isc_result_t
+isc_net_probe_ipv6only(void);
+/*%<
+ * Check if the system's kernel supports the IPV6_V6ONLY socket option.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS the option is supported for both TCP and UDP.
+ *\li #ISC_R_NOTFOUND IPv6 itself or the option is not supported.
+ *\li #ISC_R_UNEXPECTED
+ */
+
+isc_result_t
+isc_net_probe_ipv6pktinfo(void);
+/*
+ * Check if the system's kernel supports the IPV6_(RECV)PKTINFO socket option
+ * for UDP sockets.
+ *
+ * Returns:
+ *
+ * \li #ISC_R_SUCCESS the option is supported.
+ * \li #ISC_R_NOTFOUND IPv6 itself or the option is not supported.
+ * \li #ISC_R_UNEXPECTED
+ */
+
+void
+isc_net_disableipv4(void);
+
+void
+isc_net_disableipv6(void);
+
+void
+isc_net_enableipv4(void);
+
+void
+isc_net_enableipv6(void);
+
+isc_result_t
+isc_net_probeunix(void);
+/*
+ * Returns whether UNIX domain sockets are supported.
+ */
+
+isc_result_t
+isc_net_getudpportrange(int af, in_port_t *low, in_port_t *high);
+/*%<
+ * Returns system's default range of ephemeral UDP ports, if defined.
+ * If the range is not available or unknown, ISC_NET_PORTRANGELOW and
+ * ISC_NET_PORTRANGEHIGH will be returned.
+ *
+ * Requires:
+ *
+ *\li 'low' and 'high' must be non NULL.
+ *
+ * Returns:
+ *
+ *\li *low and *high will be the ports specifying the low and high ends of
+ * the range.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/netaddr.h b/lib/isc/include/isc/netaddr.h
new file mode 100644
index 0000000..112051f
--- /dev/null
+++ b/lib/isc/include/isc/netaddr.h
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/netaddr.h */
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/lang.h>
+#include <isc/net.h>
+#include <isc/types.h>
+
+#include <sys/types.h>
+#include <sys/un.h>
+
+ISC_LANG_BEGINDECLS
+
+/*
+ * Any updates to this structure should also be applied in
+ * contrib/modules/dlz/dlz_minmal.h.
+ */
+struct isc_netaddr {
+ unsigned int family;
+ union {
+ struct in_addr in;
+ struct in6_addr in6;
+ char un[sizeof(((struct sockaddr_un *)0)->sun_path)];
+ } type;
+ uint32_t zone;
+};
+
+struct isc_netprefix {
+ isc_netaddr_t addr;
+ unsigned int prefixlen;
+};
+
+bool
+isc_netaddr_equal(const isc_netaddr_t *a, const isc_netaddr_t *b);
+
+/*%<
+ * Compare network addresses 'a' and 'b'. Return #true if
+ * they are equal, #false if not.
+ */
+
+bool
+isc_netaddr_eqprefix(const isc_netaddr_t *a, const isc_netaddr_t *b,
+ unsigned int prefixlen);
+/*%<
+ * Compare the 'prefixlen' most significant bits of the network
+ * addresses 'a' and 'b'. If 'b''s scope is zero then 'a''s scope is
+ * ignored. Return #true if they are equal, #false if not.
+ */
+
+isc_result_t
+isc_netaddr_masktoprefixlen(const isc_netaddr_t *s, unsigned int *lenp);
+/*%<
+ * Convert a netmask in 's' into a prefix length in '*lenp'.
+ * The mask should consist of zero or more '1' bits in the
+ * most significant part of the address, followed by '0' bits.
+ * If this is not the case, #ISC_R_MASKNONCONTIG is returned.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_MASKNONCONTIG
+ */
+
+isc_result_t
+isc_netaddr_totext(const isc_netaddr_t *netaddr, isc_buffer_t *target);
+/*%<
+ * Append a text representation of 'sockaddr' to the buffer 'target'.
+ * The text is NOT null terminated. Handles IPv4 and IPv6 addresses.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOSPACE The text or the null termination did not fit.
+ *\li #ISC_R_FAILURE Unspecified failure
+ */
+
+void
+isc_netaddr_format(const isc_netaddr_t *na, char *array, unsigned int size);
+/*%<
+ * Format a human-readable representation of the network address '*na'
+ * into the character array 'array', which is of size 'size'.
+ * The resulting string is guaranteed to be null-terminated.
+ */
+
+#define ISC_NETADDR_FORMATSIZE \
+ sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:XXX.XXX.XXX.XXX%SSSSSSSSSS")
+/*%<
+ * Minimum size of array to pass to isc_netaddr_format().
+ */
+
+void
+isc_netaddr_fromsockaddr(isc_netaddr_t *netaddr, const isc_sockaddr_t *source);
+
+void
+isc_netaddr_fromin(isc_netaddr_t *netaddr, const struct in_addr *ina);
+
+void
+isc_netaddr_fromin6(isc_netaddr_t *netaddr, const struct in6_addr *ina6);
+
+isc_result_t
+isc_netaddr_frompath(isc_netaddr_t *netaddr, const char *path);
+
+void
+isc_netaddr_setzone(isc_netaddr_t *netaddr, uint32_t zone);
+
+uint32_t
+isc_netaddr_getzone(const isc_netaddr_t *netaddr);
+
+void
+isc_netaddr_any(isc_netaddr_t *netaddr);
+/*%<
+ * Return the IPv4 wildcard address.
+ */
+
+void
+isc_netaddr_any6(isc_netaddr_t *netaddr);
+/*%<
+ * Return the IPv6 wildcard address.
+ */
+
+void
+isc_netaddr_unspec(isc_netaddr_t *netaddr);
+/*%<
+ * Initialize as AF_UNSPEC address.
+ */
+
+bool
+isc_netaddr_ismulticast(const isc_netaddr_t *na);
+/*%<
+ * Returns true if the address is a multicast address.
+ */
+
+bool
+isc_netaddr_isexperimental(const isc_netaddr_t *na);
+/*%<
+ * Returns true if the address is a experimental (CLASS E) address.
+ */
+
+bool
+isc_netaddr_islinklocal(const isc_netaddr_t *na);
+/*%<
+ * Returns #true if the address is a link local address.
+ */
+
+bool
+isc_netaddr_issitelocal(const isc_netaddr_t *na);
+/*%<
+ * Returns #true if the address is a site local address.
+ */
+
+bool
+isc_netaddr_isnetzero(const isc_netaddr_t *na);
+/*%<
+ * Returns #true if the address is in net zero.
+ */
+
+void
+isc_netaddr_fromv4mapped(isc_netaddr_t *t, const isc_netaddr_t *s);
+/*%<
+ * Convert an IPv6 v4mapped address into an IPv4 address.
+ */
+
+isc_result_t
+isc_netaddr_prefixok(const isc_netaddr_t *na, unsigned int prefixlen);
+/*
+ * Test whether the netaddr 'na' and 'prefixlen' are consistent.
+ * e.g. prefixlen within range.
+ * na does not have bits set which are not covered by the prefixlen.
+ *
+ * Returns:
+ * ISC_R_SUCCESS
+ * ISC_R_RANGE prefixlen out of range
+ * ISC_R_NOTIMPLEMENTED unsupported family
+ * ISC_R_FAILURE extra bits.
+ */
+
+bool
+isc_netaddr_isloopback(const isc_netaddr_t *na);
+/*
+ * Test whether the netaddr 'na' is a loopback IPv4 or IPv6 address (in
+ * 127.0.0.0/8 or ::1).
+ */
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/netdb.h b/lib/isc/include/isc/netdb.h
new file mode 100644
index 0000000..93799d7
--- /dev/null
+++ b/lib/isc/include/isc/netdb.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*****
+***** Module Info
+*****/
+
+/*! \file
+ * \brief
+ * Portable netdb.h support.
+ *
+ * This module is responsible for defining the get<x>by<y> APIs.
+ *
+ * MP:
+ *\li No impact.
+ *
+ * Reliability:
+ *\li No anticipated impact.
+ *
+ * Resources:
+ *\li N/A.
+ *
+ * Security:
+ *\li No anticipated impact.
+ *
+ * Standards:
+ *\li BSD API
+ */
+
+/***
+ *** Imports.
+ ***/
+
+#include <netdb.h>
+
+#include <isc/net.h>
diff --git a/lib/isc/include/isc/netmgr.h b/lib/isc/include/isc/netmgr.h
new file mode 100644
index 0000000..eff33f6
--- /dev/null
+++ b/lib/isc/include/isc/netmgr.h
@@ -0,0 +1,811 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <unistd.h>
+
+#include <isc/mem.h>
+#include <isc/region.h>
+#include <isc/result.h>
+#include <isc/tls.h>
+#include <isc/types.h>
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#if defined(SO_REUSEPORT_LB) || (defined(SO_REUSEPORT) && defined(__linux__))
+#define HAVE_SO_REUSEPORT_LB 1
+#endif
+
+/*
+ * Replacement for isc_sockettype_t provided by socket.h.
+ */
+typedef enum {
+ isc_socktype_tcp = 1,
+ isc_socktype_udp = 2,
+ isc_socktype_unix = 3,
+ isc_socktype_raw = 4
+} isc_socktype_t;
+
+typedef void (*isc_nm_recv_cb_t)(isc_nmhandle_t *handle, isc_result_t eresult,
+ isc_region_t *region, void *cbarg);
+/*%<
+ * Callback function to be used when receiving a packet.
+ *
+ * 'handle' the handle that can be used to send back the answer.
+ * 'eresult' the result of the event.
+ * 'region' contains the received data, if any. It will be freed
+ * after return by caller.
+ * 'cbarg' the callback argument passed to isc_nm_listenudp(),
+ * isc_nm_listentcpdns(), or isc_nm_read().
+ */
+typedef isc_result_t (*isc_nm_accept_cb_t)(isc_nmhandle_t *handle,
+ isc_result_t result, void *cbarg);
+/*%<
+ * Callback function to be used when accepting a connection. (This differs
+ * from isc_nm_cb_t below in that it returns a result code.)
+ *
+ * 'handle' the handle that can be used to send back the answer.
+ * 'eresult' the result of the event.
+ * 'cbarg' the callback argument passed to isc_nm_listentcp() or
+ * isc_nm_listentcpdns().
+ */
+
+typedef void (*isc_nm_cb_t)(isc_nmhandle_t *handle, isc_result_t result,
+ void *cbarg);
+/*%<
+ * Callback function for other network completion events (send, connect).
+ *
+ * 'handle' the handle on which the event took place.
+ * 'eresult' the result of the event.
+ * 'cbarg' the callback argument passed to isc_nm_send(),
+ * isc_nm_tcp_connect(), or isc_nm_listentcp()
+ */
+
+typedef void (*isc_nm_opaquecb_t)(void *arg);
+/*%<
+ * Opaque callback function, used for isc_nmhandle 'reset' and 'free'
+ * callbacks.
+ */
+
+typedef void (*isc_nm_workcb_t)(void *arg);
+typedef void (*isc_nm_after_workcb_t)(void *arg, isc_result_t result);
+/*%<
+ * Callback functions for libuv threadpool work (see uv_work_t)
+ */
+
+void
+isc_nm_attach(isc_nm_t *mgr, isc_nm_t **dst);
+void
+isc_nm_detach(isc_nm_t **mgr0);
+/*%<
+ * Attach/detach a network manager. When all references have been
+ * released, the network manager is shut down, freeing all resources.
+ * Destroy is working the same way as detach, but it actively waits
+ * for all other references to be gone.
+ */
+
+/* Return thread ID of current thread, or ISC_NETMGR_TID_UNKNOWN */
+int
+isc_nm_tid(void);
+
+void
+isc_nmsocket_close(isc_nmsocket_t **sockp);
+/*%<
+ * isc_nmsocket_close() detaches a listening socket that was
+ * created by isc_nm_listenudp(), isc_nm_listentcp(), or
+ * isc_nm_listentcpdns(). Once there are no remaining child
+ * sockets with active handles, the socket will be closed.
+ */
+
+void
+isc_nmsocket_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx);
+/*%<
+ * Asynchronously replace the TLS context within the listener socket object.
+ * The function is intended to be used during reconfiguration.
+ *
+ * Requires:
+ * \li 'listener' is a pointer to a valid network manager listener socket
+ object with TLS support;
+ * \li 'tlsctx' is a valid pointer to a TLS context object.
+ */
+
+void
+isc_nmsocket_set_max_streams(isc_nmsocket_t *listener,
+ const uint32_t max_streams);
+/*%<
+ * Set the maximum allowed number of concurrent streams for accepted
+ * client connections. The implementation might be asynchronous
+ * depending on the listener socket type.
+ *
+ * The call is a no-op for any listener socket type that does not
+ * support concept of multiple sessions per a client
+ * connection. Currently, it works only for HTTP/2 listeners.
+ *
+ * Setting 'max_streams' to '0' instructs the listener that there is
+ * no limit for concurrent streams.
+ *
+ * Requires:
+ * \li 'listener' is a pointer to a valid network manager listener socket.
+ */
+
+#ifdef NETMGR_TRACE
+#define isc_nmhandle_attach(handle, dest) \
+ isc__nmhandle_attach(handle, dest, __FILE__, __LINE__, __func__)
+#define isc_nmhandle_detach(handlep) \
+ isc__nmhandle_detach(handlep, __FILE__, __LINE__, __func__)
+#define FLARG , const char *file, unsigned int line, const char *func
+#else
+#define isc_nmhandle_attach(handle, dest) isc__nmhandle_attach(handle, dest)
+#define isc_nmhandle_detach(handlep) isc__nmhandle_detach(handlep)
+#define FLARG
+#endif
+
+void
+isc__nmhandle_attach(isc_nmhandle_t *handle, isc_nmhandle_t **dest FLARG);
+void
+isc__nmhandle_detach(isc_nmhandle_t **handlep FLARG);
+/*%<
+ * Increment/decrement the reference counter in a netmgr handle,
+ * but (unlike the attach/detach functions) do not change the pointer
+ * value. If reference counters drop to zero, the handle can be
+ * marked inactive, possibly triggering deletion of its associated
+ * socket.
+ *
+ * (This will be used to prevent a client from being cleaned up when
+ * it's passed to an isc_task event handler. The libuv code would not
+ * otherwise know that the handle was in use and might free it, along
+ * with the client.)
+ */
+#undef FLARG
+
+void *
+isc_nmhandle_getdata(isc_nmhandle_t *handle);
+
+void *
+isc_nmhandle_getextra(isc_nmhandle_t *handle);
+
+bool
+isc_nmhandle_is_stream(isc_nmhandle_t *handle);
+
+void
+isc_nmhandle_setdata(isc_nmhandle_t *handle, void *arg,
+ isc_nm_opaquecb_t doreset, isc_nm_opaquecb_t dofree);
+/*%<
+ * isc_nmhandle_t has a void* opaque field (for example, ns_client_t).
+ * We reuse handle and `opaque` can also be reused between calls.
+ * This function sets this field and two callbacks:
+ * - doreset resets the `opaque` to initial state
+ * - dofree frees everything associated with `opaque`
+ */
+
+void
+isc_nmhandle_settimeout(isc_nmhandle_t *handle, uint32_t timeout);
+void
+isc_nmhandle_cleartimeout(isc_nmhandle_t *handle);
+/*%<
+ * Set/clear the read/recv timeout for the socket connected to 'handle'
+ * to 'timeout' (in milliseconds), and reset the timer.
+ *
+ * When this is called on a 'wrapper' socket handle (for example,
+ * a TCPDNS socket wrapping a TCP connection), the timer is set for
+ * both socket layers.
+ */
+bool
+isc_nmhandle_timer_running(isc_nmhandle_t *handle);
+/*%<
+ * Return true if the timer for the socket connected to 'handle'
+ * is running.
+ */
+
+void
+isc_nmhandle_keepalive(isc_nmhandle_t *handle, bool value);
+/*%<
+ * Enable/disable keepalive on this connection by setting it to 'value'.
+ *
+ * When keepalive is active, we switch to using the keepalive timeout
+ * to determine when to close a connection, rather than the idle timeout.
+ *
+ * This applies only to TCP-based DNS connections (i.e., TCPDNS or
+ * TLSDNS). On other types of connection it has no effect.
+ */
+
+isc_sockaddr_t
+isc_nmhandle_peeraddr(isc_nmhandle_t *handle);
+/*%<
+ * Return the peer address for the given handle.
+ */
+isc_sockaddr_t
+isc_nmhandle_localaddr(isc_nmhandle_t *handle);
+/*%<
+ * Return the local address for the given handle.
+ */
+
+isc_nm_t *
+isc_nmhandle_netmgr(isc_nmhandle_t *handle);
+/*%<
+ * Return a pointer to the netmgr object for the given handle.
+ */
+
+isc_result_t
+isc_nm_listenudp(isc_nm_t *mgr, isc_sockaddr_t *iface, isc_nm_recv_cb_t cb,
+ void *cbarg, size_t extrasize, isc_nmsocket_t **sockp);
+/*%<
+ * Start listening for UDP packets on interface 'iface' using net manager
+ * 'mgr'.
+ *
+ * On success, 'sockp' will be updated to contain a new listening UDP socket.
+ *
+ * When a packet is received on the socket, 'cb' will be called with 'cbarg'
+ * as its argument.
+ *
+ * When handles are allocated for the socket, 'extrasize' additional bytes
+ * can be allocated along with the handle for an associated object, which
+ * can then be freed automatically when the handle is destroyed.
+ */
+
+void
+isc_nm_udpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
+ isc_nm_cb_t cb, void *cbarg, unsigned int timeout,
+ size_t extrahandlesize);
+/*%<
+ * Open a UDP socket, bind to 'local' and connect to 'peer', and
+ * immediately call 'cb' with a handle so that the caller can begin
+ * sending packets over UDP.
+ *
+ * When handles are allocated for the socket, 'extrasize' additional bytes
+ * can be allocated along with the handle for an associated object, which
+ * can then be freed automatically when the handle is destroyed.
+ *
+ * 'timeout' specifies the timeout interval in milliseconds.
+ *
+ * The connected socket can only be accessed via the handle passed to
+ * 'cb'.
+ */
+
+isc_result_t
+isc_nm_routeconnect(isc_nm_t *mgr, isc_nm_cb_t cb, void *cbarg,
+ size_t extrahandlesize);
+/*%<
+ * Open a route/netlink socket and call 'cb', so the caller can be
+ * begin listening for interface changes. This behaves similarly to
+ * isc_nm_udpconnect().
+ *
+ * Returns ISC_R_NOTIMPLEMENTED on systems where route/netlink sockets
+ * are not supported.
+ */
+
+void
+isc_nm_stoplistening(isc_nmsocket_t *sock);
+/*%<
+ * Stop listening on socket 'sock'.
+ */
+
+void
+isc_nm_pause(isc_nm_t *mgr);
+/*%<
+ * Pause all processing, equivalent to taskmgr exclusive tasks.
+ * It won't return until all workers have been paused.
+ */
+
+void
+isc_nm_resume(isc_nm_t *mgr);
+/*%<
+ * Resume paused processing. It will return immediately after signalling
+ * workers to resume.
+ */
+
+void
+isc_nm_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg);
+/*
+ * Begin (or continue) reading on the socket associated with 'handle', and
+ * update its recv callback to 'cb', which will be called as soon as there
+ * is data to process.
+ */
+
+void
+isc_nm_pauseread(isc_nmhandle_t *handle);
+/*%<
+ * Pause reading on this handle's socket, but remember the callback.
+ *
+ * Requires:
+ * \li 'handle' is a valid netmgr handle.
+ */
+
+void
+isc_nm_cancelread(isc_nmhandle_t *handle);
+/*%<
+ * Cancel reading on a connected socket. Calls the read/recv callback on
+ * active handles with a result code of ISC_R_CANCELED.
+ *
+ * Requires:
+ * \li 'sock' is a valid netmgr socket
+ * \li ...for which a read/recv callback has been defined.
+ */
+
+void
+isc_nm_resumeread(isc_nmhandle_t *handle);
+/*%<
+ * Resume reading on the handle's socket.
+ *
+ * Requires:
+ * \li 'handle' is a valid netmgr handle.
+ * \li ...for a socket with a defined read/recv callback.
+ */
+
+void
+isc_nm_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
+ void *cbarg);
+/*%<
+ * Send the data in 'region' via 'handle'. Afterward, the callback 'cb' is
+ * called with the argument 'cbarg'.
+ *
+ * 'region' is not copied; it has to be allocated beforehand and freed
+ * in 'cb'.
+ */
+
+isc_result_t
+isc_nm_listentcp(isc_nm_t *mgr, isc_sockaddr_t *iface,
+ isc_nm_accept_cb_t accept_cb, void *accept_cbarg,
+ size_t extrahandlesize, int backlog, isc_quota_t *quota,
+ isc_nmsocket_t **sockp);
+/*%<
+ * Start listening for raw messages over the TCP interface 'iface', using
+ * net manager 'mgr'.
+ *
+ * On success, 'sockp' will be updated to contain a new listening TCP
+ * socket.
+ *
+ * When connection is accepted on the socket, 'accept_cb' will be called with
+ * 'accept_cbarg' as its argument. The callback is expected to start a read.
+ *
+ * When handles are allocated for the socket, 'extrasize' additional bytes
+ * will be allocated along with the handle for an associated object.
+ *
+ * If 'quota' is not NULL, then the socket is attached to the specified
+ * quota. This allows us to enforce TCP client quota limits.
+ *
+ */
+
+void
+isc_nm_tcpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
+ isc_nm_cb_t cb, void *cbarg, unsigned int timeout,
+ size_t extrahandlesize);
+/*%<
+ * Create a socket using netmgr 'mgr', bind it to the address 'local',
+ * and connect it to the address 'peer'.
+ *
+ * When the connection is complete or has timed out, call 'cb' with
+ * argument 'cbarg'. Allocate 'extrahandlesize' additional bytes along
+ * with the handle to use for an associated object.
+ *
+ * 'timeout' specifies the timeout interval in milliseconds.
+ *
+ * The connected socket can only be accessed via the handle passed to
+ * 'cb'.
+ */
+
+isc_result_t
+isc_nm_listentcpdns(isc_nm_t *mgr, isc_sockaddr_t *iface,
+ isc_nm_recv_cb_t recv_cb, void *recv_cbarg,
+ isc_nm_accept_cb_t accept_cb, void *accept_cbarg,
+ size_t extrahandlesize, int backlog, isc_quota_t *quota,
+ isc_nmsocket_t **sockp);
+/*%<
+ * Start listening for DNS messages over the TCP interface 'iface', using
+ * net manager 'mgr'.
+ *
+ * On success, 'sockp' will be updated to contain a new listening TCPDNS
+ * socket. This is a wrapper around a raw TCP socket, which sends and
+ * receives DNS messages via that socket. It handles message buffering
+ * and pipelining, and automatically prepends messages with a two-byte
+ * length field.
+ *
+ * When a complete DNS message is received on the socket, 'cb' will be
+ * called with 'cbarg' as its argument.
+ *
+ * When a new TCPDNS connection is accepted, 'accept_cb' will be called
+ * with 'accept_cbarg' as its argument.
+ *
+ * When handles are allocated for the socket, 'extrasize' additional bytes
+ * will be allocated along with the handle for an associated object
+ * (typically ns_client).
+ *
+ * 'quota' is passed to isc_nm_listentcp() when opening the raw TCP socket.
+ */
+
+isc_result_t
+isc_nm_listentlsdns(isc_nm_t *mgr, isc_sockaddr_t *iface,
+ isc_nm_recv_cb_t recv_cb, void *recv_cbarg,
+ isc_nm_accept_cb_t accept_cb, void *accept_cbarg,
+ size_t extrahandlesize, int backlog, isc_quota_t *quota,
+ isc_tlsctx_t *sslctx, isc_nmsocket_t **sockp);
+/*%<
+ * Same as isc_nm_listentcpdns but for an SSL (DoT) socket.
+ */
+
+void
+isc_nm_sequential(isc_nmhandle_t *handle);
+/*%<
+ * Disable pipelining on this connection. Each DNS packet will be only
+ * processed after the previous completes.
+ *
+ * The socket must be unpaused after the query is processed. This is done
+ * the response is sent, or if we're dropping the query, it will be done
+ * when a handle is fully dereferenced by calling the socket's
+ * closehandle_cb callback.
+ *
+ * Note: This can only be run while a message is being processed; if it is
+ * run before any messages are read, no messages will be read.
+ *
+ * Also note: once this has been set, it cannot be reversed for a given
+ * connection.
+ */
+
+void
+isc_nm_settimeouts(isc_nm_t *mgr, uint32_t init, uint32_t idle,
+ uint32_t keepalive, uint32_t advertised);
+/*%<
+ * Sets the initial, idle, and keepalive timeout values (in milliseconds) to use
+ * for TCP connections, and the timeout value to advertise in responses using
+ * the EDNS TCP Keepalive option (which should ordinarily be the same
+ * as 'keepalive'), in milliseconds.
+ *
+ * Requires:
+ * \li 'mgr' is a valid netmgr.
+ */
+
+void
+isc_nm_setnetbuffers(isc_nm_t *mgr, int32_t recv_tcp, int32_t send_tcp,
+ int32_t recv_udp, int32_t send_udp);
+/*%<
+ * If not 0, sets the SO_RCVBUF and SO_SNDBUF socket options for TCP and UDP
+ * respectively.
+ *
+ * Requires:
+ * \li 'mgr' is a valid netmgr.
+ */
+
+bool
+isc_nm_getloadbalancesockets(isc_nm_t *mgr);
+void
+isc_nm_setloadbalancesockets(isc_nm_t *mgr, bool enabled);
+/*%<
+ * Get and set value of load balancing of the sockets.
+ *
+ * Requires:
+ * \li 'mgr' is a valid netmgr.
+ */
+
+void
+isc_nm_gettimeouts(isc_nm_t *mgr, uint32_t *initial, uint32_t *idle,
+ uint32_t *keepalive, uint32_t *advertised);
+/*%<
+ * Gets the initial, idle, keepalive, or advertised timeout values,
+ * in milliseconds.
+ *
+ * Any integer pointer parameter not set to NULL will be updated to
+ * contain the corresponding timeout value.
+ *
+ * Requires:
+ * \li 'mgr' is a valid netmgr.
+ */
+
+void
+isc_nm_maxudp(isc_nm_t *mgr, uint32_t maxudp);
+/*%<
+ * Simulate a broken firewall that blocks UDP messages larger than a given
+ * size.
+ */
+
+void
+isc_nm_setstats(isc_nm_t *mgr, isc_stats_t *stats);
+/*%<
+ * Set a socket statistics counter set 'stats' for 'mgr'.
+ *
+ * Requires:
+ *\li 'mgr' is valid and doesn't have stats already set.
+ *
+ *\li stats is a valid set of statistics counters supporting the
+ * full range of socket-related stats counter numbers.
+ */
+
+isc_result_t
+isc_nm_checkaddr(const isc_sockaddr_t *addr, isc_socktype_t type);
+/*%<
+ * Check whether the specified address is available on the local system
+ * by opening a socket and immediately closing it.
+ *
+ * Requires:
+ *\li 'addr' is not NULL.
+ */
+
+void
+isc_nm_tcpdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
+ isc_nm_cb_t cb, void *cbarg, unsigned int timeout,
+ size_t extrahandlesize);
+void
+isc_nm_tlsdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
+ isc_nm_cb_t cb, void *cbarg, unsigned int timeout,
+ size_t extrahandlesize, isc_tlsctx_t *sslctx,
+ isc_tlsctx_client_session_cache_t *client_sess_cache);
+/*%<
+ * Establish a DNS client connection via a TCP or TLS connection, bound to
+ * the address 'local' and connected to the address 'peer'.
+ *
+ * When the connection is complete or has timed out, call 'cb' with
+ * argument 'cbarg'. Allocate 'extrahandlesize' additional bytes along
+ * with the handle to use for an associated object.
+ *
+ * 'timeout' specifies the timeout interval in milliseconds.
+ *
+ * The connected socket can only be accessed via the handle passed to
+ * 'cb'.
+ */
+
+/*%<
+ * Returns 'true' iff 'handle' is associated with a socket of type
+ * 'isc_nm_tlsdnssocket'.
+ */
+
+bool
+isc_nm_is_http_handle(isc_nmhandle_t *handle);
+/*%<
+ * Returns 'true' iff 'handle' is associated with a socket of type
+ * 'isc_nm_httpsocket'.
+ */
+
+#if HAVE_LIBNGHTTP2
+
+#define ISC_NM_HTTP_DEFAULT_PATH "/dns-query"
+
+isc_result_t
+isc_nm_listentls(isc_nm_t *mgr, isc_sockaddr_t *iface,
+ isc_nm_accept_cb_t accept_cb, void *accept_cbarg,
+ size_t extrahandlesize, int backlog, isc_quota_t *quota,
+ isc_tlsctx_t *sslctx, isc_nmsocket_t **sockp);
+
+void
+isc_nm_tlsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
+ isc_nm_cb_t cb, void *cbarg, isc_tlsctx_t *ctx,
+ isc_tlsctx_client_session_cache_t *client_sess_cache,
+ unsigned int timeout, size_t extrahandlesize);
+
+void
+isc_nm_httpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
+ const char *uri, bool POST, isc_nm_cb_t cb, void *cbarg,
+ isc_tlsctx_t *ctx,
+ isc_tlsctx_client_session_cache_t *client_sess_cache,
+ unsigned int timeout, size_t extrahandlesize);
+
+isc_result_t
+isc_nm_listenhttp(isc_nm_t *mgr, isc_sockaddr_t *iface, int backlog,
+ isc_quota_t *quota, isc_tlsctx_t *ctx,
+ isc_nm_http_endpoints_t *eps, uint32_t max_concurrent_streams,
+ isc_nmsocket_t **sockp);
+
+isc_nm_http_endpoints_t *
+isc_nm_http_endpoints_new(isc_mem_t *mctx);
+/*%<
+ * Create a new, empty HTTP endpoints set object.
+ *
+ * Requires:
+ * \li 'mctx' a valid memory context object.
+ */
+
+isc_result_t
+isc_nm_http_endpoints_add(isc_nm_http_endpoints_t *restrict eps,
+ const char *uri, const isc_nm_recv_cb_t cb,
+ void *cbarg, const size_t extrahandlesize);
+/*%< Adds a new endpoint to the given HTTP endpoints set object.
+ *
+ * NOTE: adding an endpoint is allowed only if the endpoint object has
+ * not been passed to isc_nm_listenhttp() yet.
+ *
+ * Requires:
+ * \li 'eps' is a valid pointer to a valid isc_nm_http_endpoints_t
+ * object;
+ * \li 'uri' is a valid pointer to a string of length > 0;
+ * \li 'cb' is a valid pointer to a read callback function.
+ */
+
+void
+isc_nm_http_endpoints_attach(isc_nm_http_endpoints_t *source,
+ isc_nm_http_endpoints_t **targetp);
+/*%<
+ * Attaches to an HTTP endpoints set object.
+ *
+ * Requires:
+ * \li 'source' is a non-NULL pointer to a valid
+ * isc_nm_http_endpoints_t object;
+ * \li 'target' is a pointer to a pointer, containing NULL.
+ */
+
+void
+isc_nm_http_endpoints_detach(isc_nm_http_endpoints_t **restrict epsp);
+/*%<
+ * Detaches from an HTTP endpoints set object. When reference count
+ * reaches 0, the object get deleted.
+ *
+ * Requires:
+ * \li 'epsp' is a pointer to a pointer to a valid
+ * isc_nm_http_endpoints_t object.
+ */
+
+bool
+isc_nm_http_path_isvalid(const char *path);
+/*%<
+ * Returns 'true' if 'path' matches the format requirements for
+ * the path component of a URI as defined in RFC 3986 section 3.3.
+ */
+
+void
+isc_nm_http_makeuri(const bool https, const isc_sockaddr_t *sa,
+ const char *hostname, const uint16_t http_port,
+ const char *abs_path, char *outbuf,
+ const size_t outbuf_len);
+/*%<
+ * Makes a URI connection string out of na isc_sockaddr_t object 'sa'
+ * or the specified 'hostname' and 'http_port'.
+ *
+ * Requires:
+ * \li 'abs_path' is a valid absolute HTTP path string;
+ * \li 'outbuf' is a valid pointer to a buffer which will get the result;
+ * \li 'outbuf_len' is a size of the result buffer and is greater than zero.
+ */
+
+void
+isc_nm_http_set_endpoints(isc_nmsocket_t *listener,
+ isc_nm_http_endpoints_t *eps);
+/*%<
+ * Asynchronously replace the set of HTTP endpoints (paths) within
+ * the listener socket object. The function is intended to be used
+ * during reconfiguration.
+ *
+ * Requires:
+ * \li 'listener' is a pointer to a valid network manager HTTP listener socket;
+ * \li 'eps' is a valid pointer to an HTTP endpoints set.
+ */
+
+#endif /* HAVE_LIBNGHTTP2 */
+
+void
+isc_nm_bad_request(isc_nmhandle_t *handle);
+/*%<
+ * Perform a transport protocol specific action on the handle in case of a
+ * bad/malformed incoming DNS message.
+ *
+ * NOTE: The function currently is no-op for any protocol except HTTP/2.
+ *
+ * Requires:
+ * \li 'handle' is a valid netmgr handle object.
+ */
+
+isc_result_t
+isc_nm_xfr_checkperm(isc_nmhandle_t *handle);
+/*%<
+ * Check if it is permitted to do a zone transfer over the given handle.
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS Success, permission check passed successfully
+ * \li #ISC_R_DOTALPNERROR No permission because of ALPN tag mismatch
+ * \li #ISC_R_NOPERM No permission because of other restrictions
+ * \li any other result indicates failure (i.e. no permission)
+ *
+ * Requires:
+ * \li 'handle' is a valid connection handle.
+ */
+
+void
+isc_nm_set_maxage(isc_nmhandle_t *handle, const uint32_t ttl);
+/*%<
+ * Set the minimal time to live from the server's response Answer
+ * section as a hint to the underlying transport.
+ *
+ * NOTE: The function currently is no-op for any protocol except HTTP/2.
+ *
+ * Requires:
+ *
+ * \li 'handle' is a valid netmgr handle object associated with an accepted
+ * connection.
+ */
+
+isc_nmsocket_type
+isc_nm_socket_type(const isc_nmhandle_t *handle);
+/*%<
+ * Returns the handle's underlying socket type.
+ *
+ * Requires:
+ * \li 'handle' is a valid netmgr handle object.
+ */
+
+bool
+isc_nm_has_encryption(const isc_nmhandle_t *handle);
+/*%<
+ * Returns 'true' iff the handle's underlying transport does encryption.
+ *
+ * Requires:
+ * \li 'handle' is a valid netmgr handle object.
+ */
+
+const char *
+isc_nm_verify_tls_peer_result_string(const isc_nmhandle_t *handle);
+/*%<
+ * Returns user-readable message describing TLS peer's certificate
+ * validation result. Returns 'NULL' for the transport handles for
+ * which peer verification was not performed.
+ *
+ * Requires:
+ * \li 'handle' is a valid netmgr handle object.
+ */
+
+void
+isc_nm_task_enqueue(isc_nm_t *mgr, isc_task_t *task, int threadid);
+/*%<
+ * Enqueue the 'task' onto the netmgr ievents queue.
+ *
+ * Requires:
+ * \li 'mgr' is a valid netmgr object
+ * \li 'task' is a valid task
+ * \li 'threadid' is either the preferred netmgr tid or -1, in which case
+ * tid will be picked randomly. The threadid is capped (by modulo) to
+ * maximum number of 'workers' as specifed in isc_nm_start()
+ */
+
+void
+isc_nm_work_offload(isc_nm_t *mgr, isc_nm_workcb_t work_cb,
+ isc_nm_after_workcb_t after_work_cb, void *data);
+/*%<
+ * Schedules a job to be handled by the libuv thread pool (see uv_work_t).
+ * The function specified in `work_cb` will be run by a thread in the
+ * thread pool; when complete, the `after_work_cb` function will run.
+ *
+ * Requires:
+ * \li 'mgr' is a valid netmgr object.
+ * \li We are currently running in a network manager thread.
+ */
+
+void
+isc__nm_force_tid(int tid);
+/*%<
+ * Force the thread ID to 'tid'. This is STRICTLY for use in unit
+ * tests and should not be used in any production code.
+ */
+
+void
+isc_nmhandle_setwritetimeout(isc_nmhandle_t *handle, uint64_t write_timeout);
+
+/*
+ * Timer related functions
+ */
+
+typedef struct isc_nm_timer isc_nm_timer_t;
+
+typedef void (*isc_nm_timer_cb)(void *, isc_result_t);
+
+void
+isc_nm_timer_create(isc_nmhandle_t *, isc_nm_timer_cb, void *,
+ isc_nm_timer_t **);
+
+void
+isc_nm_timer_attach(isc_nm_timer_t *, isc_nm_timer_t **);
+
+void
+isc_nm_timer_detach(isc_nm_timer_t **);
+
+void
+isc_nm_timer_start(isc_nm_timer_t *, uint64_t);
+
+void
+isc_nm_timer_stop(isc_nm_timer_t *);
diff --git a/lib/isc/include/isc/netscope.h b/lib/isc/include/isc/netscope.h
new file mode 100644
index 0000000..307864a
--- /dev/null
+++ b/lib/isc/include/isc/netscope.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/netscope.h */
+
+#include <inttypes.h>
+
+ISC_LANG_BEGINDECLS
+
+/*%
+ * Convert a string of an IPv6 scope zone to zone index. If the conversion
+ * succeeds, 'zoneid' will store the index value.
+ *
+ * XXXJT: when a standard interface for this purpose is defined,
+ * we should use it.
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS: conversion succeeds
+ * \li ISC_R_FAILURE: conversion fails
+ */
+isc_result_t
+isc_netscope_pton(int af, char *scopename, void *addr, uint32_t *zoneid);
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/nonce.h b/lib/isc/include/isc/nonce.h
new file mode 100644
index 0000000..b593e41
--- /dev/null
+++ b/lib/isc/include/isc/nonce.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <stdlib.h>
+
+#include <isc/lang.h>
+
+/*! \file isc/nonce.h
+ * \brief Provides a function for generating an arbitrarily long nonce.
+ */
+
+ISC_LANG_BEGINDECLS
+
+void
+isc_nonce_buf(void *buf, size_t buflen);
+/*!<
+ * Fill 'buf', up to 'buflen' bytes, with random data from the
+ * crypto provider's random function.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/offset.h b/lib/isc/include/isc/offset.h
new file mode 100644
index 0000000..2b62e2a
--- /dev/null
+++ b/lib/isc/include/isc/offset.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file
+ * \brief
+ * File offsets are operating-system dependent.
+ */
+#include <limits.h> /* Required for CHAR_BIT. */
+#include <stddef.h> /* For Linux Standard Base. */
+
+#include <sys/types.h>
+
+typedef off_t isc_offset_t;
diff --git a/lib/isc/include/isc/once.h b/lib/isc/include/isc/once.h
new file mode 100644
index 0000000..7d341ca
--- /dev/null
+++ b/lib/isc/include/isc/once.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file */
+
+#include <pthread.h>
+
+#include <isc/result.h>
+
+typedef pthread_once_t isc_once_t;
+
+#define ISC_ONCE_INIT PTHREAD_ONCE_INIT
+
+/* XXX We could do fancier error handling... */
+
+#define isc_once_do(op, f) \
+ ((pthread_once((op), (f)) == 0) ? ISC_R_SUCCESS : ISC_R_UNEXPECTED)
diff --git a/lib/isc/include/isc/os.h b/lib/isc/include/isc/os.h
new file mode 100644
index 0000000..fd7e5cf
--- /dev/null
+++ b/lib/isc/include/isc/os.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/os.h */
+#include <isc/lang.h>
+#include <isc/types.h>
+
+#include <sys/stat.h>
+
+ISC_LANG_BEGINDECLS
+
+/*%<
+ * Hardcode the L1 cacheline size of the CPU to 64, this is checked in
+ * the os.c library constructor if operating system provide means to
+ * get the L1 cacheline size using sysconf().
+ */
+#define ISC_OS_CACHELINE_SIZE 64
+
+unsigned int
+isc_os_ncpus(void);
+/*%<
+ * Return the number of CPUs available on the system, or 1 if this cannot
+ * be determined.
+ */
+
+unsigned long
+isc_os_cacheline(void);
+/*%<
+ * Return L1 cacheline size of the CPU.
+ * If L1 cache is greater than ISC_OS_CACHELINE_SIZE, ensure it is used
+ * instead of constant. Is common on ppc64le architecture.
+ */
+
+mode_t
+isc_os_umask(void);
+/*%<
+ * Return umask of the current process as initialized at the program start
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/parseint.h b/lib/isc/include/isc/parseint.h
new file mode 100644
index 0000000..aa54772
--- /dev/null
+++ b/lib/isc/include/isc/parseint.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+/*! \file isc/parseint.h
+ * \brief Parse integers, in a saner way than atoi() or strtoul() do.
+ */
+
+/***
+ *** Functions
+ ***/
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+isc_parse_uint32(uint32_t *uip, const char *string, int base);
+
+isc_result_t
+isc_parse_uint16(uint16_t *uip, const char *string, int base);
+
+isc_result_t
+isc_parse_uint8(uint8_t *uip, const char *string, int base);
+/*%<
+ * Parse the null-terminated string 'string' containing a base 'base'
+ * integer, storing the result in '*uip'.
+ * The base is interpreted
+ * as in strtoul(). Unlike strtoul(), leading whitespace, minus or
+ * plus signs are not accepted, and all errors (including overflow)
+ * are reported uniformly through the return value.
+ *
+ * Requires:
+ *\li 'string' points to a null-terminated string
+ *\li 0 <= 'base' <= 36
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_BADNUMBER The string is not numeric (in the given base)
+ *\li #ISC_R_RANGE The number is not representable as the requested type.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/pool.h b/lib/isc/include/isc/pool.h
new file mode 100644
index 0000000..90b8934
--- /dev/null
+++ b/lib/isc/include/isc/pool.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*****
+***** Module Info
+*****/
+
+/*! \file isc/pool.h
+ * \brief An object pool is a mechanism for sharing a small pool of
+ * fungible objects among a large number of objects that depend on them.
+ *
+ * This is useful, for example, when it causes performance problems for
+ * large number of zones to share a single memory context or task object,
+ * but it would create a different set of problems for them each to have an
+ * independent task or memory context.
+ */
+
+/***
+ *** Imports.
+ ***/
+
+#include <isc/lang.h>
+#include <isc/mem.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/*****
+***** Types.
+*****/
+
+typedef void (*isc_pooldeallocator_t)(void **object);
+
+typedef isc_result_t (*isc_poolinitializer_t)(void **target, void *arg);
+
+typedef struct isc_pool isc_pool_t;
+
+/*****
+***** Functions.
+*****/
+
+isc_result_t
+isc_pool_create(isc_mem_t *mctx, unsigned int count, isc_pooldeallocator_t free,
+ isc_poolinitializer_t init, void *initarg, isc_pool_t **poolp);
+/*%<
+ * Create a pool of "count" object pointers. If 'free' is not NULL,
+ * it points to a function that will detach the objects. 'init'
+ * points to a function that will initialize the arguments, and
+ * 'arg' to an argument to be passed into that function (for example,
+ * a relevant manager or context object).
+ *
+ * Requires:
+ *
+ *\li 'mctx' is a valid memory context.
+ *
+ *\li init != NULL
+ *
+ *\li poolp != NULL && *poolp == NULL
+ *
+ * Ensures:
+ *
+ *\li On success, '*poolp' points to the new object pool.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *\li #ISC_R_UNEXPECTED
+ */
+
+void *
+isc_pool_get(isc_pool_t *pool);
+/*%<
+ * Returns a pointer to an object from the pool. Currently the object
+ * is chosen from the pool at random. (This may be changed in the future
+ * to something that guaratees balance.)
+ */
+
+int
+isc_pool_count(isc_pool_t *pool);
+/*%<
+ * Returns the number of objcts in the pool 'pool'.
+ */
+
+isc_result_t
+isc_pool_expand(isc_pool_t **sourcep, unsigned int count, isc_pool_t **targetp);
+
+/*%<
+ * If 'size' is larger than the number of objects in the pool pointed to by
+ * 'sourcep', then a new pool of size 'count' is allocated, the existing
+ * objects are copied into it, additional ones created to bring the
+ * total number up to 'count', and the resulting pool is attached to
+ * 'targetp'.
+ *
+ * If 'count' is less than or equal to the number of objects in 'source', then
+ * 'sourcep' is attached to 'targetp' without any other action being taken.
+ *
+ * In either case, 'sourcep' is detached.
+ *
+ * Requires:
+ *
+ * \li 'sourcep' is not NULL and '*source' is not NULL
+ * \li 'targetp' is not NULL and '*source' is NULL
+ *
+ * Ensures:
+ *
+ * \li On success, '*targetp' points to a valid task pool.
+ * \li On success, '*sourcep' points to NULL.
+ *
+ * Returns:
+ *
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOMEMORY
+ */
+
+void
+isc_pool_destroy(isc_pool_t **poolp);
+/*%<
+ * Destroy a task pool. The tasks in the pool are detached but not
+ * shut down.
+ *
+ * Requires:
+ * \li '*poolp' is a valid task pool.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/portset.h b/lib/isc/include/isc/portset.h
new file mode 100644
index 0000000..fa5d51e
--- /dev/null
+++ b/lib/isc/include/isc/portset.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file isc/portset.h
+ * \brief Transport Protocol Port Manipulation Module
+ *
+ * This module provides simple utilities to handle a set of transport protocol
+ * (UDP or TCP) port numbers, e.g., for creating an ACL list. An isc_portset_t
+ * object is an opaque instance of a port set, for which the user can add or
+ * remove a specific port or a range of consecutive ports. This object is
+ * expected to be used as a temporary work space only, and does not protect
+ * simultaneous access from multiple threads. Therefore it must not be stored
+ * in a place that can be accessed from multiple threads.
+ */
+
+#pragma once
+
+/***
+ *** Imports
+ ***/
+
+#include <stdbool.h>
+
+#include <isc/net.h>
+
+/***
+ *** Functions
+ ***/
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+isc_portset_create(isc_mem_t *mctx, isc_portset_t **portsetp);
+/*%<
+ * Create a port set and initialize it as an empty set.
+ *
+ * Requires:
+ *\li 'mctx' to be valid.
+ *\li 'portsetp' to be non NULL and '*portsetp' to be NULL;
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ */
+
+void
+isc_portset_destroy(isc_mem_t *mctx, isc_portset_t **portsetp);
+/*%<
+ * Destroy a port set.
+ *
+ * Requires:
+ *\li 'mctx' to be valid and must be the same context given when the port set
+ * was created.
+ *\li '*portsetp' to be a valid set.
+ */
+
+bool
+isc_portset_isset(isc_portset_t *portset, in_port_t port);
+/*%<
+ * Test whether the given port is stored in the portset.
+ *
+ * Requires:
+ *\li 'portset' to be a valid set.
+ *
+ * Returns
+ * \li #true if the port is found, false otherwise.
+ */
+
+unsigned int
+isc_portset_nports(isc_portset_t *portset);
+/*%<
+ * Provides the number of ports stored in the given portset.
+ *
+ * Requires:
+ *\li 'portset' to be a valid set.
+ *
+ * Returns
+ * \li the number of ports stored in portset.
+ */
+
+void
+isc_portset_add(isc_portset_t *portset, in_port_t port);
+/*%<
+ * Add the given port to the portset. The port may or may not be stored in
+ * the portset.
+ *
+ * Requires:
+ *\li 'portlist' to be valid.
+ */
+
+void
+isc_portset_remove(isc_portset_t *portset, in_port_t port);
+/*%<
+ * Remove the given port to the portset. The port may or may not be stored in
+ * the portset.
+ *
+ * Requires:
+ *\li 'portlist' to be valid.
+ */
+
+void
+isc_portset_addrange(isc_portset_t *portset, in_port_t port_lo,
+ in_port_t port_hi);
+/*%<
+ * Add a subset of [port_lo, port_hi] (inclusive) to the portset. Ports in the
+ * subset may or may not be stored in portset.
+ *
+ * Requires:
+ *\li 'portlist' to be valid.
+ *\li port_lo <= port_hi
+ */
+
+void
+isc_portset_removerange(isc_portset_t *portset, in_port_t port_lo,
+ in_port_t port_hi);
+/*%<
+ * Subtract a subset of [port_lo, port_hi] (inclusive) from the portset. Ports
+ * in the subset may or may not be stored in portset.
+ *
+ * Requires:
+ *\li 'portlist' to be valid.
+ *\li port_lo <= port_hi
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/print.h b/lib/isc/include/isc/print.h
new file mode 100644
index 0000000..d48bf70
--- /dev/null
+++ b/lib/isc/include/isc/print.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/print.h */
+
+/***
+ *** Imports
+ ***/
+
+#include <isc/formatcheck.h> /* Required for ISC_FORMAT_PRINTF() macro. */
+#include <isc/lang.h>
+
+/***
+ *** Functions
+ ***/
+
+#include <stdio.h>
diff --git a/lib/isc/include/isc/quota.h b/lib/isc/include/isc/quota.h
new file mode 100644
index 0000000..2a25fa1
--- /dev/null
+++ b/lib/isc/include/isc/quota.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*****
+***** Module Info
+*****/
+
+/*! \file isc/quota.h
+ *
+ * \brief The isc_quota_t object is a simple helper object for implementing
+ * quotas on things like the number of simultaneous connections to
+ * a server. It keeps track of the amount of quota in use, and
+ * encapsulates the locking necessary to allow multiple tasks to
+ * share a quota.
+ */
+
+/***
+ *** Imports.
+ ***/
+
+#include <isc/atomic.h>
+#include <isc/lang.h>
+#include <isc/magic.h>
+#include <isc/mutex.h>
+#include <isc/types.h>
+
+/*****
+***** Types.
+*****/
+
+ISC_LANG_BEGINDECLS
+
+/*% isc_quota_cb - quota callback structure */
+typedef struct isc_quota_cb isc_quota_cb_t;
+typedef void (*isc_quota_cb_func_t)(isc_quota_t *quota, void *data);
+struct isc_quota_cb {
+ int magic;
+ isc_quota_cb_func_t cb_func;
+ void *data;
+ ISC_LINK(isc_quota_cb_t) link;
+};
+
+/*% isc_quota structure */
+struct isc_quota {
+ int magic;
+ atomic_uint_fast32_t max;
+ atomic_uint_fast32_t used;
+ atomic_uint_fast32_t soft;
+ atomic_uint_fast32_t waiting;
+ isc_mutex_t cblock;
+ ISC_LIST(isc_quota_cb_t) cbs;
+ ISC_LINK(isc_quota_t) link;
+};
+
+void
+isc_quota_init(isc_quota_t *quota, unsigned int max);
+/*%<
+ * Initialize a quota object.
+ */
+
+void
+isc_quota_destroy(isc_quota_t *quota);
+/*%<
+ * Destroy a quota object.
+ */
+
+void
+isc_quota_soft(isc_quota_t *quota, unsigned int soft);
+/*%<
+ * Set a soft quota.
+ */
+
+void
+isc_quota_max(isc_quota_t *quota, unsigned int max);
+/*%<
+ * Re-set a maximum quota.
+ */
+
+unsigned int
+isc_quota_getmax(isc_quota_t *quota);
+/*%<
+ * Get the maximum quota.
+ */
+
+unsigned int
+isc_quota_getsoft(isc_quota_t *quota);
+/*%<
+ * Get the soft quota.
+ */
+
+unsigned int
+isc_quota_getused(isc_quota_t *quota);
+/*%<
+ * Get the current usage of quota.
+ */
+
+isc_result_t
+isc_quota_attach(isc_quota_t *quota, isc_quota_t **p);
+/*%<
+ *
+ * Attempt to reserve one unit of 'quota', and also attaches '*p' to the quota
+ * if successful (ISC_R_SUCCESS or ISC_R_SOFTQUOTA).
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS Success
+ * \li #ISC_R_SOFTQUOTA Success soft quota reached
+ * \li #ISC_R_QUOTA Quota is full
+ */
+
+isc_result_t
+isc_quota_attach_cb(isc_quota_t *quota, isc_quota_t **p, isc_quota_cb_t *cb);
+/*%<
+ *
+ * Like isc_quota_attach(), but if there's no quota left then cb->cb_func will
+ * be called when we are attached to quota.
+ *
+ * Note: It's the caller's responsibility to make sure that we don't end up
+ * with a huge number of callbacks waiting, making it easy to create a
+ * resource exhaustion attack. For example, in the case of TCP listening,
+ * we simply don't accept new connections when the quota is exceeded, so
+ * the number of callbacks waiting in the queue will be limited by the
+ * listen() backlog.
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS Success
+ * \li #ISC_R_SOFTQUOTA Success soft quota reached
+ * \li #ISC_R_QUOTA Quota is full
+ */
+
+void
+isc_quota_cb_init(isc_quota_cb_t *cb, isc_quota_cb_func_t cb_func, void *data);
+/*%<
+ * Initialize isc_quota_cb_t - setup the list, set the callback and data.
+ */
+
+void
+isc_quota_detach(isc_quota_t **p);
+/*%<
+ * Release one unit of quota, and also detaches '*p' from the quota.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/radix.h b/lib/isc/include/isc/radix.h
new file mode 100644
index 0000000..9a91118
--- /dev/null
+++ b/lib/isc/include/isc/radix.h
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+#include <string.h>
+
+#include <isc/magic.h>
+#include <isc/mutex.h>
+#include <isc/net.h>
+#include <isc/refcount.h>
+#include <isc/types.h>
+
+#define NETADDR_TO_PREFIX_T(na, pt, bits) \
+ do { \
+ const void *p = na; \
+ memset(&(pt), 0, sizeof(pt)); \
+ if (p != NULL) { \
+ (pt).family = (na)->family; \
+ (pt).bitlen = (bits); \
+ if ((pt).family == AF_INET6) { \
+ memmove(&(pt).add.sin6, &(na)->type.in6, \
+ ((bits) + 7) / 8); \
+ } else \
+ memmove(&(pt).add.sin, &(na)->type.in, \
+ ((bits) + 7) / 8); \
+ } else { \
+ (pt).family = AF_UNSPEC; \
+ (pt).bitlen = 0; \
+ } \
+ isc_refcount_init(&(pt).refcount, 0); \
+ } while (0)
+
+typedef struct isc_prefix {
+ isc_mem_t *mctx;
+ unsigned int family; /* AF_INET | AF_INET6, or AF_UNSPEC for
+ * "any" */
+ unsigned int bitlen; /* 0 for "any" */
+ isc_refcount_t refcount;
+ union {
+ struct in_addr sin;
+ struct in6_addr sin6;
+ } add;
+} isc_prefix_t;
+
+typedef void (*isc_radix_destroyfunc_t)(void *);
+typedef void (*isc_radix_processfunc_t)(isc_prefix_t *, void **);
+
+#define isc_prefix_tochar(prefix) ((char *)&(prefix)->add.sin)
+#define isc_prefix_touchar(prefix) ((u_char *)&(prefix)->add.sin)
+
+/*
+ * We need "first match" when we search the radix tree to preserve
+ * compatibility with the existing ACL implementation. Radix trees
+ * naturally lend themselves to "best match". In order to get "first match"
+ * behavior, we keep track of the order in which entries are added to the
+ * tree--and when a search is made, we find all matching entries, and
+ * return the one that was added first.
+ *
+ * An IPv4 prefix and an IPv6 prefix may share a radix tree node if they
+ * have the same length and bit pattern (e.g., 127/8 and 7f::/8). To
+ * disambiguate between them, node_num and data are two-element arrays:
+ *
+ * - node_num[0] and data[0] are used for IPv4 client addresses
+ * - node_num[1] and data[1] are used for IPv6 client addresses
+ *
+ * A prefix of 0/0 (aka "any" or "none"), is always stored as IPv4,
+ * but matches all IPv6 addresses too.
+ */
+
+#define RADIX_V4 0
+#define RADIX_V6 1
+#define RADIX_FAMILIES 2
+
+#define ISC_RADIX_FAMILY(p) (((p)->family == AF_INET6) ? RADIX_V6 : RADIX_V4)
+
+typedef struct isc_radix_node {
+ isc_mem_t *mctx;
+ uint32_t bit; /* bit length of the prefix */
+ isc_prefix_t *prefix; /* who we are in radix tree */
+ struct isc_radix_node *l, *r; /* left and right children */
+ struct isc_radix_node *parent; /* may be used */
+ void *data[RADIX_FAMILIES]; /* pointers to IPv4
+ * and IPV6 data */
+ int node_num[RADIX_FAMILIES]; /* which node
+ * this was in
+ * the tree,
+ * or -1 for glue
+ * nodes */
+} isc_radix_node_t;
+
+#define RADIX_TREE_MAGIC ISC_MAGIC('R', 'd', 'x', 'T');
+#define RADIX_TREE_VALID(a) ISC_MAGIC_VALID(a, RADIX_TREE_MAGIC);
+
+typedef struct isc_radix_tree {
+ unsigned int magic;
+ isc_mem_t *mctx;
+ isc_radix_node_t *head;
+ uint32_t maxbits; /* for IP, 32 bit addresses */
+ int num_active_node; /* for debugging purposes */
+ int num_added_node; /* total number of nodes */
+} isc_radix_tree_t;
+
+isc_result_t
+isc_radix_search(isc_radix_tree_t *radix, isc_radix_node_t **target,
+ isc_prefix_t *prefix);
+/*%<
+ * Search 'radix' for the best match to 'prefix'.
+ * Return the node found in '*target'.
+ *
+ * Requires:
+ * \li 'radix' to be valid.
+ * \li 'target' is not NULL and "*target" is NULL.
+ * \li 'prefix' to be valid.
+ *
+ * Returns:
+ * \li ISC_R_NOTFOUND
+ * \li ISC_R_SUCCESS
+ */
+
+isc_result_t
+isc_radix_insert(isc_radix_tree_t *radix, isc_radix_node_t **target,
+ isc_radix_node_t *source, isc_prefix_t *prefix);
+/*%<
+ * Insert 'source' or 'prefix' into the radix tree 'radix'.
+ * Return the node added in 'target'.
+ *
+ * Requires:
+ * \li 'radix' to be valid.
+ * \li 'target' is not NULL and "*target" is NULL.
+ * \li 'prefix' to be valid or 'source' to be non NULL and contain
+ * a valid prefix.
+ *
+ * Returns:
+ * \li ISC_R_NOMEMORY
+ * \li ISC_R_SUCCESS
+ */
+
+void
+isc_radix_remove(isc_radix_tree_t *radix, isc_radix_node_t *node);
+/*%<
+ * Remove the node 'node' from the radix tree 'radix'.
+ *
+ * Requires:
+ * \li 'radix' to be valid.
+ * \li 'node' to be valid.
+ */
+
+isc_result_t
+isc_radix_create(isc_mem_t *mctx, isc_radix_tree_t **target, int maxbits);
+/*%<
+ * Create a radix tree with a maximum depth of 'maxbits';
+ *
+ * Requires:
+ * \li 'mctx' to be valid.
+ * \li 'target' to be non NULL and '*target' to be NULL.
+ * \li 'maxbits' to be less than or equal to RADIX_MAXBITS.
+ *
+ * Returns:
+ * \li ISC_R_NOMEMORY
+ * \li ISC_R_SUCCESS
+ */
+
+void
+isc_radix_destroy(isc_radix_tree_t *radix, isc_radix_destroyfunc_t func);
+/*%<
+ * Destroy a radix tree optionally calling 'func' to clean up node data.
+ *
+ * Requires:
+ * \li 'radix' to be valid.
+ */
+
+void
+isc_radix_process(isc_radix_tree_t *radix, isc_radix_processfunc_t func);
+/*%<
+ * Walk a radix tree calling 'func' to process node data.
+ *
+ * Requires:
+ * \li 'radix' to be valid.
+ * \li 'func' to point to a function.
+ */
+
+#define RADIX_MAXBITS 128
+#define RADIX_NBIT(x) (0x80 >> ((x)&0x7f))
+#define RADIX_NBYTE(x) ((x) >> 3)
+
+#define RADIX_WALK(Xhead, Xnode) \
+ do { \
+ isc_radix_node_t *Xstack[RADIX_MAXBITS + 1]; \
+ isc_radix_node_t **Xsp = Xstack; \
+ isc_radix_node_t *Xrn = (Xhead); \
+ while ((Xnode = Xrn)) { \
+ if (Xnode->prefix)
+
+#define RADIX_WALK_END \
+ if (Xrn->l) { \
+ if (Xrn->r) { \
+ *Xsp++ = Xrn->r; \
+ } \
+ Xrn = Xrn->l; \
+ } else if (Xrn->r) { \
+ Xrn = Xrn->r; \
+ } else if (Xsp != Xstack) { \
+ Xrn = *(--Xsp); \
+ } else { \
+ Xrn = (isc_radix_node_t *)0; \
+ } \
+ } \
+ } \
+ while (0)
diff --git a/lib/isc/include/isc/random.h b/lib/isc/include/isc/random.h
new file mode 100644
index 0000000..1e30d0c
--- /dev/null
+++ b/lib/isc/include/isc/random.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+/*! \file isc/random.h
+ * \brief Implements wrapper around a non-cryptographically secure
+ * pseudo-random number generator.
+ *
+ */
+
+ISC_LANG_BEGINDECLS
+
+uint8_t
+isc_random8(void);
+/*!<
+ * \brief Returns a single 8-bit random value.
+ */
+
+uint16_t
+isc_random16(void);
+/*!<
+ * \brief Returns a single 16-bit random value.
+ */
+
+uint32_t
+isc_random32(void);
+/*!<
+ * \brief Returns a single 32-bit random value.
+ */
+
+void
+isc_random_buf(void *buf, size_t buflen);
+/*!<
+ * \brief Fills the region buf of length buflen with random data.
+ */
+
+uint32_t
+isc_random_uniform(uint32_t upper_bound);
+/*!<
+ * \brief Will return a single 32-bit value, uniformly distributed but
+ * less than upper_bound. This is recommended over
+ * constructions like ``isc_random() % upper_bound'' as it
+ * avoids "modulo bias" when the upper bound is not a power of
+ * two. In the worst case, this function may require multiple
+ * iterations to ensure uniformity.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/ratelimiter.h b/lib/isc/include/isc/ratelimiter.h
new file mode 100644
index 0000000..45ec447
--- /dev/null
+++ b/lib/isc/include/isc/ratelimiter.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*****
+***** Module Info
+*****/
+
+/*! \file isc/ratelimiter.h
+ * \brief A rate limiter is a mechanism for dispatching events at a limited
+ * rate. This is intended to be used when sending zone maintenance
+ * SOA queries, NOTIFY messages, etc.
+ */
+
+/***
+ *** Imports.
+ ***/
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/*****
+***** Functions.
+*****/
+
+isc_result_t
+isc_ratelimiter_create(isc_mem_t *mctx, isc_timermgr_t *timermgr,
+ isc_task_t *task, isc_ratelimiter_t **ratelimiterp);
+/*%<
+ * Create a rate limiter. The execution interval is initially undefined.
+ */
+
+isc_result_t
+isc_ratelimiter_setinterval(isc_ratelimiter_t *rl, isc_interval_t *interval);
+/*!<
+ * Set the minimum interval between event executions.
+ * The interval value is copied, so the caller need not preserve it.
+ *
+ * Requires:
+ * '*interval' is a nonzero interval.
+ */
+
+void
+isc_ratelimiter_setpertic(isc_ratelimiter_t *rl, uint32_t perint);
+/*%<
+ * Set the number of events processed per interval timer tick.
+ * If 'perint' is zero it is treated as 1.
+ */
+
+void
+isc_ratelimiter_setpushpop(isc_ratelimiter_t *rl, bool pushpop);
+/*%<
+ * Set / clear the ratelimiter to from push pop mode rather
+ * first in - first out mode (default).
+ */
+
+isc_result_t
+isc_ratelimiter_enqueue(isc_ratelimiter_t *rl, isc_task_t *task,
+ isc_event_t **eventp);
+/*%<
+ * Queue an event for rate-limited execution.
+ *
+ * This is similar
+ * to doing an isc_task_send() to the 'task', except that the
+ * execution may be delayed to achieve the desired rate of
+ * execution.
+ *
+ * '(*eventp)->ev_sender' is used to hold the task. The caller
+ * must ensure that the task exists until the event is delivered.
+ *
+ * Requires:
+ *\li An interval has been set by calling
+ * isc_ratelimiter_setinterval().
+ *
+ *\li 'task' to be non NULL.
+ *\li '(*eventp)->ev_sender' to be NULL.
+ */
+
+isc_result_t
+isc_ratelimiter_dequeue(isc_ratelimiter_t *rl, isc_event_t *event);
+/*
+ * Dequeue a event off the ratelimiter queue.
+ *
+ * Returns:
+ * \li ISC_R_NOTFOUND if the event is no longer linked to the rate limiter.
+ * \li ISC_R_SUCCESS
+ */
+
+void
+isc_ratelimiter_shutdown(isc_ratelimiter_t *ratelimiter);
+/*%<
+ * Shut down a rate limiter.
+ *
+ * Ensures:
+ *\li All events that have not yet been
+ * dispatched to the task are dispatched immediately with
+ * the #ISC_EVENTATTR_CANCELED bit set in ev_attributes.
+ *
+ *\li Further attempts to enqueue events will fail with
+ * #ISC_R_SHUTTINGDOWN.
+ *
+ *\li The rate limiter is no longer attached to its task.
+ */
+
+void
+isc_ratelimiter_attach(isc_ratelimiter_t *source, isc_ratelimiter_t **target);
+/*%<
+ * Attach to a rate limiter.
+ */
+
+void
+isc_ratelimiter_detach(isc_ratelimiter_t **ratelimiterp);
+/*%<
+ * Detach from a rate limiter.
+ */
+
+isc_result_t
+isc_ratelimiter_stall(isc_ratelimiter_t *rl);
+/*%<
+ * Stall event processing.
+ */
+
+isc_result_t
+isc_ratelimiter_release(isc_ratelimiter_t *rl);
+/*%<
+ * Release a stalled rate limiter.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/refcount.h b/lib/isc/include/isc/refcount.h
new file mode 100644
index 0000000..6b1f7cd
--- /dev/null
+++ b/lib/isc/include/isc/refcount.h
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+
+#include <isc/assertions.h>
+#include <isc/atomic.h>
+#include <isc/error.h>
+#include <isc/lang.h>
+#include <isc/mutex.h>
+#include <isc/types.h>
+
+/*! \file isc/refcount.h
+ * \brief Implements a locked reference counter.
+ *
+ * These macros uses C11(-like) atomic functions to implement reference
+ * counting. The isc_refcount_t type must not be accessed directly.
+ */
+
+ISC_LANG_BEGINDECLS
+
+typedef atomic_uint_fast32_t isc_refcount_t;
+
+/** \def isc_refcount_init(ref, n)
+ * \brief Initialize the reference counter.
+ * \param[in] ref pointer to reference counter.
+ * \param[in] n an initial number of references.
+ * \return nothing.
+ *
+ * \warning No memory barrier are being imposed here.
+ */
+#define isc_refcount_init(target, value) atomic_init(target, value)
+
+/** \def isc_refcount_current(ref)
+ * \brief Returns current number of references.
+ * \param[in] ref pointer to reference counter.
+ * \returns current value of reference counter.
+ *
+ * Undo implicit promotion to 64 bits in our Windows implementation of
+ * atomic_load_explicit() by casting to uint_fast32_t.
+ */
+
+#define isc_refcount_current(target) (uint_fast32_t) atomic_load_acquire(target)
+
+/** \def isc_refcount_destroy(ref)
+ * \brief a destructor that makes sure that all references were cleared.
+ * \param[in] ref pointer to reference counter.
+ * \returns nothing.
+ */
+#define isc_refcount_destroy(target) \
+ ISC_REQUIRE(isc_refcount_current(target) == 0)
+
+/** \def isc_refcount_increment0(ref)
+ * \brief increases reference counter by 1.
+ * \param[in] ref pointer to reference counter.
+ * \returns previous value of reference counter.
+ */
+#if _MSC_VER
+static inline uint_fast32_t
+isc_refcount_increment0(isc_refcount_t *target) {
+ uint_fast32_t __v;
+ __v = (uint_fast32_t)atomic_fetch_add_relaxed(target, 1);
+ INSIST(__v < UINT32_MAX);
+ return (__v);
+}
+#else /* _MSC_VER */
+#define isc_refcount_increment0(target) \
+ ({ \
+ uint_fast32_t __v; \
+ __v = atomic_fetch_add_relaxed(target, 1); \
+ INSIST(__v < UINT32_MAX); \
+ __v; \
+ })
+#endif /* _MSC_VER */
+
+/** \def isc_refcount_increment(ref)
+ * \brief increases reference counter by 1.
+ * \param[in] ref pointer to reference counter.
+ * \returns previous value of reference counter.
+ */
+#if _MSC_VER
+static inline uint_fast32_t
+isc_refcount_increment(isc_refcount_t *target) {
+ uint_fast32_t __v;
+ __v = (uint_fast32_t)atomic_fetch_add_relaxed(target, 1);
+ INSIST(__v > 0 && __v < UINT32_MAX);
+ return (__v);
+}
+#else /* _MSC_VER */
+#define isc_refcount_increment(target) \
+ ({ \
+ uint_fast32_t __v; \
+ __v = atomic_fetch_add_relaxed(target, 1); \
+ INSIST(__v > 0 && __v < UINT32_MAX); \
+ __v; \
+ })
+#endif /* _MSC_VER */
+
+/** \def isc_refcount_decrement(ref)
+ * \brief decreases reference counter by 1.
+ * \param[in] ref pointer to reference counter.
+ * \returns previous value of reference counter.
+ */
+#if _MSC_VER
+static inline uint_fast32_t
+isc_refcount_decrement(isc_refcount_t *target) {
+ uint_fast32_t __v;
+ __v = (uint_fast32_t)atomic_fetch_sub_acq_rel(target, 1);
+ INSIST(__v > 0);
+ return (__v);
+}
+#else /* _MSC_VER */
+#define isc_refcount_decrement(target) \
+ ({ \
+ uint_fast32_t __v; \
+ __v = atomic_fetch_sub_acq_rel(target, 1); \
+ INSIST(__v > 0); \
+ __v; \
+ })
+#endif /* _MSC_VER */
+
+#define isc_refcount_decrementz(target) \
+ do { \
+ uint_fast32_t _refs = isc_refcount_decrement(target); \
+ ISC_INSIST(_refs == 1); \
+ } while (0)
+
+#define isc_refcount_decrement1(target) \
+ do { \
+ uint_fast32_t _refs = isc_refcount_decrement(target); \
+ ISC_INSIST(_refs > 1); \
+ } while (0)
+
+#define isc_refcount_decrement0(target) \
+ do { \
+ uint_fast32_t _refs = isc_refcount_decrement(target); \
+ ISC_INSIST(_refs > 0); \
+ } while (0)
+
+#define ISC_REFCOUNT_TRACE_DECL(name) \
+ name##_t *name##__ref(name##_t *ptr, const char *func, \
+ const char *file, unsigned int line); \
+ void name##__unref(name##_t *ptr, const char *func, const char *file, \
+ unsigned int line); \
+ void name##__attach(name##_t *ptr, name##_t **ptrp, const char *func, \
+ const char *file, unsigned int line); \
+ void name##__detach(name##_t **ptrp, const char *func, \
+ const char *file, unsigned int line)
+
+#define ISC_REFCOUNT_TRACE_IMPL(name, destroy) \
+ name##_t *name##__ref(name##_t *ptr, const char *func, \
+ const char *file, unsigned int line) { \
+ REQUIRE(ptr != NULL); \
+ uint_fast32_t refs = \
+ isc_refcount_increment(&ptr->references) + 1; \
+ fprintf(stderr, \
+ "%s:%s:%s:%u:%p->references = %" PRIuFAST32 "\n", \
+ __func__, func, file, line, ptr, refs); \
+ return (ptr); \
+ } \
+ \
+ void name##__unref(name##_t *ptr, const char *func, const char *file, \
+ unsigned int line) { \
+ REQUIRE(ptr != NULL); \
+ uint_fast32_t refs = \
+ isc_refcount_decrement(&ptr->references) - 1; \
+ if (refs == 0) { \
+ destroy(ptr); \
+ } \
+ fprintf(stderr, \
+ "%s:%s:%s:%u:%p->references = %" PRIuFAST32 "\n", \
+ __func__, func, file, line, ptr, refs); \
+ } \
+ void name##__attach(name##_t *ptr, name##_t **ptrp, const char *func, \
+ const char *file, unsigned int line) { \
+ REQUIRE(ptrp != NULL && *ptrp == NULL); \
+ uint_fast32_t refs = \
+ isc_refcount_increment(&ptr->references) + 1; \
+ fprintf(stderr, \
+ "%s:%s:%s:%u:%p->references = %" PRIuFAST32 "\n", \
+ __func__, func, file, line, ptr, refs); \
+ *ptrp = ptr; \
+ } \
+ \
+ void name##__detach(name##_t **ptrp, const char *func, \
+ const char *file, unsigned int line) { \
+ REQUIRE(ptrp != NULL && *ptrp != NULL); \
+ name##_t *ptr = *ptrp; \
+ *ptrp = NULL; \
+ uint_fast32_t refs = \
+ isc_refcount_decrement(&ptr->references) - 1; \
+ if (refs == 0) { \
+ destroy(ptr); \
+ } \
+ fprintf(stderr, \
+ "%s:%s:%s:%u:%p->references = %" PRIuFAST32 "\n", \
+ __func__, func, file, line, ptr, refs); \
+ }
+
+#define ISC_REFCOUNT_DECL(name) \
+ name##_t *name##_ref(name##_t *ptr); \
+ void name##_unref(name##_t *ptr); \
+ void name##_attach(name##_t *ptr, name##_t **ptrp); \
+ void name##_detach(name##_t **ptrp)
+
+#define ISC_REFCOUNT_IMPL(name, destroy) \
+ name##_t *name##_ref(name##_t *ptr) { \
+ REQUIRE(ptr != NULL); \
+ isc_refcount_increment(&ptr->references); \
+ return (ptr); \
+ } \
+ \
+ void name##_unref(name##_t *ptr) { \
+ REQUIRE(ptr != NULL); \
+ if (isc_refcount_decrement(&ptr->references) == 1) { \
+ destroy(ptr); \
+ } \
+ } \
+ void name##_attach(name##_t *ptr, name##_t **ptrp) { \
+ REQUIRE(ptrp != NULL && *ptrp == NULL); \
+ name##_ref(ptr); \
+ *ptrp = ptr; \
+ } \
+ \
+ void name##_detach(name##_t **ptrp) { \
+ REQUIRE(ptrp != NULL && *ptrp != NULL); \
+ name##_t *ptr = *ptrp; \
+ *ptrp = NULL; \
+ name##_unref(ptr); \
+ }
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/regex.h b/lib/isc/include/isc/regex.h
new file mode 100644
index 0000000..989d7b1
--- /dev/null
+++ b/lib/isc/include/isc/regex.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/regex.h */
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+int
+isc_regex_validate(const char *expression);
+/*%<
+ * Check a regular expression for syntactic correctness.
+ *
+ * Returns:
+ *\li -1 on error.
+ *\li the number of groups in the expression.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/region.h b/lib/isc/include/isc/region.h
new file mode 100644
index 0000000..6f775cf
--- /dev/null
+++ b/lib/isc/include/isc/region.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/region.h */
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+struct isc_region {
+ unsigned char *base;
+ unsigned int length;
+};
+
+struct isc_textregion {
+ char *base;
+ unsigned int length;
+};
+
+/* XXXDCL questionable ... bears discussion. we have been putting off
+ * discussing the region api.
+ */
+struct isc_constregion {
+ const void *base;
+ unsigned int length;
+};
+
+struct isc_consttextregion {
+ const char *base;
+ unsigned int length;
+};
+
+/*@{*/
+/*!
+ * The region structure is not opaque, and is usually directly manipulated.
+ * Some macros are defined below for convenience.
+ */
+
+#define isc_region_consume(r, l) \
+ do { \
+ isc_region_t *_r = (r); \
+ unsigned int _l = (l); \
+ INSIST(_r->length >= _l); \
+ _r->base += _l; \
+ _r->length -= _l; \
+ } while (0)
+
+#define isc_textregion_consume(r, l) \
+ do { \
+ isc_textregion_t *_r = (r); \
+ unsigned int _l = (l); \
+ INSIST(_r->length >= _l); \
+ _r->base += _l; \
+ _r->length -= _l; \
+ } while (0)
+
+#define isc_constregion_consume(r, l) \
+ do { \
+ isc_constregion_t *_r = (r); \
+ unsigned int _l = (l); \
+ INSIST(_r->length >= _l); \
+ _r->base += _l; \
+ _r->length -= _l; \
+ } while (0)
+/*@}*/
+
+ISC_LANG_BEGINDECLS
+
+int
+isc_region_compare(isc_region_t *r1, isc_region_t *r2);
+/*%<
+ * Compares the contents of two regions
+ *
+ * Requires:
+ *\li 'r1' is a valid region
+ *\li 'r2' is a valid region
+ *
+ * Returns:
+ *\li < 0 if r1 is lexicographically less than r2
+ *\li = 0 if r1 is lexicographically identical to r2
+ *\li > 0 if r1 is lexicographically greater than r2
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/resource.h b/lib/isc/include/isc/resource.h
new file mode 100644
index 0000000..dc4b2b1
--- /dev/null
+++ b/lib/isc/include/isc/resource.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/resource.h */
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+#define ISC_RESOURCE_UNLIMITED ((isc_resourcevalue_t)UINT64_MAX)
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+isc_resource_setlimit(isc_resource_t resource, isc_resourcevalue_t value);
+/*%<
+ * Set the maximum limit for a system resource.
+ *
+ * Notes:
+ *\li If 'value' exceeds the maximum possible on the operating system,
+ * it is silently limited to that maximum -- or to "infinity", if
+ * the operating system has that concept. #ISC_RESOURCE_UNLIMITED
+ * can be used to explicitly ask for the maximum.
+ *
+ * Requires:
+ *\li 'resource' is a valid member of the isc_resource_t enumeration.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS Success.
+ *\li #ISC_R_NOTIMPLEMENTED 'resource' is not a type known by the OS.
+ *\li #ISC_R_NOPERM The calling process did not have adequate permission
+ * to change the resource limit.
+ */
+
+isc_result_t
+isc_resource_getlimit(isc_resource_t resource, isc_resourcevalue_t *value);
+/*%<
+ * Get the maximum limit for a system resource.
+ *
+ * Notes:
+ *\li 'value' is set to the maximum limit.
+ *
+ *\li #ISC_RESOURCE_UNLIMITED is the maximum value of isc_resourcevalue_t.
+ *
+ *\li On many (all?) Unix systems, RLIM_INFINITY is a valid value that is
+ * significantly less than #ISC_RESOURCE_UNLIMITED, but which in practice
+ * behaves the same.
+ *
+ *\li The current ISC libdns configuration file parser assigns a value
+ * of UINT32_MAX for a size_spec of "unlimited" and ISC_UNIT32_MAX - 1
+ * for "default", the latter of which is supposed to represent "the
+ * limit that was in force when the server started". Since these are
+ * valid values in the middle of the range of isc_resourcevalue_t,
+ * there is the possibility for confusion over what exactly those
+ * particular values are supposed to represent in a particular context --
+ * discrete integral values or generalized concepts.
+ *
+ * Requires:
+ *\li 'resource' is a valid member of the isc_resource_t enumeration.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS Success.
+ *\li #ISC_R_NOTIMPLEMENTED 'resource' is not a type known by the OS.
+ */
+
+isc_result_t
+isc_resource_getcurlimit(isc_resource_t resource, isc_resourcevalue_t *value);
+/*%<
+ * Same as isc_resource_getlimit(), but returns the current (soft) limit.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS Success.
+ *\li #ISC_R_NOTIMPLEMENTED 'resource' is not a type known by the OS.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/result.h b/lib/isc/include/isc/result.h
new file mode 100644
index 0000000..a43772e
--- /dev/null
+++ b/lib/isc/include/isc/result.h
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/result.h */
+
+#include <inttypes.h>
+
+#include <isc/lang.h>
+
+typedef enum isc_result {
+ ISC_R_SUCCESS, /*%< success */
+ ISC_R_NOMEMORY, /*%< out of memory */
+ ISC_R_TIMEDOUT, /*%< timed out */
+ ISC_R_NOTHREADS, /*%< no available threads */
+ ISC_R_ADDRNOTAVAIL, /*%< address not available */
+ ISC_R_ADDRINUSE, /*%< address in use */
+ ISC_R_NOPERM, /*%< permission denied */
+ ISC_R_NOCONN, /*%< no pending connections */
+ ISC_R_NETUNREACH, /*%< network unreachable */
+ ISC_R_HOSTUNREACH, /*%< host unreachable */
+ ISC_R_NETDOWN, /*%< network down */
+ ISC_R_HOSTDOWN, /*%< host down */
+ ISC_R_CONNREFUSED, /*%< connection refused */
+ ISC_R_NORESOURCES, /*%< not enough free resources */
+ ISC_R_EOF, /*%< end of file */
+ ISC_R_BOUND, /*%< socket already bound */
+ ISC_R_RELOAD, /*%< reload */
+ ISC_R_SUSPEND = ISC_R_RELOAD, /*%< alias of 'reload' */
+ ISC_R_LOCKBUSY, /*%< lock busy */
+ ISC_R_EXISTS, /*%< already exists */
+ ISC_R_NOSPACE, /*%< ran out of space */
+ ISC_R_CANCELED, /*%< operation canceled */
+ ISC_R_NOTBOUND, /*%< socket is not bound */
+ ISC_R_SHUTTINGDOWN, /*%< shutting down */
+ ISC_R_NOTFOUND, /*%< not found */
+ ISC_R_UNEXPECTEDEND, /*%< unexpected end of input */
+ ISC_R_FAILURE, /*%< generic failure */
+ ISC_R_IOERROR, /*%< I/O error */
+ ISC_R_NOTIMPLEMENTED, /*%< not implemented */
+ ISC_R_UNBALANCED, /*%< unbalanced parentheses */
+ ISC_R_NOMORE, /*%< no more */
+ ISC_R_INVALIDFILE, /*%< invalid file */
+ ISC_R_BADBASE64, /*%< bad base64 encoding */
+ ISC_R_UNEXPECTEDTOKEN, /*%< unexpected token */
+ ISC_R_QUOTA, /*%< quota reached */
+ ISC_R_UNEXPECTED, /*%< unexpected error */
+ ISC_R_ALREADYRUNNING, /*%< already running */
+ ISC_R_IGNORE, /*%< ignore */
+ ISC_R_MASKNONCONTIG, /*%< addr mask not contiguous */
+ ISC_R_FILENOTFOUND, /*%< file not found */
+ ISC_R_FILEEXISTS, /*%< file already exists */
+ ISC_R_NOTCONNECTED, /*%< socket is not connected */
+ ISC_R_RANGE, /*%< out of range */
+ ISC_R_NOENTROPY, /*%< out of entropy */
+ ISC_R_MULTICAST, /*%< invalid use of multicast */
+ ISC_R_NOTFILE, /*%< not a file */
+ ISC_R_NOTDIRECTORY, /*%< not a directory */
+ ISC_R_EMPTY, /*%< queue is empty */
+ ISC_R_FAMILYMISMATCH, /*%< address family mismatch */
+ ISC_R_FAMILYNOSUPPORT, /*%< AF not supported */
+ ISC_R_BADHEX, /*%< bad hex encoding */
+ ISC_R_TOOMANYOPENFILES, /*%< too many open files */
+ ISC_R_NOTBLOCKING, /*%< not blocking */
+ ISC_R_UNBALANCEDQUOTES, /*%< unbalanced quotes */
+ ISC_R_INPROGRESS, /*%< operation in progress */
+ ISC_R_CONNECTIONRESET, /*%< connection reset */
+ ISC_R_SOFTQUOTA, /*%< soft quota reached */
+ ISC_R_BADNUMBER, /*%< not a valid number */
+ ISC_R_DISABLED, /*%< disabled */
+ ISC_R_MAXSIZE, /*%< max size */
+ ISC_R_BADADDRESSFORM, /*%< invalid address format */
+ ISC_R_BADBASE32, /*%< bad base32 encoding */
+ ISC_R_UNSET, /*%< unset */
+ ISC_R_MULTIPLE, /*%< multiple */
+ ISC_R_WOULDBLOCK, /*%< would block */
+ ISC_R_COMPLETE, /*%< complete */
+ ISC_R_CRYPTOFAILURE, /*%< cryptography library failure */
+ ISC_R_DISCQUOTA, /*%< disc quota */
+ ISC_R_DISCFULL, /*%< disc full */
+ ISC_R_DEFAULT, /*%< default */
+ ISC_R_IPV4PREFIX, /*%< IPv4 prefix */
+ ISC_R_TLSERROR, /*%< TLS error */
+ ISC_R_TLSBADPEERCERT, /*%< TLS peer certificate verification failed */
+ ISC_R_HTTP2ALPNERROR, /*%< ALPN for HTTP/2 failed */
+ ISC_R_DOTALPNERROR, /*%< ALPN for DoT failed */
+ ISC_R_INVALIDPROTO, /*%< invalid protocol */
+
+ DNS_R_LABELTOOLONG,
+ DNS_R_BADESCAPE,
+ DNS_R_EMPTYLABEL,
+ DNS_R_BADDOTTEDQUAD,
+ DNS_R_INVALIDNS,
+ DNS_R_UNKNOWN,
+ DNS_R_BADLABELTYPE,
+ DNS_R_BADPOINTER,
+ DNS_R_TOOMANYHOPS,
+ DNS_R_DISALLOWED,
+ DNS_R_EXTRATOKEN,
+ DNS_R_EXTRADATA,
+ DNS_R_TEXTTOOLONG,
+ DNS_R_NOTZONETOP,
+ DNS_R_SYNTAX,
+ DNS_R_BADCKSUM,
+ DNS_R_BADAAAA,
+ DNS_R_NOOWNER,
+ DNS_R_NOTTL,
+ DNS_R_BADCLASS,
+ DNS_R_NAMETOOLONG,
+ DNS_R_PARTIALMATCH,
+ DNS_R_NEWORIGIN,
+ DNS_R_UNCHANGED,
+ DNS_R_BADTTL,
+ DNS_R_NOREDATA,
+ DNS_R_CONTINUE,
+ DNS_R_DELEGATION,
+ DNS_R_GLUE,
+ DNS_R_DNAME,
+ DNS_R_CNAME,
+ DNS_R_BADDB,
+ DNS_R_ZONECUT,
+ DNS_R_BADZONE,
+ DNS_R_MOREDATA,
+ DNS_R_UPTODATE,
+ DNS_R_TSIGVERIFYFAILURE,
+ DNS_R_TSIGERRORSET,
+ DNS_R_SIGINVALID,
+ DNS_R_SIGEXPIRED,
+ DNS_R_SIGFUTURE,
+ DNS_R_KEYUNAUTHORIZED,
+ DNS_R_INVALIDTIME,
+ DNS_R_EXPECTEDTSIG,
+ DNS_R_UNEXPECTEDTSIG,
+ DNS_R_INVALIDTKEY,
+ DNS_R_HINT,
+ DNS_R_DROP,
+ DNS_R_NOTLOADED,
+ DNS_R_NCACHENXDOMAIN,
+ DNS_R_NCACHENXRRSET,
+ DNS_R_WAIT,
+ DNS_R_NOTVERIFIEDYET,
+ DNS_R_NOIDENTITY,
+ DNS_R_NOJOURNAL,
+ DNS_R_ALIAS,
+ DNS_R_USETCP,
+ DNS_R_NOVALIDSIG,
+ DNS_R_NOVALIDNSEC,
+ DNS_R_NOTINSECURE,
+ DNS_R_UNKNOWNSERVICE,
+ DNS_R_RECOVERABLE,
+ DNS_R_UNKNOWNOPT,
+ DNS_R_UNEXPECTEDID,
+ DNS_R_SEENINCLUDE,
+ DNS_R_NOTEXACT,
+ DNS_R_BLACKHOLED,
+ DNS_R_BADALG,
+ DNS_R_METATYPE,
+ DNS_R_CNAMEANDOTHER,
+ DNS_R_SINGLETON,
+ DNS_R_HINTNXRRSET,
+ DNS_R_NOMASTERFILE,
+ DNS_R_UNKNOWNPROTO,
+ DNS_R_CLOCKSKEW,
+ DNS_R_BADIXFR,
+ DNS_R_NOTAUTHORITATIVE,
+ DNS_R_NOVALIDKEY,
+ DNS_R_OBSOLETE,
+ DNS_R_FROZEN,
+ DNS_R_UNKNOWNFLAG,
+ DNS_R_EXPECTEDRESPONSE,
+ DNS_R_NOVALIDDS,
+ DNS_R_NSISADDRESS,
+ DNS_R_REMOTEFORMERR,
+ DNS_R_TRUNCATEDTCP,
+ DNS_R_LAME,
+ DNS_R_UNEXPECTEDRCODE,
+ DNS_R_UNEXPECTEDOPCODE,
+ DNS_R_CHASEDSSERVERS,
+ DNS_R_EMPTYNAME,
+ DNS_R_EMPTYWILD,
+ DNS_R_BADBITMAP,
+ DNS_R_FROMWILDCARD,
+ DNS_R_BADOWNERNAME,
+ DNS_R_BADNAME,
+ DNS_R_DYNAMIC,
+ DNS_R_UNKNOWNCOMMAND,
+ DNS_R_MUSTBESECURE,
+ DNS_R_COVERINGNSEC,
+ DNS_R_MXISADDRESS,
+ DNS_R_DUPLICATE,
+ DNS_R_INVALIDNSEC3,
+ DNS_R_NOTPRIMARY,
+ DNS_R_BROKENCHAIN,
+ DNS_R_EXPIRED,
+ DNS_R_NOTDYNAMIC,
+ DNS_R_BADEUI,
+ DNS_R_NTACOVERED,
+ DNS_R_BADCDS,
+ DNS_R_BADCDNSKEY,
+ DNS_R_OPTERR,
+ DNS_R_BADDNSTAP,
+ DNS_R_BADTSIG,
+ DNS_R_BADSIG0,
+ DNS_R_TOOMANYRECORDS,
+ DNS_R_VERIFYFAILURE,
+ DNS_R_ATZONETOP,
+ DNS_R_NOKEYMATCH,
+ DNS_R_TOOMANYKEYS,
+ DNS_R_KEYNOTACTIVE,
+ DNS_R_NSEC3ITERRANGE,
+ DNS_R_NSEC3SALTRANGE,
+ DNS_R_NSEC3BADALG,
+ DNS_R_NSEC3RESALT,
+ DNS_R_INCONSISTENTRR,
+ DNS_R_NOALPN,
+
+ DST_R_UNSUPPORTEDALG,
+ DST_R_CRYPTOFAILURE,
+ /* compat */
+ DST_R_OPENSSLFAILURE = DST_R_CRYPTOFAILURE,
+ DST_R_NOCRYPTO,
+ DST_R_NULLKEY,
+ DST_R_INVALIDPUBLICKEY,
+ DST_R_INVALIDPRIVATEKEY,
+ DST_R_WRITEERROR,
+ DST_R_INVALIDPARAM,
+ DST_R_SIGNFAILURE,
+ DST_R_VERIFYFAILURE,
+ DST_R_NOTPUBLICKEY,
+ DST_R_NOTPRIVATEKEY,
+ DST_R_KEYCANNOTCOMPUTESECRET,
+ DST_R_COMPUTESECRETFAILURE,
+ DST_R_NORANDOMNESS,
+ DST_R_BADKEYTYPE,
+ DST_R_NOENGINE,
+ DST_R_EXTERNALKEY,
+
+ DNS_R_NOERROR,
+ DNS_R_FORMERR,
+ DNS_R_SERVFAIL,
+ DNS_R_NXDOMAIN,
+ DNS_R_NOTIMP,
+ DNS_R_REFUSED,
+ DNS_R_YXDOMAIN,
+ DNS_R_YXRRSET,
+ DNS_R_NXRRSET,
+ DNS_R_NOTAUTH,
+ DNS_R_NOTZONE,
+ DNS_R_RCODE11,
+ DNS_R_RCODE12,
+ DNS_R_RCODE13,
+ DNS_R_RCODE14,
+ DNS_R_RCODE15,
+ DNS_R_BADVERS,
+ DNS_R_BADCOOKIE = DNS_R_NOERROR + 23,
+
+ ISCCC_R_UNKNOWNVERSION,
+ ISCCC_R_SYNTAX,
+ ISCCC_R_BADAUTH,
+ ISCCC_R_EXPIRED,
+ ISCCC_R_CLOCKSKEW,
+ ISCCC_R_DUPLICATE,
+ ISCCC_R_MAXDEPTH,
+
+ ISC_R_NRESULTS, /*% The number of results. */
+ ISC_R_MAKE_ENUM_32BIT = INT32_MAX,
+} isc_result_t;
+
+ISC_LANG_BEGINDECLS
+
+const char *isc_result_totext(isc_result_t);
+/*%<
+ * Convert an isc_result_t into a string message describing the result.
+ */
+
+const char *isc_result_toid(isc_result_t);
+/*%<
+ * Convert an isc_result_t into a string identifier such as
+ * "ISC_R_SUCCESS".
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/rwlock.h b/lib/isc/include/isc/rwlock.h
new file mode 100644
index 0000000..a0d7083
--- /dev/null
+++ b/lib/isc/include/isc/rwlock.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+
+/*! \file isc/rwlock.h */
+
+#include <isc/atomic.h>
+#include <isc/condition.h>
+#include <isc/lang.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+typedef enum {
+ isc_rwlocktype_none = 0,
+ isc_rwlocktype_read,
+ isc_rwlocktype_write
+} isc_rwlocktype_t;
+
+#if USE_PTHREAD_RWLOCK
+#include <pthread.h>
+
+struct isc_rwlock {
+ pthread_rwlock_t rwlock;
+ atomic_bool downgrade;
+};
+
+#else /* USE_PTHREAD_RWLOCK */
+
+struct isc_rwlock {
+ /* Unlocked. */
+ unsigned int magic;
+ isc_mutex_t lock;
+ atomic_int_fast32_t spins;
+
+ /*
+ * When some atomic instructions with hardware assistance are
+ * available, rwlock will use those so that concurrent readers do not
+ * interfere with each other through mutex as long as no writers
+ * appear, massively reducing the lock overhead in the typical case.
+ *
+ * The basic algorithm of this approach is the "simple
+ * writer-preference lock" shown in the following URL:
+ * http://www.cs.rochester.edu/u/scott/synchronization/pseudocode/rw.html
+ * but our implementation does not rely on the spin lock unlike the
+ * original algorithm to be more portable as a user space application.
+ */
+
+ /* Read or modified atomically. */
+ atomic_int_fast32_t write_requests;
+ atomic_int_fast32_t write_completions;
+ atomic_int_fast32_t cnt_and_flag;
+
+ /* Locked by lock. */
+ isc_condition_t readable;
+ isc_condition_t writeable;
+ unsigned int readers_waiting;
+
+ /* Locked by rwlock itself. */
+ atomic_uint_fast32_t write_granted;
+
+ /* Unlocked. */
+ unsigned int write_quota;
+};
+
+#endif /* USE_PTHREAD_RWLOCK */
+
+void
+isc_rwlock_init(isc_rwlock_t *rwl, unsigned int read_quota,
+ unsigned int write_quota);
+
+isc_result_t
+isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type);
+
+isc_result_t
+isc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type);
+
+isc_result_t
+isc_rwlock_unlock(isc_rwlock_t *rwl, isc_rwlocktype_t type);
+
+isc_result_t
+isc_rwlock_tryupgrade(isc_rwlock_t *rwl);
+
+void
+isc_rwlock_downgrade(isc_rwlock_t *rwl);
+
+void
+isc_rwlock_destroy(isc_rwlock_t *rwl);
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/safe.h b/lib/isc/include/isc/safe.h
new file mode 100644
index 0000000..35e7759
--- /dev/null
+++ b/lib/isc/include/isc/safe.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/safe.h */
+
+#include <isc/lang.h>
+
+ISC_LANG_BEGINDECLS
+
+int
+isc_safe_memequal(const void *, const void *, size_t);
+
+/*%<
+ * Returns true iff. two blocks of memory are equal, otherwise
+ * false.
+ *
+ */
+
+void
+isc_safe_memwipe(void *, size_t);
+
+/*%<
+ * Clear the memory of length `len` pointed to by `ptr`.
+ *
+ * Some crypto code calls memset() on stack allocated buffers just
+ * before return so that they are wiped. Such memset() calls can be
+ * optimized away by the compiler. We provide this external non-inline C
+ * function to perform the memset operation so that the compiler cannot
+ * infer about what the function does and optimize the call away.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/serial.h b/lib/isc/include/isc/serial.h
new file mode 100644
index 0000000..b7bfa5f
--- /dev/null
+++ b/lib/isc/include/isc/serial.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+/*! \file isc/serial.h
+ * \brief Implement 32 bit serial space arithmetic comparison functions.
+ * Note: Undefined results are returned as false.
+ */
+
+/***
+ *** Functions
+ ***/
+
+ISC_LANG_BEGINDECLS
+
+bool
+isc_serial_lt(uint32_t a, uint32_t b);
+/*%<
+ * Return true if 'a' < 'b' otherwise false.
+ */
+
+bool
+isc_serial_gt(uint32_t a, uint32_t b);
+/*%<
+ * Return true if 'a' > 'b' otherwise false.
+ */
+
+bool
+isc_serial_le(uint32_t a, uint32_t b);
+/*%<
+ * Return true if 'a' <= 'b' otherwise false.
+ */
+
+bool
+isc_serial_ge(uint32_t a, uint32_t b);
+/*%<
+ * Return true if 'a' >= 'b' otherwise false.
+ */
+
+bool
+isc_serial_eq(uint32_t a, uint32_t b);
+/*%<
+ * Return true if 'a' == 'b' otherwise false.
+ */
+
+bool
+isc_serial_ne(uint32_t a, uint32_t b);
+/*%<
+ * Return true if 'a' != 'b' otherwise false.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/siphash.h b/lib/isc/include/isc/siphash.h
new file mode 100644
index 0000000..69d4742
--- /dev/null
+++ b/lib/isc/include/isc/siphash.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file isc/siphash.h */
+
+#pragma once
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+#define ISC_SIPHASH24_KEY_LENGTH 128 / 8
+#define ISC_SIPHASH24_TAG_LENGTH 64 / 8
+
+#define ISC_HALFSIPHASH24_KEY_LENGTH 64 / 8
+#define ISC_HALFSIPHASH24_TAG_LENGTH 32 / 8
+
+ISC_LANG_BEGINDECLS
+
+void
+isc_siphash24(const uint8_t *key, const uint8_t *in, const size_t inlen,
+ uint8_t *out);
+void
+isc_halfsiphash24(const uint8_t *key, const uint8_t *in, const size_t inlen,
+ uint8_t *out);
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/sockaddr.h b/lib/isc/include/isc/sockaddr.h
new file mode 100644
index 0000000..9f3986b
--- /dev/null
+++ b/lib/isc/include/isc/sockaddr.h
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/sockaddr.h */
+
+#include <stdbool.h>
+
+#include <isc/lang.h>
+#include <isc/net.h>
+#include <isc/types.h>
+
+#include <sys/un.h>
+
+/*
+ * Any updates to this structure should also be applied in
+ * contrib/modules/dlz/dlz_minmal.h.
+ */
+struct isc_sockaddr {
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ struct sockaddr_storage ss;
+ struct sockaddr_un sunix;
+ } type;
+ unsigned int length; /* XXXRTH beginning? */
+ ISC_LINK(struct isc_sockaddr) link;
+};
+
+#define ISC_SOCKADDR_CMPADDR \
+ 0x0001 /*%< compare the address \
+ * sin_addr/sin6_addr */
+#define ISC_SOCKADDR_CMPPORT \
+ 0x0002 /*%< compare the port \
+ * sin_port/sin6_port */
+#define ISC_SOCKADDR_CMPSCOPE \
+ 0x0004 /*%< compare the scope \
+ * sin6_scope */
+#define ISC_SOCKADDR_CMPSCOPEZERO \
+ 0x0008 /*%< when comparing scopes \
+ * zero scopes always match */
+
+ISC_LANG_BEGINDECLS
+
+bool
+isc_sockaddr_compare(const isc_sockaddr_t *a, const isc_sockaddr_t *b,
+ unsigned int flags);
+/*%<
+ * Compare the elements of the two address ('a' and 'b') as specified
+ * by 'flags' and report if they are equal or not.
+ *
+ * 'flags' is set from ISC_SOCKADDR_CMP*.
+ */
+
+bool
+isc_sockaddr_equal(const isc_sockaddr_t *a, const isc_sockaddr_t *b);
+/*%<
+ * Return true iff the socket addresses 'a' and 'b' are equal.
+ */
+
+bool
+isc_sockaddr_eqaddr(const isc_sockaddr_t *a, const isc_sockaddr_t *b);
+/*%<
+ * Return true iff the address parts of the socket addresses
+ * 'a' and 'b' are equal, ignoring the ports.
+ */
+
+bool
+isc_sockaddr_eqaddrprefix(const isc_sockaddr_t *a, const isc_sockaddr_t *b,
+ unsigned int prefixlen);
+/*%<
+ * Return true iff the most significant 'prefixlen' bits of the
+ * socket addresses 'a' and 'b' are equal, ignoring the ports.
+ * If 'b''s scope is zero then 'a''s scope will be ignored.
+ */
+
+unsigned int
+isc_sockaddr_hash(const isc_sockaddr_t *sockaddr, bool address_only);
+/*%<
+ * Return a hash value for the socket address 'sockaddr'. If 'address_only'
+ * is true, the hash value will not depend on the port.
+ *
+ * IPv6 addresses containing mapped IPv4 addresses generate the same hash
+ * value as the equivalent IPv4 address.
+ */
+
+void
+isc_sockaddr_any(isc_sockaddr_t *sockaddr);
+/*%<
+ * Return the IPv4 wildcard address.
+ */
+
+void
+isc_sockaddr_any6(isc_sockaddr_t *sockaddr);
+/*%<
+ * Return the IPv6 wildcard address.
+ */
+
+void
+isc_sockaddr_anyofpf(isc_sockaddr_t *sockaddr, int family);
+/*%<
+ * Set '*sockaddr' to the wildcard address of protocol family
+ * 'family'.
+ *
+ * Requires:
+ * \li 'family' is AF_INET or AF_INET6.
+ */
+
+void
+isc_sockaddr_fromin(isc_sockaddr_t *sockaddr, const struct in_addr *ina,
+ in_port_t port);
+/*%<
+ * Construct an isc_sockaddr_t from an IPv4 address and port.
+ */
+
+void
+isc_sockaddr_fromin6(isc_sockaddr_t *sockaddr, const struct in6_addr *ina6,
+ in_port_t port);
+/*%<
+ * Construct an isc_sockaddr_t from an IPv6 address and port.
+ */
+
+void
+isc_sockaddr_v6fromin(isc_sockaddr_t *sockaddr, const struct in_addr *ina,
+ in_port_t port);
+/*%<
+ * Construct an IPv6 isc_sockaddr_t representing a mapped IPv4 address.
+ */
+
+void
+isc_sockaddr_fromnetaddr(isc_sockaddr_t *sockaddr, const isc_netaddr_t *na,
+ in_port_t port);
+/*%<
+ * Construct an isc_sockaddr_t from an isc_netaddr_t and port.
+ */
+
+int
+isc_sockaddr_pf(const isc_sockaddr_t *sockaddr);
+/*%<
+ * Get the protocol family of 'sockaddr'.
+ *
+ * Requires:
+ *
+ *\li 'sockaddr' is a valid sockaddr with an address family of AF_INET
+ * or AF_INET6.
+ *
+ * Returns:
+ *
+ *\li The protocol family of 'sockaddr', e.g. PF_INET or PF_INET6.
+ */
+
+void
+isc_sockaddr_setport(isc_sockaddr_t *sockaddr, in_port_t port);
+/*%<
+ * Set the port of 'sockaddr' to 'port'.
+ */
+
+in_port_t
+isc_sockaddr_getport(const isc_sockaddr_t *sockaddr);
+/*%<
+ * Get the port stored in 'sockaddr'.
+ */
+
+isc_result_t
+isc_sockaddr_totext(const isc_sockaddr_t *sockaddr, isc_buffer_t *target);
+/*%<
+ * Append a text representation of 'sockaddr' to the buffer 'target'.
+ * The text will include both the IP address (v4 or v6) and the port.
+ * The text is null terminated, but the terminating null is not
+ * part of the buffer's used region.
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li ISC_R_NOSPACE The text or the null termination did not fit.
+ */
+
+void
+isc_sockaddr_format(const isc_sockaddr_t *sa, char *array, unsigned int size);
+/*%<
+ * Format a human-readable representation of the socket address '*sa'
+ * into the character array 'array', which is of size 'size'.
+ * The resulting string is guaranteed to be null-terminated.
+ */
+
+bool
+isc_sockaddr_ismulticast(const isc_sockaddr_t *sa);
+/*%<
+ * Returns #true if the address is a multicast address.
+ */
+
+bool
+isc_sockaddr_isexperimental(const isc_sockaddr_t *sa);
+/*
+ * Returns true if the address is a experimental (CLASS E) address.
+ */
+
+bool
+isc_sockaddr_islinklocal(const isc_sockaddr_t *sa);
+/*%<
+ * Returns true if the address is a link local address.
+ */
+
+bool
+isc_sockaddr_issitelocal(const isc_sockaddr_t *sa);
+/*%<
+ * Returns true if the address is a sitelocal address.
+ */
+
+bool
+isc_sockaddr_isnetzero(const isc_sockaddr_t *sa);
+/*%<
+ * Returns true if the address is in net zero.
+ */
+
+isc_result_t
+isc_sockaddr_frompath(isc_sockaddr_t *sockaddr, const char *path);
+/*
+ * Create a UNIX domain sockaddr that refers to path.
+ *
+ * Returns:
+ * \li ISC_R_NOSPACE
+ * \li ISC_R_NOTIMPLEMENTED
+ * \li ISC_R_SUCCESS
+ */
+
+isc_result_t
+isc_sockaddr_fromsockaddr(isc_sockaddr_t *isa, const struct sockaddr *sa);
+
+#define ISC_SOCKADDR_FORMATSIZE \
+ sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:XXX.XXX.XXX.XXX%SSSSSSSSSS#" \
+ "YYYYY")
+/*%<
+ * Minimum size of array to pass to isc_sockaddr_format().
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/stat.h b/lib/isc/include/isc/stat.h
new file mode 100644
index 0000000..fad37c9
--- /dev/null
+++ b/lib/isc/include/isc/stat.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*****
+***** Module Info
+*****/
+
+/*
+ * Portable <sys/stat.h> support.
+ *
+ * This module is responsible for defining S_IS??? macros.
+ *
+ * MP:
+ * No impact.
+ *
+ * Reliability:
+ * No anticipated impact.
+ *
+ * Resources:
+ * N/A.
+ *
+ * Security:
+ * No anticipated impact.
+ *
+ */
+
+/***
+ *** Imports.
+ ***/
+
+#include <sys/stat.h>
+#include <sys/types.h>
diff --git a/lib/isc/include/isc/stats.h b/lib/isc/include/isc/stats.h
new file mode 100644
index 0000000..5bed7d5
--- /dev/null
+++ b/lib/isc/include/isc/stats.h
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/stats.h */
+
+#include <inttypes.h>
+
+#include <isc/types.h>
+
+/*%
+ * Statistics counters. Used as isc_statscounter_t values.
+ */
+enum {
+ /*%
+ * Socket statistics counters.
+ */
+ isc_sockstatscounter_udp4open = 0,
+ isc_sockstatscounter_udp6open = 1,
+ isc_sockstatscounter_tcp4open = 2,
+ isc_sockstatscounter_tcp6open = 3,
+ isc_sockstatscounter_unixopen = 4,
+
+ isc_sockstatscounter_udp4openfail = 5,
+ isc_sockstatscounter_udp6openfail = 6,
+ isc_sockstatscounter_tcp4openfail = 7,
+ isc_sockstatscounter_tcp6openfail = 8,
+ isc_sockstatscounter_unixopenfail = 9,
+
+ isc_sockstatscounter_udp4close = 10,
+ isc_sockstatscounter_udp6close = 11,
+ isc_sockstatscounter_tcp4close = 12,
+ isc_sockstatscounter_tcp6close = 13,
+ isc_sockstatscounter_unixclose = 14,
+ isc_sockstatscounter_fdwatchclose = 15,
+
+ isc_sockstatscounter_udp4bindfail = 16,
+ isc_sockstatscounter_udp6bindfail = 17,
+ isc_sockstatscounter_tcp4bindfail = 18,
+ isc_sockstatscounter_tcp6bindfail = 19,
+ isc_sockstatscounter_unixbindfail = 20,
+ isc_sockstatscounter_fdwatchbindfail = 21,
+
+ isc_sockstatscounter_udp4connect = 22,
+ isc_sockstatscounter_udp6connect = 23,
+ isc_sockstatscounter_tcp4connect = 24,
+ isc_sockstatscounter_tcp6connect = 25,
+ isc_sockstatscounter_unixconnect = 26,
+ isc_sockstatscounter_fdwatchconnect = 27,
+
+ isc_sockstatscounter_udp4connectfail = 28,
+ isc_sockstatscounter_udp6connectfail = 29,
+ isc_sockstatscounter_tcp4connectfail = 30,
+ isc_sockstatscounter_tcp6connectfail = 31,
+ isc_sockstatscounter_unixconnectfail = 32,
+ isc_sockstatscounter_fdwatchconnectfail = 33,
+
+ isc_sockstatscounter_tcp4accept = 34,
+ isc_sockstatscounter_tcp6accept = 35,
+ isc_sockstatscounter_unixaccept = 36,
+
+ isc_sockstatscounter_tcp4acceptfail = 37,
+ isc_sockstatscounter_tcp6acceptfail = 38,
+ isc_sockstatscounter_unixacceptfail = 39,
+
+ isc_sockstatscounter_udp4sendfail = 40,
+ isc_sockstatscounter_udp6sendfail = 41,
+ isc_sockstatscounter_tcp4sendfail = 42,
+ isc_sockstatscounter_tcp6sendfail = 43,
+ isc_sockstatscounter_unixsendfail = 44,
+ isc_sockstatscounter_fdwatchsendfail = 45,
+
+ isc_sockstatscounter_udp4recvfail = 46,
+ isc_sockstatscounter_udp6recvfail = 47,
+ isc_sockstatscounter_tcp4recvfail = 48,
+ isc_sockstatscounter_tcp6recvfail = 49,
+ isc_sockstatscounter_unixrecvfail = 50,
+ isc_sockstatscounter_fdwatchrecvfail = 51,
+
+ isc_sockstatscounter_udp4active = 52,
+ isc_sockstatscounter_udp6active = 53,
+ isc_sockstatscounter_tcp4active = 54,
+ isc_sockstatscounter_tcp6active = 55,
+ isc_sockstatscounter_unixactive = 56,
+
+ isc_sockstatscounter_rawopen = 57,
+ isc_sockstatscounter_rawopenfail = 58,
+ isc_sockstatscounter_rawclose = 59,
+ isc_sockstatscounter_rawrecvfail = 60,
+ isc_sockstatscounter_rawactive = 61,
+
+ isc_sockstatscounter_max = 62
+};
+
+ISC_LANG_BEGINDECLS
+
+/*%<
+ * Flag(s) for isc_stats_dump().
+ */
+#define ISC_STATSDUMP_VERBOSE 0x00000001 /*%< dump 0-value counters */
+
+/*%<
+ * Dump callback type.
+ */
+typedef void (*isc_stats_dumper_t)(isc_statscounter_t, uint64_t, void *);
+
+isc_result_t
+isc_stats_create(isc_mem_t *mctx, isc_stats_t **statsp, int ncounters);
+/*%<
+ * Create a statistics counter structure of general type. It counts a general
+ * set of counters indexed by an ID between 0 and ncounters -1.
+ *
+ * Requires:
+ *\li 'mctx' must be a valid memory context.
+ *
+ *\li 'statsp' != NULL && '*statsp' == NULL.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS -- all ok
+ *
+ *\li anything else -- failure
+ */
+
+void
+isc_stats_attach(isc_stats_t *stats, isc_stats_t **statsp);
+/*%<
+ * Attach to a statistics set.
+ *
+ * Requires:
+ *\li 'stats' is a valid isc_stats_t.
+ *
+ *\li 'statsp' != NULL && '*statsp' == NULL
+ */
+
+void
+isc_stats_detach(isc_stats_t **statsp);
+/*%<
+ * Detaches from the statistics set.
+ *
+ * Requires:
+ *\li 'statsp' != NULL and '*statsp' is a valid isc_stats_t.
+ */
+
+int
+isc_stats_ncounters(isc_stats_t *stats);
+/*%<
+ * Returns the number of counters contained in stats.
+ *
+ * Requires:
+ *\li 'stats' is a valid isc_stats_t.
+ *
+ */
+
+void
+isc_stats_increment(isc_stats_t *stats, isc_statscounter_t counter);
+/*%<
+ * Increment the counter-th counter of stats.
+ *
+ * Requires:
+ *\li 'stats' is a valid isc_stats_t.
+ *
+ *\li counter is less than the maximum available ID for the stats specified
+ * on creation.
+ */
+
+void
+isc_stats_decrement(isc_stats_t *stats, isc_statscounter_t counter);
+/*%<
+ * Decrement the counter-th counter of stats.
+ *
+ * Requires:
+ *\li 'stats' is a valid isc_stats_t.
+ */
+
+void
+isc_stats_dump(isc_stats_t *stats, isc_stats_dumper_t dump_fn, void *arg,
+ unsigned int options);
+/*%<
+ * Dump the current statistics counters in a specified way. For each counter
+ * in stats, dump_fn is called with its current value and the given argument
+ * arg. By default counters that have a value of 0 is skipped; if options has
+ * the ISC_STATSDUMP_VERBOSE flag, even such counters are dumped.
+ *
+ * Requires:
+ *\li 'stats' is a valid isc_stats_t.
+ */
+
+void
+isc_stats_set(isc_stats_t *stats, uint64_t val, isc_statscounter_t counter);
+/*%<
+ * Set the given counter to the specified value.
+ *
+ * Requires:
+ *\li 'stats' is a valid isc_stats_t.
+ */
+
+void
+isc_stats_set(isc_stats_t *stats, uint64_t val, isc_statscounter_t counter);
+/*%<
+ * Set the given counter to the specified value.
+ *
+ * Requires:
+ *\li 'stats' is a valid isc_stats_t.
+ */
+
+void
+isc_stats_update_if_greater(isc_stats_t *stats, isc_statscounter_t counter,
+ isc_statscounter_t value);
+/*%<
+ * Atomically assigns 'value' to 'counter' if value > counter.
+ *
+ * Requires:
+ *\li 'stats' is a valid isc_stats_t.
+ *
+ *\li counter is less than the maximum available ID for the stats specified
+ * on creation.
+ */
+
+isc_statscounter_t
+isc_stats_get_counter(isc_stats_t *stats, isc_statscounter_t counter);
+/*%<
+ * Returns value currently stored in counter.
+ *
+ * Requires:
+ *\li 'stats' is a valid isc_stats_t.
+ *
+ *\li counter is less than the maximum available ID for the stats specified
+ * on creation.
+ */
+
+void
+isc_stats_resize(isc_stats_t **stats, int ncounters);
+/*%<
+ * Resize a statistics counter structure of general type. The new set of
+ * counters are indexed by an ID between 0 and ncounters -1.
+ *
+ * Requires:
+ *\li 'stats' is a valid isc_stats_t.
+ *\li 'ncounters' is a non-zero positive number.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/stdatomic.h b/lib/isc/include/isc/stdatomic.h
new file mode 100644
index 0000000..7c2e21c
--- /dev/null
+++ b/lib/isc/include/isc/stdatomic.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+#if HAVE_UCHAR_H
+#include <uchar.h>
+#endif /* HAVE_UCHAR_H */
+
+/* GCC 4.7.0 introduced __atomic builtins, but not the __GNUC_ATOMICS define */
+#if !defined(__GNUC_ATOMICS) && __GNUC__ == 4 && __GNUC_MINOR__ >= 7
+#define __GNUC_ATOMICS
+#endif
+
+#if !defined(__GNUC_ATOMICS)
+#error "isc/stdatomic.h does not support your compiler"
+#endif /* if !defined(__GNUC_ATOMICS) */
+
+typedef enum memory_order {
+ memory_order_relaxed = __ATOMIC_RELAXED,
+ memory_order_consume = __ATOMIC_CONSUME,
+ memory_order_acquire = __ATOMIC_ACQUIRE,
+ memory_order_release = __ATOMIC_RELEASE,
+ memory_order_acq_rel = __ATOMIC_ACQ_REL,
+ memory_order_seq_cst = __ATOMIC_SEQ_CST
+} memory_order;
+
+#ifndef HAVE_UCHAR_H
+typedef uint_least16_t char16_t;
+typedef uint_least32_t char32_t;
+#endif /* HAVE_UCHAR_H */
+
+typedef bool atomic_bool;
+typedef char atomic_char;
+typedef signed char atomic_schar;
+typedef unsigned char atomic_uchar;
+typedef short atomic_short;
+typedef unsigned short atomic_ushort;
+typedef int atomic_int;
+typedef unsigned int atomic_uint;
+typedef long atomic_long;
+typedef unsigned long atomic_ulong;
+typedef long long atomic_llong;
+typedef unsigned long long atomic_ullong;
+typedef char16_t atomic_char16_t;
+typedef char32_t atomic_char32_t;
+typedef wchar_t atomic_wchar_t;
+typedef int_least8_t atomic_int_least8_t;
+typedef uint_least8_t atomic_uint_least8_t;
+typedef int_least16_t atomic_int_least16_t;
+typedef uint_least16_t atomic_uint_least16_t;
+typedef int_least32_t atomic_int_least32_t;
+typedef uint_least32_t atomic_uint_least32_t;
+typedef int_least64_t atomic_int_least64_t;
+typedef uint_least64_t atomic_uint_least64_t;
+typedef int_fast8_t atomic_int_fast8_t;
+typedef uint_fast8_t atomic_uint_fast8_t;
+typedef int_fast16_t atomic_int_fast16_t;
+typedef uint_fast16_t atomic_uint_fast16_t;
+typedef int_fast32_t atomic_int_fast32_t;
+typedef uint_fast32_t atomic_uint_fast32_t;
+typedef int_fast64_t atomic_int_fast64_t;
+typedef uint_fast64_t atomic_uint_fast64_t;
+typedef intptr_t atomic_intptr_t;
+typedef uintptr_t atomic_uintptr_t;
+typedef size_t atomic_size_t;
+typedef ptrdiff_t atomic_ptrdiff_t;
+typedef intmax_t atomic_intmax_t;
+typedef uintmax_t atomic_uintmax_t;
+
+#define atomic_init(obj, desired) (*obj = desired)
+#define atomic_load_explicit(obj, order) __atomic_load_n(obj, order)
+#define atomic_store_explicit(obj, desired, order) \
+ __atomic_store_n(obj, desired, order)
+#define atomic_fetch_add_explicit(obj, arg, order) \
+ __atomic_fetch_add(obj, arg, order)
+#define atomic_fetch_sub_explicit(obj, arg, order) \
+ __atomic_fetch_sub(obj, arg, order)
+#define atomic_fetch_and_explicit(obj, arg, order) \
+ __atomic_fetch_and(obj, arg, order)
+#define atomic_fetch_or_explicit(obj, arg, order) \
+ __atomic_fetch_or(obj, arg, order)
+#define atomic_compare_exchange_strong_explicit(obj, expected, desired, succ, \
+ fail) \
+ __atomic_compare_exchange_n(obj, expected, desired, 0, succ, fail)
+#define atomic_compare_exchange_weak_explicit(obj, expected, desired, succ, \
+ fail) \
+ __atomic_compare_exchange_n(obj, expected, desired, 1, succ, fail)
+#define atomic_exchange_explicit(obj, desired, order) \
+ __atomic_exchange_n(obj, desired, order)
+
+#define atomic_load(obj) atomic_load_explicit(obj, memory_order_seq_cst)
+#define atomic_store(obj, arg) \
+ atomic_store_explicit(obj, arg, memory_order_seq_cst)
+#define atomic_fetch_add(obj, arg) \
+ atomic_fetch_add_explicit(obj, arg, memory_order_seq_cst)
+#define atomic_fetch_sub(obj, arg) \
+ atomic_fetch_sub_explicit(obj, arg, memory_order_seq_cst)
+#define atomic_fetch_and(obj, arg) \
+ atomic_fetch_and_explicit(obj, arg, memory_order_seq_cst)
+#define atomic_fetch_or(obj, arg) \
+ atomic_fetch_or_explicit(obj, arg, memory_order_seq_cst)
+#define atomic_compare_exchange_strong(obj, expected, desired) \
+ atomic_compare_exchange_strong_explicit(obj, expected, desired, \
+ memory_order_seq_cst, \
+ memory_order_seq_cst)
+#define atomic_compare_exchange_weak(obj, expected, desired) \
+ atomic_compare_exchange_weak_explicit(obj, expected, desired, \
+ memory_order_seq_cst, \
+ memory_order_seq_cst)
+#define atomic_exchange(obj, desired) \
+ atomic_exchange_explicit(obj, desired, memory_order_seq_cst)
diff --git a/lib/isc/include/isc/stdio.h b/lib/isc/include/isc/stdio.h
new file mode 100644
index 0000000..c8bac4d
--- /dev/null
+++ b/lib/isc/include/isc/stdio.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/stdio.h */
+
+/*%
+ * These functions are wrappers around the corresponding stdio functions.
+ *
+ * They return a detailed error code in the form of an an isc_result_t. ANSI C
+ * does not guarantee that stdio functions set errno, hence these functions
+ * must use platform dependent methods (e.g., the POSIX errno) to construct the
+ * error code.
+ */
+
+#include <stdio.h>
+
+#include <isc/lang.h>
+#include <isc/result.h>
+
+ISC_LANG_BEGINDECLS
+
+/*% Open */
+isc_result_t
+isc_stdio_open(const char *filename, const char *mode, FILE **fp);
+
+/*% Close */
+isc_result_t
+isc_stdio_close(FILE *f);
+
+/*% Seek */
+isc_result_t
+isc_stdio_seek(FILE *f, off_t offset, int whence);
+
+/*% Tell */
+isc_result_t
+isc_stdio_tell(FILE *f, off_t *offsetp);
+
+/*% Read */
+isc_result_t
+isc_stdio_read(void *ptr, size_t size, size_t nmemb, FILE *f, size_t *nret);
+
+/*% Write */
+isc_result_t
+isc_stdio_write(const void *ptr, size_t size, size_t nmemb, FILE *f,
+ size_t *nret);
+
+/*% Flush */
+isc_result_t
+isc_stdio_flush(FILE *f);
+
+isc_result_t
+isc_stdio_sync(FILE *f);
+/*%<
+ * Invoke fsync() on the file descriptor underlying an stdio stream, or an
+ * equivalent system-dependent operation. Note that this function has no
+ * direct counterpart in the stdio library.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/stdtime.h b/lib/isc/include/isc/stdtime.h
new file mode 100644
index 0000000..45ec50f
--- /dev/null
+++ b/lib/isc/include/isc/stdtime.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file */
+
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include <isc/lang.h>
+
+/*%
+ * It's public information that 'isc_stdtime_t' is an unsigned integral type.
+ * Applications that want maximum portability should not assume anything
+ * about its size.
+ */
+typedef uint32_t isc_stdtime_t;
+
+ISC_LANG_BEGINDECLS
+/* */
+void
+isc_stdtime_get(isc_stdtime_t *t);
+/*%<
+ * Set 't' to the number of seconds since 00:00:00 UTC, January 1, 1970.
+ *
+ * Requires:
+ *
+ *\li 't' is a valid pointer.
+ */
+
+void
+isc_stdtime_tostring(isc_stdtime_t t, char *out, size_t outlen);
+/*
+ * Convert 't' into a null-terminated string of the form
+ * "Wed Jun 30 21:49:08 1993". Store the string in the 'out'
+ * buffer.
+ *
+ * Requires:
+ *
+ * 't' is a valid time.
+ * 'out' is a valid pointer.
+ * 'outlen' is at least 26.
+ */
+
+#define isc_stdtime_convert32(t, t32p) (*(t32p) = t)
+/*
+ * Convert the standard time to its 32-bit version.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/strerr.h b/lib/isc/include/isc/strerr.h
new file mode 100644
index 0000000..563ff48
--- /dev/null
+++ b/lib/isc/include/isc/strerr.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/strerr.h */
+
+#include <isc/string.h>
+
+/***
+ *** Default strerror_r buffer size
+ ***/
+
+#define ISC_STRERRORSIZE 128
+
+#if defined(strerror_r)
+#undef strerror_r
+#endif /* if defined(strerror_r) */
+#define strerror_r isc_string_strerror_r
diff --git a/lib/isc/include/isc/string.h b/lib/isc/include/isc/string.h
new file mode 100644
index 0000000..fbf6129
--- /dev/null
+++ b/lib/isc/include/isc/string.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/string.h */
+
+#include <string.h>
+
+#include <isc/lang.h>
+
+ISC_LANG_BEGINDECLS
+
+#if !defined(HAVE_STRLCPY)
+size_t
+strlcpy(char *dst, const char *src, size_t size);
+#endif /* !defined(HAVE_STRLCPY) */
+
+#if !defined(HAVE_STRLCAT)
+size_t
+strlcat(char *dst, const char *src, size_t size);
+#endif /* if !defined(HAVE_STRLCAT) */
+
+#if !defined(HAVE_STRNSTR)
+char *
+strnstr(const char *s, const char *find, size_t slen);
+#endif /* if !defined(HAVE_STRNSTR) */
+
+int
+isc_string_strerror_r(int errnum, char *buf, size_t buflen);
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/symtab.h b/lib/isc/include/isc/symtab.h
new file mode 100644
index 0000000..2be3a8b
--- /dev/null
+++ b/lib/isc/include/isc/symtab.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*****
+***** Module Info
+*****/
+
+/*! \file isc/symtab.h
+ * \brief Provides a simple memory-based symbol table.
+ *
+ * Keys are C strings, and key comparisons are case-insensitive. A type may
+ * be specified when looking up, defining, or undefining. A type value of
+ * 0 means "match any type"; any other value will only match the given
+ * type.
+ *
+ * It's possible that a client will attempt to define a <key, type, value>
+ * tuple when a tuple with the given key and type already exists in the table.
+ * What to do in this case is specified by the client. Possible policies are:
+ *
+ *\li #isc_symexists_reject Disallow the define, returning #ISC_R_EXISTS
+ *\li #isc_symexists_replace Replace the old value with the new. The
+ * undefine action (if provided) will be called
+ * with the old <key, type, value> tuple.
+ *\li #isc_symexists_add Add the new tuple, leaving the old tuple in
+ * the table. Subsequent lookups will retrieve
+ * the most-recently-defined tuple.
+ *
+ * A lookup of a key using type 0 will return the most-recently defined
+ * symbol with that key. An undefine of a key using type 0 will undefine the
+ * most-recently defined symbol with that key. Trying to define a key with
+ * type 0 is illegal.
+ *
+ * The symbol table library does not make a copy the key field, so the
+ * caller must ensure that any key it passes to isc_symtab_define() will not
+ * change until it calls isc_symtab_undefine() or isc_symtab_destroy().
+ *
+ * A user-specified action will be called (if provided) when a symbol is
+ * undefined. It can be used to free memory associated with keys and/or
+ * values.
+ *
+ * A symbol table is implemented as a hash table of lists; the size of the
+ * hash table is set by the 'size' parameter to isc_symtbl_create(). When
+ * the number of entries in the symbol table reaches three quarters of this
+ * value, the hash table is reallocated with size doubled, in order to
+ * optimize lookup performance. This has a negative effect on insertion
+ * performance, which can be mitigated by sizing the table appropriately
+ * when creating it.
+ *
+ * \li MP:
+ * The callers of this module must ensure any required synchronization.
+ *
+ * \li Reliability:
+ * No anticipated impact.
+ *
+ * \li Resources:
+ * TBS
+ *
+ * \li Security:
+ * No anticipated impact.
+ *
+ * \li Standards:
+ * None.
+ */
+
+/***
+ *** Imports.
+ ***/
+
+#include <stdbool.h>
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+/*
+ *** Symbol Tables.
+ ***/
+/*% Symbol table value. */
+typedef union isc_symvalue {
+ void *as_pointer;
+ const void *as_cpointer;
+ int as_integer;
+ unsigned int as_uinteger;
+} isc_symvalue_t;
+
+typedef void (*isc_symtabaction_t)(char *key, unsigned int type,
+ isc_symvalue_t value, void *userarg);
+/*% Symbol table exists. */
+typedef enum {
+ isc_symexists_reject = 0, /*%< Disallow the define */
+ isc_symexists_replace = 1, /*%< Replace the old value with the new */
+ isc_symexists_add = 2 /*%< Add the new tuple */
+} isc_symexists_t;
+
+ISC_LANG_BEGINDECLS
+
+/*% Create a symbol table. */
+isc_result_t
+isc_symtab_create(isc_mem_t *mctx, unsigned int size,
+ isc_symtabaction_t undefine_action, void *undefine_arg,
+ bool case_sensitive, isc_symtab_t **symtabp);
+
+/*% Destroy a symbol table. */
+void
+isc_symtab_destroy(isc_symtab_t **symtabp);
+
+/*% Lookup a symbol table. */
+isc_result_t
+isc_symtab_lookup(isc_symtab_t *symtab, const char *key, unsigned int type,
+ isc_symvalue_t *value);
+
+/*% Define a symbol table. */
+isc_result_t
+isc_symtab_define(isc_symtab_t *symtab, const char *key, unsigned int type,
+ isc_symvalue_t value, isc_symexists_t exists_policy);
+
+/*% Undefine a symbol table. */
+isc_result_t
+isc_symtab_undefine(isc_symtab_t *symtab, const char *key, unsigned int type);
+
+/*% Return the number of items in a symbol table. */
+unsigned int
+isc_symtab_count(isc_symtab_t *symtab);
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/syslog.h b/lib/isc/include/isc/syslog.h
new file mode 100644
index 0000000..021f7fa
--- /dev/null
+++ b/lib/isc/include/isc/syslog.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file */
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+isc_syslog_facilityfromstring(const char *str, int *facilityp);
+/*%<
+ * Convert 'str' to the appropriate syslog facility constant.
+ *
+ * Requires:
+ *
+ *\li 'str' is not NULL
+ *\li 'facilityp' is not NULL
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOTFOUND
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/task.h b/lib/isc/include/isc/task.h
new file mode 100644
index 0000000..75b7cdb
--- /dev/null
+++ b/lib/isc/include/isc/task.h
@@ -0,0 +1,646 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file isc/task.h
+ * \brief The task system provides a lightweight execution context, which is
+ * basically an event queue.
+ *
+ * When a task's event queue is non-empty, the
+ * task is runnable. A small work crew of threads, typically one per CPU,
+ * execute runnable tasks by dispatching the events on the tasks' event
+ * queues. Context switching between tasks is fast.
+ *
+ * \li MP:
+ * The module ensures appropriate synchronization of data structures it
+ * creates and manipulates.
+ * The caller must ensure that isc_taskmgr_destroy() is called only
+ * once for a given manager.
+ *
+ * \li Reliability:
+ * No anticipated impact.
+ *
+ * \li Resources:
+ * TBS
+ *
+ * \li Security:
+ * No anticipated impact.
+ *
+ * \li Standards:
+ * None.
+ *
+ * \section purge Purging and Unsending
+ *
+ * Events which have been queued for a task but not delivered may be removed
+ * from the task's event queue by purging or unsending.
+ *
+ * With both types, the caller specifies a matching pattern that selects
+ * events based upon their sender, type, and tag.
+ *
+ * Purging calls isc_event_free() on the matching events.
+ *
+ */
+
+/***
+ *** Imports.
+ ***/
+
+#include <stdbool.h>
+
+#include <isc/eventclass.h>
+#include <isc/lang.h>
+#include <isc/netmgr.h>
+#include <isc/stdtime.h>
+#include <isc/types.h>
+
+#define ISC_TASKEVENT_FIRSTEVENT (ISC_EVENTCLASS_TASK + 0)
+#define ISC_TASKEVENT_SHUTDOWN (ISC_EVENTCLASS_TASK + 1)
+#define ISC_TASKEVENT_TEST (ISC_EVENTCLASS_TASK + 1)
+#define ISC_TASKEVENT_LASTEVENT (ISC_EVENTCLASS_TASK + 65535)
+
+/*****
+ ***** Tasks.
+ *****/
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Types
+ ***/
+
+typedef enum {
+ isc_taskmgrmode_normal = 0,
+ isc_taskmgrmode_privileged
+} isc_taskmgrmode_t;
+
+isc_result_t
+isc_task_create(isc_taskmgr_t *manager, unsigned int quantum,
+ isc_task_t **taskp);
+isc_result_t
+isc_task_create_bound(isc_taskmgr_t *manager, unsigned int quantum,
+ isc_task_t **taskp, int threadid);
+/*%<
+ * Create a task, optionally bound to a particular threadid.
+ *
+ * Notes:
+ *
+ *\li If 'quantum' is non-zero, then only that many events can be dispatched
+ * before the task must yield to other tasks waiting to execute. If
+ * quantum is zero, then the default quantum of the task manager will
+ * be used.
+ *
+ *\li The 'quantum' option may be removed from isc_task_create() in the
+ * future. If this happens, isc_task_getquantum() and
+ * isc_task_setquantum() will be provided.
+ *
+ * Requires:
+ *
+ *\li 'manager' is a valid task manager.
+ *
+ *\li taskp != NULL && *taskp == NULL
+ *
+ * Ensures:
+ *
+ *\li On success, '*taskp' is bound to the new task.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *\li #ISC_R_UNEXPECTED
+ *\li #ISC_R_SHUTTINGDOWN
+ */
+
+void
+isc_task_ready(isc_task_t *task);
+/*%<
+ * Enqueue the task onto netmgr queue.
+ */
+
+isc_result_t
+isc_task_run(isc_task_t *task);
+/*%<
+ * Run all the queued events for the 'task', returning
+ * when the queue is empty or the number of events executed
+ * exceeds the 'quantum' specified when the task was created.
+ *
+ * Requires:
+ *
+ *\li 'task' is a valid task.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_QUOTA
+ */
+
+void
+isc_task_attach(isc_task_t *source, isc_task_t **targetp);
+/*%<
+ * Attach *targetp to source.
+ *
+ * Requires:
+ *
+ *\li 'source' is a valid task.
+ *
+ *\li 'targetp' points to a NULL isc_task_t *.
+ *
+ * Ensures:
+ *
+ *\li *targetp is attached to source.
+ */
+
+void
+isc_task_detach(isc_task_t **taskp);
+/*%<
+ * Detach *taskp from its task.
+ *
+ * Requires:
+ *
+ *\li '*taskp' is a valid task.
+ *
+ * Ensures:
+ *
+ *\li *taskp is NULL.
+ *
+ *\li If '*taskp' is the last reference to the task, the task is idle (has
+ * an empty event queue), and has not been shutdown, the task will be
+ * shutdown.
+ *
+ *\li If '*taskp' is the last reference to the task and
+ * the task has been shutdown,
+ * all resources used by the task will be freed.
+ */
+
+void
+isc_task_send(isc_task_t *task, isc_event_t **eventp);
+
+void
+isc_task_sendto(isc_task_t *task, isc_event_t **eventp, int c);
+/*%<
+ * Send '*event' to 'task', if task is idle try starting it on cpu 'c'
+ * If 'c' is smaller than 0 then cpu is selected randomly.
+ *
+ * Requires:
+ *
+ *\li 'task' is a valid task.
+ *\li eventp != NULL && *eventp != NULL.
+ *
+ * Ensures:
+ *
+ *\li *eventp == NULL.
+ */
+
+void
+isc_task_sendtoanddetach(isc_task_t **taskp, isc_event_t **eventp, int c);
+
+void
+isc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp);
+/*%<
+ * Send '*event' to '*taskp' and then detach '*taskp' from its
+ * task. If task is idle try starting it on cpu 'c'
+ * If 'c' is smaller than 0 then cpu is selected randomly.
+ *
+ * Requires:
+ *
+ *\li '*taskp' is a valid task.
+ *\li eventp != NULL && *eventp != NULL.
+ *
+ * Ensures:
+ *
+ *\li *eventp == NULL.
+ *
+ *\li *taskp == NULL.
+ *
+ *\li If '*taskp' is the last reference to the task, the task is
+ * idle (has an empty event queue), and has not been shutdown,
+ * the task will be shutdown.
+ *
+ *\li If '*taskp' is the last reference to the task and
+ * the task has been shutdown,
+ * all resources used by the task will be freed.
+ */
+
+unsigned int
+isc_task_purgerange(isc_task_t *task, void *sender, isc_eventtype_t first,
+ isc_eventtype_t last, void *tag);
+/*%<
+ * Purge events from a task's event queue.
+ *
+ * Requires:
+ *
+ *\li 'task' is a valid task.
+ *
+ *\li last >= first
+ *
+ * Ensures:
+ *
+ *\li Events in the event queue of 'task' whose sender is 'sender', whose
+ * type is >= first and <= last, and whose tag is 'tag' will be purged,
+ * unless they are marked as unpurgable.
+ *
+ *\li A sender of NULL will match any sender. A NULL tag matches any
+ * tag.
+ *
+ * Returns:
+ *
+ *\li The number of events purged.
+ */
+
+unsigned int
+isc_task_purge(isc_task_t *task, void *sender, isc_eventtype_t type, void *tag);
+/*%<
+ * Purge events from a task's event queue.
+ *
+ * Notes:
+ *
+ *\li This function is equivalent to
+ *
+ *\code
+ * isc_task_purgerange(task, sender, type, type, tag);
+ *\endcode
+ *
+ * Requires:
+ *
+ *\li 'task' is a valid task.
+ *
+ * Ensures:
+ *
+ *\li Events in the event queue of 'task' whose sender is 'sender', whose
+ * type is 'type', and whose tag is 'tag' will be purged, unless they
+ * are marked as unpurgable.
+ *
+ *\li A sender of NULL will match any sender. A NULL tag matches any
+ * tag.
+ *
+ * Returns:
+ *
+ *\li The number of events purged.
+ */
+
+bool
+isc_task_purgeevent(isc_task_t *task, isc_event_t *event);
+/*%<
+ * Purge 'event' from a task's event queue.
+ *
+ * XXXRTH: WARNING: This method may be removed before beta.
+ *
+ * Notes:
+ *
+ *\li If 'event' is on the task's event queue, it will be purged,
+ * unless it is marked as unpurgeable. 'event' does not have to be
+ * on the task's event queue; in fact, it can even be an invalid
+ * pointer. Purging only occurs if the event is actually on the task's
+ * event queue.
+ *
+ * \li Purging never changes the state of the task.
+ *
+ * Requires:
+ *
+ *\li 'task' is a valid task.
+ *
+ * Ensures:
+ *
+ *\li 'event' is not in the event queue for 'task'.
+ *
+ * Returns:
+ *
+ *\li #true The event was purged.
+ *\li #false The event was not in the event queue,
+ * or was marked unpurgeable.
+ */
+
+unsigned int
+isc_task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type, void *tag,
+ isc_eventlist_t *events);
+/*%<
+ * Remove events from a task's event queue.
+ *
+ * Notes:
+ *
+ *\li This function is equivalent to
+ *
+ *\code
+ * isc_task_unsendrange(task, sender, type, type, tag, events);
+ *\endcode
+ *
+ * Requires:
+ *
+ *\li 'task' is a valid task.
+ *
+ *\li *events is a valid list.
+ *
+ * Ensures:
+ *
+ *\li Events in the event queue of 'task' whose sender is 'sender', whose
+ * type is 'type', and whose tag is 'tag' will be dequeued and appended
+ * to *events.
+ *
+ * Returns:
+ *
+ *\li The number of events unsent.
+ */
+
+isc_result_t
+isc_task_onshutdown(isc_task_t *task, isc_taskaction_t action, void *arg);
+/*%<
+ * Send a shutdown event with action 'action' and argument 'arg' when
+ * 'task' is shutdown.
+ *
+ * Notes:
+ *
+ *\li Shutdown events are posted in LIFO order.
+ *
+ * Requires:
+ *
+ *\li 'task' is a valid task.
+ *
+ *\li 'action' is a valid task action.
+ *
+ * Ensures:
+ *
+ *\li When the task is shutdown, shutdown events requested with
+ * isc_task_onshutdown() will be appended to the task's event queue.
+ *
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *\li #ISC_R_SHUTTINGDOWN Task is shutting down.
+ */
+
+void
+isc_task_shutdown(isc_task_t *task);
+/*%<
+ * Shutdown 'task'.
+ *
+ * Notes:
+ *
+ *\li Shutting down a task causes any shutdown events requested with
+ * isc_task_onshutdown() to be posted (in LIFO order). The task
+ * moves into a "shutting down" mode which prevents further calls
+ * to isc_task_onshutdown().
+ *
+ *\li Trying to shutdown a task that has already been shutdown has no
+ * effect.
+ *
+ * Requires:
+ *
+ *\li 'task' is a valid task.
+ *
+ * Ensures:
+ *
+ *\li Any shutdown events requested with isc_task_onshutdown() have been
+ * posted (in LIFO order).
+ */
+
+void
+isc_task_destroy(isc_task_t **taskp);
+/*%<
+ * Destroy '*taskp'.
+ *
+ * Notes:
+ *
+ *\li This call is equivalent to:
+ *
+ *\code
+ * isc_task_shutdown(*taskp);
+ * isc_task_detach(taskp);
+ *\endcode
+ *
+ * Requires:
+ *
+ * '*taskp' is a valid task.
+ *
+ * Ensures:
+ *
+ *\li Any shutdown events requested with isc_task_onshutdown() have been
+ * posted (in LIFO order).
+ *
+ *\li *taskp == NULL
+ *
+ *\li If '*taskp' is the last reference to the task,
+ * all resources used by the task will be freed.
+ */
+
+void
+isc_task_setname(isc_task_t *task, const char *name, void *tag);
+/*%<
+ * Name 'task'.
+ *
+ * Notes:
+ *
+ *\li Only the first 15 characters of 'name' will be copied.
+ *
+ *\li Naming a task is currently only useful for debugging purposes.
+ *
+ * Requires:
+ *
+ *\li 'task' is a valid task.
+ */
+
+const char *
+isc_task_getname(isc_task_t *task);
+/*%<
+ * Get the name of 'task', as previously set using isc_task_setname().
+ *
+ * Notes:
+ *\li This function is for debugging purposes only.
+ *
+ * Requires:
+ *\li 'task' is a valid task.
+ *
+ * Returns:
+ *\li A non-NULL pointer to a null-terminated string.
+ * If the task has not been named, the string is
+ * empty.
+ *
+ */
+
+isc_nm_t *
+isc_task_getnetmgr(isc_task_t *task);
+
+void *
+isc_task_gettag(isc_task_t *task);
+/*%<
+ * Get the tag value for 'task', as previously set using isc_task_settag().
+ *
+ * Notes:
+ *\li This function is for debugging purposes only.
+ *
+ * Requires:
+ *\li 'task' is a valid task.
+ */
+
+void
+isc_task_setquantum(isc_task_t *task, unsigned int quantum);
+/*%<
+ * Set future 'task' quantum to 'quantum'. The current 'task' quantum will be
+ * kept for the current isc_task_run() loop, and will be changed for the next
+ * run. Therefore, the function is save to use from the event callback as it
+ * will not affect the current event loop processing.
+ */
+
+isc_result_t
+isc_task_beginexclusive(isc_task_t *task);
+/*%<
+ * Request exclusive access for 'task', which must be the calling
+ * task. Waits for any other concurrently executing tasks to finish their
+ * current event, and prevents any new events from executing in any of the
+ * tasks sharing a task manager with 'task'.
+ * It also pauses processing of network events in netmgr if it was provided
+ * when taskmgr was created.
+ *
+ * The exclusive access must be relinquished by calling
+ * isc_task_endexclusive() before returning from the current event handler.
+ *
+ * Requires:
+ *\li 'task' is the calling task.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS The current task now has exclusive access.
+ *\li #ISC_R_LOCKBUSY Another task has already requested exclusive
+ * access.
+ */
+
+void
+isc_task_endexclusive(isc_task_t *task);
+/*%<
+ * Relinquish the exclusive access obtained by isc_task_beginexclusive(),
+ * allowing other tasks to execute.
+ *
+ * Requires:
+ *\li 'task' is the calling task, and has obtained
+ * exclusive access by calling isc_task_spl().
+ */
+
+bool
+isc_task_exiting(isc_task_t *t);
+/*%<
+ * Returns true if the task is in the process of shutting down,
+ * false otherwise.
+ *
+ * Requires:
+ *\li 'task' is a valid task.
+ */
+
+void
+isc_task_setprivilege(isc_task_t *task, bool priv);
+/*%<
+ * Set or unset the task's "privileged" flag depending on the value of
+ * 'priv'.
+ *
+ * Under normal circumstances this flag has no effect on the task behavior,
+ * but when the task manager has been set to privileged execution mode via
+ * isc_taskmgr_setmode(), only tasks with the flag set will be executed,
+ * and all other tasks will wait until they're done. Once all privileged
+ * tasks have finished executing, the task manager resumes running
+ * non-privileged tasks.
+ *
+ * Requires:
+ *\li 'task' is a valid task.
+ */
+
+bool
+isc_task_getprivilege(isc_task_t *task);
+/*%<
+ * Returns the current value of the task's privilege flag.
+ *
+ * Requires:
+ *\li 'task' is a valid task.
+ */
+
+bool
+isc_task_privileged(isc_task_t *task);
+/*%<
+ * Returns true if the task's privilege flag is set *and* the task
+ * manager is currently in privileged mode.
+ *
+ * Requires:
+ *\li 'task' is a valid task.
+ */
+
+/*****
+ ***** Task Manager.
+ *****/
+
+void
+isc_taskmgr_attach(isc_taskmgr_t *, isc_taskmgr_t **);
+void
+isc_taskmgr_detach(isc_taskmgr_t **);
+/*%<
+ * Attach/detach the task manager.
+ */
+
+void
+isc_taskmgr_setmode(isc_taskmgr_t *manager, isc_taskmgrmode_t mode);
+isc_taskmgrmode_t
+isc_taskmgr_mode(isc_taskmgr_t *manager);
+/*%<
+ * Set/get the operating mode of the task manager. Valid modes are:
+ *
+ *\li isc_taskmgrmode_normal
+ *\li isc_taskmgrmode_privileged
+ *
+ * In privileged execution mode, only tasks that have had the "privilege"
+ * flag set via isc_task_setprivilege() can be executed. When all such
+ * tasks are complete, non-privileged tasks resume running. The task calling
+ * this function should be in task-exclusive mode; the privileged tasks
+ * will be run after isc_task_endexclusive() is called.
+ *
+ * Requires:
+ *
+ *\li 'manager' is a valid task manager.
+ */
+
+void
+isc_taskmgr_setexcltask(isc_taskmgr_t *mgr, isc_task_t *task);
+/*%<
+ * Set a task which will be used for all task-exclusive operations.
+ *
+ * Requires:
+ *\li 'manager' is a valid task manager.
+ *
+ *\li 'task' is a valid task.
+ */
+
+isc_result_t
+isc_taskmgr_excltask(isc_taskmgr_t *mgr, isc_task_t **taskp);
+/*%<
+ * Attach '*taskp' to the task set by isc_taskmgr_getexcltask().
+ * This task should be used whenever running in task-exclusive mode,
+ * so as to prevent deadlock between two exclusive tasks.
+ *
+ * Requires:
+ *\li 'manager' is a valid task manager.
+ *
+ *\li taskp != NULL && *taskp == NULL
+ */
+
+#ifdef HAVE_LIBXML2
+int
+isc_taskmgr_renderxml(isc_taskmgr_t *mgr, void *writer0);
+#endif /* ifdef HAVE_LIBXML2 */
+
+#ifdef HAVE_JSON_C
+isc_result_t
+isc_taskmgr_renderjson(isc_taskmgr_t *mgr, void *tasksobj0);
+#endif /* HAVE_JSON_C */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/taskpool.h b/lib/isc/include/isc/taskpool.h
new file mode 100644
index 0000000..cde2287
--- /dev/null
+++ b/lib/isc/include/isc/taskpool.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*****
+***** Module Info
+*****/
+
+/*! \file isc/taskpool.h
+ * \brief A task pool is a mechanism for sharing a small number of tasks
+ * among a large number of objects such that each object is
+ * assigned a unique task, but each task may be shared by several
+ * objects.
+ *
+ * Task pools are used to let objects that can exist in large
+ * numbers (e.g., zones) use tasks for synchronization without
+ * the memory overhead and unfair scheduling competition that
+ * could result from creating a separate task for each object.
+ */
+
+/***
+ *** Imports.
+ ***/
+
+#include <stdbool.h>
+
+#include <isc/lang.h>
+#include <isc/task.h>
+
+ISC_LANG_BEGINDECLS
+
+/*****
+***** Types.
+*****/
+
+typedef struct isc_taskpool isc_taskpool_t;
+
+/*****
+***** Functions.
+*****/
+
+isc_result_t
+isc_taskpool_create(isc_taskmgr_t *tmgr, isc_mem_t *mctx, unsigned int ntasks,
+ unsigned int quantum, bool priv, isc_taskpool_t **poolp);
+/*%<
+ * Create a task pool of "ntasks" tasks, each with quantum
+ * "quantum".
+ *
+ * Requires:
+ *
+ *\li 'tmgr' is a valid task manager.
+ *
+ *\li 'mctx' is a valid memory context.
+ *
+ *\li poolp != NULL && *poolp == NULL
+ *
+ * Ensures:
+ *
+ *\li On success, '*taskp' points to the new task pool.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *\li #ISC_R_UNEXPECTED
+ */
+
+void
+isc_taskpool_gettask(isc_taskpool_t *pool, isc_task_t **targetp);
+/*%<
+ * Attach to a task from the pool. Currently the next task is chosen
+ * from the pool at random. (This may be changed in the future to
+ * something that guaratees balance.)
+ */
+
+int
+isc_taskpool_size(isc_taskpool_t *pool);
+/*%<
+ * Returns the number of tasks in the task pool 'pool'.
+ */
+
+isc_result_t
+isc_taskpool_expand(isc_taskpool_t **sourcep, unsigned int size, bool priv,
+ isc_taskpool_t **targetp);
+
+/*%<
+ * If 'size' is larger than the number of tasks in the pool pointed to by
+ * 'sourcep', then a new taskpool of size 'size' is allocated, the existing
+ * tasks from are moved into it, additional tasks are created to bring the
+ * total number up to 'size', and the resulting pool is attached to
+ * 'targetp'.
+ *
+ * If 'size' is less than or equal to the tasks in pool 'source', then
+ * 'sourcep' is attached to 'targetp' without any other action being taken.
+ *
+ * In either case, 'sourcep' is detached.
+ *
+ * Requires:
+ *
+ * \li 'sourcep' is not NULL and '*source' is not NULL
+ * \li 'targetp' is not NULL and '*source' is NULL
+ *
+ * Ensures:
+ *
+ * \li On success, '*targetp' points to a valid task pool.
+ * \li On success, '*sourcep' points to NULL.
+ *
+ * Returns:
+ *
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOMEMORY
+ */
+
+void
+isc_taskpool_destroy(isc_taskpool_t **poolp);
+/*%<
+ * Destroy a task pool. The tasks in the pool are detached but not
+ * shut down.
+ *
+ * Requires:
+ * \li '*poolp' is a valid task pool.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/thread.h b/lib/isc/include/isc/thread.h
new file mode 100644
index 0000000..a70e2c7
--- /dev/null
+++ b/lib/isc/include/isc/thread.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file */
+
+#include <pthread.h>
+#if HAVE_THREADS_H
+#include <threads.h>
+#endif
+
+#if defined(HAVE_PTHREAD_NP_H)
+#include <pthread_np.h>
+#endif /* if defined(HAVE_PTHREAD_NP_H) */
+
+#include <isc/lang.h>
+#include <isc/result.h>
+
+extern thread_local size_t isc_tid_v;
+
+ISC_LANG_BEGINDECLS
+
+typedef pthread_t isc_thread_t;
+typedef void *isc_threadresult_t;
+typedef void *isc_threadarg_t;
+typedef isc_threadresult_t (*isc_threadfunc_t)(isc_threadarg_t);
+
+void
+isc_thread_create(isc_threadfunc_t, isc_threadarg_t, isc_thread_t *);
+
+void
+isc_thread_join(isc_thread_t thread, isc_threadresult_t *result);
+
+void
+isc_thread_yield(void);
+
+void
+isc_thread_setname(isc_thread_t thread, const char *name);
+
+#define isc_thread_self (uintptr_t) pthread_self
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/time.h b/lib/isc/include/isc/time.h
new file mode 100644
index 0000000..9c9da37
--- /dev/null
+++ b/lib/isc/include/isc/time.h
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file */
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+enum {
+ MS_PER_SEC = 1000, /*%< Milliseonds per second. */
+ US_PER_MS = 1000, /*%< Microseconds per millisecond. */
+ US_PER_SEC = 1000 * 1000, /*%< Microseconds per second. */
+ NS_PER_US = 1000, /*%< Nanoseconds per millisecond. */
+ NS_PER_MS = 1000 * 1000, /*%< Nanoseconds per microsecond. */
+ NS_PER_SEC = 1000 * 1000 * 1000, /*%< Nanoseconds per second. */
+};
+
+/***
+ *** Intervals
+ ***/
+
+/*!
+ * \brief
+ * The contents of this structure are private, and MUST NOT be accessed
+ * directly by callers.
+ *
+ * The contents are exposed only to allow callers to avoid dynamic allocation.
+ */
+struct isc_interval {
+ unsigned int seconds;
+ unsigned int nanoseconds;
+};
+
+extern const isc_interval_t *const isc_interval_zero;
+
+/*
+ * ISC_FORMATHTTPTIMESTAMP_SIZE needs to be 30 in C locale and potentially
+ * more for other locales to handle longer national abbreviations when
+ * expanding strftime's %a and %b.
+ */
+#define ISC_FORMATHTTPTIMESTAMP_SIZE 50
+
+ISC_LANG_BEGINDECLS
+
+void
+isc_interval_set(isc_interval_t *i, unsigned int seconds,
+ unsigned int nanoseconds);
+/*%<
+ * Set 'i' to a value representing an interval of 'seconds' seconds and
+ * 'nanoseconds' nanoseconds, suitable for use in isc_time_add() and
+ * isc_time_subtract().
+ *
+ * Requires:
+ *
+ *\li 't' is a valid pointer.
+ *\li nanoseconds < 1000000000.
+ */
+
+bool
+isc_interval_iszero(const isc_interval_t *i);
+/*%<
+ * Returns true iff. 'i' is the zero interval.
+ *
+ * Requires:
+ *
+ *\li 'i' is a valid pointer.
+ */
+
+unsigned int
+isc_interval_ms(const isc_interval_t *i);
+/*%<
+ * Returns interval 'i' expressed as a number of milliseconds.
+ *
+ * Requires:
+ *
+ *\li 'i' is a valid pointer.
+ */
+
+/***
+ *** Absolute Times
+ ***/
+
+/*%
+ * The contents of this structure are private, and MUST NOT be accessed
+ * directly by callers.
+ *
+ * The contents are exposed only to allow callers to avoid dynamic allocation.
+ */
+
+struct isc_time {
+ unsigned int seconds;
+ unsigned int nanoseconds;
+};
+
+extern const isc_time_t *const isc_time_epoch;
+
+void
+isc_time_set(isc_time_t *t, unsigned int seconds, unsigned int nanoseconds);
+/*%<
+ * Set 't' to a value which represents the given number of seconds and
+ * nanoseconds since 00:00:00 January 1, 1970, UTC.
+ *
+ * Notes:
+ *\li The Unix version of this call is equivalent to:
+ *\code
+ * isc_time_settoepoch(t);
+ * isc_interval_set(i, seconds, nanoseconds);
+ * isc_time_add(t, i, t);
+ *\endcode
+ *
+ * Requires:
+ *\li 't' is a valid pointer.
+ *\li nanoseconds < 1000000000.
+ */
+
+void
+isc_time_settoepoch(isc_time_t *t);
+/*%<
+ * Set 't' to the time of the epoch.
+ *
+ * Notes:
+ *\li The date of the epoch is platform-dependent.
+ *
+ * Requires:
+ *
+ *\li 't' is a valid pointer.
+ */
+
+bool
+isc_time_isepoch(const isc_time_t *t);
+/*%<
+ * Returns true iff. 't' is the epoch ("time zero").
+ *
+ * Requires:
+ *
+ *\li 't' is a valid pointer.
+ */
+
+isc_result_t
+isc_time_now(isc_time_t *t);
+/*%<
+ * Set 't' to the current absolute time.
+ *
+ * Requires:
+ *
+ *\li 't' is a valid pointer.
+ *
+ * Returns:
+ *
+ *\li Success
+ *\li Unexpected error
+ * Getting the time from the system failed.
+ *\li Out of range
+ * The time from the system is too large to be represented
+ * in the current definition of isc_time_t.
+ */
+
+isc_result_t
+isc_time_now_hires(isc_time_t *t);
+/*%<
+ * Set 't' to the current absolute time. Uses higher resolution clocks
+ * recommended when microsecond accuracy is required.
+ *
+ * Requires:
+ *
+ *\li 't' is a valid pointer.
+ *
+ * Returns:
+ *
+ *\li Success
+ *\li Unexpected error
+ * Getting the time from the system failed.
+ *\li Out of range
+ * The time from the system is too large to be represented
+ * in the current definition of isc_time_t.
+ */
+
+isc_result_t
+isc_time_nowplusinterval(isc_time_t *t, const isc_interval_t *i);
+/*%<
+ * Set *t to the current absolute time + i.
+ *
+ * Note:
+ *\li This call is equivalent to:
+ *
+ *\code
+ * isc_time_now(t);
+ * isc_time_add(t, i, t);
+ *\endcode
+ *
+ * Requires:
+ *
+ *\li 't' and 'i' are valid pointers.
+ *
+ * Returns:
+ *
+ *\li Success
+ *\li Unexpected error
+ * Getting the time from the system failed.
+ *\li Out of range
+ * The interval added to the time from the system is too large to
+ * be represented in the current definition of isc_time_t.
+ */
+
+int
+isc_time_compare(const isc_time_t *t1, const isc_time_t *t2);
+/*%<
+ * Compare the times referenced by 't1' and 't2'
+ *
+ * Requires:
+ *
+ *\li 't1' and 't2' are valid pointers.
+ *
+ * Returns:
+ *
+ *\li -1 t1 < t2 (comparing times, not pointers)
+ *\li 0 t1 = t2
+ *\li 1 t1 > t2
+ */
+
+isc_result_t
+isc_time_add(const isc_time_t *t, const isc_interval_t *i, isc_time_t *result);
+/*%<
+ * Add 'i' to 't', storing the result in 'result'.
+ *
+ * Requires:
+ *
+ *\li 't', 'i', and 'result' are valid pointers.
+ *
+ * Returns:
+ *\li Success
+ *\li Out of range
+ * The interval added to the time is too large to
+ * be represented in the current definition of isc_time_t.
+ */
+
+isc_result_t
+isc_time_subtract(const isc_time_t *t, const isc_interval_t *i,
+ isc_time_t *result);
+/*%<
+ * Subtract 'i' from 't', storing the result in 'result'.
+ *
+ * Requires:
+ *
+ *\li 't', 'i', and 'result' are valid pointers.
+ *
+ * Returns:
+ *\li Success
+ *\li Out of range
+ * The interval is larger than the time since the epoch.
+ */
+
+uint64_t
+isc_time_microdiff(const isc_time_t *t1, const isc_time_t *t2);
+/*%<
+ * Find the difference in microseconds between time t1 and time t2.
+ * t2 is the subtrahend of t1; ie, difference = t1 - t2.
+ *
+ * Requires:
+ *
+ *\li 't1' and 't2' are valid pointers.
+ *
+ * Returns:
+ *\li The difference of t1 - t2, or 0 if t1 <= t2.
+ */
+
+uint32_t
+isc_time_seconds(const isc_time_t *t);
+/*%<
+ * Return the number of seconds since the epoch stored in a time structure.
+ *
+ * Requires:
+ *
+ *\li 't' is a valid pointer.
+ */
+
+isc_result_t
+isc_time_secondsastimet(const isc_time_t *t, time_t *secondsp);
+/*%<
+ * Ensure the number of seconds in an isc_time_t is representable by a time_t.
+ *
+ * Notes:
+ *\li The number of seconds stored in an isc_time_t might be larger
+ * than the number of seconds a time_t is able to handle. Since
+ * time_t is mostly opaque according to the ANSI/ISO standard
+ * (essentially, all you can be sure of is that it is an arithmetic type,
+ * not even necessarily integral), it can be tricky to ensure that
+ * the isc_time_t is in the range a time_t can handle. Use this
+ * function in place of isc_time_seconds() any time you need to set a
+ * time_t from an isc_time_t.
+ *
+ * Requires:
+ *\li 't' is a valid pointer.
+ *
+ * Returns:
+ *\li Success
+ *\li Out of range
+ */
+
+uint32_t
+isc_time_nanoseconds(const isc_time_t *t);
+/*%<
+ * Return the number of nanoseconds stored in a time structure.
+ *
+ * Notes:
+ *\li This is the number of nanoseconds in excess of the number
+ * of seconds since the epoch; it will always be less than one
+ * full second.
+ *
+ * Requires:
+ *\li 't' is a valid pointer.
+ *
+ * Ensures:
+ *\li The returned value is less than 1*10^9.
+ */
+
+void
+isc_time_formattimestamp(const isc_time_t *t, char *buf, unsigned int len);
+/*%<
+ * Format the time 't' into the buffer 'buf' of length 'len',
+ * using a format like "30-Aug-2000 04:06:47.997" and the local time zone.
+ * If the text does not fit in the buffer, the result is indeterminate,
+ * but is always guaranteed to be null terminated.
+ *
+ * Requires:
+ *\li 'len' > 0
+ *\li 'buf' points to an array of at least len chars
+ *
+ */
+
+void
+isc_time_formathttptimestamp(const isc_time_t *t, char *buf, unsigned int len);
+/*%<
+ * Format the time 't' into the buffer 'buf' of length 'len',
+ * using a format like "Mon, 30 Aug 2000 04:06:47 GMT"
+ * If the text does not fit in the buffer, the result is indeterminate,
+ * but is always guaranteed to be null terminated.
+ *
+ * Requires:
+ *\li 'len' > 0
+ *\li 'buf' points to an array of at least len chars
+ *
+ */
+
+isc_result_t
+isc_time_parsehttptimestamp(char *input, isc_time_t *t);
+/*%<
+ * Parse the time in 'input' into the isc_time_t pointed to by 't',
+ * expecting a format like "Mon, 30 Aug 2000 04:06:47 GMT"
+ *
+ * Requires:
+ *\li 'buf' and 't' are not NULL.
+ */
+
+void
+isc_time_formatISO8601L(const isc_time_t *t, char *buf, unsigned int len);
+/*%<
+ * Format the time 't' into the buffer 'buf' of length 'len',
+ * using the ISO8601 format: "yyyy-mm-ddThh:mm:ss"
+ * If the text does not fit in the buffer, the result is indeterminate,
+ * but is always guaranteed to be null terminated.
+ *
+ * Requires:
+ *\li 'len' > 0
+ *\li 'buf' points to an array of at least len chars
+ *
+ */
+
+void
+isc_time_formatISO8601Lms(const isc_time_t *t, char *buf, unsigned int len);
+/*%<
+ * Format the time 't' into the buffer 'buf' of length 'len',
+ * using the ISO8601 format: "yyyy-mm-ddThh:mm:ss.sss"
+ * If the text does not fit in the buffer, the result is indeterminate,
+ * but is always guaranteed to be null terminated.
+ *
+ * Requires:
+ *\li 'len' > 0
+ *\li 'buf' points to an array of at least len chars
+ *
+ */
+
+void
+isc_time_formatISO8601Lus(const isc_time_t *t, char *buf, unsigned int len);
+/*%<
+ * Format the time 't' into the buffer 'buf' of length 'len',
+ * using the ISO8601 format: "yyyy-mm-ddThh:mm:ss.ssssss"
+ * If the text does not fit in the buffer, the result is indeterminate,
+ * but is always guaranteed to be null terminated.
+ *
+ * Requires:
+ *\li 'len' > 0
+ *\li 'buf' points to an array of at least len chars
+ *
+ */
+
+void
+isc_time_formatISO8601(const isc_time_t *t, char *buf, unsigned int len);
+/*%<
+ * Format the time 't' into the buffer 'buf' of length 'len',
+ * using the ISO8601 format: "yyyy-mm-ddThh:mm:ssZ"
+ * If the text does not fit in the buffer, the result is indeterminate,
+ * but is always guaranteed to be null terminated.
+ *
+ * Requires:
+ *\li 'len' > 0
+ *\li 'buf' points to an array of at least len chars
+ *
+ */
+
+void
+isc_time_formatISO8601ms(const isc_time_t *t, char *buf, unsigned int len);
+/*%<
+ * Format the time 't' into the buffer 'buf' of length 'len',
+ * using the ISO8601 format: "yyyy-mm-ddThh:mm:ss.sssZ"
+ * If the text does not fit in the buffer, the result is indeterminate,
+ * but is always guaranteed to be null terminated.
+ *
+ * Requires:
+ *\li 'len' > 0
+ *\li 'buf' points to an array of at least len chars
+ *
+ */
+
+void
+isc_time_formatISO8601us(const isc_time_t *t, char *buf, unsigned int len);
+/*%<
+ * Format the time 't' into the buffer 'buf' of length 'len',
+ * using the ISO8601 format: "yyyy-mm-ddThh:mm:ss.ssssssZ"
+ * If the text does not fit in the buffer, the result is indeterminate,
+ * but is always guaranteed to be null terminated.
+ *
+ * Requires:
+ *\li 'len' > 0
+ *\li 'buf' points to an array of at least len chars
+ *
+ */
+
+void
+isc_time_formatshorttimestamp(const isc_time_t *t, char *buf, unsigned int len);
+/*%<
+ * Format the time 't' into the buffer 'buf' of length 'len',
+ * using the format "yyyymmddhhmmsssss" useful for file timestamping.
+ * If the text does not fit in the buffer, the result is indeterminate,
+ * but is always guaranteed to be null terminated.
+ *
+ * Requires:
+ *\li 'len' > 0
+ *\li 'buf' points to an array of at least len chars
+ *
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/timer.h b/lib/isc/include/isc/timer.h
new file mode 100644
index 0000000..815cf42
--- /dev/null
+++ b/lib/isc/include/isc/timer.h
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*****
+***** Module Info
+*****/
+
+/*! \file isc/timer.h
+ * \brief Provides timers which are event sources in the task system.
+ *
+ * Three types of timers are supported:
+ *
+ *\li 'ticker' timers generate a periodic tick event.
+ *
+ *\li 'once' timers generate an idle timeout event if they are idle for too
+ * long, and generate a life timeout event if their lifetime expires.
+ * They are used to implement both (possibly expiring) idle timers and
+ * 'one-shot' timers.
+ *
+ *\li 'limited' timers generate a periodic tick event until they reach
+ * their lifetime when they generate a life timeout event.
+ *
+ *\li 'inactive' timers generate no events.
+ *
+ * Timers can change type. It is typical to create a timer as
+ * an 'inactive' timer and then change it into a 'ticker' or
+ * 'once' timer.
+ *
+ *\li MP:
+ * The module ensures appropriate synchronization of data structures it
+ * creates and manipulates.
+ * Clients of this module must not be holding a timer's task's lock when
+ * making a call that affects that timer. Failure to follow this rule
+ * can result in deadlock.
+ * The caller must ensure that isc_timermgr_destroy() is called only
+ * once for a given manager.
+ *
+ * \li Reliability:
+ * No anticipated impact.
+ *
+ * \li Resources:
+ * TBS
+ *
+ * \li Security:
+ * No anticipated impact.
+ *
+ * \li Standards:
+ * None.
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <stdbool.h>
+
+#include <isc/event.h>
+#include <isc/eventclass.h>
+#include <isc/lang.h>
+#include <isc/time.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Types
+ ***/
+
+/*% Timer Type */
+typedef enum {
+ isc_timertype_undefined = -1, /*%< Undefined */
+ isc_timertype_ticker = 0, /*%< Ticker */
+ isc_timertype_once = 1, /*%< Once */
+ isc_timertype_limited = 2, /*%< Limited */
+ isc_timertype_inactive = 3 /*%< Inactive */
+} isc_timertype_t;
+
+typedef struct isc_timerevent isc_timerevent_t;
+
+struct isc_timerevent {
+ struct isc_event common;
+ isc_time_t due;
+ ISC_LINK(isc_timerevent_t) ev_timerlink;
+};
+
+#define ISC_TIMEREVENT_FIRSTEVENT (ISC_EVENTCLASS_TIMER + 0)
+#define ISC_TIMEREVENT_TICK (ISC_EVENTCLASS_TIMER + 1)
+#define ISC_TIMEREVENT_IDLE (ISC_EVENTCLASS_TIMER + 2)
+#define ISC_TIMEREVENT_LIFE (ISC_EVENTCLASS_TIMER + 3)
+#define ISC_TIMEREVENT_LASTEVENT (ISC_EVENTCLASS_TIMER + 65535)
+
+/***
+ *** Timer and Timer Manager Functions
+ ***
+ *** Note: all Ensures conditions apply only if the result is success for
+ *** those functions which return an isc_result_t.
+ ***/
+
+isc_result_t
+isc_timer_create(isc_timermgr_t *manager, isc_timertype_t type,
+ const isc_time_t *expires, const isc_interval_t *interval,
+ isc_task_t *task, isc_taskaction_t action, void *arg,
+ isc_timer_t **timerp);
+/*%<
+ * Create a new 'type' timer managed by 'manager'. The timers parameters
+ * are specified by 'expires' and 'interval'. Events will be posted to
+ * 'task' and when dispatched 'action' will be called with 'arg' as the
+ * arg value. The new timer is returned in 'timerp'.
+ *
+ * Notes:
+ *
+ *\li For ticker timers, the timer will generate a 'tick' event every
+ * 'interval' seconds. The value of 'expires' is ignored.
+ *
+ *\li For once timers, 'expires' specifies the time when a life timeout
+ * event should be generated. If 'expires' is 0 (the epoch), then no life
+ * timeout will be generated. 'interval' specifies how long the timer
+ * can be idle before it generates an idle timeout. If 0, then no
+ * idle timeout will be generated.
+ *
+ *\li If 'expires' is NULL, the epoch will be used.
+ *
+ * If 'interval' is NULL, the zero interval will be used.
+ *
+ * Requires:
+ *
+ *\li 'manager' is a valid manager
+ *
+ *\li 'task' is a valid task
+ *
+ *\li 'action' is a valid action
+ *
+ *\li 'expires' points to a valid time, or is NULL.
+ *
+ *\li 'interval' points to a valid interval, or is NULL.
+ *
+ *\li type == isc_timertype_inactive ||
+ * ('expires' and 'interval' are not both 0)
+ *
+ *\li 'timerp' is a valid pointer, and *timerp == NULL
+ *
+ * Ensures:
+ *
+ *\li '*timerp' is attached to the newly created timer
+ *
+ *\li The timer is attached to the task
+ *
+ *\li An idle timeout will not be generated until at least Now + the
+ * timer's interval if 'timer' is a once timer with a non-zero
+ * interval.
+ *
+ * Returns:
+ *
+ *\li Success
+ *\li No memory
+ *\li Unexpected error
+ */
+
+isc_result_t
+isc_timer_reset(isc_timer_t *timer, isc_timertype_t type,
+ const isc_time_t *expires, const isc_interval_t *interval,
+ bool purge);
+/*%<
+ * Change the timer's type, expires, and interval values to the given
+ * values. If 'purge' is TRUE, any pending events from this timer
+ * are purged from its task's event queue.
+ *
+ * Notes:
+ *
+ *\li If 'expires' is NULL, the epoch will be used.
+ *
+ *\li If 'interval' is NULL, the zero interval will be used.
+ *
+ * Requires:
+ *
+ *\li 'timer' is a valid timer
+ *
+ *\li The same requirements that isc_timer_create() imposes on 'type',
+ * 'expires' and 'interval' apply.
+ *
+ * Ensures:
+ *
+ *\li An idle timeout will not be generated until at least Now + the
+ * timer's interval if 'timer' is a once timer with a non-zero
+ * interval.
+ *
+ * Returns:
+ *
+ *\li Success
+ *\li No memory
+ *\li Unexpected error
+ */
+
+isc_result_t
+isc_timer_touch(isc_timer_t *timer);
+/*%<
+ * Set the last-touched time of 'timer' to the current time.
+ *
+ * Requires:
+ *
+ *\li 'timer' is a valid once timer.
+ *
+ * Ensures:
+ *
+ *\li An idle timeout will not be generated until at least Now + the
+ * timer's interval if 'timer' is a once timer with a non-zero
+ * interval.
+ *
+ * Returns:
+ *
+ *\li Success
+ *\li Unexpected error
+ */
+
+void
+isc_timer_destroy(isc_timer_t **timerp);
+/*%<
+ * Destroy *timerp.
+ *
+ * Requires:
+ *
+ *\li 'timerp' points to a valid timer.
+ *
+ * Ensures:
+ *
+ *\li *timerp is NULL.
+ *
+ *\code
+ * The timer will be shutdown
+ *
+ * The timer will detach from its task
+ *
+ * All resources used by the timer have been freed
+ *
+ * Any events already posted by the timer will be purged.
+ * Therefore, if isc_timer_destroy() is called in the context
+ * of the timer's task, it is guaranteed that no more
+ * timer event callbacks will run after the call.
+ *
+ * If this function is called from the timer event callback
+ * the event itself must be destroyed before the timer
+ * itself.
+ *\endcode
+ */
+
+isc_timertype_t
+isc_timer_gettype(isc_timer_t *timer);
+/*%<
+ * Return the timer type.
+ *
+ * Requires:
+ *
+ *\li 'timer' to be a valid timer.
+ */
+
+void
+isc_timermgr_poke(isc_timermgr_t *m);
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/tls.h b/lib/isc/include/isc/tls.h
new file mode 100644
index 0000000..efffffb
--- /dev/null
+++ b/lib/isc/include/isc/tls.h
@@ -0,0 +1,587 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <isc/mem.h>
+#include <isc/region.h>
+#include <isc/result.h>
+#include <isc/types.h>
+
+typedef struct ssl_ctx_st isc_tlsctx_t;
+typedef struct ssl_st isc_tls_t;
+
+typedef struct x509_store_st isc_tls_cert_store_t;
+
+void
+isc_tlsctx_free(isc_tlsctx_t **ctpx);
+/*%<
+ * Free a TLS client or server context.
+ *
+ * Requires:
+ *\li 'ctxp' != NULL and '*ctxp' != NULL.
+ */
+
+void
+isc_tlsctx_attach(isc_tlsctx_t *src, isc_tlsctx_t **ptarget);
+/*%<
+ * Attach to the TLS context.
+ *
+ * Requires:
+ *\li 'src' != NULL;
+ *\li 'ptarget' != NULL;
+ *\li '*ptarget' == NULL.
+ */
+
+isc_result_t
+isc_tlsctx_createserver(const char *keyfile, const char *certfile,
+ isc_tlsctx_t **ctxp);
+/*%<
+ * Set up a TLS server context, using the key and certificate specified in
+ * 'keyfile' and 'certfile', or a self-generated ephemeral key and
+ * certificdate if both 'keyfile' and 'certfile' are NULL.
+ *
+ * Requires:
+ *\li 'ctxp' != NULL and '*ctxp' == NULL.
+ *\li 'keyfile' and 'certfile' are either both NULL or both non-NULL.
+ */
+
+isc_result_t
+isc_tlsctx_createclient(isc_tlsctx_t **ctxp);
+/*%<
+ * Set up a TLS client context.
+ *
+ * Requires:
+ *\li 'ctxp' != NULL and '*ctxp' == NULL.
+ */
+
+isc_result_t
+isc_tlsctx_load_certificate(isc_tlsctx_t *ctx, const char *keyfile,
+ const char *certfile);
+/*%<
+ * Load a TLS certificate into a TLS context.
+ *
+ * Requires:
+ *\li 'ctx' != NULL;
+ *\li 'keyfile' and 'certfile' are both non-NULL.
+ */
+
+typedef enum isc_tls_protocol_version {
+ /* these must be the powers of two */
+ ISC_TLS_PROTO_VER_1_2 = 1 << 0,
+ ISC_TLS_PROTO_VER_1_3 = 1 << 1,
+ ISC_TLS_PROTO_VER_UNDEFINED,
+} isc_tls_protocol_version_t;
+
+void
+isc_tlsctx_set_protocols(isc_tlsctx_t *ctx, const uint32_t tls_versions);
+/*%<
+ * Sets the supported TLS protocol versions via the 'tls_versions' bit
+ * set argument (see `isc_tls_protocol_version_t` enum for the
+ * expected values).
+ *
+ * Requires:
+ *\li 'ctx' != NULL;
+ *\li 'tls_versions' != 0.
+ */
+
+bool
+isc_tls_protocol_supported(const isc_tls_protocol_version_t tls_ver);
+/*%<
+ * Check in runtime that the specified TLS protocol versions is supported.
+ */
+
+isc_tls_protocol_version_t
+isc_tls_protocol_name_to_version(const char *name);
+/*%<
+ * Convert the protocol version string into the version of
+ * 'isc_tls_protocol_version_t' type.
+ * Requires:
+ *\li 'name' != NULL.
+ */
+
+bool
+isc_tlsctx_load_dhparams(isc_tlsctx_t *ctx, const char *dhparams_file);
+/*%<
+ * Load Diffie-Hellman parameters file and apply it to the given TLS context
+ * 'ctx'.
+ *
+ * Requires:
+ * \li 'ctx' != NULL;
+ * \li 'dhaprams_file' a valid pointer to a non empty string.
+ */
+
+bool
+isc_tls_cipherlist_valid(const char *cipherlist);
+/*%<
+ * Check if cipher list string is valid.
+ *
+ * Requires:
+ * \li 'cipherlist' a valid pointer to a non empty string.
+ */
+
+void
+isc_tlsctx_set_cipherlist(isc_tlsctx_t *ctx, const char *cipherlist);
+/*%<
+ * Set cipher list string for on the given TLS context 'ctx'.
+ *
+ * Requires:
+ * \li 'ctx' != NULL;
+ * \li 'cipherlist' a valid pointer to a non empty string.
+ */
+
+void
+isc_tlsctx_prefer_server_ciphers(isc_tlsctx_t *ctx, const bool prefer);
+/*%<
+ * Make the given TLS context 'ctx' to prefer or to not prefer
+ * server side ciphers during the ciphers negotiation.
+ *
+ * Requires:
+ * \li 'ctx' != NULL.
+ */
+
+void
+isc_tlsctx_session_tickets(isc_tlsctx_t *ctx, const bool use);
+/*%<
+ * Enable/Disable stateless session resumptions tickets on the given
+ * TLS context 'ctx' (see RFC5077).
+ *
+ * Requires:
+ * \li 'ctx' != NULL.
+ */
+
+isc_tls_t *
+isc_tls_create(isc_tlsctx_t *ctx);
+/*%<
+ * Set up the structure to hold data for a new TLS connection.
+ *
+ * Requires:
+ *\li 'ctx' != NULL.
+ */
+
+void
+isc_tls_free(isc_tls_t **tlsp);
+/*%<
+ * Free a TLS structure.
+ *
+ * Requires:
+ *\li 'tlsp' != NULL and '*tlsp' != NULL.
+ */
+
+const char *
+isc_tls_verify_peer_result_string(isc_tls_t *tls);
+/*%<
+ * Return a user readable description of a remote peer's certificate
+ * validation.
+ *
+ * Requires:
+ *\li 'tls' != NULL.
+ */
+
+#if HAVE_LIBNGHTTP2
+void
+isc_tlsctx_enable_http2client_alpn(isc_tlsctx_t *ctx);
+void
+isc_tlsctx_enable_http2server_alpn(isc_tlsctx_t *ctx);
+/*%<
+ * Enable HTTP/2 Application Layer Protocol Negotation for 'ctx'.
+ *
+ * Requires:
+ *\li 'ctx' is not NULL.
+ */
+#endif /* HAVE_LIBNGHTTP2 */
+
+void
+isc_tls_get_selected_alpn(isc_tls_t *tls, const unsigned char **alpn,
+ unsigned int *alpnlen);
+
+#define ISC_TLS_DOT_PROTO_ALPN_ID "dot"
+#define ISC_TLS_DOT_PROTO_ALPN_ID_LEN 3
+
+void
+isc_tlsctx_enable_dot_client_alpn(isc_tlsctx_t *ctx);
+void
+isc_tlsctx_enable_dot_server_alpn(isc_tlsctx_t *ctx);
+/*%<
+ * Enable DoT Application Layer Protocol Negotation for 'ctx'.
+ *
+ * Requires:
+ *\li 'ctx' is not NULL.
+ */
+
+isc_result_t
+isc_tlsctx_enable_peer_verification(isc_tlsctx_t *ctx, const bool is_server,
+ isc_tls_cert_store_t *store,
+ const char *hostname,
+ bool hostname_ignore_subject);
+/*%<
+ * Enable peer certificate and, optionally, hostname (for client contexts)
+ * verification.
+ *
+ * Requires:
+ *\li 'ctx' is not NULL;
+ *\li 'store' is not NULL.
+ */
+
+isc_result_t
+isc_tlsctx_load_client_ca_names(isc_tlsctx_t *ctx, const char *ca_bundle_file);
+/*%<
+ * Load the list of CA-certificate names from a CA-bundle file to
+ * send by the server to a client when requesting a peer certificate.
+ * Usually used in conjunction with
+ * isc_tlsctx_enable_peer_validation().
+ *
+ * Requires:
+ *\li 'ctx' is not NULL;
+ *\li 'ca_bundle_file' is not NULL.
+ */
+
+isc_result_t
+isc_tls_cert_store_create(const char *ca_bundle_filename,
+ isc_tls_cert_store_t **pstore);
+/*%<
+ * Create X509 certificate store. The 'ca_bundle_filename' might be
+ * 'NULL' or an empty string, which means use the default system wide
+ * bundle/directory.
+ *
+ * Requires:
+ *\li 'pstore' is a valid pointer to a pointer containing 'NULL'.
+ */
+
+void
+isc_tls_cert_store_free(isc_tls_cert_store_t **pstore);
+/*%<
+ * Free X509 certificate store.
+ *
+ * Requires:
+ *\li 'pstore' is a valid pointer to a pointer containing a non-'NULL' value.
+ */
+
+typedef struct isc_tlsctx_client_session_cache isc_tlsctx_client_session_cache_t;
+/*%<
+ * TLS client session cache is an object which allows efficient
+ * storing and retrieval of previously saved TLS sessions so that they
+ * can be resumed. This object is supposed to be a foundation for
+ * implementing TLS session resumption - a standard technique to
+ * reduce the cost of re-establishing a connection to the remote
+ * server endpoint.
+ *
+ * OpenSSL does server-side TLS session caching transparently by
+ * default. However, on the client-side, a TLS session to resume must
+ * be manually specified when establishing the TLS connection. The TLS
+ * client session cache is precisely the foundation for that.
+ *
+ * The cache has been designed to have the following characteristics:
+ *
+ * - Fixed maximal number of entries to not keep too many obsolete
+ * sessions within the cache;
+ *
+ * - The cache is indexed by character string keys. Each string is a
+ * key representing a remote endpoint (unique remote endpoint name,
+ * e.g. combination of the remote IP address and port);
+ *
+ * - Least Recently Used (LRU) cache replacement policy while allowing
+ * easy removal of obsolete entries;
+ *
+ * - Ability to store multiple TLS sessions associated with the given
+ * key (remote endpoint name). This characteristic is important if
+ * multiple connections to the same remote server can be established;
+ *
+ * - Ability to efficiently retrieve the most recent TLS sessions
+ * associated with the key (remote endpoint name).
+ *
+ * Because of these characteristics, the cache will end up having the
+ * necessary amount of resumable TLS session parameters to the most
+ * used remote endpoints ("hot entries") after a short period of
+ * initial use ("warmup").
+ *
+ * Attempting to resume TLS sessions is an optimisation, which is not
+ * guaranteed to succeed because it requires the same session to be
+ * present in the server session caches. If it is not the case, the
+ * usual handshake procedure takes place. However, when session
+ * resumption is successful, it reduces the amount of the
+ * computational resources required as well as the amount of data to
+ * be transmitted when (re)establishing the connection. Also, it
+ * reduces round trip time (by reducing the number of packets to
+ * transmit).
+ *
+ * This optimisation is important in the context of DNS because the
+ * amount of data within the full handshake messages might be
+ * comparable to or surpass the size of a typical DNS message.
+ */
+
+void
+isc_tlsctx_client_session_cache_create(
+ isc_mem_t *mctx, isc_tlsctx_t *ctx, const size_t max_entries,
+ isc_tlsctx_client_session_cache_t **cachep);
+/*%<
+ * Create a new TLS client session cache object.
+ *
+ * Requires:
+ *\li 'mctx' is a valid memory context object;
+ *\li 'ctx' is a valid TLS context object;
+ *\li 'max_entries' is a positive number;
+ *\li 'cachep' is a valid pointer to a pointer which must be equal to NULL.
+ */
+
+void
+isc_tlsctx_client_session_cache_attach(
+ isc_tlsctx_client_session_cache_t *source,
+ isc_tlsctx_client_session_cache_t **targetp);
+/*%<
+ * Create a reference to the TLS client session cache object.
+ *
+ * Requires:
+ *\li 'source' is a valid TLS client session cache object;
+ *\li 'targetp' is a valid pointer to a pointer which must equal NULL.
+ */
+
+void
+isc_tlsctx_client_session_cache_detach(
+ isc_tlsctx_client_session_cache_t **cachep);
+/*%<
+ * Remove a reference to the TLS client session cache object.
+ *
+ * Requires:
+ *\li 'cachep' is a pointer to a pointer to a valid TLS client session cache
+ *object.
+ */
+
+void
+isc_tlsctx_client_session_cache_keep(isc_tlsctx_client_session_cache_t *cache,
+ char *remote_peer_name, isc_tls_t *tls);
+/*%<
+ * Add a resumable TLS client session within 'tls' to the TLS client
+ * session cache object 'cache' and associate it with
+ * 'remote_server_name' string. Also, the oldest entry from the cache
+ * might get removed if the cache is full.
+ *
+ * Requires:
+ *\li 'cache' is a pointer to a valid TLS client session cache object;
+ *\li 'remote_peer_name' is a pointer to a non empty character string;
+ *\li 'tls' is a valid, non-'NULL' pointer to a TLS connection state object.
+ */
+
+void
+isc_tlsctx_client_session_cache_keep_sockaddr(
+ isc_tlsctx_client_session_cache_t *cache, isc_sockaddr_t *remote_peer,
+ isc_tls_t *tls);
+/*%<
+ * The same as 'isc_tlsctx_client_session_cache_keep()', but uses a
+ * 'isc_sockaddr_t' as a key, instead of a character string.
+ *
+ * Requires:
+ *\li 'remote_peer' is a valid, non-'NULL', pointer to an 'isc_sockaddr_t'
+ *object.
+ */
+
+void
+isc_tlsctx_client_session_cache_reuse(isc_tlsctx_client_session_cache_t *cache,
+ char *remote_server_name, isc_tls_t *tls);
+/*%
+ * Try to restore a previously stored TLS session denoted by a remote
+ * server name as a key ('remote_server_name') into the given TLS
+ * connection state object ('tls'). The successfully restored session
+ * gets removed from the cache.
+ *
+ * Requires:
+ *\li 'cache' is a pointer to a valid TLS client session cache object;
+ *\li 'remote_peer_name' is a pointer to a non empty character string;
+ *\li 'tls' is a valid, non-'NULL' pointer to a TLS connection state object.
+ */
+
+void
+isc_tlsctx_client_session_cache_reuse_sockaddr(
+ isc_tlsctx_client_session_cache_t *cache, isc_sockaddr_t *remote_peer,
+ isc_tls_t *tls);
+/*%<
+ * The same as 'isc_tlsctx_client_session_cache_reuse()', but uses a
+ * 'isc_sockaddr_t' as a key, instead of a character string.
+ *
+ * Requires:
+ *\li 'remote_peer' is a valid, non-'NULL' pointer to an 'isc_sockaddr_t'
+ *object.
+ */
+
+const isc_tlsctx_t *
+isc_tlsctx_client_session_cache_getctx(isc_tlsctx_client_session_cache_t *cache);
+/*%<
+ * Returns a TLS context associated with the given TLS client
+ * session cache object. The function is intended to be used to
+ * implement the sanity checks ('INSIST()'s and 'REQUIRE()'s).
+ *
+ * Requires:
+ *\li 'cache' is a pointer to a valid TLS client session cache object.
+ */
+
+#define ISC_TLSCTX_CLIENT_SESSION_CACHE_DEFAULT_SIZE (150)
+/*%<
+ * The default maximum size of a TLS client session cache. The value
+ * should be large enough to hold enough sessions to successfully
+ * re-establish connections to the most remote TLS servers, but not
+ * too big to avoid keeping too much obsolete sessions.
+ */
+
+typedef struct isc_tlsctx_cache isc_tlsctx_cache_t;
+/*%<
+ * The TLS context cache is an object which allows retrieving a
+ * previously created TLS context based on the following tuple:
+ *
+ * 1. The name of a TLS entry, as defined in the configuration file;
+ * 2. A transport type. Currently, only TLS (DoT) and HTTPS (DoH) are
+ * supported;
+ * 3. An IP address family (AF_INET or AF_INET6).
+ *
+ * There are multiple uses for this object:
+ *
+ * First, it allows reuse of client-side contexts during zone transfers.
+ * That, in turn, allows use of session caches associated with these
+ * contexts, which enables TLS session resumption, making establishment
+ * of XoT connections faster and computationally cheaper.
+ *
+ * Second, it can be extended to be used as storage for TLS context related
+ * data, as defined in 'tls' statements in the configuration file (for
+ * example, CA-bundle intermediate certificate storage, client-side contexts
+ * with pre-loaded certificates in a case of Mutual TLS, etc). This will
+ * be used to implement Strict/Mutual TLS.
+ *
+ * Third, it avoids creating an excessive number of server-side TLS
+ * contexts, which might help to reduce the number of contexts
+ * created during server initialisation and reconfiguration.
+ */
+
+typedef enum {
+ isc_tlsctx_cache_none = 0,
+ isc_tlsctx_cache_tls,
+ isc_tlsctx_cache_https,
+ isc_tlsctx_cache_count
+} isc_tlsctx_cache_transport_t;
+/*%< TLS context cache transport type values. */
+
+void
+isc_tlsctx_cache_create(isc_mem_t *mctx, isc_tlsctx_cache_t **cachep);
+/*%<
+ * Create a new TLS context cache object.
+ *
+ * Requires:
+ *\li 'mctx' is a valid memory context;
+ *\li 'cachep' is a valid pointer to a pointer which must be equal to NULL.
+ */
+
+void
+isc_tlsctx_cache_attach(isc_tlsctx_cache_t *source,
+ isc_tlsctx_cache_t **targetp);
+/*%<
+ * Create a reference to the TLS context cache object.
+ *
+ * Requires:
+ *\li 'source' is a valid TLS context cache object;
+ *\li 'targetp' is a valid pointer to a pointer which must equal NULL.
+ */
+
+void
+isc_tlsctx_cache_detach(isc_tlsctx_cache_t **cachep);
+/*%<
+ * Remove a reference to the TLS context cache object.
+ *
+ * Requires:
+ *\li 'cachep' is a pointer to a pointer to a valid TLS
+ * context cache object.
+ */
+
+isc_result_t
+isc_tlsctx_cache_add(
+ isc_tlsctx_cache_t *cache, const char *name,
+ const isc_tlsctx_cache_transport_t transport, const uint16_t family,
+ isc_tlsctx_t *ctx, isc_tls_cert_store_t *store,
+ isc_tlsctx_client_session_cache_t *client_sess_cache,
+ isc_tlsctx_t **pfound, isc_tls_cert_store_t **pfound_store,
+ isc_tlsctx_client_session_cache_t **pfound_client_sess_cache);
+/*%<
+ *
+ * Add a new TLS context and its associated data to the TLS context
+ * cache. 'pfound' is an optional pointer, which can be used to
+ * retrieve an already existing TLS context object in a case it
+ * exists.
+ *
+ * The passed certificates store object ('store') possession is
+ * transferred to the cache object in a case of success. In some cases
+ * it might be destroyed immediately upon the call completion.
+ *
+ * The possession of the passed TLS client session cache
+ * ('client_sess_cache') is also transferred to the cache object in a
+ * case of success.
+ *
+ * Requires:
+ *\li 'cache' is a valid pointer to a TLS context cache object;
+ *\li 'name' is a valid pointer to a non-empty string;
+ *\li 'transport' is a valid transport identifier (currently only
+ * TLS/DoT and HTTPS/DoH are supported);
+ *\li 'family' - either 'AF_INET' or 'AF_INET6';
+ *\li 'ctx' - a valid pointer to a valid TLS context object;
+ *\li 'store' - a valid pointer to a valid TLS certificates store object or
+ * 'NULL';
+ *\li 'client_sess_cache' - a valid pointer to a valid TLS client sessions
+ *cache object or 'NULL.
+ *
+ * Returns:
+ *\li #ISC_R_EXISTS - node of the same key already exists;
+ *\li #ISC_R_SUCCESS - the new entry has been added successfully.
+ */
+
+isc_result_t
+isc_tlsctx_cache_find(
+ isc_tlsctx_cache_t *cache, const char *name,
+ const isc_tlsctx_cache_transport_t transport, const uint16_t family,
+ isc_tlsctx_t **pctx, isc_tls_cert_store_t **pstore,
+ isc_tlsctx_client_session_cache_t **pfound_client_sess_cache);
+/*%<
+ * Look up a TLS context and its associated data in the TLS context cache.
+ *
+ * Requires:
+ *\li 'cache' is a valid pointer to a TLS context cache object;
+ *\li 'name' is a valid pointer to a non empty string;
+ *\li 'transport' - a valid transport identifier (currently only
+ * TLS/DoT and HTTPS/DoH are supported;
+ *\li 'family' - either 'AF_INET' or 'AF_INET6';
+ *\li 'pctx' - a valid pointer to a non-NULL pointer;
+ *\li 'pstore' - a valid pointer to a non-NULL pointer or 'NULL'.
+ *\li 'pfound_client_sess_cache' - a valid pointer to a non-NULL pointer or
+ *'NULL'.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS - the context has been found;
+ *\li #ISC_R_NOTFOUND - the context has not been found. In such a case,
+ * 'pstore' still might get initialised as there is one to many
+ * relation between stores and contexts.
+ */
+
+void
+isc_tlsctx_set_random_session_id_context(isc_tlsctx_t *ctx);
+/*%<
+ * Set context within which session can be reused to a randomly
+ * generated value. This one is used for TLS session resumption using
+ * session IDs. See OpenSSL documentation for
+ * 'SSL_CTX_set_session_id_context()'.
+ *
+ * It might be worth noting that usually session ID contexts are kept
+ * static for an application and particular certificate
+ * combination. However, for the cases when exporting server side TLS
+ * session cache to/loading from external memory is not required, we
+ * might use random IDs just fine. See,
+ * e.g. 'ngx_ssl_session_id_context()' in NGINX for an example of how
+ * a session ID might be obtained.
+ *
+ * Requires:
+ *\li 'ctx' - a valid non-NULL pointer;
+ */
diff --git a/lib/isc/include/isc/tm.h b/lib/isc/include/isc/tm.h
new file mode 100644
index 0000000..58feec4
--- /dev/null
+++ b/lib/isc/include/isc/tm.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isc/tm.h
+ * Provides portable conversion routines for struct tm.
+ */
+#include <time.h>
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+time_t
+isc_tm_timegm(struct tm *tm);
+/*
+ * Convert a tm structure to time_t, using UTC rather than the local
+ * time zone.
+ */
+
+char *
+isc_tm_strptime(const char *buf, const char *fmt, struct tm *tm);
+/*
+ * Parse a formatted date string into struct tm.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/types.h b/lib/isc/include/isc/types.h
new file mode 100644
index 0000000..7e0fc02
--- /dev/null
+++ b/lib/isc/include/isc/types.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <isc/result.h>
+
+/*! \file isc/types.h
+ * \brief
+ * OS-specific types, from the OS-specific include directories.
+ */
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/offset.h>
+
+/*
+ * XXXDCL This is just for ISC_LIST and ISC_LINK, but gets all of the other
+ * list macros too.
+ */
+#include <isc/list.h>
+
+/* Core Types. Alphabetized by defined type. */
+
+typedef struct isc_astack isc_astack_t; /*%< Array-based fast stack */
+typedef struct isc_appctx isc_appctx_t; /*%< Application context */
+typedef struct isc_buffer isc_buffer_t; /*%< Buffer */
+typedef ISC_LIST(isc_buffer_t) isc_bufferlist_t; /*%< Buffer List */
+typedef struct isc_constregion isc_constregion_t; /*%< Const region */
+typedef struct isc_consttextregion isc_consttextregion_t; /*%< Const Text Region
+ */
+typedef struct isc_counter isc_counter_t; /*%< Counter */
+typedef struct isc_event isc_event_t; /*%< Event */
+typedef ISC_LIST(isc_event_t) isc_eventlist_t; /*%< Event List */
+typedef unsigned int isc_eventtype_t; /*%< Event Type */
+typedef struct isc_hash isc_hash_t; /*%< Hash */
+typedef struct isc_httpd isc_httpd_t; /*%< HTTP client */
+typedef void(isc_httpdfree_t)(isc_buffer_t *, void *); /*%< HTTP free function
+ */
+typedef struct isc_httpdmgr isc_httpdmgr_t; /*%< HTTP manager */
+typedef struct isc_httpdurl isc_httpdurl_t; /*%< HTTP URL */
+typedef void(isc_httpdondestroy_t)(void *); /*%< Callback on destroying httpd */
+typedef struct isc_interface isc_interface_t; /*%< Interface */
+typedef struct isc_interfaceiter isc_interfaceiter_t; /*%< Interface Iterator */
+typedef struct isc_interval isc_interval_t; /*%< Interval */
+typedef struct isc_lex isc_lex_t; /*%< Lex */
+typedef struct isc_log isc_log_t; /*%< Log */
+typedef struct isc_logcategory isc_logcategory_t; /*%< Log Category */
+typedef struct isc_logconfig isc_logconfig_t; /*%< Log Configuration */
+typedef struct isc_logmodule isc_logmodule_t; /*%< Log Module */
+typedef struct isc_mem isc_mem_t; /*%< Memory */
+typedef struct isc_mempool isc_mempool_t; /*%< Memory Pool */
+typedef struct isc_netaddr isc_netaddr_t; /*%< Net Address */
+typedef struct isc_netprefix isc_netprefix_t; /*%< Net Prefix */
+typedef struct isc_nm isc_nm_t; /*%< Network manager */
+typedef struct isc_nmsocket isc_nmsocket_t; /*%< Network manager socket */
+typedef struct isc_nmhandle isc_nmhandle_t; /*%< Network manager handle */
+typedef struct isc_portset isc_portset_t; /*%< Port Set */
+typedef struct isc_quota isc_quota_t; /*%< Quota */
+typedef struct isc_ratelimiter isc_ratelimiter_t; /*%< Rate Limiter */
+typedef struct isc_region isc_region_t; /*%< Region */
+typedef uint64_t isc_resourcevalue_t; /*%< Resource Value */
+typedef struct isc_rwlock isc_rwlock_t; /*%< Read Write Lock */
+typedef struct isc_sockaddr isc_sockaddr_t; /*%< Socket Address */
+typedef ISC_LIST(isc_sockaddr_t) isc_sockaddrlist_t; /*%< Socket Address List
+ * */
+typedef struct isc_stats isc_stats_t; /*%< Statistics */
+typedef int_fast64_t isc_statscounter_t;
+typedef struct isc_symtab isc_symtab_t; /*%< Symbol Table */
+typedef struct isc_task isc_task_t; /*%< Task */
+typedef ISC_LIST(isc_task_t) isc_tasklist_t; /*%< Task List */
+typedef struct isc_taskmgr isc_taskmgr_t; /*%< Task Manager */
+typedef struct isc_textregion isc_textregion_t; /*%< Text Region */
+typedef struct isc_time isc_time_t; /*%< Time */
+typedef struct isc_timer isc_timer_t; /*%< Timer */
+typedef struct isc_timermgr isc_timermgr_t; /*%< Timer Manager */
+
+#if HAVE_LIBNGHTTP2
+typedef struct isc_nm_http_endpoints isc_nm_http_endpoints_t;
+/*%< HTTP endpoints set */
+#endif /* HAVE_LIBNGHTTP2 */
+
+typedef void (*isc_taskaction_t)(isc_task_t *, isc_event_t *);
+
+/*% Resource */
+typedef enum {
+ isc_resource_coresize = 1,
+ isc_resource_cputime,
+ isc_resource_datasize,
+ isc_resource_filesize,
+ isc_resource_lockedmemory,
+ isc_resource_openfiles,
+ isc_resource_processes,
+ isc_resource_residentsize,
+ isc_resource_stacksize
+} isc_resource_t;
+
+/*% Statistics formats (text file or XML) */
+typedef enum {
+ isc_statsformat_file,
+ isc_statsformat_xml,
+ isc_statsformat_json
+} isc_statsformat_t;
+
+typedef enum isc_nmsocket_type {
+ isc_nm_nonesocket = 0,
+ isc_nm_udpsocket = 1 << 1,
+ isc_nm_tcpsocket = 1 << 2,
+ isc_nm_tcpdnssocket = 1 << 3,
+ isc_nm_tlssocket = 1 << 4,
+ isc_nm_tlsdnssocket = 1 << 5,
+ isc_nm_httpsocket = 1 << 6,
+ isc_nm_maxsocket,
+
+ isc_nm_udplistener, /* Aggregate of nm_udpsocks */
+ isc_nm_tcplistener,
+ isc_nm_tlslistener,
+ isc_nm_tcpdnslistener,
+ isc_nm_tlsdnslistener,
+ isc_nm_httplistener
+} isc_nmsocket_type;
+
+typedef isc_nmsocket_type isc_nmsocket_type_t;
diff --git a/lib/isc/include/isc/url.h b/lib/isc/include/isc/url.h
new file mode 100644
index 0000000..d3b935b
--- /dev/null
+++ b/lib/isc/include/isc/url.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 and MIT
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <isc/result.h>
+
+/*
+ * Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
+ * faster
+ */
+#ifndef HTTP_PARSER_STRICT
+#define HTTP_PARSER_STRICT 1
+#endif
+
+typedef enum {
+ ISC_UF_SCHEMA = 0,
+ ISC_UF_HOST = 1,
+ ISC_UF_PORT = 2,
+ ISC_UF_PATH = 3,
+ ISC_UF_QUERY = 4,
+ ISC_UF_FRAGMENT = 5,
+ ISC_UF_USERINFO = 6,
+ ISC_UF_MAX = 7
+} isc_url_field_t;
+
+/* Result structure for isc_url_parse().
+ *
+ * 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.
+ */
+typedef struct {
+ 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[ISC_UF_MAX];
+} isc_url_parser_t;
+
+isc_result_t
+isc_url_parse(const char *buf, size_t buflen, bool is_connect,
+ isc_url_parser_t *up);
+/*%<
+ * Parse a URL; return nonzero on failure
+ */
diff --git a/lib/isc/include/isc/utf8.h b/lib/isc/include/isc/utf8.h
new file mode 100644
index 0000000..5643c5d
--- /dev/null
+++ b/lib/isc/include/isc/utf8.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file isc/utf8.h */
+
+#pragma once
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+bool
+isc_utf8_bom(const unsigned char *buf, size_t len);
+/*<
+ * Returns 'true' if the string of bytes in 'buf' starts
+ * with an UTF-8 Byte Order Mark.
+ *
+ * Requires:
+ *\li 'buf' != NULL
+ */
+
+bool
+isc_utf8_valid(const unsigned char *buf, size_t len);
+/*<
+ * Returns 'true' if the string of bytes in 'buf' is a valid UTF-8
+ * byte sequence otherwise 'false' is returned.
+ *
+ * Requires:
+ *\li 'buf' != NULL
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isc/include/isc/util.h b/lib/isc/include/isc/util.h
new file mode 100644
index 0000000..922661b
--- /dev/null
+++ b/lib/isc/include/isc/util.h
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+
+/*! \file isc/util.h
+ * NOTE:
+ *
+ * This file is not to be included from any <isc/???.h> (or other) library
+ * files.
+ *
+ * \brief
+ * Including this file puts several macros in your name space that are
+ * not protected (as all the other ISC functions/macros do) by prepending
+ * ISC_ or isc_ to the name.
+ */
+
+/***
+ *** Clang Compatibility Macros
+ ***/
+
+#if !defined(__has_attribute)
+#define __has_attribute(x) 0
+#endif /* if !defined(__has_attribute) */
+
+#if !defined(__has_c_attribute)
+#define __has_c_attribute(x) 0
+#endif /* if !defined(__has_c_attribute) */
+
+#if !defined(__has_feature)
+#define __has_feature(x) 0
+#endif /* if !defined(__has_feature) */
+
+/***
+ *** General Macros.
+ ***/
+
+/*%
+ * Use this to hide unused function arguments.
+ * \code
+ * int
+ * foo(char *bar)
+ * {
+ * UNUSED(bar);
+ * }
+ * \endcode
+ */
+#define UNUSED(x) (void)(x)
+
+#if __GNUC__ >= 8 && !defined(__clang__)
+#define ISC_NONSTRING __attribute__((nonstring))
+#else /* if __GNUC__ >= 8 && !defined(__clang__) */
+#define ISC_NONSTRING
+#endif /* __GNUC__ */
+
+#if __has_c_attribute(fallthrough)
+#define FALLTHROUGH [[fallthrough]]
+#elif __GNUC__ >= 7 && !defined(__clang__)
+#define FALLTHROUGH __attribute__((fallthrough))
+#else
+/* clang-format off */
+#define FALLTHROUGH do {} while (0) /* FALLTHROUGH */
+/* clang-format on */
+#endif
+
+#if HAVE_FUNC_ATTRIBUTE_CONSTRUCTOR && HAVE_FUNC_ATTRIBUTE_DESTRUCTOR
+#define ISC_CONSTRUCTOR __attribute__((constructor))
+#define ISC_DESTRUCTOR __attribute__((destructor))
+#else
+#define ISC_CONSTRUCTOR
+#define ISC_DESTRUCTOR
+#endif
+
+/*%
+ * The opposite: silent warnings about stored values which are never read.
+ */
+#define POST(x) (void)(x)
+
+#define ISC_MAX(a, b) ((a) > (b) ? (a) : (b))
+#define ISC_MIN(a, b) ((a) < (b) ? (a) : (b))
+
+#define ISC_CLAMP(v, x, y) ((v) < (x) ? (x) : ((v) > (y) ? (y) : (v)))
+
+/*%
+ * Use this to remove the const qualifier of a variable to assign it to
+ * a non-const variable or pass it as a non-const function argument ...
+ * but only when you are sure it won't then be changed!
+ * This is necessary to sometimes shut up some compilers
+ * (as with gcc -Wcast-qual) when there is just no other good way to avoid the
+ * situation.
+ */
+#define DE_CONST(konst, var) \
+ do { \
+ union { \
+ const void *k; \
+ void *v; \
+ } _u; \
+ _u.k = konst; \
+ var = _u.v; \
+ } while (0)
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
+
+/*%
+ * Use this in translation units that would otherwise be empty, to
+ * suppress compiler warnings.
+ */
+#define EMPTY_TRANSLATION_UNIT extern int isc__empty;
+
+/*%
+ * We use macros instead of calling the routines directly because
+ * the capital letters make the locking stand out.
+ * We RUNTIME_CHECK for success since in general there's no way
+ * for us to continue if they fail.
+ */
+
+#ifdef ISC_UTIL_TRACEON
+#define ISC_UTIL_TRACE(a) a
+#include <stdio.h> /* Required for fprintf/stderr when tracing. */
+#else /* ifdef ISC_UTIL_TRACEON */
+#define ISC_UTIL_TRACE(a)
+#endif /* ifdef ISC_UTIL_TRACEON */
+
+#include <isc/result.h> /* Contractual promise. */
+
+#define LOCK(lp) \
+ do { \
+ ISC_UTIL_TRACE(fprintf(stderr, "LOCKING %p %s %d\n", (lp), \
+ __FILE__, __LINE__)); \
+ RUNTIME_CHECK(isc_mutex_lock((lp)) == ISC_R_SUCCESS); \
+ ISC_UTIL_TRACE(fprintf(stderr, "LOCKED %p %s %d\n", (lp), \
+ __FILE__, __LINE__)); \
+ } while (0)
+#define UNLOCK(lp) \
+ do { \
+ RUNTIME_CHECK(isc_mutex_unlock((lp)) == ISC_R_SUCCESS); \
+ ISC_UTIL_TRACE(fprintf(stderr, "UNLOCKED %p %s %d\n", (lp), \
+ __FILE__, __LINE__)); \
+ } while (0)
+
+#define BROADCAST(cvp) \
+ do { \
+ ISC_UTIL_TRACE(fprintf(stderr, "BROADCAST %p %s %d\n", (cvp), \
+ __FILE__, __LINE__)); \
+ RUNTIME_CHECK(isc_condition_broadcast((cvp)) == \
+ ISC_R_SUCCESS); \
+ } while (0)
+#define SIGNAL(cvp) \
+ do { \
+ ISC_UTIL_TRACE(fprintf(stderr, "SIGNAL %p %s %d\n", (cvp), \
+ __FILE__, __LINE__)); \
+ RUNTIME_CHECK(isc_condition_signal((cvp)) == ISC_R_SUCCESS); \
+ } while (0)
+#define WAIT(cvp, lp) \
+ do { \
+ ISC_UTIL_TRACE(fprintf(stderr, "WAIT %p LOCK %p %s %d\n", \
+ (cvp), (lp), __FILE__, __LINE__)); \
+ RUNTIME_CHECK(isc_condition_wait((cvp), (lp)) == \
+ ISC_R_SUCCESS); \
+ ISC_UTIL_TRACE(fprintf(stderr, "WAITED %p LOCKED %p %s %d\n", \
+ (cvp), (lp), __FILE__, __LINE__)); \
+ } while (0)
+
+/*
+ * isc_condition_waituntil can return ISC_R_TIMEDOUT, so we
+ * don't RUNTIME_CHECK the result.
+ *
+ * XXX Also, can't really debug this then...
+ */
+
+#define WAITUNTIL(cvp, lp, tp) isc_condition_waituntil((cvp), (lp), (tp))
+
+#define RWLOCK(lp, t) \
+ do { \
+ ISC_UTIL_TRACE(fprintf(stderr, "RWLOCK %p, %d %s %d\n", (lp), \
+ (t), __FILE__, __LINE__)); \
+ RUNTIME_CHECK(isc_rwlock_lock((lp), (t)) == ISC_R_SUCCESS); \
+ ISC_UTIL_TRACE(fprintf(stderr, "RWLOCKED %p, %d %s %d\n", \
+ (lp), (t), __FILE__, __LINE__)); \
+ } while (0)
+#define RWUNLOCK(lp, t) \
+ do { \
+ ISC_UTIL_TRACE(fprintf(stderr, "RWUNLOCK %p, %d %s %d\n", \
+ (lp), (t), __FILE__, __LINE__)); \
+ RUNTIME_CHECK(isc_rwlock_unlock((lp), (t)) == ISC_R_SUCCESS); \
+ } while (0)
+
+/*
+ * List Macros.
+ */
+#include <isc/list.h> /* Contractual promise. */
+
+#define LIST(type) ISC_LIST(type)
+#define INIT_LIST(type) ISC_LIST_INIT(type)
+#define LINK(type) ISC_LINK(type)
+#define INIT_LINK(elt, link) ISC_LINK_INIT(elt, link)
+#define HEAD(list) ISC_LIST_HEAD(list)
+#define TAIL(list) ISC_LIST_TAIL(list)
+#define EMPTY(list) ISC_LIST_EMPTY(list)
+#define PREV(elt, link) ISC_LIST_PREV(elt, link)
+#define NEXT(elt, link) ISC_LIST_NEXT(elt, link)
+#define APPEND(list, elt, link) ISC_LIST_APPEND(list, elt, link)
+#define PREPEND(list, elt, link) ISC_LIST_PREPEND(list, elt, link)
+#define UNLINK(list, elt, link) ISC_LIST_UNLINK(list, elt, link)
+#define ENQUEUE(list, elt, link) ISC_LIST_APPEND(list, elt, link)
+#define DEQUEUE(list, elt, link) ISC_LIST_UNLINK(list, elt, link)
+#define INSERTBEFORE(li, b, e, ln) ISC_LIST_INSERTBEFORE(li, b, e, ln)
+#define INSERTAFTER(li, a, e, ln) ISC_LIST_INSERTAFTER(li, a, e, ln)
+#define APPENDLIST(list1, list2, link) ISC_LIST_APPENDLIST(list1, list2, link)
+
+/*%
+ * Performance
+ */
+
+/* GCC defines __SANITIZE_ADDRESS__, so reuse the macro for clang */
+#if __has_feature(address_sanitizer)
+#define __SANITIZE_ADDRESS__ 1
+#endif /* if __has_feature(address_sanitizer) */
+
+#if __has_feature(thread_sanitizer)
+#define __SANITIZE_THREAD__ 1
+#endif /* if __has_feature(thread_sanitizer) */
+
+#if __SANITIZE_THREAD__
+#define ISC_NO_SANITIZE_THREAD __attribute__((no_sanitize("thread")))
+#else /* if __SANITIZE_THREAD__ */
+#define ISC_NO_SANITIZE_THREAD
+#endif /* if __SANITIZE_THREAD__ */
+
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR >= 6)
+#define STATIC_ASSERT(cond, msg) _Static_assert(cond, msg)
+#elif __has_feature(c_static_assert)
+#define STATIC_ASSERT(cond, msg) _Static_assert(cond, msg)
+#else /* if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR >= 6) */
+
+/* Courtesy of Joseph Quinsey: https://godbolt.org/z/K9RvWS */
+#define TOKENPASTE(a, b) a##b /* "##" is the "Token Pasting Operator" */
+#define EXPAND_THEN_PASTE(a, b) TOKENPASTE(a, b) /* expand then paste */
+#define STATIC_ASSERT(x, msg) \
+ enum { EXPAND_THEN_PASTE(ASSERT_line_, __LINE__) = 1 / ((msg) && (x)) }
+#endif /* if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR >= 6) */
+
+#ifdef UNIT_TESTING
+extern void
+mock_assert(const int result, const char *const expression,
+ const char *const file, const int line);
+/*
+ * Allow clang to determine that the following code is not reached
+ * by calling abort() if the condition fails. The abort() will
+ * never be executed as mock_assert() and _assert_true() longjmp
+ * or exit if the condition is false.
+ */
+#define REQUIRE(expression) \
+ ((!(expression)) \
+ ? (mock_assert(0, #expression, __FILE__, __LINE__), abort()) \
+ : (void)0)
+#define ENSURE(expression) \
+ ((!(int)(expression)) \
+ ? (mock_assert(0, #expression, __FILE__, __LINE__), abort()) \
+ : (void)0)
+#define INSIST(expression) \
+ ((!(expression)) \
+ ? (mock_assert(0, #expression, __FILE__, __LINE__), abort()) \
+ : (void)0)
+#define INVARIANT(expression) \
+ ((!(expression)) \
+ ? (mock_assert(0, #expression, __FILE__, __LINE__), abort()) \
+ : (void)0)
+#define UNREACHABLE() \
+ (mock_assert(0, "unreachable", __FILE__, __LINE__), abort())
+#define _assert_true(c, e, f, l) \
+ ((c) ? (void)0 : (_assert_true(0, e, f, l), abort()))
+#define _assert_int_equal(a, b, f, l) \
+ (((a) == (b)) ? (void)0 : (_assert_int_equal(a, b, f, l), abort()))
+#define _assert_int_not_equal(a, b, f, l) \
+ (((a) != (b)) ? (void)0 : (_assert_int_not_equal(a, b, f, l), abort()))
+#else /* UNIT_TESTING */
+
+/*
+ * Assertions
+ */
+#include <isc/assertions.h> /* Contractual promise. */
+
+/*% Require Assertion */
+#define REQUIRE(e) ISC_REQUIRE(e)
+/*% Ensure Assertion */
+#define ENSURE(e) ISC_ENSURE(e)
+/*% Insist Assertion */
+#define INSIST(e) ISC_INSIST(e)
+/*% Invariant Assertion */
+#define INVARIANT(e) ISC_INVARIANT(e)
+
+#define UNREACHABLE() ISC_UNREACHABLE()
+
+#endif /* UNIT_TESTING */
+
+/*
+ * Errors
+ */
+#include <isc/error.h> /* Contractual promise. */
+#include <isc/strerr.h> /* for ISC_STRERRORSIZE */
+
+#define UNEXPECTED_ERROR(...) \
+ isc_error_unexpected(__FILE__, __LINE__, __func__, __VA_ARGS__)
+
+#define FATAL_ERROR(...) \
+ isc_error_fatal(__FILE__, __LINE__, __func__, __VA_ARGS__)
+
+#define REPORT_SYSERROR(report, err, fmt, ...) \
+ { \
+ char strerr[ISC_STRERRORSIZE]; \
+ strerror_r(err, strerr, sizeof(strerr)); \
+ report(__FILE__, __LINE__, __func__, fmt ": %s (%d)", \
+ ##__VA_ARGS__, strerr, err); \
+ }
+
+#define UNEXPECTED_SYSERROR(err, ...) \
+ REPORT_SYSERROR(isc_error_unexpected, err, __VA_ARGS__)
+
+#define FATAL_SYSERROR(err, ...) \
+ REPORT_SYSERROR(isc_error_fatal, err, __VA_ARGS__)
+
+#ifdef UNIT_TESTING
+
+#define RUNTIME_CHECK(cond) \
+ ((cond) ? (void)0 \
+ : (mock_assert(0, #cond, __FILE__, __LINE__), abort()))
+
+#else /* UNIT_TESTING */
+
+#define RUNTIME_CHECK(cond) \
+ ((cond) ? (void)0 : FATAL_ERROR("RUNTIME_CHECK(%s) failed", #cond))
+
+#endif /* UNIT_TESTING */
+
+/*%
+ * Time
+ */
+#define TIME_NOW(tp) RUNTIME_CHECK(isc_time_now((tp)) == ISC_R_SUCCESS)
+#define TIME_NOW_HIRES(tp) \
+ RUNTIME_CHECK(isc_time_now_hires((tp)) == ISC_R_SUCCESS)
+
+/*%
+ * Alignment
+ */
+#ifdef __GNUC__
+#define ISC_ALIGN(x, a) (((x) + (a)-1) & ~((typeof(x))(a)-1))
+#else /* ifdef __GNUC__ */
+#define ISC_ALIGN(x, a) (((x) + (a)-1) & ~((uintmax_t)(a)-1))
+#endif /* ifdef __GNUC__ */
+
+/*%
+ * Misc
+ */
+#include <isc/deprecated.h>
+
+/*%
+ * Swap
+ */
+#define ISC_SWAP(a, b) \
+ { \
+ typeof(a) __tmp_swap = a; \
+ a = b; \
+ b = __tmp_swap; \
+ }
diff --git a/lib/isc/interfaceiter.c b/lib/isc/interfaceiter.c
new file mode 100644
index 0000000..566c5bd
--- /dev/null
+++ b/lib/isc/interfaceiter.c
@@ -0,0 +1,518 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h> /* Required for ifiter_ioctl.c. */
+#endif /* ifdef HAVE_SYS_SOCKIO_H */
+
+#include <errno.h>
+#include <ifaddrs.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <isc/interfaceiter.h>
+#include <isc/log.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/net.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/strerr.h>
+#include <isc/string.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+/* Must follow <isc/net.h>. */
+#ifdef HAVE_NET_IF6_H
+#include <net/if6.h>
+#endif /* ifdef HAVE_NET_IF6_H */
+#include <net/if.h>
+
+/* Common utility functions */
+
+/*%
+ * Extract the network address part from a "struct sockaddr".
+ * \brief
+ * The address family is given explicitly
+ * instead of using src->sa_family, because the latter does not work
+ * for copying a network mask obtained by SIOCGIFNETMASK (it does
+ * not have a valid address family).
+ */
+
+static void
+get_addr(unsigned int family, isc_netaddr_t *dst, struct sockaddr *src,
+ char *ifname) {
+ struct sockaddr_in6 *sa6;
+
+#if !defined(HAVE_IF_NAMETOINDEX)
+ UNUSED(ifname);
+#endif /* if !defined(HAVE_IF_NAMETOINDEX) */
+
+ /* clear any remaining value for safety */
+ memset(dst, 0, sizeof(*dst));
+
+ dst->family = family;
+ switch (family) {
+ case AF_INET:
+ memmove(&dst->type.in, &((struct sockaddr_in *)src)->sin_addr,
+ sizeof(struct in_addr));
+ break;
+ case AF_INET6:
+ sa6 = (struct sockaddr_in6 *)src;
+ memmove(&dst->type.in6, &sa6->sin6_addr,
+ sizeof(struct in6_addr));
+ if (sa6->sin6_scope_id != 0) {
+ isc_netaddr_setzone(dst, sa6->sin6_scope_id);
+ } else {
+ /*
+ * BSD variants embed scope zone IDs in the 128bit
+ * address as a kernel internal form. Unfortunately,
+ * the embedded IDs are not hidden from applications
+ * when getting access to them by sysctl or ioctl.
+ * We convert the internal format to the pure address
+ * part and the zone ID part.
+ * Since multicast addresses should not appear here
+ * and they cannot be distinguished from netmasks,
+ * we only consider unicast link-local addresses.
+ */
+ if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) {
+ uint16_t zone16;
+
+ memmove(&zone16, &sa6->sin6_addr.s6_addr[2],
+ sizeof(zone16));
+ zone16 = ntohs(zone16);
+ if (zone16 != 0) {
+ /* the zone ID is embedded */
+ isc_netaddr_setzone(dst,
+ (uint32_t)zone16);
+ dst->type.in6.s6_addr[2] = 0;
+ dst->type.in6.s6_addr[3] = 0;
+#ifdef HAVE_IF_NAMETOINDEX
+ } else if (ifname != NULL) {
+ unsigned int zone;
+
+ /*
+ * sin6_scope_id is still not provided,
+ * but the corresponding interface name
+ * is know. Use the interface ID as
+ * the link ID.
+ */
+ zone = if_nametoindex(ifname);
+ if (zone != 0) {
+ isc_netaddr_setzone(
+ dst, (uint32_t)zone);
+ }
+#endif /* ifdef HAVE_IF_NAMETOINDEX */
+ }
+ }
+ }
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+/*
+ * Include system-dependent code.
+ */
+
+#ifdef __linux
+#define ISC_IF_INET6_SZ \
+ sizeof("00000000000000000000000000000001 01 80 10 80 " \
+ "XXXXXXloXXXXXXXX\n")
+static isc_result_t
+linux_if_inet6_next(isc_interfaceiter_t *);
+static isc_result_t
+linux_if_inet6_current(isc_interfaceiter_t *);
+static void
+linux_if_inet6_first(isc_interfaceiter_t *iter);
+#endif /* ifdef __linux */
+
+/*% Iterator Magic */
+#define IFITER_MAGIC ISC_MAGIC('I', 'F', 'I', 'G')
+/*% Valid Iterator */
+#define VALID_IFITER(t) ISC_MAGIC_VALID(t, IFITER_MAGIC)
+
+#ifdef __linux
+static bool seenv6 = false;
+#endif /* ifdef __linux */
+
+/*% Iterator structure */
+struct isc_interfaceiter {
+ unsigned int magic; /*%< Magic number. */
+ isc_mem_t *mctx;
+ void *buf; /*%< (unused) */
+ unsigned int bufsize; /*%< (always 0) */
+ struct ifaddrs *ifaddrs; /*%< List of ifaddrs */
+ struct ifaddrs *pos; /*%< Ptr to current ifaddr */
+ isc_interface_t current; /*%< Current interface data. */
+ isc_result_t result; /*%< Last result code. */
+#ifdef __linux
+ FILE *proc;
+ char entry[ISC_IF_INET6_SZ];
+ isc_result_t valid;
+#endif /* ifdef __linux */
+};
+
+isc_result_t
+isc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp) {
+ isc_interfaceiter_t *iter;
+ isc_result_t result;
+ char strbuf[ISC_STRERRORSIZE];
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(iterp != NULL);
+ REQUIRE(*iterp == NULL);
+
+ iter = isc_mem_get(mctx, sizeof(*iter));
+
+ iter->mctx = mctx;
+ iter->buf = NULL;
+ iter->bufsize = 0;
+ iter->ifaddrs = NULL;
+#ifdef __linux
+ /*
+ * Only open "/proc/net/if_inet6" if we have never seen a IPv6
+ * address returned by getifaddrs().
+ */
+ if (!seenv6) {
+ iter->proc = fopen("/proc/net/if_inet6", "r");
+ } else {
+ iter->proc = NULL;
+ }
+ iter->valid = ISC_R_FAILURE;
+#endif /* ifdef __linux */
+
+ if (getifaddrs(&iter->ifaddrs) < 0) {
+ strerror_r(errno, strbuf, sizeof(strbuf));
+ UNEXPECTED_ERROR("getting interface addresses: getifaddrs: %s",
+ strbuf);
+ result = ISC_R_UNEXPECTED;
+ goto failure;
+ }
+
+ /*
+ * A newly created iterator has an undefined position
+ * until isc_interfaceiter_first() is called.
+ */
+ iter->pos = NULL;
+ iter->result = ISC_R_FAILURE;
+
+ iter->magic = IFITER_MAGIC;
+ *iterp = iter;
+ return (ISC_R_SUCCESS);
+
+failure:
+#ifdef __linux
+ if (iter->proc != NULL) {
+ fclose(iter->proc);
+ }
+#endif /* ifdef __linux */
+ if (iter->ifaddrs != NULL) { /* just in case */
+ freeifaddrs(iter->ifaddrs);
+ }
+ isc_mem_put(mctx, iter, sizeof(*iter));
+ return (result);
+}
+
+/*
+ * Get information about the current interface to iter->current.
+ * If successful, return ISC_R_SUCCESS.
+ * If the interface has an unsupported address family,
+ * return ISC_R_IGNORE.
+ */
+
+static isc_result_t
+internal_current(isc_interfaceiter_t *iter) {
+ struct ifaddrs *ifa;
+ int family;
+ unsigned int namelen;
+
+ REQUIRE(VALID_IFITER(iter));
+
+ ifa = iter->pos;
+
+#ifdef __linux
+ if (iter->pos == NULL) {
+ return (linux_if_inet6_current(iter));
+ }
+#endif /* ifdef __linux */
+
+ INSIST(ifa != NULL);
+ INSIST(ifa->ifa_name != NULL);
+
+ if (ifa->ifa_addr == NULL) {
+ return (ISC_R_IGNORE);
+ }
+
+ family = ifa->ifa_addr->sa_family;
+ if (family != AF_INET && family != AF_INET6) {
+ return (ISC_R_IGNORE);
+ }
+
+#ifdef __linux
+ if (family == AF_INET6) {
+ seenv6 = true;
+ }
+#endif /* ifdef __linux */
+
+ memset(&iter->current, 0, sizeof(iter->current));
+
+ namelen = strlen(ifa->ifa_name);
+ if (namelen > sizeof(iter->current.name) - 1) {
+ namelen = sizeof(iter->current.name) - 1;
+ }
+
+ memset(iter->current.name, 0, sizeof(iter->current.name));
+ memmove(iter->current.name, ifa->ifa_name, namelen);
+
+ iter->current.flags = 0;
+
+ if ((ifa->ifa_flags & IFF_UP) != 0) {
+ iter->current.flags |= INTERFACE_F_UP;
+ }
+
+ if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0) {
+ iter->current.flags |= INTERFACE_F_POINTTOPOINT;
+ }
+
+ if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) {
+ iter->current.flags |= INTERFACE_F_LOOPBACK;
+ }
+
+ iter->current.af = family;
+
+ get_addr(family, &iter->current.address, ifa->ifa_addr, ifa->ifa_name);
+
+ if (ifa->ifa_netmask != NULL) {
+ get_addr(family, &iter->current.netmask, ifa->ifa_netmask,
+ ifa->ifa_name);
+ }
+
+ if (ifa->ifa_dstaddr != NULL &&
+ (iter->current.flags & INTERFACE_F_POINTTOPOINT) != 0)
+ {
+ get_addr(family, &iter->current.dstaddress, ifa->ifa_dstaddr,
+ ifa->ifa_name);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Step the iterator to the next interface. Unlike
+ * isc_interfaceiter_next(), this may leave the iterator
+ * positioned on an interface that will ultimately
+ * be ignored. Return ISC_R_NOMORE if there are no more
+ * interfaces, otherwise ISC_R_SUCCESS.
+ */
+static isc_result_t
+internal_next(isc_interfaceiter_t *iter) {
+ if (iter->pos != NULL) {
+ iter->pos = iter->pos->ifa_next;
+ }
+ if (iter->pos == NULL) {
+#ifdef __linux
+ if (!seenv6) {
+ return (linux_if_inet6_next(iter));
+ }
+#endif /* ifdef __linux */
+ return (ISC_R_NOMORE);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+internal_destroy(isc_interfaceiter_t *iter) {
+#ifdef __linux
+ if (iter->proc != NULL) {
+ fclose(iter->proc);
+ }
+ iter->proc = NULL;
+#endif /* ifdef __linux */
+ if (iter->ifaddrs) {
+ freeifaddrs(iter->ifaddrs);
+ }
+ iter->ifaddrs = NULL;
+}
+
+static void
+internal_first(isc_interfaceiter_t *iter) {
+#ifdef __linux
+ linux_if_inet6_first(iter);
+#endif /* ifdef __linux */
+ iter->pos = iter->ifaddrs;
+}
+
+#ifdef __linux
+static void
+linux_if_inet6_first(isc_interfaceiter_t *iter) {
+ if (iter->proc != NULL) {
+ rewind(iter->proc);
+ (void)linux_if_inet6_next(iter);
+ } else {
+ iter->valid = ISC_R_NOMORE;
+ }
+}
+
+static isc_result_t
+linux_if_inet6_next(isc_interfaceiter_t *iter) {
+ if (iter->proc != NULL &&
+ fgets(iter->entry, sizeof(iter->entry), iter->proc) != NULL)
+ {
+ iter->valid = ISC_R_SUCCESS;
+ } else {
+ iter->valid = ISC_R_NOMORE;
+ }
+ return (iter->valid);
+}
+
+static isc_result_t
+linux_if_inet6_current(isc_interfaceiter_t *iter) {
+ char address[33];
+ char name[IF_NAMESIZE + 1];
+ struct in6_addr addr6;
+ unsigned int ifindex, prefix, flag3, flag4;
+ int res;
+ unsigned int i;
+
+ if (iter->valid != ISC_R_SUCCESS) {
+ return (iter->valid);
+ }
+ if (iter->proc == NULL) {
+ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_INTERFACE, ISC_LOG_ERROR,
+ "/proc/net/if_inet6:iter->proc == NULL");
+ return (ISC_R_FAILURE);
+ }
+
+ res = sscanf(iter->entry, "%32[a-f0-9] %x %x %x %x %16s\n", address,
+ &ifindex, &prefix, &flag3, &flag4, name);
+ if (res != 6) {
+ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_INTERFACE, ISC_LOG_ERROR,
+ "/proc/net/if_inet6:sscanf() -> %d (expected 6)",
+ res);
+ return (ISC_R_FAILURE);
+ }
+ if (strlen(address) != 32) {
+ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_INTERFACE, ISC_LOG_ERROR,
+ "/proc/net/if_inet6:strlen(%s) != 32", address);
+ return (ISC_R_FAILURE);
+ }
+ for (i = 0; i < 16; i++) {
+ unsigned char byte;
+ static const char hex[] = "0123456789abcdef";
+ byte = ((strchr(hex, address[i * 2]) - hex) << 4) |
+ (strchr(hex, address[i * 2 + 1]) - hex);
+ addr6.s6_addr[i] = byte;
+ }
+ iter->current.af = AF_INET6;
+ iter->current.flags = INTERFACE_F_UP;
+ isc_netaddr_fromin6(&iter->current.address, &addr6);
+ if (isc_netaddr_islinklocal(&iter->current.address)) {
+ isc_netaddr_setzone(&iter->current.address, (uint32_t)ifindex);
+ }
+ for (i = 0; i < 16; i++) {
+ if (prefix > 8) {
+ addr6.s6_addr[i] = 0xff;
+ prefix -= 8;
+ } else {
+ addr6.s6_addr[i] = (0xff << (8 - prefix)) & 0xff;
+ prefix = 0;
+ }
+ }
+ isc_netaddr_fromin6(&iter->current.netmask, &addr6);
+ strlcpy(iter->current.name, name, sizeof(iter->current.name));
+ return (ISC_R_SUCCESS);
+}
+#endif /* ifdef __linux */
+
+/*
+ * The remaining code is common to the sysctl and ioctl case.
+ */
+
+isc_result_t
+isc_interfaceiter_current(isc_interfaceiter_t *iter, isc_interface_t *ifdata) {
+ REQUIRE(iter->result == ISC_R_SUCCESS);
+ memmove(ifdata, &iter->current, sizeof(*ifdata));
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_interfaceiter_first(isc_interfaceiter_t *iter) {
+ isc_result_t result;
+
+ REQUIRE(VALID_IFITER(iter));
+
+ internal_first(iter);
+ for (;;) {
+ result = internal_current(iter);
+ if (result != ISC_R_IGNORE) {
+ break;
+ }
+ result = internal_next(iter);
+ if (result != ISC_R_SUCCESS) {
+ break;
+ }
+ }
+ iter->result = result;
+ return (result);
+}
+
+isc_result_t
+isc_interfaceiter_next(isc_interfaceiter_t *iter) {
+ isc_result_t result;
+
+ REQUIRE(VALID_IFITER(iter));
+ REQUIRE(iter->result == ISC_R_SUCCESS);
+
+ for (;;) {
+ result = internal_next(iter);
+ if (result != ISC_R_SUCCESS) {
+ break;
+ }
+ result = internal_current(iter);
+ if (result != ISC_R_IGNORE) {
+ break;
+ }
+ }
+ iter->result = result;
+ return (result);
+}
+
+void
+isc_interfaceiter_destroy(isc_interfaceiter_t **iterp) {
+ isc_interfaceiter_t *iter;
+ REQUIRE(iterp != NULL);
+ iter = *iterp;
+ *iterp = NULL;
+ REQUIRE(VALID_IFITER(iter));
+
+ internal_destroy(iter);
+ if (iter->buf != NULL) {
+ isc_mem_put(iter->mctx, iter->buf, iter->bufsize);
+ }
+
+ iter->magic = 0;
+ isc_mem_put(iter->mctx, iter, sizeof(*iter));
+}
diff --git a/lib/isc/iterated_hash.c b/lib/isc/iterated_hash.c
new file mode 100644
index 0000000..ad62381
--- /dev/null
+++ b/lib/isc/iterated_hash.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <stdio.h>
+
+#include <openssl/err.h>
+#include <openssl/opensslv.h>
+
+#include <isc/iterated_hash.h>
+#include <isc/util.h>
+
+#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000
+
+#include <openssl/sha.h>
+
+int
+isc_iterated_hash(unsigned char *out, const unsigned int hashalg,
+ const int iterations, const unsigned char *salt,
+ const int saltlength, const unsigned char *in,
+ const int inlength) {
+ REQUIRE(out != NULL);
+
+ int n = 0;
+ size_t len;
+ const unsigned char *buf;
+ SHA_CTX ctx;
+
+ if (hashalg != 1) {
+ return (0);
+ }
+
+ buf = in;
+ len = inlength;
+
+ do {
+ if (SHA1_Init(&ctx) != 1) {
+ ERR_clear_error();
+ return (0);
+ }
+
+ if (SHA1_Update(&ctx, buf, len) != 1) {
+ ERR_clear_error();
+ return (0);
+ }
+
+ if (SHA1_Update(&ctx, salt, saltlength) != 1) {
+ ERR_clear_error();
+ return (0);
+ }
+
+ if (SHA1_Final(out, &ctx) != 1) {
+ ERR_clear_error();
+ return (0);
+ }
+
+ buf = out;
+ len = SHA_DIGEST_LENGTH;
+ } while (n++ < iterations);
+
+ return (SHA_DIGEST_LENGTH);
+}
+
+#else
+
+#include <openssl/evp.h>
+
+#include <isc/md.h>
+
+int
+isc_iterated_hash(unsigned char *out, const unsigned int hashalg,
+ const int iterations, const unsigned char *salt,
+ const int saltlength, const unsigned char *in,
+ const int inlength) {
+ REQUIRE(out != NULL);
+
+ int n = 0;
+ size_t len;
+ unsigned int outlength = 0;
+ const unsigned char *buf;
+ EVP_MD_CTX *ctx;
+ ;
+ EVP_MD *md;
+
+ if (hashalg != 1) {
+ return (0);
+ }
+
+ ctx = EVP_MD_CTX_new();
+ RUNTIME_CHECK(ctx != NULL);
+ md = EVP_MD_fetch(NULL, "SHA1", NULL);
+ RUNTIME_CHECK(md != NULL);
+
+ buf = in;
+ len = inlength;
+
+ do {
+ if (EVP_DigestInit_ex(ctx, md, NULL) != 1) {
+ goto fail;
+ }
+
+ if (EVP_DigestUpdate(ctx, buf, len) != 1) {
+ goto fail;
+ }
+
+ if (EVP_DigestUpdate(ctx, salt, saltlength) != 1) {
+ goto fail;
+ }
+
+ if (EVP_DigestFinal_ex(ctx, out, &outlength) != 1) {
+ goto fail;
+ }
+
+ buf = out;
+ len = outlength;
+ } while (n++ < iterations);
+
+ EVP_MD_CTX_free(ctx);
+ EVP_MD_free(md);
+
+ return (outlength);
+
+fail:
+ EVP_MD_CTX_free(ctx);
+ EVP_MD_free(md);
+ ERR_clear_error();
+ return (0);
+}
+
+#endif
diff --git a/lib/isc/jemalloc_shim.h b/lib/isc/jemalloc_shim.h
new file mode 100644
index 0000000..493bf5f
--- /dev/null
+++ b/lib/isc/jemalloc_shim.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#if !defined(HAVE_JEMALLOC)
+
+#include <stddef.h>
+
+#include <isc/util.h>
+
+const char *malloc_conf = NULL;
+
+/* Without jemalloc, isc_mem_get_align() is equal to isc_mem_get() */
+#define MALLOCX_ALIGN(a) (a & 0) /* Clear the flag */
+#define MALLOCX_ZERO ((int)0x40)
+#define MALLOCX_TCACHE_NONE (0)
+#define MALLOCX_ARENA(a) (0)
+
+#if defined(HAVE_MALLOC_SIZE) || defined(HAVE_MALLOC_USABLE_SIZE)
+
+#include <stdlib.h>
+
+static inline void *
+mallocx(size_t size, int flags) {
+ UNUSED(flags);
+
+ return (malloc(size));
+}
+
+static inline void
+sdallocx(void *ptr, size_t size, int flags) {
+ UNUSED(size);
+ UNUSED(flags);
+
+ free(ptr);
+}
+
+static inline void *
+rallocx(void *ptr, size_t size, int flags) {
+ UNUSED(flags);
+ REQUIRE(size != 0);
+
+ return (realloc(ptr, size));
+}
+
+#ifdef HAVE_MALLOC_SIZE
+
+#include <malloc/malloc.h>
+
+static inline size_t
+sallocx(void *ptr, int flags) {
+ UNUSED(flags);
+
+ return (malloc_size(ptr));
+}
+
+#elif HAVE_MALLOC_USABLE_SIZE
+
+#ifdef __DragonFly__
+/*
+ * On DragonFly BSD 'man 3 malloc' advises us to include the following
+ * header to have access to malloc_usable_size().
+ */
+#include <malloc_np.h>
+#else
+#include <malloc.h>
+#endif
+
+static inline size_t
+sallocx(void *ptr, int flags) {
+ UNUSED(flags);
+
+ return (malloc_usable_size(ptr));
+}
+
+#endif /* HAVE_MALLOC_SIZE */
+
+#else /* defined(HAVE_MALLOC_SIZE) || defined (HAVE_MALLOC_USABLE_SIZE) */
+
+#include <stdlib.h>
+
+typedef union {
+ size_t size;
+ max_align_t __alignment;
+} size_info;
+
+static inline void *
+mallocx(size_t size, int flags) {
+ void *ptr = NULL;
+
+ UNUSED(flags);
+
+ size_info *si = malloc(size + sizeof(*si));
+ INSIST(si != NULL);
+
+ si->size = size;
+ ptr = &si[1];
+
+ return (ptr);
+}
+
+static inline void
+sdallocx(void *ptr, size_t size, int flags) {
+ size_info *si = &(((size_info *)ptr)[-1]);
+
+ UNUSED(size);
+ UNUSED(flags);
+
+ free(si);
+}
+
+static inline size_t
+sallocx(void *ptr, int flags) {
+ size_info *si = &(((size_info *)ptr)[-1]);
+
+ UNUSED(flags);
+
+ return (si[0].size);
+}
+
+static inline void *
+rallocx(void *ptr, size_t size, int flags) {
+ size_info *si = &(((size_info *)ptr)[-1]);
+
+ UNUSED(flags);
+
+ si = realloc(si, size + sizeof(*si));
+ INSIST(si != NULL);
+
+ si->size = size;
+ ptr = &si[1];
+
+ return (ptr);
+}
+
+#endif /* defined(HAVE_MALLOC_SIZE) || defined (HAVE_MALLOC_USABLE_SIZE) */
+
+#endif /* !defined(HAVE_JEMALLOC) */
diff --git a/lib/isc/lex.c b/lib/isc/lex.c
new file mode 100644
index 0000000..19ed4a7
--- /dev/null
+++ b/lib/isc/lex.c
@@ -0,0 +1,1136 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <ctype.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <isc/buffer.h>
+#include <isc/file.h>
+#include <isc/lex.h>
+#include <isc/mem.h>
+#include <isc/parseint.h>
+#include <isc/print.h>
+#include <isc/stdio.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include "errno2result.h"
+
+typedef struct inputsource {
+ isc_result_t result;
+ bool is_file;
+ bool need_close;
+ bool at_eof;
+ bool last_was_eol;
+ isc_buffer_t *pushback;
+ unsigned int ignored;
+ void *input;
+ char *name;
+ unsigned long line;
+ unsigned long saved_line;
+ ISC_LINK(struct inputsource) link;
+} inputsource;
+
+#define LEX_MAGIC ISC_MAGIC('L', 'e', 'x', '!')
+#define VALID_LEX(l) ISC_MAGIC_VALID(l, LEX_MAGIC)
+
+struct isc_lex {
+ /* Unlocked. */
+ unsigned int magic;
+ isc_mem_t *mctx;
+ size_t max_token;
+ char *data;
+ unsigned int comments;
+ bool comment_ok;
+ bool last_was_eol;
+ unsigned int brace_count;
+ unsigned int paren_count;
+ unsigned int saved_paren_count;
+ isc_lexspecials_t specials;
+ LIST(struct inputsource) sources;
+};
+
+static isc_result_t
+grow_data(isc_lex_t *lex, size_t *remainingp, char **currp, char **prevp) {
+ char *tmp;
+
+ tmp = isc_mem_get(lex->mctx, lex->max_token * 2 + 1);
+ memmove(tmp, lex->data, lex->max_token + 1);
+ *currp = tmp + (*currp - lex->data);
+ if (*prevp != NULL) {
+ *prevp = tmp + (*prevp - lex->data);
+ }
+ isc_mem_put(lex->mctx, lex->data, lex->max_token + 1);
+ lex->data = tmp;
+ *remainingp += lex->max_token;
+ lex->max_token *= 2;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_lex_create(isc_mem_t *mctx, size_t max_token, isc_lex_t **lexp) {
+ isc_lex_t *lex;
+
+ /*
+ * Create a lexer.
+ */
+ REQUIRE(lexp != NULL && *lexp == NULL);
+
+ if (max_token == 0U) {
+ max_token = 1;
+ }
+
+ lex = isc_mem_get(mctx, sizeof(*lex));
+ lex->data = isc_mem_get(mctx, max_token + 1);
+ lex->mctx = mctx;
+ lex->max_token = max_token;
+ lex->comments = 0;
+ lex->comment_ok = true;
+ lex->last_was_eol = true;
+ lex->brace_count = 0;
+ lex->paren_count = 0;
+ lex->saved_paren_count = 0;
+ memset(lex->specials, 0, 256);
+ INIT_LIST(lex->sources);
+ lex->magic = LEX_MAGIC;
+
+ *lexp = lex;
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_lex_destroy(isc_lex_t **lexp) {
+ isc_lex_t *lex;
+
+ /*
+ * Destroy the lexer.
+ */
+
+ REQUIRE(lexp != NULL);
+ lex = *lexp;
+ *lexp = NULL;
+ REQUIRE(VALID_LEX(lex));
+
+ while (!EMPTY(lex->sources)) {
+ RUNTIME_CHECK(isc_lex_close(lex) == ISC_R_SUCCESS);
+ }
+ if (lex->data != NULL) {
+ isc_mem_put(lex->mctx, lex->data, lex->max_token + 1);
+ }
+ lex->magic = 0;
+ isc_mem_put(lex->mctx, lex, sizeof(*lex));
+}
+
+unsigned int
+isc_lex_getcomments(isc_lex_t *lex) {
+ /*
+ * Return the current lexer commenting styles.
+ */
+
+ REQUIRE(VALID_LEX(lex));
+
+ return (lex->comments);
+}
+
+void
+isc_lex_setcomments(isc_lex_t *lex, unsigned int comments) {
+ /*
+ * Set allowed lexer commenting styles.
+ */
+
+ REQUIRE(VALID_LEX(lex));
+
+ lex->comments = comments;
+}
+
+void
+isc_lex_getspecials(isc_lex_t *lex, isc_lexspecials_t specials) {
+ /*
+ * Put the current list of specials into 'specials'.
+ */
+
+ REQUIRE(VALID_LEX(lex));
+
+ memmove(specials, lex->specials, 256);
+}
+
+void
+isc_lex_setspecials(isc_lex_t *lex, isc_lexspecials_t specials) {
+ /*
+ * The characters in 'specials' are returned as tokens. Along with
+ * whitespace, they delimit strings and numbers.
+ */
+
+ REQUIRE(VALID_LEX(lex));
+
+ memmove(lex->specials, specials, 256);
+}
+
+static isc_result_t
+new_source(isc_lex_t *lex, bool is_file, bool need_close, void *input,
+ const char *name) {
+ inputsource *source;
+
+ source = isc_mem_get(lex->mctx, sizeof(*source));
+ source->result = ISC_R_SUCCESS;
+ source->is_file = is_file;
+ source->need_close = need_close;
+ source->at_eof = false;
+ source->last_was_eol = lex->last_was_eol;
+ source->input = input;
+ source->name = isc_mem_strdup(lex->mctx, name);
+ source->pushback = NULL;
+ isc_buffer_allocate(lex->mctx, &source->pushback,
+ (unsigned int)lex->max_token);
+ source->ignored = 0;
+ source->line = 1;
+ ISC_LIST_INITANDPREPEND(lex->sources, source, link);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_lex_openfile(isc_lex_t *lex, const char *filename) {
+ isc_result_t result;
+ FILE *stream = NULL;
+
+ /*
+ * Open 'filename' and make it the current input source for 'lex'.
+ */
+
+ REQUIRE(VALID_LEX(lex));
+
+ result = isc_stdio_open(filename, "r", &stream);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ result = new_source(lex, true, true, stream, filename);
+ if (result != ISC_R_SUCCESS) {
+ (void)fclose(stream);
+ }
+ return (result);
+}
+
+isc_result_t
+isc_lex_openstream(isc_lex_t *lex, FILE *stream) {
+ char name[128];
+
+ /*
+ * Make 'stream' the current input source for 'lex'.
+ */
+
+ REQUIRE(VALID_LEX(lex));
+
+ snprintf(name, sizeof(name), "stream-%p", stream);
+
+ return (new_source(lex, true, false, stream, name));
+}
+
+isc_result_t
+isc_lex_openbuffer(isc_lex_t *lex, isc_buffer_t *buffer) {
+ char name[128];
+
+ /*
+ * Make 'buffer' the current input source for 'lex'.
+ */
+
+ REQUIRE(VALID_LEX(lex));
+
+ snprintf(name, sizeof(name), "buffer-%p", buffer);
+
+ return (new_source(lex, false, false, buffer, name));
+}
+
+isc_result_t
+isc_lex_close(isc_lex_t *lex) {
+ inputsource *source;
+
+ /*
+ * Close the most recently opened object (i.e. file or buffer).
+ */
+
+ REQUIRE(VALID_LEX(lex));
+
+ source = HEAD(lex->sources);
+ if (source == NULL) {
+ return (ISC_R_NOMORE);
+ }
+
+ ISC_LIST_UNLINK(lex->sources, source, link);
+ lex->last_was_eol = source->last_was_eol;
+ if (source->is_file) {
+ if (source->need_close) {
+ (void)fclose((FILE *)(source->input));
+ }
+ }
+ isc_mem_free(lex->mctx, source->name);
+ isc_buffer_free(&source->pushback);
+ isc_mem_put(lex->mctx, source, sizeof(*source));
+
+ return (ISC_R_SUCCESS);
+}
+
+typedef enum {
+ lexstate_start,
+ lexstate_crlf,
+ lexstate_string,
+ lexstate_number,
+ lexstate_maybecomment,
+ lexstate_ccomment,
+ lexstate_ccommentend,
+ lexstate_eatline,
+ lexstate_qstring,
+ lexstate_btext,
+ lexstate_vpair,
+ lexstate_vpairstart,
+ lexstate_qvpair,
+} lexstate;
+
+#define IWSEOL (ISC_LEXOPT_INITIALWS | ISC_LEXOPT_EOL)
+
+static void
+pushback(inputsource *source, int c) {
+ REQUIRE(source->pushback->current > 0);
+ if (c == EOF) {
+ source->at_eof = false;
+ return;
+ }
+ source->pushback->current--;
+ if (c == '\n') {
+ source->line--;
+ }
+}
+
+static isc_result_t
+pushandgrow(isc_lex_t *lex, inputsource *source, int c) {
+ if (isc_buffer_availablelength(source->pushback) == 0) {
+ isc_buffer_t *tbuf = NULL;
+ unsigned int oldlen;
+ isc_region_t used;
+ isc_result_t result;
+
+ oldlen = isc_buffer_length(source->pushback);
+ isc_buffer_allocate(lex->mctx, &tbuf, oldlen * 2);
+ isc_buffer_usedregion(source->pushback, &used);
+ result = isc_buffer_copyregion(tbuf, &used);
+ INSIST(result == ISC_R_SUCCESS);
+ tbuf->current = source->pushback->current;
+ isc_buffer_free(&source->pushback);
+ source->pushback = tbuf;
+ }
+ isc_buffer_putuint8(source->pushback, (uint8_t)c);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_lex_gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *tokenp) {
+ inputsource *source;
+ int c;
+ bool done = false;
+ bool no_comments = false;
+ bool escaped = false;
+ lexstate state = lexstate_start;
+ lexstate saved_state = lexstate_start;
+ isc_buffer_t *buffer;
+ FILE *stream;
+ char *curr, *prev;
+ size_t remaining;
+ uint32_t as_ulong;
+ unsigned int saved_options;
+ isc_result_t result;
+
+ /*
+ * Get the next token.
+ */
+
+ REQUIRE(VALID_LEX(lex));
+ source = HEAD(lex->sources);
+ REQUIRE(tokenp != NULL);
+
+ if (source == NULL) {
+ if ((options & ISC_LEXOPT_NOMORE) != 0) {
+ tokenp->type = isc_tokentype_nomore;
+ return (ISC_R_SUCCESS);
+ }
+ return (ISC_R_NOMORE);
+ }
+
+ if (source->result != ISC_R_SUCCESS) {
+ return (source->result);
+ }
+
+ lex->saved_paren_count = lex->paren_count;
+ source->saved_line = source->line;
+
+ if (isc_buffer_remaininglength(source->pushback) == 0 && source->at_eof)
+ {
+ if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 &&
+ lex->paren_count != 0)
+ {
+ lex->paren_count = 0;
+ return (ISC_R_UNBALANCED);
+ }
+ if ((options & ISC_LEXOPT_BTEXT) != 0 && lex->brace_count != 0)
+ {
+ lex->brace_count = 0;
+ return (ISC_R_UNBALANCED);
+ }
+ if ((options & ISC_LEXOPT_EOF) != 0) {
+ tokenp->type = isc_tokentype_eof;
+ return (ISC_R_SUCCESS);
+ }
+ return (ISC_R_EOF);
+ }
+
+ isc_buffer_compact(source->pushback);
+
+ saved_options = options;
+ if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 && lex->paren_count > 0) {
+ options &= ~IWSEOL;
+ }
+
+ curr = lex->data;
+ *curr = '\0';
+
+ prev = NULL;
+ remaining = lex->max_token;
+
+#ifdef HAVE_FLOCKFILE
+ if (source->is_file) {
+ flockfile(source->input);
+ }
+#endif /* ifdef HAVE_FLOCKFILE */
+
+ do {
+ if (isc_buffer_remaininglength(source->pushback) == 0) {
+ if (source->is_file) {
+ stream = source->input;
+
+#if defined(HAVE_FLOCKFILE) && defined(HAVE_GETC_UNLOCKED)
+ c = getc_unlocked(stream);
+#else /* if defined(HAVE_FLOCKFILE) && defined(HAVE_GETC_UNLOCKED) */
+ c = getc(stream);
+#endif /* if defined(HAVE_FLOCKFILE) && defined(HAVE_GETC_UNLOCKED) */
+ if (c == EOF) {
+ if (ferror(stream)) {
+ source->result =
+ isc__errno2result(
+ errno);
+ result = source->result;
+ goto done;
+ }
+ source->at_eof = true;
+ }
+ } else {
+ buffer = source->input;
+
+ if (buffer->current == buffer->used) {
+ c = EOF;
+ source->at_eof = true;
+ } else {
+ c = *((unsigned char *)buffer->base +
+ buffer->current);
+ buffer->current++;
+ }
+ }
+ if (c != EOF) {
+ source->result = pushandgrow(lex, source, c);
+ if (source->result != ISC_R_SUCCESS) {
+ result = source->result;
+ goto done;
+ }
+ }
+ }
+
+ if (!source->at_eof) {
+ if (state == lexstate_start) {
+ /* Token has not started yet. */
+ source->ignored = isc_buffer_consumedlength(
+ source->pushback);
+ }
+ c = isc_buffer_getuint8(source->pushback);
+ } else {
+ c = EOF;
+ }
+
+ if (c == '\n') {
+ source->line++;
+ }
+
+ if (lex->comment_ok && !no_comments) {
+ if (!escaped && c == ';' &&
+ ((lex->comments & ISC_LEXCOMMENT_DNSMASTERFILE) !=
+ 0))
+ {
+ saved_state = state;
+ state = lexstate_eatline;
+ no_comments = true;
+ continue;
+ } else if (c == '/' &&
+ (lex->comments &
+ (ISC_LEXCOMMENT_C |
+ ISC_LEXCOMMENT_CPLUSPLUS)) != 0)
+ {
+ saved_state = state;
+ state = lexstate_maybecomment;
+ no_comments = true;
+ continue;
+ } else if (c == '#' && ((lex->comments &
+ ISC_LEXCOMMENT_SHELL) != 0))
+ {
+ saved_state = state;
+ state = lexstate_eatline;
+ no_comments = true;
+ continue;
+ }
+ }
+
+ no_read:
+ /* INSIST(c == EOF || (c >= 0 && c <= 255)); */
+ switch (state) {
+ case lexstate_start:
+ if (c == EOF) {
+ lex->last_was_eol = false;
+ if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 &&
+ lex->paren_count != 0)
+ {
+ lex->paren_count = 0;
+ result = ISC_R_UNBALANCED;
+ goto done;
+ }
+ if ((options & ISC_LEXOPT_BTEXT) != 0 &&
+ lex->brace_count != 0)
+ {
+ lex->brace_count = 0;
+ result = ISC_R_UNBALANCED;
+ goto done;
+ }
+ if ((options & ISC_LEXOPT_EOF) == 0) {
+ result = ISC_R_EOF;
+ goto done;
+ }
+ tokenp->type = isc_tokentype_eof;
+ done = true;
+ } else if (c == ' ' || c == '\t') {
+ if (lex->last_was_eol &&
+ (options & ISC_LEXOPT_INITIALWS) != 0)
+ {
+ lex->last_was_eol = false;
+ tokenp->type = isc_tokentype_initialws;
+ tokenp->value.as_char = c;
+ done = true;
+ }
+ } else if (c == '\n') {
+ if ((options & ISC_LEXOPT_EOL) != 0) {
+ tokenp->type = isc_tokentype_eol;
+ done = true;
+ }
+ lex->last_was_eol = true;
+ } else if (c == '\r') {
+ if ((options & ISC_LEXOPT_EOL) != 0) {
+ state = lexstate_crlf;
+ }
+ } else if (c == '"' &&
+ (options & ISC_LEXOPT_QSTRING) != 0)
+ {
+ lex->last_was_eol = false;
+ no_comments = true;
+ state = lexstate_qstring;
+ } else if (lex->specials[c]) {
+ lex->last_was_eol = false;
+ if ((c == '(' || c == ')') &&
+ (options & ISC_LEXOPT_DNSMULTILINE) != 0)
+ {
+ if (c == '(') {
+ if (lex->paren_count == 0) {
+ options &= ~IWSEOL;
+ }
+ lex->paren_count++;
+ } else {
+ if (lex->paren_count == 0) {
+ result =
+ ISC_R_UNBALANCED;
+ goto done;
+ }
+ lex->paren_count--;
+ if (lex->paren_count == 0) {
+ options = saved_options;
+ }
+ }
+ continue;
+ } else if (c == '{' &&
+ (options & ISC_LEXOPT_BTEXT) != 0)
+ {
+ if (lex->brace_count != 0) {
+ result = ISC_R_UNBALANCED;
+ goto done;
+ }
+ lex->brace_count++;
+ options &= ~IWSEOL;
+ state = lexstate_btext;
+ no_comments = true;
+ continue;
+ }
+ tokenp->type = isc_tokentype_special;
+ tokenp->value.as_char = c;
+ done = true;
+ } else if (isdigit((unsigned char)c) &&
+ (options & ISC_LEXOPT_NUMBER) != 0)
+ {
+ lex->last_was_eol = false;
+ if ((options & ISC_LEXOPT_OCTAL) != 0 &&
+ (c == '8' || c == '9'))
+ {
+ state = lexstate_string;
+ } else {
+ state = lexstate_number;
+ }
+ goto no_read;
+ } else {
+ lex->last_was_eol = false;
+ state = lexstate_string;
+ goto no_read;
+ }
+ break;
+ case lexstate_crlf:
+ if (c != '\n') {
+ pushback(source, c);
+ }
+ tokenp->type = isc_tokentype_eol;
+ done = true;
+ lex->last_was_eol = true;
+ break;
+ case lexstate_number:
+ if (c == EOF || !isdigit((unsigned char)c)) {
+ if (c == ' ' || c == '\t' || c == '\r' ||
+ c == '\n' || c == EOF || lex->specials[c])
+ {
+ int base;
+ if ((options & ISC_LEXOPT_OCTAL) != 0) {
+ base = 8;
+ } else if ((options &
+ ISC_LEXOPT_CNUMBER) != 0)
+ {
+ base = 0;
+ } else {
+ base = 10;
+ }
+ pushback(source, c);
+
+ result = isc_parse_uint32(
+ &as_ulong, lex->data, base);
+ if (result == ISC_R_SUCCESS) {
+ tokenp->type =
+ isc_tokentype_number;
+ tokenp->value.as_ulong =
+ as_ulong;
+ } else if (result == ISC_R_BADNUMBER) {
+ isc_tokenvalue_t *v;
+
+ tokenp->type =
+ isc_tokentype_string;
+ v = &(tokenp->value);
+ v->as_textregion.base =
+ lex->data;
+ v->as_textregion.length =
+ (unsigned int)(lex->max_token -
+ remaining);
+ } else {
+ goto done;
+ }
+ done = true;
+ continue;
+ } else if ((options & ISC_LEXOPT_CNUMBER) ==
+ 0 ||
+ ((c != 'x' && c != 'X') ||
+ (curr != &lex->data[1]) ||
+ (lex->data[0] != '0')))
+ {
+ /* Above test supports hex numbers */
+ state = lexstate_string;
+ }
+ } else if ((options & ISC_LEXOPT_OCTAL) != 0 &&
+ (c == '8' || c == '9'))
+ {
+ state = lexstate_string;
+ }
+ if (remaining == 0U) {
+ result = grow_data(lex, &remaining, &curr,
+ &prev);
+ if (result != ISC_R_SUCCESS) {
+ goto done;
+ }
+ }
+ INSIST(remaining > 0U);
+ *curr++ = c;
+ *curr = '\0';
+ remaining--;
+ break;
+ case lexstate_string:
+ if (!escaped && c == '=' &&
+ (options & ISC_LEXOPT_VPAIR) != 0)
+ {
+ if (remaining == 0U) {
+ result = grow_data(lex, &remaining,
+ &curr, &prev);
+ if (result != ISC_R_SUCCESS) {
+ goto done;
+ }
+ }
+ INSIST(remaining > 0U);
+ *curr++ = c;
+ *curr = '\0';
+ remaining--;
+ state = lexstate_vpairstart;
+ break;
+ }
+ FALLTHROUGH;
+ case lexstate_vpairstart:
+ if (state == lexstate_vpairstart) {
+ if (c == '"' &&
+ (options & ISC_LEXOPT_QVPAIR) != 0)
+ {
+ no_comments = true;
+ state = lexstate_qvpair;
+ break;
+ }
+ state = lexstate_vpair;
+ }
+ FALLTHROUGH;
+ case lexstate_vpair:
+ /*
+ * EOF needs to be checked before lex->specials[c]
+ * as lex->specials[EOF] is not a good idea.
+ */
+ if (c == '\r' || c == '\n' || c == EOF ||
+ (!escaped &&
+ (c == ' ' || c == '\t' || lex->specials[c])))
+ {
+ pushback(source, c);
+ if (source->result != ISC_R_SUCCESS) {
+ result = source->result;
+ goto done;
+ }
+ if (escaped && c == EOF) {
+ result = ISC_R_UNEXPECTEDEND;
+ goto done;
+ }
+ tokenp->type = (state == lexstate_string)
+ ? isc_tokentype_string
+ : isc_tokentype_vpair;
+ tokenp->value.as_textregion.base = lex->data;
+ tokenp->value.as_textregion.length =
+ (unsigned int)(lex->max_token -
+ remaining);
+ done = true;
+ continue;
+ }
+ if ((options & ISC_LEXOPT_ESCAPE) != 0) {
+ escaped = (!escaped && c == '\\') ? true
+ : false;
+ }
+ if (remaining == 0U) {
+ result = grow_data(lex, &remaining, &curr,
+ &prev);
+ if (result != ISC_R_SUCCESS) {
+ goto done;
+ }
+ }
+ INSIST(remaining > 0U);
+ *curr++ = c;
+ *curr = '\0';
+ remaining--;
+ break;
+ case lexstate_maybecomment:
+ if (c == '*' && (lex->comments & ISC_LEXCOMMENT_C) != 0)
+ {
+ state = lexstate_ccomment;
+ continue;
+ } else if (c == '/' && (lex->comments &
+ ISC_LEXCOMMENT_CPLUSPLUS) != 0)
+ {
+ state = lexstate_eatline;
+ continue;
+ }
+ pushback(source, c);
+ c = '/';
+ no_comments = false;
+ state = saved_state;
+ goto no_read;
+ case lexstate_ccomment:
+ if (c == EOF) {
+ result = ISC_R_UNEXPECTEDEND;
+ goto done;
+ }
+ if (c == '*') {
+ state = lexstate_ccommentend;
+ }
+ break;
+ case lexstate_ccommentend:
+ if (c == EOF) {
+ result = ISC_R_UNEXPECTEDEND;
+ goto done;
+ }
+ if (c == '/') {
+ /*
+ * C-style comments become a single space.
+ * We do this to ensure that a comment will
+ * act as a delimiter for strings and
+ * numbers.
+ */
+ c = ' ';
+ no_comments = false;
+ state = saved_state;
+ goto no_read;
+ } else if (c != '*') {
+ state = lexstate_ccomment;
+ }
+ break;
+ case lexstate_eatline:
+ if ((c == '\n') || (c == EOF)) {
+ no_comments = false;
+ state = saved_state;
+ goto no_read;
+ }
+ break;
+ case lexstate_qstring:
+ case lexstate_qvpair:
+ if (c == EOF) {
+ result = ISC_R_UNEXPECTEDEND;
+ goto done;
+ }
+ if (c == '"') {
+ if (escaped) {
+ escaped = false;
+ /*
+ * Overwrite the preceding backslash.
+ */
+ INSIST(prev != NULL);
+ *prev = '"';
+ } else {
+ tokenp->type =
+ (state == lexstate_qstring)
+ ? isc_tokentype_qstring
+ : isc_tokentype_qvpair;
+ tokenp->value.as_textregion.base =
+ lex->data;
+ tokenp->value.as_textregion.length =
+ (unsigned int)(lex->max_token -
+ remaining);
+ no_comments = false;
+ done = true;
+ }
+ } else {
+ if (c == '\n' && !escaped &&
+ (options & ISC_LEXOPT_QSTRINGMULTILINE) ==
+ 0)
+ {
+ pushback(source, c);
+ result = ISC_R_UNBALANCEDQUOTES;
+ goto done;
+ }
+ if (c == '\\' && !escaped) {
+ escaped = true;
+ } else {
+ escaped = false;
+ }
+ if (remaining == 0U) {
+ result = grow_data(lex, &remaining,
+ &curr, &prev);
+ if (result != ISC_R_SUCCESS) {
+ goto done;
+ }
+ }
+ INSIST(remaining > 0U);
+ prev = curr;
+ *curr++ = c;
+ *curr = '\0';
+ remaining--;
+ }
+ break;
+ case lexstate_btext:
+ if (c == EOF) {
+ result = ISC_R_UNEXPECTEDEND;
+ goto done;
+ }
+ if (c == '{') {
+ if (escaped) {
+ escaped = false;
+ } else {
+ lex->brace_count++;
+ }
+ } else if (c == '}') {
+ if (escaped) {
+ escaped = false;
+ } else {
+ INSIST(lex->brace_count > 0);
+ lex->brace_count--;
+ }
+
+ if (lex->brace_count == 0) {
+ tokenp->type = isc_tokentype_btext;
+ tokenp->value.as_textregion.base =
+ lex->data;
+ tokenp->value.as_textregion.length =
+ (unsigned int)(lex->max_token -
+ remaining);
+ no_comments = false;
+ done = true;
+ break;
+ }
+ }
+
+ if (c == '\\' && !escaped) {
+ escaped = true;
+ } else {
+ escaped = false;
+ }
+
+ if (remaining == 0U) {
+ result = grow_data(lex, &remaining, &curr,
+ &prev);
+ if (result != ISC_R_SUCCESS) {
+ goto done;
+ }
+ }
+ INSIST(remaining > 0U);
+ prev = curr;
+ *curr++ = c;
+ *curr = '\0';
+ remaining--;
+ break;
+ default:
+ FATAL_ERROR("Unexpected state %d", state);
+ }
+ } while (!done);
+
+ result = ISC_R_SUCCESS;
+done:
+#ifdef HAVE_FLOCKFILE
+ if (source->is_file) {
+ funlockfile(source->input);
+ }
+#endif /* ifdef HAVE_FLOCKFILE */
+ return (result);
+}
+
+isc_result_t
+isc_lex_getmastertoken(isc_lex_t *lex, isc_token_t *token,
+ isc_tokentype_t expect, bool eol) {
+ unsigned int options = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF |
+ ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE;
+ isc_result_t result;
+
+ if (expect == isc_tokentype_vpair) {
+ options |= ISC_LEXOPT_VPAIR;
+ } else if (expect == isc_tokentype_qvpair) {
+ options |= ISC_LEXOPT_VPAIR;
+ options |= ISC_LEXOPT_QVPAIR;
+ } else if (expect == isc_tokentype_qstring) {
+ options |= ISC_LEXOPT_QSTRING;
+ } else if (expect == isc_tokentype_number) {
+ options |= ISC_LEXOPT_NUMBER;
+ }
+ result = isc_lex_gettoken(lex, options, token);
+ if (result == ISC_R_RANGE) {
+ isc_lex_ungettoken(lex, token);
+ }
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ if (eol && ((token->type == isc_tokentype_eol) ||
+ (token->type == isc_tokentype_eof)))
+ {
+ return (ISC_R_SUCCESS);
+ }
+ if (token->type == isc_tokentype_string &&
+ (expect == isc_tokentype_qstring || expect == isc_tokentype_qvpair))
+ {
+ return (ISC_R_SUCCESS);
+ }
+ if (token->type == isc_tokentype_vpair &&
+ expect == isc_tokentype_qvpair)
+ {
+ return (ISC_R_SUCCESS);
+ }
+ if (token->type != expect) {
+ isc_lex_ungettoken(lex, token);
+ if (token->type == isc_tokentype_eol ||
+ token->type == isc_tokentype_eof)
+ {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ if (expect == isc_tokentype_number) {
+ return (ISC_R_BADNUMBER);
+ }
+ return (ISC_R_UNEXPECTEDTOKEN);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_lex_getoctaltoken(isc_lex_t *lex, isc_token_t *token, bool eol) {
+ unsigned int options = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF |
+ ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE |
+ ISC_LEXOPT_NUMBER | ISC_LEXOPT_OCTAL;
+ isc_result_t result;
+
+ result = isc_lex_gettoken(lex, options, token);
+ if (result == ISC_R_RANGE) {
+ isc_lex_ungettoken(lex, token);
+ }
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ if (eol && ((token->type == isc_tokentype_eol) ||
+ (token->type == isc_tokentype_eof)))
+ {
+ return (ISC_R_SUCCESS);
+ }
+ if (token->type != isc_tokentype_number) {
+ isc_lex_ungettoken(lex, token);
+ if (token->type == isc_tokentype_eol ||
+ token->type == isc_tokentype_eof)
+ {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ return (ISC_R_BADNUMBER);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_lex_ungettoken(isc_lex_t *lex, isc_token_t *tokenp) {
+ inputsource *source;
+ /*
+ * Unget the current token.
+ */
+
+ REQUIRE(VALID_LEX(lex));
+ source = HEAD(lex->sources);
+ REQUIRE(source != NULL);
+ REQUIRE(tokenp != NULL);
+ REQUIRE(isc_buffer_consumedlength(source->pushback) != 0 ||
+ tokenp->type == isc_tokentype_eof);
+
+ UNUSED(tokenp);
+
+ isc_buffer_first(source->pushback);
+ lex->paren_count = lex->saved_paren_count;
+ source->line = source->saved_line;
+ source->at_eof = false;
+}
+
+void
+isc_lex_getlasttokentext(isc_lex_t *lex, isc_token_t *tokenp, isc_region_t *r) {
+ inputsource *source;
+
+ REQUIRE(VALID_LEX(lex));
+ source = HEAD(lex->sources);
+ REQUIRE(source != NULL);
+ REQUIRE(tokenp != NULL);
+ REQUIRE(isc_buffer_consumedlength(source->pushback) != 0 ||
+ tokenp->type == isc_tokentype_eof);
+
+ UNUSED(tokenp);
+
+ INSIST(source->ignored <= isc_buffer_consumedlength(source->pushback));
+ r->base = (unsigned char *)isc_buffer_base(source->pushback) +
+ source->ignored;
+ r->length = isc_buffer_consumedlength(source->pushback) -
+ source->ignored;
+}
+
+char *
+isc_lex_getsourcename(isc_lex_t *lex) {
+ inputsource *source;
+
+ REQUIRE(VALID_LEX(lex));
+ source = HEAD(lex->sources);
+
+ if (source == NULL) {
+ return (NULL);
+ }
+
+ return (source->name);
+}
+
+unsigned long
+isc_lex_getsourceline(isc_lex_t *lex) {
+ inputsource *source;
+
+ REQUIRE(VALID_LEX(lex));
+ source = HEAD(lex->sources);
+
+ if (source == NULL) {
+ return (0);
+ }
+
+ return (source->line);
+}
+
+isc_result_t
+isc_lex_setsourcename(isc_lex_t *lex, const char *name) {
+ inputsource *source;
+ char *newname;
+
+ REQUIRE(VALID_LEX(lex));
+ source = HEAD(lex->sources);
+
+ if (source == NULL) {
+ return (ISC_R_NOTFOUND);
+ }
+ newname = isc_mem_strdup(lex->mctx, name);
+ isc_mem_free(lex->mctx, source->name);
+ source->name = newname;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_lex_setsourceline(isc_lex_t *lex, unsigned long line) {
+ inputsource *source;
+
+ REQUIRE(VALID_LEX(lex));
+ source = HEAD(lex->sources);
+
+ if (source == NULL) {
+ return (ISC_R_NOTFOUND);
+ }
+
+ source->line = line;
+ return (ISC_R_SUCCESS);
+}
+
+bool
+isc_lex_isfile(isc_lex_t *lex) {
+ inputsource *source;
+
+ REQUIRE(VALID_LEX(lex));
+
+ source = HEAD(lex->sources);
+
+ if (source == NULL) {
+ return (false);
+ }
+
+ return (source->is_file);
+}
diff --git a/lib/isc/lib.c b/lib/isc/lib.c
new file mode 100644
index 0000000..343fedf
--- /dev/null
+++ b/lib/isc/lib.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <isc/mem.h>
+#include <isc/os.h>
+#include <isc/tls.h>
+#include <isc/util.h>
+
+#include "config.h"
+#include "mem_p.h"
+#include "os_p.h"
+#include "tls_p.h"
+#include "trampoline_p.h"
+
+#ifndef ISC_CONSTRUCTOR
+#error Either __attribute__((constructor|destructor))__ or DllMain support needed to compile BIND 9.
+#endif
+
+/***
+ *** Functions
+ ***/
+
+void
+isc__initialize(void) ISC_CONSTRUCTOR;
+void
+isc__shutdown(void) ISC_DESTRUCTOR;
+
+void
+isc__initialize(void) {
+ isc__os_initialize();
+ isc__mem_initialize();
+ isc__tls_initialize();
+ isc__trampoline_initialize();
+ (void)isc_os_ncpus();
+}
+
+void
+isc__shutdown(void) {
+ isc__trampoline_shutdown();
+ isc__tls_shutdown();
+ isc__mem_shutdown();
+ isc__os_shutdown();
+}
diff --git a/lib/isc/log.c b/lib/isc/log.c
new file mode 100644
index 0000000..523fd2d
--- /dev/null
+++ b/lib/isc/log.c
@@ -0,0 +1,1909 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/types.h> /* dev_t FreeBSD 2.1 */
+#include <time.h>
+#include <unistd.h>
+
+#include <isc/atomic.h>
+#include <isc/dir.h>
+#include <isc/errno.h>
+#include <isc/file.h>
+#include <isc/log.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/rwlock.h>
+#include <isc/stat.h>
+#include <isc/stdio.h>
+#include <isc/string.h>
+#include <isc/thread.h>
+#include <isc/time.h>
+#include <isc/util.h>
+
+#define LCTX_MAGIC ISC_MAGIC('L', 'c', 't', 'x')
+#define VALID_CONTEXT(lctx) ISC_MAGIC_VALID(lctx, LCTX_MAGIC)
+
+#define LCFG_MAGIC ISC_MAGIC('L', 'c', 'f', 'g')
+#define VALID_CONFIG(lcfg) ISC_MAGIC_VALID(lcfg, LCFG_MAGIC)
+
+#define RDLOCK(lp) RWLOCK(lp, isc_rwlocktype_read);
+#define WRLOCK(lp) RWLOCK(lp, isc_rwlocktype_write);
+#define RDUNLOCK(lp) RWUNLOCK(lp, isc_rwlocktype_read);
+#define WRUNLOCK(lp) RWUNLOCK(lp, isc_rwlocktype_write);
+
+static thread_local bool forcelog = false;
+
+/*
+ * XXXDCL make dynamic?
+ */
+#define LOG_BUFFER_SIZE (8 * 1024)
+
+/*!
+ * This is the structure that holds each named channel. A simple linked
+ * list chains all of the channels together, so an individual channel is
+ * found by doing strcmp()s with the names down the list. Their should
+ * be no performance penalty from this as it is expected that the number
+ * of named channels will be no more than a dozen or so, and name lookups
+ * from the head of the list are only done when isc_log_usechannel() is
+ * called, which should also be very infrequent.
+ */
+typedef struct isc_logchannel isc_logchannel_t;
+
+struct isc_logchannel {
+ char *name;
+ unsigned int type;
+ int level;
+ unsigned int flags;
+ isc_logdestination_t destination;
+ ISC_LINK(isc_logchannel_t) link;
+};
+
+/*!
+ * The logchannellist structure associates categories and modules with
+ * channels. First the appropriate channellist is found based on the
+ * category, and then each structure in the linked list is checked for
+ * a matching module. It is expected that the number of channels
+ * associated with any given category will be very short, no more than
+ * three or four in the more unusual cases.
+ */
+typedef struct isc_logchannellist isc_logchannellist_t;
+
+struct isc_logchannellist {
+ const isc_logmodule_t *module;
+ isc_logchannel_t *channel;
+ ISC_LINK(isc_logchannellist_t) link;
+};
+
+/*!
+ * This structure is used to remember messages for pruning via
+ * isc_log_[v]write1().
+ */
+typedef struct isc_logmessage isc_logmessage_t;
+
+struct isc_logmessage {
+ char *text;
+ isc_time_t time;
+ ISC_LINK(isc_logmessage_t) link;
+};
+
+/*!
+ * The isc_logconfig structure is used to store the configurable information
+ * about where messages are actually supposed to be sent -- the information
+ * that could changed based on some configuration file, as opposed to the
+ * the category/module specification of isc_log_[v]write[1] that is compiled
+ * into a program, or the debug_level which is dynamic state information.
+ */
+struct isc_logconfig {
+ unsigned int magic;
+ isc_log_t *lctx;
+ ISC_LIST(isc_logchannel_t) channels;
+ ISC_LIST(isc_logchannellist_t) * channellists;
+ unsigned int channellist_count;
+ unsigned int duplicate_interval;
+ int_fast32_t highest_level;
+ char *tag;
+ bool dynamic;
+};
+
+/*!
+ * This isc_log structure provides the context for the isc_log functions.
+ * The log context locks itself in isc_log_doit, the internal backend to
+ * isc_log_write. The locking is necessary both to provide exclusive access
+ * to the buffer into which the message is formatted and to guard against
+ * competing threads trying to write to the same syslog resource. (On
+ * some systems, such as BSD/OS, stdio is thread safe but syslog is not.)
+ * Unfortunately, the lock cannot guard against a _different_ logging
+ * context in the same program competing for syslog's attention. Thus
+ * There Can Be Only One, but this is not enforced.
+ * XXXDCL enforce it?
+ *
+ * Note that the category and module information is not locked.
+ * This is because in the usual case, only one isc_log_t is ever created
+ * in a program, and the category/module registration happens only once.
+ * XXXDCL it might be wise to add more locking overall.
+ */
+struct isc_log {
+ /* Not locked. */
+ unsigned int magic;
+ isc_mem_t *mctx;
+ isc_logcategory_t *categories;
+ unsigned int category_count;
+ isc_logmodule_t *modules;
+ unsigned int module_count;
+ atomic_int_fast32_t debug_level;
+ isc_rwlock_t lcfg_rwl;
+ /* Locked by isc_log lcfg_rwl */
+ isc_logconfig_t *logconfig;
+ isc_mutex_t lock;
+ /* Locked by isc_log lock. */
+ char buffer[LOG_BUFFER_SIZE];
+ ISC_LIST(isc_logmessage_t) messages;
+ atomic_bool dynamic;
+ atomic_int_fast32_t highest_level;
+};
+
+/*!
+ * Used when ISC_LOG_PRINTLEVEL is enabled for a channel.
+ */
+static const char *log_level_strings[] = { "debug", "info", "notice",
+ "warning", "error", "critical" };
+
+/*!
+ * Used to convert ISC_LOG_* priorities into syslog priorities.
+ * XXXDCL This will need modification for NT.
+ */
+static const int syslog_map[] = { LOG_DEBUG, LOG_INFO, LOG_NOTICE,
+ LOG_WARNING, LOG_ERR, LOG_CRIT };
+
+/*!
+ * When adding new categories, a corresponding ISC_LOGCATEGORY_foo
+ * definition needs to be added to <isc/log.h>.
+ *
+ * The default category is provided so that the internal default can
+ * be overridden. Since the default is always looked up as the first
+ * channellist in the log context, it must come first in isc_categories[].
+ */
+isc_logcategory_t isc_categories[] = { { "default", 0 }, /* "default
+ must come
+ first. */
+ { "general", 0 },
+ { "sslkeylog", 0 },
+ { NULL, 0 } };
+
+/*!
+ * See above comment for categories, and apply it to modules.
+ */
+isc_logmodule_t isc_modules[] = { { "socket", 0 }, { "time", 0 },
+ { "interface", 0 }, { "timer", 0 },
+ { "file", 0 }, { "netmgr", 0 },
+ { "other", 0 }, { NULL, 0 } };
+
+/*!
+ * This essentially constant structure must be filled in at run time,
+ * because its channel member is pointed to a channel that is created
+ * dynamically with isc_log_createchannel.
+ */
+static isc_logchannellist_t default_channel;
+
+/*!
+ * libisc logs to this context.
+ */
+isc_log_t *isc_lctx = NULL;
+
+/*!
+ * Forward declarations.
+ */
+static void
+assignchannel(isc_logconfig_t *lcfg, unsigned int category_id,
+ const isc_logmodule_t *module, isc_logchannel_t *channel);
+
+static void
+sync_channellist(isc_logconfig_t *lcfg);
+
+static void
+sync_highest_level(isc_log_t *lctx, isc_logconfig_t *lcfg);
+
+static isc_result_t
+greatest_version(isc_logfile_t *file, int versions, int *greatest);
+
+static void
+isc_log_doit(isc_log_t *lctx, isc_logcategory_t *category,
+ isc_logmodule_t *module, int level, bool write_once,
+ const char *format, va_list args) ISC_FORMAT_PRINTF(6, 0);
+
+/*@{*/
+/*!
+ * Convenience macros.
+ */
+
+#define FACILITY(channel) (channel->destination.facility)
+#define FILE_NAME(channel) (channel->destination.file.name)
+#define FILE_STREAM(channel) (channel->destination.file.stream)
+#define FILE_VERSIONS(channel) (channel->destination.file.versions)
+#define FILE_SUFFIX(channel) (channel->destination.file.suffix)
+#define FILE_MAXSIZE(channel) (channel->destination.file.maximum_size)
+#define FILE_MAXREACHED(channel) (channel->destination.file.maximum_reached)
+
+/*@}*/
+/****
+**** Public interfaces.
+****/
+
+/*
+ * Establish a new logging context, with default channels.
+ */
+void
+isc_log_create(isc_mem_t *mctx, isc_log_t **lctxp, isc_logconfig_t **lcfgp) {
+ isc_log_t *lctx;
+ isc_logconfig_t *lcfg = NULL;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(lctxp != NULL && *lctxp == NULL);
+ REQUIRE(lcfgp == NULL || *lcfgp == NULL);
+
+ lctx = isc_mem_get(mctx, sizeof(*lctx));
+ lctx->mctx = NULL;
+ isc_mem_attach(mctx, &lctx->mctx);
+ lctx->categories = NULL;
+ lctx->category_count = 0;
+ lctx->modules = NULL;
+ lctx->module_count = 0;
+ atomic_init(&lctx->debug_level, 0);
+
+ ISC_LIST_INIT(lctx->messages);
+
+ isc_mutex_init(&lctx->lock);
+ isc_rwlock_init(&lctx->lcfg_rwl, 0, 0);
+
+ /*
+ * Normally setting the magic number is the last step done
+ * in a creation function, but a valid log context is needed
+ * by isc_log_registercategories and isc_logconfig_create.
+ * If either fails, the lctx is destroyed and not returned
+ * to the caller.
+ */
+ lctx->magic = LCTX_MAGIC;
+
+ isc_log_registercategories(lctx, isc_categories);
+ isc_log_registermodules(lctx, isc_modules);
+ isc_logconfig_create(lctx, &lcfg);
+
+ sync_channellist(lcfg);
+
+ lctx->logconfig = lcfg;
+
+ atomic_init(&lctx->highest_level, lcfg->highest_level);
+ atomic_init(&lctx->dynamic, lcfg->dynamic);
+
+ *lctxp = lctx;
+ if (lcfgp != NULL) {
+ *lcfgp = lcfg;
+ }
+}
+
+void
+isc_logconfig_create(isc_log_t *lctx, isc_logconfig_t **lcfgp) {
+ isc_logconfig_t *lcfg;
+ isc_logdestination_t destination;
+ int level = ISC_LOG_INFO;
+
+ REQUIRE(lcfgp != NULL && *lcfgp == NULL);
+ REQUIRE(VALID_CONTEXT(lctx));
+
+ lcfg = isc_mem_get(lctx->mctx, sizeof(*lcfg));
+
+ lcfg->lctx = lctx;
+ lcfg->channellists = NULL;
+ lcfg->channellist_count = 0;
+ lcfg->duplicate_interval = 0;
+ lcfg->highest_level = level;
+ lcfg->tag = NULL;
+ lcfg->dynamic = false;
+ ISC_LIST_INIT(lcfg->channels);
+ lcfg->magic = LCFG_MAGIC;
+
+ /*
+ * Create the default channels:
+ * default_syslog, default_stderr, default_debug and null.
+ */
+ destination.facility = LOG_DAEMON;
+ isc_log_createchannel(lcfg, "default_syslog", ISC_LOG_TOSYSLOG, level,
+ &destination, 0);
+
+ destination.file.stream = stderr;
+ destination.file.name = NULL;
+ destination.file.versions = ISC_LOG_ROLLNEVER;
+ destination.file.suffix = isc_log_rollsuffix_increment;
+ destination.file.maximum_size = 0;
+ isc_log_createchannel(lcfg, "default_stderr", ISC_LOG_TOFILEDESC, level,
+ &destination, ISC_LOG_PRINTTIME);
+
+ /*
+ * Set the default category's channel to default_stderr,
+ * which is at the head of the channels list because it was
+ * just created.
+ */
+ default_channel.channel = ISC_LIST_HEAD(lcfg->channels);
+
+ destination.file.stream = stderr;
+ destination.file.name = NULL;
+ destination.file.versions = ISC_LOG_ROLLNEVER;
+ destination.file.suffix = isc_log_rollsuffix_increment;
+ destination.file.maximum_size = 0;
+ isc_log_createchannel(lcfg, "default_debug", ISC_LOG_TOFILEDESC,
+ ISC_LOG_DYNAMIC, &destination, ISC_LOG_PRINTTIME);
+
+ isc_log_createchannel(lcfg, "null", ISC_LOG_TONULL, ISC_LOG_DYNAMIC,
+ NULL, 0);
+
+ *lcfgp = lcfg;
+}
+
+void
+isc_logconfig_use(isc_log_t *lctx, isc_logconfig_t *lcfg) {
+ isc_logconfig_t *old_cfg;
+
+ REQUIRE(VALID_CONTEXT(lctx));
+ REQUIRE(VALID_CONFIG(lcfg));
+ REQUIRE(lcfg->lctx == lctx);
+
+ /*
+ * Ensure that lcfg->channellist_count == lctx->category_count.
+ * They won't be equal if isc_log_usechannel has not been called
+ * since any call to isc_log_registercategories.
+ */
+ sync_channellist(lcfg);
+
+ WRLOCK(&lctx->lcfg_rwl);
+ old_cfg = lctx->logconfig;
+ lctx->logconfig = lcfg;
+ sync_highest_level(lctx, lcfg);
+ WRUNLOCK(&lctx->lcfg_rwl);
+
+ isc_logconfig_destroy(&old_cfg);
+}
+
+void
+isc_log_destroy(isc_log_t **lctxp) {
+ isc_log_t *lctx;
+ isc_logconfig_t *lcfg;
+ isc_mem_t *mctx;
+ isc_logmessage_t *message;
+
+ REQUIRE(lctxp != NULL && VALID_CONTEXT(*lctxp));
+
+ lctx = *lctxp;
+ *lctxp = NULL;
+ mctx = lctx->mctx;
+
+ /* Stop the logging as a first thing */
+ atomic_store_release(&lctx->debug_level, 0);
+ atomic_store_release(&lctx->highest_level, 0);
+ atomic_store_release(&lctx->dynamic, false);
+
+ WRLOCK(&lctx->lcfg_rwl);
+ lcfg = lctx->logconfig;
+ lctx->logconfig = NULL;
+ WRUNLOCK(&lctx->lcfg_rwl);
+
+ if (lcfg != NULL) {
+ isc_logconfig_destroy(&lcfg);
+ }
+
+ isc_rwlock_destroy(&lctx->lcfg_rwl);
+ isc_mutex_destroy(&lctx->lock);
+
+ while ((message = ISC_LIST_HEAD(lctx->messages)) != NULL) {
+ ISC_LIST_UNLINK(lctx->messages, message, link);
+
+ isc_mem_put(mctx, message,
+ sizeof(*message) + strlen(message->text) + 1);
+ }
+
+ lctx->buffer[0] = '\0';
+ lctx->categories = NULL;
+ lctx->category_count = 0;
+ lctx->modules = NULL;
+ lctx->module_count = 0;
+ lctx->mctx = NULL;
+ lctx->magic = 0;
+
+ isc_mem_putanddetach(&mctx, lctx, sizeof(*lctx));
+}
+
+void
+isc_logconfig_destroy(isc_logconfig_t **lcfgp) {
+ isc_logconfig_t *lcfg;
+ isc_mem_t *mctx;
+ isc_logchannel_t *channel;
+ char *filename;
+ unsigned int i;
+
+ REQUIRE(lcfgp != NULL && VALID_CONFIG(*lcfgp));
+
+ lcfg = *lcfgp;
+ *lcfgp = NULL;
+
+ /*
+ * This function cannot be called with a logconfig that is in
+ * use by a log context.
+ */
+ REQUIRE(lcfg->lctx != NULL);
+
+ RDLOCK(&lcfg->lctx->lcfg_rwl);
+ REQUIRE(lcfg->lctx->logconfig != lcfg);
+ RDUNLOCK(&lcfg->lctx->lcfg_rwl);
+
+ mctx = lcfg->lctx->mctx;
+
+ while ((channel = ISC_LIST_HEAD(lcfg->channels)) != NULL) {
+ ISC_LIST_UNLINK(lcfg->channels, channel, link);
+
+ if (channel->type == ISC_LOG_TOFILE) {
+ /*
+ * The filename for the channel may have ultimately
+ * started its life in user-land as a const string,
+ * but in isc_log_createchannel it gets copied
+ * into writable memory and is not longer truly const.
+ */
+ DE_CONST(FILE_NAME(channel), filename);
+ isc_mem_free(mctx, filename);
+
+ if (FILE_STREAM(channel) != NULL) {
+ (void)fclose(FILE_STREAM(channel));
+ }
+ }
+
+ isc_mem_free(mctx, channel->name);
+ isc_mem_put(mctx, channel, sizeof(*channel));
+ }
+
+ for (i = 0; i < lcfg->channellist_count; i++) {
+ isc_logchannellist_t *item;
+ while ((item = ISC_LIST_HEAD(lcfg->channellists[i])) != NULL) {
+ ISC_LIST_UNLINK(lcfg->channellists[i], item, link);
+ isc_mem_put(mctx, item, sizeof(*item));
+ }
+ }
+
+ if (lcfg->channellist_count > 0) {
+ isc_mem_put(mctx, lcfg->channellists,
+ lcfg->channellist_count *
+ sizeof(ISC_LIST(isc_logchannellist_t)));
+ }
+
+ lcfg->dynamic = false;
+ if (lcfg->tag != NULL) {
+ isc_mem_free(lcfg->lctx->mctx, lcfg->tag);
+ }
+ lcfg->tag = NULL;
+ lcfg->highest_level = 0;
+ lcfg->duplicate_interval = 0;
+ lcfg->magic = 0;
+
+ isc_mem_put(mctx, lcfg, sizeof(*lcfg));
+}
+
+void
+isc_log_registercategories(isc_log_t *lctx, isc_logcategory_t categories[]) {
+ isc_logcategory_t *catp;
+
+ REQUIRE(VALID_CONTEXT(lctx));
+ REQUIRE(categories != NULL && categories[0].name != NULL);
+
+ /*
+ * XXXDCL This somewhat sleazy situation of using the last pointer
+ * in one category array to point to the next array exists because
+ * this registration function returns void and I didn't want to have
+ * change everything that used it by making it return an isc_result_t.
+ * It would need to do that if it had to allocate memory to store
+ * pointers to each array passed in.
+ */
+ if (lctx->categories == NULL) {
+ lctx->categories = categories;
+ } else {
+ /*
+ * Adjust the last (NULL) pointer of the already registered
+ * categories to point to the incoming array.
+ */
+ for (catp = lctx->categories; catp->name != NULL;) {
+ if (catp->id == UINT_MAX) {
+ /*
+ * The name pointer points to the next array.
+ * Ick.
+ */
+ DE_CONST(catp->name, catp);
+ } else {
+ catp++;
+ }
+ }
+
+ catp->name = (void *)categories;
+ catp->id = UINT_MAX;
+ }
+
+ /*
+ * Update the id number of the category with its new global id.
+ */
+ for (catp = categories; catp->name != NULL; catp++) {
+ catp->id = lctx->category_count++;
+ }
+}
+
+isc_logcategory_t *
+isc_log_categorybyname(isc_log_t *lctx, const char *name) {
+ isc_logcategory_t *catp;
+
+ REQUIRE(VALID_CONTEXT(lctx));
+ REQUIRE(name != NULL);
+
+ for (catp = lctx->categories; catp->name != NULL;) {
+ if (catp->id == UINT_MAX) {
+ /*
+ * catp is neither modified nor returned to the
+ * caller, so removing its const qualifier is ok.
+ */
+ DE_CONST(catp->name, catp);
+ } else {
+ if (strcmp(catp->name, name) == 0) {
+ return (catp);
+ }
+ catp++;
+ }
+ }
+
+ return (NULL);
+}
+
+void
+isc_log_registermodules(isc_log_t *lctx, isc_logmodule_t modules[]) {
+ isc_logmodule_t *modp;
+
+ REQUIRE(VALID_CONTEXT(lctx));
+ REQUIRE(modules != NULL && modules[0].name != NULL);
+
+ /*
+ * XXXDCL This somewhat sleazy situation of using the last pointer
+ * in one category array to point to the next array exists because
+ * this registration function returns void and I didn't want to have
+ * change everything that used it by making it return an isc_result_t.
+ * It would need to do that if it had to allocate memory to store
+ * pointers to each array passed in.
+ */
+ if (lctx->modules == NULL) {
+ lctx->modules = modules;
+ } else {
+ /*
+ * Adjust the last (NULL) pointer of the already registered
+ * modules to point to the incoming array.
+ */
+ for (modp = lctx->modules; modp->name != NULL;) {
+ if (modp->id == UINT_MAX) {
+ /*
+ * The name pointer points to the next array.
+ * Ick.
+ */
+ DE_CONST(modp->name, modp);
+ } else {
+ modp++;
+ }
+ }
+
+ modp->name = (void *)modules;
+ modp->id = UINT_MAX;
+ }
+
+ /*
+ * Update the id number of the module with its new global id.
+ */
+ for (modp = modules; modp->name != NULL; modp++) {
+ modp->id = lctx->module_count++;
+ }
+}
+
+isc_logmodule_t *
+isc_log_modulebyname(isc_log_t *lctx, const char *name) {
+ isc_logmodule_t *modp;
+
+ REQUIRE(VALID_CONTEXT(lctx));
+ REQUIRE(name != NULL);
+
+ for (modp = lctx->modules; modp->name != NULL;) {
+ if (modp->id == UINT_MAX) {
+ /*
+ * modp is neither modified nor returned to the
+ * caller, so removing its const qualifier is ok.
+ */
+ DE_CONST(modp->name, modp);
+ } else {
+ if (strcmp(modp->name, name) == 0) {
+ return (modp);
+ }
+ modp++;
+ }
+ }
+
+ return (NULL);
+}
+
+void
+isc_log_createchannel(isc_logconfig_t *lcfg, const char *name,
+ unsigned int type, int level,
+ const isc_logdestination_t *destination,
+ unsigned int flags) {
+ isc_logchannel_t *channel;
+ isc_mem_t *mctx;
+ unsigned int permitted = ISC_LOG_PRINTALL | ISC_LOG_DEBUGONLY |
+ ISC_LOG_BUFFERED | ISC_LOG_ISO8601 |
+ ISC_LOG_UTC;
+
+ REQUIRE(VALID_CONFIG(lcfg));
+ REQUIRE(name != NULL);
+ REQUIRE(type == ISC_LOG_TOSYSLOG || type == ISC_LOG_TOFILE ||
+ type == ISC_LOG_TOFILEDESC || type == ISC_LOG_TONULL);
+ REQUIRE(destination != NULL || type == ISC_LOG_TONULL);
+ REQUIRE(level >= ISC_LOG_CRITICAL);
+ REQUIRE((flags & ~permitted) == 0);
+
+ /* XXXDCL find duplicate names? */
+
+ mctx = lcfg->lctx->mctx;
+
+ channel = isc_mem_get(mctx, sizeof(*channel));
+
+ channel->name = isc_mem_strdup(mctx, name);
+
+ channel->type = type;
+ channel->level = level;
+ channel->flags = flags;
+ ISC_LINK_INIT(channel, link);
+
+ switch (type) {
+ case ISC_LOG_TOSYSLOG:
+ FACILITY(channel) = destination->facility;
+ break;
+
+ case ISC_LOG_TOFILE:
+ /*
+ * The file name is copied because greatest_version wants
+ * to scribble on it, so it needs to be definitely in
+ * writable memory.
+ */
+ FILE_NAME(channel) = isc_mem_strdup(mctx,
+ destination->file.name);
+ FILE_STREAM(channel) = NULL;
+ FILE_VERSIONS(channel) = destination->file.versions;
+ FILE_SUFFIX(channel) = destination->file.suffix;
+ FILE_MAXSIZE(channel) = destination->file.maximum_size;
+ FILE_MAXREACHED(channel) = false;
+ break;
+
+ case ISC_LOG_TOFILEDESC:
+ FILE_NAME(channel) = NULL;
+ FILE_STREAM(channel) = destination->file.stream;
+ FILE_MAXSIZE(channel) = 0;
+ FILE_VERSIONS(channel) = ISC_LOG_ROLLNEVER;
+ FILE_SUFFIX(channel) = isc_log_rollsuffix_increment;
+ break;
+
+ case ISC_LOG_TONULL:
+ /* Nothing. */
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+
+ ISC_LIST_PREPEND(lcfg->channels, channel, link);
+
+ /*
+ * If default_stderr was redefined, make the default category
+ * point to the new default_stderr.
+ */
+ if (strcmp(name, "default_stderr") == 0) {
+ default_channel.channel = channel;
+ }
+}
+
+isc_result_t
+isc_log_usechannel(isc_logconfig_t *lcfg, const char *name,
+ const isc_logcategory_t *category,
+ const isc_logmodule_t *module) {
+ isc_log_t *lctx;
+ isc_logchannel_t *channel;
+
+ REQUIRE(VALID_CONFIG(lcfg));
+ REQUIRE(name != NULL);
+
+ lctx = lcfg->lctx;
+
+ REQUIRE(category == NULL || category->id < lctx->category_count);
+ REQUIRE(module == NULL || module->id < lctx->module_count);
+
+ for (channel = ISC_LIST_HEAD(lcfg->channels); channel != NULL;
+ channel = ISC_LIST_NEXT(channel, link))
+ {
+ if (strcmp(name, channel->name) == 0) {
+ break;
+ }
+ }
+
+ if (channel == NULL) {
+ return (ISC_R_NOTFOUND);
+ }
+
+ if (category != NULL) {
+ assignchannel(lcfg, category->id, module, channel);
+ } else {
+ /*
+ * Assign to all categories. Note that this includes
+ * the default channel.
+ */
+ for (size_t i = 0; i < lctx->category_count; i++) {
+ assignchannel(lcfg, i, module, channel);
+ }
+ }
+
+ /*
+ * Update the highest logging level, if the current lcfg is in use.
+ */
+ if (lcfg->lctx->logconfig == lcfg) {
+ sync_highest_level(lctx, lcfg);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_log_write(isc_log_t *lctx, isc_logcategory_t *category,
+ isc_logmodule_t *module, int level, const char *format, ...) {
+ va_list args;
+
+ /*
+ * Contract checking is done in isc_log_doit().
+ */
+
+ va_start(args, format);
+ isc_log_doit(lctx, category, module, level, false, format, args);
+ va_end(args);
+}
+
+void
+isc_log_vwrite(isc_log_t *lctx, isc_logcategory_t *category,
+ isc_logmodule_t *module, int level, const char *format,
+ va_list args) {
+ /*
+ * Contract checking is done in isc_log_doit().
+ */
+ isc_log_doit(lctx, category, module, level, false, format, args);
+}
+
+void
+isc_log_write1(isc_log_t *lctx, isc_logcategory_t *category,
+ isc_logmodule_t *module, int level, const char *format, ...) {
+ va_list args;
+
+ /*
+ * Contract checking is done in isc_log_doit().
+ */
+
+ va_start(args, format);
+ isc_log_doit(lctx, category, module, level, true, format, args);
+ va_end(args);
+}
+
+void
+isc_log_vwrite1(isc_log_t *lctx, isc_logcategory_t *category,
+ isc_logmodule_t *module, int level, const char *format,
+ va_list args) {
+ /*
+ * Contract checking is done in isc_log_doit().
+ */
+ isc_log_doit(lctx, category, module, level, true, format, args);
+}
+
+void
+isc_log_setcontext(isc_log_t *lctx) {
+ isc_lctx = lctx;
+}
+
+void
+isc_log_setdebuglevel(isc_log_t *lctx, unsigned int level) {
+ REQUIRE(VALID_CONTEXT(lctx));
+
+ atomic_store_release(&lctx->debug_level, level);
+ /*
+ * Close ISC_LOG_DEBUGONLY channels if level is zero.
+ */
+ if (level == 0) {
+ RDLOCK(&lctx->lcfg_rwl);
+ isc_logconfig_t *lcfg = lctx->logconfig;
+ if (lcfg != NULL) {
+ LOCK(&lctx->lock);
+ for (isc_logchannel_t *channel =
+ ISC_LIST_HEAD(lcfg->channels);
+ channel != NULL;
+ channel = ISC_LIST_NEXT(channel, link))
+ {
+ if (channel->type == ISC_LOG_TOFILE &&
+ (channel->flags & ISC_LOG_DEBUGONLY) != 0 &&
+ FILE_STREAM(channel) != NULL)
+ {
+ (void)fclose(FILE_STREAM(channel));
+ FILE_STREAM(channel) = NULL;
+ }
+ }
+ UNLOCK(&lctx->lock);
+ }
+ RDUNLOCK(&lctx->lcfg_rwl);
+ }
+}
+
+unsigned int
+isc_log_getdebuglevel(isc_log_t *lctx) {
+ REQUIRE(VALID_CONTEXT(lctx));
+
+ return (atomic_load_acquire(&lctx->debug_level));
+}
+
+void
+isc_log_setduplicateinterval(isc_logconfig_t *lcfg, unsigned int interval) {
+ REQUIRE(VALID_CONFIG(lcfg));
+
+ lcfg->duplicate_interval = interval;
+}
+
+unsigned int
+isc_log_getduplicateinterval(isc_logconfig_t *lcfg) {
+ REQUIRE(VALID_CONTEXT(lcfg));
+
+ return (lcfg->duplicate_interval);
+}
+
+void
+isc_log_settag(isc_logconfig_t *lcfg, const char *tag) {
+ REQUIRE(VALID_CONFIG(lcfg));
+
+ if (tag != NULL && *tag != '\0') {
+ if (lcfg->tag != NULL) {
+ isc_mem_free(lcfg->lctx->mctx, lcfg->tag);
+ }
+ lcfg->tag = isc_mem_strdup(lcfg->lctx->mctx, tag);
+ } else {
+ if (lcfg->tag != NULL) {
+ isc_mem_free(lcfg->lctx->mctx, lcfg->tag);
+ }
+ lcfg->tag = NULL;
+ }
+}
+
+char *
+isc_log_gettag(isc_logconfig_t *lcfg) {
+ REQUIRE(VALID_CONFIG(lcfg));
+
+ return (lcfg->tag);
+}
+
+/* XXXDCL NT -- This interface will assuredly be changing. */
+void
+isc_log_opensyslog(const char *tag, int options, int facility) {
+ (void)openlog(tag, options, facility);
+}
+
+void
+isc_log_closefilelogs(isc_log_t *lctx) {
+ REQUIRE(VALID_CONTEXT(lctx));
+
+ RDLOCK(&lctx->lcfg_rwl);
+ isc_logconfig_t *lcfg = lctx->logconfig;
+ if (lcfg != NULL) {
+ LOCK(&lctx->lock);
+ for (isc_logchannel_t *channel = ISC_LIST_HEAD(lcfg->channels);
+ channel != NULL; channel = ISC_LIST_NEXT(channel, link))
+ {
+ if (channel->type == ISC_LOG_TOFILE &&
+ FILE_STREAM(channel) != NULL)
+ {
+ (void)fclose(FILE_STREAM(channel));
+ FILE_STREAM(channel) = NULL;
+ }
+ }
+ UNLOCK(&lctx->lock);
+ }
+ RDUNLOCK(&lctx->lcfg_rwl);
+}
+
+/****
+**** Internal functions
+****/
+
+static void
+assignchannel(isc_logconfig_t *lcfg, unsigned int category_id,
+ const isc_logmodule_t *module, isc_logchannel_t *channel) {
+ isc_logchannellist_t *new_item;
+ isc_log_t *lctx;
+
+ REQUIRE(VALID_CONFIG(lcfg));
+
+ lctx = lcfg->lctx;
+
+ REQUIRE(category_id < lctx->category_count);
+ REQUIRE(module == NULL || module->id < lctx->module_count);
+ REQUIRE(channel != NULL);
+
+ /*
+ * Ensure lcfg->channellist_count == lctx->category_count.
+ */
+ sync_channellist(lcfg);
+
+ new_item = isc_mem_get(lctx->mctx, sizeof(*new_item));
+
+ new_item->channel = channel;
+ new_item->module = module;
+ ISC_LIST_INITANDPREPEND(lcfg->channellists[category_id], new_item,
+ link);
+
+ /*
+ * Remember the highest logging level set by any channel in the
+ * logging config, so isc_log_doit() can quickly return if the
+ * message is too high to be logged by any channel.
+ */
+ if (channel->type != ISC_LOG_TONULL) {
+ if (lcfg->highest_level < channel->level) {
+ lcfg->highest_level = channel->level;
+ }
+ if (channel->level == ISC_LOG_DYNAMIC) {
+ lcfg->dynamic = true;
+ }
+ }
+}
+
+/*
+ * This would ideally be part of isc_log_registercategories(), except then
+ * that function would have to return isc_result_t instead of void.
+ */
+static void
+sync_channellist(isc_logconfig_t *lcfg) {
+ unsigned int bytes;
+ isc_log_t *lctx;
+ void *lists;
+
+ REQUIRE(VALID_CONFIG(lcfg));
+
+ lctx = lcfg->lctx;
+
+ REQUIRE(lctx->category_count != 0);
+
+ if (lctx->category_count == lcfg->channellist_count) {
+ return;
+ }
+
+ bytes = lctx->category_count * sizeof(ISC_LIST(isc_logchannellist_t));
+
+ lists = isc_mem_get(lctx->mctx, bytes);
+
+ memset(lists, 0, bytes);
+
+ if (lcfg->channellist_count != 0) {
+ bytes = lcfg->channellist_count *
+ sizeof(ISC_LIST(isc_logchannellist_t));
+ memmove(lists, lcfg->channellists, bytes);
+ isc_mem_put(lctx->mctx, lcfg->channellists, bytes);
+ }
+
+ lcfg->channellists = lists;
+ lcfg->channellist_count = lctx->category_count;
+}
+
+static void
+sync_highest_level(isc_log_t *lctx, isc_logconfig_t *lcfg) {
+ atomic_store(&lctx->highest_level, lcfg->highest_level);
+ atomic_store(&lctx->dynamic, lcfg->dynamic);
+}
+
+static isc_result_t
+greatest_version(isc_logfile_t *file, int versions, int *greatestp) {
+ char *digit_end;
+ char dirbuf[PATH_MAX + 1];
+ const char *bname;
+ const char *dirname = ".";
+ int version, greatest = -1;
+ isc_dir_t dir;
+ isc_result_t result;
+ size_t bnamelen;
+
+ bname = strrchr(file->name, '/');
+ if (bname != NULL) {
+ /*
+ * Copy the complete file name to dirbuf.
+ */
+ size_t len = strlcpy(dirbuf, file->name, sizeof(dirbuf));
+ if (len >= sizeof(dirbuf)) {
+ result = ISC_R_NOSPACE;
+ syslog(LOG_ERR, "unable to remove log files: %s",
+ isc_result_totext(result));
+ return (result);
+ }
+
+ /*
+ * Truncate after trailing '/' so the code works for
+ * files in the root directory.
+ */
+ bname++;
+ dirbuf[bname - file->name] = '\0';
+ dirname = dirbuf;
+ } else {
+ bname = file->name;
+ }
+ bnamelen = strlen(bname);
+
+ isc_dir_init(&dir);
+ result = isc_dir_open(&dir, dirname);
+
+ /*
+ * Return if the directory open failed.
+ */
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ while (isc_dir_read(&dir) == ISC_R_SUCCESS) {
+ if (dir.entry.length > bnamelen &&
+ strncmp(dir.entry.name, bname, bnamelen) == 0 &&
+ dir.entry.name[bnamelen] == '.')
+ {
+ version = strtol(&dir.entry.name[bnamelen + 1],
+ &digit_end, 10);
+ /*
+ * Remove any backup files that exceed versions.
+ */
+ if (*digit_end == '\0' && version >= versions) {
+ int n = unlinkat(dirfd(dir.handle),
+ dir.entry.name, 0);
+ if (n < 0) {
+ result = isc_errno_toresult(errno);
+ if (result != ISC_R_SUCCESS &&
+ result != ISC_R_FILENOTFOUND)
+ {
+ syslog(LOG_ERR,
+ "unable to remove log "
+ "file '%s%s': %s",
+ bname == file->name
+ ? ""
+ : dirname,
+ dir.entry.name,
+ isc_result_totext(
+ result));
+ }
+ }
+ } else if (*digit_end == '\0' && version > greatest) {
+ greatest = version;
+ }
+ }
+ }
+ isc_dir_close(&dir);
+
+ *greatestp = greatest;
+ return (ISC_R_SUCCESS);
+}
+
+static void
+insert_sort(int64_t to_keep[], int64_t versions, int64_t version) {
+ int i = 0;
+ while (i < versions && version < to_keep[i]) {
+ i++;
+ }
+ if (i == versions) {
+ return;
+ }
+ if (i < versions - 1) {
+ memmove(&to_keep[i + 1], &to_keep[i],
+ sizeof(to_keep[0]) * (versions - i - 1));
+ }
+ to_keep[i] = version;
+}
+
+static int64_t
+last_to_keep(int64_t versions, isc_dir_t *dirp, const char *bname,
+ size_t bnamelen) {
+ int64_t to_keep[ISC_LOG_MAX_VERSIONS] = { 0 };
+ int64_t version = 0;
+
+ if (versions <= 0) {
+ return (INT64_MAX);
+ }
+
+ if (versions > ISC_LOG_MAX_VERSIONS) {
+ versions = ISC_LOG_MAX_VERSIONS;
+ }
+ /*
+ * First we fill 'to_keep' structure using insertion sort
+ */
+ memset(to_keep, 0, sizeof(to_keep));
+ while (isc_dir_read(dirp) == ISC_R_SUCCESS) {
+ char *digit_end = NULL;
+ char *ename = NULL;
+
+ if (dirp->entry.length <= bnamelen ||
+ strncmp(dirp->entry.name, bname, bnamelen) != 0 ||
+ dirp->entry.name[bnamelen] != '.')
+ {
+ continue;
+ }
+
+ ename = &dirp->entry.name[bnamelen + 1];
+ version = strtoull(ename, &digit_end, 10);
+ if (*digit_end == '\0') {
+ insert_sort(to_keep, versions, version);
+ }
+ }
+
+ isc_dir_reset(dirp);
+
+ /*
+ * to_keep[versions - 1] is the last one we want to keep
+ */
+ return (to_keep[versions - 1]);
+}
+
+static isc_result_t
+remove_old_tsversions(isc_logfile_t *file, int versions) {
+ char *digit_end;
+ char dirbuf[PATH_MAX + 1];
+ const char *bname;
+ const char *dirname = ".";
+ int64_t version, last = INT64_MAX;
+ isc_dir_t dir;
+ isc_result_t result;
+ size_t bnamelen;
+
+ bname = strrchr(file->name, '/');
+ if (bname != NULL) {
+ /*
+ * Copy the complete file name to dirbuf.
+ */
+ size_t len = strlcpy(dirbuf, file->name, sizeof(dirbuf));
+ if (len >= sizeof(dirbuf)) {
+ result = ISC_R_NOSPACE;
+ syslog(LOG_ERR, "unable to remove log files: %s",
+ isc_result_totext(result));
+ return (result);
+ }
+
+ /*
+ * Truncate after trailing '/' so the code works for
+ * files in the root directory.
+ */
+ bname++;
+ dirbuf[bname - file->name] = '\0';
+ dirname = dirbuf;
+ } else {
+ bname = file->name;
+ }
+ bnamelen = strlen(bname);
+
+ isc_dir_init(&dir);
+ result = isc_dir_open(&dir, dirname);
+
+ /*
+ * Return if the directory open failed.
+ */
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ last = last_to_keep(versions, &dir, bname, bnamelen);
+
+ while (isc_dir_read(&dir) == ISC_R_SUCCESS) {
+ if (dir.entry.length > bnamelen &&
+ strncmp(dir.entry.name, bname, bnamelen) == 0 &&
+ dir.entry.name[bnamelen] == '.')
+ {
+ version = strtoull(&dir.entry.name[bnamelen + 1],
+ &digit_end, 10);
+ /*
+ * Remove any backup files that exceed versions.
+ */
+ if (*digit_end == '\0' && version < last) {
+ int n = unlinkat(dirfd(dir.handle),
+ dir.entry.name, 0);
+ if (n < 0) {
+ result = isc_errno_toresult(errno);
+ if (result != ISC_R_SUCCESS &&
+ result != ISC_R_FILENOTFOUND)
+ {
+ syslog(LOG_ERR,
+ "unable to remove log "
+ "file '%s%s': %s",
+ bname == file->name
+ ? ""
+ : dirname,
+ dir.entry.name,
+ isc_result_totext(
+ result));
+ }
+ }
+ }
+ }
+ }
+ isc_dir_close(&dir);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+roll_increment(isc_logfile_t *file) {
+ int i, n, greatest;
+ char current[PATH_MAX + 1];
+ char newpath[PATH_MAX + 1];
+ const char *path;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ REQUIRE(file != NULL);
+ REQUIRE(file->versions != 0);
+
+ path = file->name;
+
+ if (file->versions == ISC_LOG_ROLLINFINITE) {
+ /*
+ * Find the first missing entry in the log file sequence.
+ */
+ for (greatest = 0; greatest < INT_MAX; greatest++) {
+ n = snprintf(current, sizeof(current), "%s.%u", path,
+ (unsigned)greatest);
+ if (n >= (int)sizeof(current) || n < 0 ||
+ !isc_file_exists(current))
+ {
+ break;
+ }
+ }
+ } else {
+ /*
+ * Get the largest existing version and remove any
+ * version greater than the permitted version.
+ */
+ result = greatest_version(file, file->versions, &greatest);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ /*
+ * Increment if greatest is not the actual maximum value.
+ */
+ if (greatest < file->versions - 1) {
+ greatest++;
+ }
+ }
+
+ for (i = greatest; i > 0; i--) {
+ result = ISC_R_SUCCESS;
+ n = snprintf(current, sizeof(current), "%s.%u", path,
+ (unsigned)(i - 1));
+ if (n >= (int)sizeof(current) || n < 0) {
+ result = ISC_R_NOSPACE;
+ }
+ if (result == ISC_R_SUCCESS) {
+ n = snprintf(newpath, sizeof(newpath), "%s.%u", path,
+ (unsigned)i);
+ if (n >= (int)sizeof(newpath) || n < 0) {
+ result = ISC_R_NOSPACE;
+ }
+ }
+ if (result == ISC_R_SUCCESS) {
+ result = isc_file_rename(current, newpath);
+ }
+ if (result != ISC_R_SUCCESS && result != ISC_R_FILENOTFOUND) {
+ syslog(LOG_ERR,
+ "unable to rename log file '%s.%u' to "
+ "'%s.%u': %s",
+ path, i - 1, path, i, isc_result_totext(result));
+ }
+ }
+
+ n = snprintf(newpath, sizeof(newpath), "%s.0", path);
+ if (n >= (int)sizeof(newpath) || n < 0) {
+ result = ISC_R_NOSPACE;
+ } else {
+ result = isc_file_rename(path, newpath);
+ }
+ if (result != ISC_R_SUCCESS && result != ISC_R_FILENOTFOUND) {
+ syslog(LOG_ERR, "unable to rename log file '%s' to '%s.0': %s",
+ path, path, isc_result_totext(result));
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+roll_timestamp(isc_logfile_t *file) {
+ int n;
+ char newts[PATH_MAX + 1];
+ char newpath[PATH_MAX + 1];
+ const char *path;
+ isc_time_t now;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ REQUIRE(file != NULL);
+ REQUIRE(file->versions != 0);
+
+ path = file->name;
+
+ /*
+ * First find all the logfiles and remove the oldest ones
+ * Save one fewer than file->versions because we'll be renaming
+ * the existing file to a timestamped version after this.
+ */
+ if (file->versions != ISC_LOG_ROLLINFINITE) {
+ remove_old_tsversions(file, file->versions - 1);
+ }
+
+ /* Then just rename the current logfile */
+ isc_time_now(&now);
+ isc_time_formatshorttimestamp(&now, newts, PATH_MAX + 1);
+ n = snprintf(newpath, sizeof(newpath), "%s.%s", path, newts);
+ if (n >= (int)sizeof(newpath) || n < 0) {
+ result = ISC_R_NOSPACE;
+ } else {
+ result = isc_file_rename(path, newpath);
+ }
+ if (result != ISC_R_SUCCESS && result != ISC_R_FILENOTFOUND) {
+ syslog(LOG_ERR, "unable to rename log file '%s' to '%s.0': %s",
+ path, path, isc_result_totext(result));
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_logfile_roll(isc_logfile_t *file) {
+ isc_result_t result;
+
+ REQUIRE(file != NULL);
+
+ /*
+ * Do nothing (not even excess version trimming) if ISC_LOG_ROLLNEVER
+ * is specified. Apparently complete external control over the log
+ * files is desired.
+ */
+ if (file->versions == ISC_LOG_ROLLNEVER) {
+ return (ISC_R_SUCCESS);
+ } else if (file->versions == 0) {
+ result = isc_file_remove(file->name);
+ if (result != ISC_R_SUCCESS && result != ISC_R_FILENOTFOUND) {
+ syslog(LOG_ERR, "unable to remove log file '%s': %s",
+ file->name, isc_result_totext(result));
+ }
+ return (ISC_R_SUCCESS);
+ }
+
+ switch (file->suffix) {
+ case isc_log_rollsuffix_increment:
+ return (roll_increment(file));
+ case isc_log_rollsuffix_timestamp:
+ return (roll_timestamp(file));
+ default:
+ return (ISC_R_UNEXPECTED);
+ }
+}
+
+static isc_result_t
+isc_log_open(isc_logchannel_t *channel) {
+ struct stat statbuf;
+ bool regular_file;
+ bool roll = false;
+ isc_result_t result = ISC_R_SUCCESS;
+ const char *path;
+
+ REQUIRE(channel->type == ISC_LOG_TOFILE);
+ REQUIRE(FILE_STREAM(channel) == NULL);
+
+ path = FILE_NAME(channel);
+
+ REQUIRE(path != NULL && *path != '\0');
+
+ /*
+ * Determine type of file; only regular files will be
+ * version renamed, and only if the base file exists
+ * and either has no size limit or has reached its size limit.
+ */
+ if (stat(path, &statbuf) == 0) {
+ regular_file = S_ISREG(statbuf.st_mode) ? true : false;
+ /* XXXDCL if not regular_file complain? */
+ if ((FILE_MAXSIZE(channel) == 0 &&
+ FILE_VERSIONS(channel) != ISC_LOG_ROLLNEVER) ||
+ (FILE_MAXSIZE(channel) > 0 &&
+ statbuf.st_size >= FILE_MAXSIZE(channel)))
+ {
+ roll = regular_file;
+ }
+ } else if (errno == ENOENT) {
+ regular_file = true;
+ POST(regular_file);
+ } else {
+ result = ISC_R_INVALIDFILE;
+ }
+
+ /*
+ * Version control.
+ */
+ if (result == ISC_R_SUCCESS && roll) {
+ if (FILE_VERSIONS(channel) == ISC_LOG_ROLLNEVER) {
+ return (ISC_R_MAXSIZE);
+ }
+ result = isc_logfile_roll(&channel->destination.file);
+ if (result != ISC_R_SUCCESS) {
+ if ((channel->flags & ISC_LOG_OPENERR) == 0) {
+ syslog(LOG_ERR,
+ "isc_log_open: isc_logfile_roll '%s' "
+ "failed: %s",
+ FILE_NAME(channel),
+ isc_result_totext(result));
+ channel->flags |= ISC_LOG_OPENERR;
+ }
+ return (result);
+ }
+ }
+
+ result = isc_stdio_open(path, "a", &FILE_STREAM(channel));
+
+ return (result);
+}
+
+ISC_NO_SANITIZE_THREAD bool
+isc_log_wouldlog(isc_log_t *lctx, int level) {
+ /*
+ * Try to avoid locking the mutex for messages which can't
+ * possibly be logged to any channels -- primarily debugging
+ * messages that the debug level is not high enough to print.
+ *
+ * If the level is (mathematically) less than or equal to the
+ * highest_level, or if there is a dynamic channel and the level is
+ * less than or equal to the debug level, the main loop must be
+ * entered to see if the message should really be output.
+ */
+ if (lctx == NULL) {
+ return (false);
+ }
+ if (forcelog) {
+ return (true);
+ }
+
+ int highest_level = atomic_load_acquire(&lctx->highest_level);
+ if (level <= highest_level) {
+ return (true);
+ }
+ if (atomic_load_acquire(&lctx->dynamic)) {
+ int debug_level = atomic_load_acquire(&lctx->debug_level);
+ if (level <= debug_level) {
+ return (true);
+ }
+ }
+
+ return (false);
+}
+
+static void
+isc_log_doit(isc_log_t *lctx, isc_logcategory_t *category,
+ isc_logmodule_t *module, int level, bool write_once,
+ const char *format, va_list args) {
+ int syslog_level;
+ const char *time_string;
+ char local_time[64];
+ char iso8601z_string[64];
+ char iso8601l_string[64];
+ char level_string[24] = { 0 };
+ struct stat statbuf;
+ bool matched = false;
+ bool printtime, iso8601, utc, printtag, printcolon;
+ bool printcategory, printmodule, printlevel, buffered;
+ isc_logchannel_t *channel;
+ isc_logchannellist_t *category_channels;
+ int_fast32_t dlevel;
+ isc_result_t result;
+
+ REQUIRE(lctx == NULL || VALID_CONTEXT(lctx));
+ REQUIRE(category != NULL);
+ REQUIRE(module != NULL);
+ REQUIRE(level != ISC_LOG_DYNAMIC);
+ REQUIRE(format != NULL);
+
+ /*
+ * Programs can use libraries that use this logging code without
+ * wanting to do any logging, thus the log context is allowed to
+ * be non-existent.
+ */
+ if (lctx == NULL) {
+ return;
+ }
+
+ REQUIRE(category->id < lctx->category_count);
+ REQUIRE(module->id < lctx->module_count);
+
+ if (!isc_log_wouldlog(lctx, level)) {
+ return;
+ }
+
+ local_time[0] = '\0';
+ iso8601l_string[0] = '\0';
+ iso8601z_string[0] = '\0';
+
+ RDLOCK(&lctx->lcfg_rwl);
+ LOCK(&lctx->lock);
+
+ lctx->buffer[0] = '\0';
+
+ isc_logconfig_t *lcfg = lctx->logconfig;
+
+ category_channels = ISC_LIST_HEAD(lcfg->channellists[category->id]);
+
+ /*
+ * XXXDCL add duplicate filtering? (To not write multiple times
+ * to the same source via various channels).
+ */
+ do {
+ /*
+ * If the channel list end was reached and a match was
+ * made, everything is finished.
+ */
+ if (category_channels == NULL && matched) {
+ break;
+ }
+
+ if (category_channels == NULL && !matched &&
+ category_channels != ISC_LIST_HEAD(lcfg->channellists[0]))
+ {
+ /*
+ * No category/module pair was explicitly
+ * configured. Try the category named "default".
+ */
+ category_channels =
+ ISC_LIST_HEAD(lcfg->channellists[0]);
+ }
+
+ if (category_channels == NULL && !matched) {
+ /*
+ * No matching module was explicitly configured
+ * for the category named "default". Use the
+ * internal default channel.
+ */
+ category_channels = &default_channel;
+ }
+
+ if (category_channels->module != NULL &&
+ category_channels->module != module)
+ {
+ category_channels = ISC_LIST_NEXT(category_channels,
+ link);
+ continue;
+ }
+
+ matched = true;
+
+ channel = category_channels->channel;
+ category_channels = ISC_LIST_NEXT(category_channels, link);
+
+ if (!forcelog) {
+ dlevel = atomic_load_acquire(&lctx->debug_level);
+ if (((channel->flags & ISC_LOG_DEBUGONLY) != 0) &&
+ dlevel == 0)
+ {
+ continue;
+ }
+
+ if (channel->level == ISC_LOG_DYNAMIC) {
+ if (dlevel < level) {
+ continue;
+ }
+ } else if (channel->level < level) {
+ continue;
+ }
+ }
+
+ if ((channel->flags & ISC_LOG_PRINTTIME) != 0 &&
+ local_time[0] == '\0')
+ {
+ isc_time_t isctime;
+
+ TIME_NOW(&isctime);
+
+ isc_time_formattimestamp(&isctime, local_time,
+ sizeof(local_time));
+ isc_time_formatISO8601ms(&isctime, iso8601z_string,
+ sizeof(iso8601z_string));
+ isc_time_formatISO8601Lms(&isctime, iso8601l_string,
+ sizeof(iso8601l_string));
+ }
+
+ if ((channel->flags & ISC_LOG_PRINTLEVEL) != 0 &&
+ level_string[0] == '\0')
+ {
+ if (level < ISC_LOG_CRITICAL) {
+ snprintf(level_string, sizeof(level_string),
+ "level %d: ", level);
+ } else if (level > ISC_LOG_DYNAMIC) {
+ snprintf(level_string, sizeof(level_string),
+ "%s %d: ", log_level_strings[0],
+ level);
+ } else {
+ snprintf(level_string, sizeof(level_string),
+ "%s: ", log_level_strings[-level]);
+ }
+ }
+
+ /*
+ * Only format the message once.
+ */
+ if (lctx->buffer[0] == '\0') {
+ (void)vsnprintf(lctx->buffer, sizeof(lctx->buffer),
+ format, args);
+
+ /*
+ * Check for duplicates.
+ */
+ if (write_once) {
+ isc_logmessage_t *message, *next;
+ isc_time_t oldest;
+ isc_interval_t interval;
+ size_t size;
+
+ isc_interval_set(&interval,
+ lcfg->duplicate_interval, 0);
+
+ /*
+ * 'oldest' is the age of the oldest
+ * messages which fall within the
+ * duplicate_interval range.
+ */
+ TIME_NOW(&oldest);
+ if (isc_time_subtract(&oldest, &interval,
+ &oldest) != ISC_R_SUCCESS)
+ {
+ /*
+ * Can't effectively do the
+ * checking without having a
+ * valid time.
+ */
+ message = NULL;
+ } else {
+ message = ISC_LIST_HEAD(lctx->messages);
+ }
+
+ while (message != NULL) {
+ if (isc_time_compare(&message->time,
+ &oldest) < 0)
+ {
+ /*
+ * This message is older
+ * than the
+ * duplicate_interval,
+ * so it should be
+ * dropped from the
+ * history.
+ *
+ * Setting the interval
+ * to be to be longer
+ * will obviously not
+ * cause the expired
+ * message to spring
+ * back into existence.
+ */
+ next = ISC_LIST_NEXT(message,
+ link);
+
+ ISC_LIST_UNLINK(lctx->messages,
+ message, link);
+
+ isc_mem_put(
+ lctx->mctx, message,
+ sizeof(*message) + 1 +
+ strlen(message->text));
+
+ message = next;
+ continue;
+ }
+
+ /*
+ * This message is in the
+ * duplicate filtering interval
+ * ...
+ */
+ if (strcmp(lctx->buffer,
+ message->text) == 0)
+ {
+ /*
+ * ... and it is a
+ * duplicate. Unlock the
+ * mutex and get the
+ * hell out of Dodge.
+ */
+ goto unlock;
+ }
+
+ message = ISC_LIST_NEXT(message, link);
+ }
+
+ /*
+ * It wasn't in the duplicate interval,
+ * so add it to the message list.
+ */
+ size = sizeof(isc_logmessage_t) +
+ strlen(lctx->buffer) + 1;
+ message = isc_mem_get(lctx->mctx, size);
+ message->text = (char *)(message + 1);
+ size -= sizeof(isc_logmessage_t);
+ strlcpy(message->text, lctx->buffer, size);
+ TIME_NOW(&message->time);
+ ISC_LINK_INIT(message, link);
+ ISC_LIST_APPEND(lctx->messages, message, link);
+ }
+ }
+
+ utc = ((channel->flags & ISC_LOG_UTC) != 0);
+ iso8601 = ((channel->flags & ISC_LOG_ISO8601) != 0);
+ printtime = ((channel->flags & ISC_LOG_PRINTTIME) != 0);
+ printtag = ((channel->flags &
+ (ISC_LOG_PRINTTAG | ISC_LOG_PRINTPREFIX)) != 0 &&
+ lcfg->tag != NULL);
+ printcolon = ((channel->flags & ISC_LOG_PRINTTAG) != 0 &&
+ lcfg->tag != NULL);
+ printcategory = ((channel->flags & ISC_LOG_PRINTCATEGORY) != 0);
+ printmodule = ((channel->flags & ISC_LOG_PRINTMODULE) != 0);
+ printlevel = ((channel->flags & ISC_LOG_PRINTLEVEL) != 0);
+ buffered = ((channel->flags & ISC_LOG_BUFFERED) != 0);
+
+ if (printtime) {
+ if (iso8601) {
+ if (utc) {
+ time_string = iso8601z_string;
+ } else {
+ time_string = iso8601l_string;
+ }
+ } else {
+ time_string = local_time;
+ }
+ } else {
+ time_string = "";
+ }
+
+ switch (channel->type) {
+ case ISC_LOG_TOFILE:
+ if (FILE_MAXREACHED(channel)) {
+ /*
+ * If the file can be rolled, OR
+ * If the file no longer exists, OR
+ * If the file is less than the maximum
+ * size, (such as if it had been renamed
+ * and a new one touched, or it was
+ * truncated in place)
+ * ... then close it to trigger
+ * reopening.
+ */
+ if (FILE_VERSIONS(channel) !=
+ ISC_LOG_ROLLNEVER ||
+ (stat(FILE_NAME(channel), &statbuf) != 0 &&
+ errno == ENOENT) ||
+ statbuf.st_size < FILE_MAXSIZE(channel))
+ {
+ (void)fclose(FILE_STREAM(channel));
+ FILE_STREAM(channel) = NULL;
+ FILE_MAXREACHED(channel) = false;
+ } else {
+ /*
+ * Eh, skip it.
+ */
+ break;
+ }
+ }
+
+ if (FILE_STREAM(channel) == NULL) {
+ result = isc_log_open(channel);
+ if (result != ISC_R_SUCCESS &&
+ result != ISC_R_MAXSIZE &&
+ (channel->flags & ISC_LOG_OPENERR) == 0)
+ {
+ syslog(LOG_ERR,
+ "isc_log_open '%s' "
+ "failed: %s",
+ FILE_NAME(channel),
+ isc_result_totext(result));
+ channel->flags |= ISC_LOG_OPENERR;
+ }
+ if (result != ISC_R_SUCCESS) {
+ break;
+ }
+ channel->flags &= ~ISC_LOG_OPENERR;
+ }
+ FALLTHROUGH;
+
+ case ISC_LOG_TOFILEDESC:
+ fprintf(FILE_STREAM(channel), "%s%s%s%s%s%s%s%s%s%s\n",
+ printtime ? time_string : "",
+ printtime ? " " : "", printtag ? lcfg->tag : "",
+ printcolon ? ": " : "",
+ printcategory ? category->name : "",
+ printcategory ? ": " : "",
+ printmodule ? (module != NULL ? module->name
+ : "no_module")
+ : "",
+ printmodule ? ": " : "",
+ printlevel ? level_string : "", lctx->buffer);
+
+ if (!buffered) {
+ fflush(FILE_STREAM(channel));
+ }
+
+ /*
+ * If the file now exceeds its maximum size
+ * threshold, note it so that it will not be
+ * logged to any more.
+ */
+ if (FILE_MAXSIZE(channel) > 0) {
+ INSIST(channel->type == ISC_LOG_TOFILE);
+
+ /* XXXDCL NT fstat/fileno */
+ /* XXXDCL complain if fstat fails? */
+ if (fstat(fileno(FILE_STREAM(channel)),
+ &statbuf) >= 0 &&
+ statbuf.st_size > FILE_MAXSIZE(channel))
+ {
+ FILE_MAXREACHED(channel) = true;
+ }
+ }
+
+ break;
+
+ case ISC_LOG_TOSYSLOG:
+ if (level > 0) {
+ syslog_level = LOG_DEBUG;
+ } else if (level < ISC_LOG_CRITICAL) {
+ syslog_level = LOG_CRIT;
+ } else {
+ syslog_level = syslog_map[-level];
+ }
+
+ (void)syslog(
+ FACILITY(channel) | syslog_level,
+ "%s%s%s%s%s%s%s%s%s%s",
+ printtime ? time_string : "",
+ printtime ? " " : "", printtag ? lcfg->tag : "",
+ printcolon ? ": " : "",
+ printcategory ? category->name : "",
+ printcategory ? ": " : "",
+ printmodule ? (module != NULL ? module->name
+ : "no_module")
+ : "",
+ printmodule ? ": " : "",
+ printlevel ? level_string : "", lctx->buffer);
+ break;
+
+ case ISC_LOG_TONULL:
+ break;
+ }
+ } while (1);
+
+unlock:
+ UNLOCK(&lctx->lock);
+ RDUNLOCK(&lctx->lcfg_rwl);
+}
+
+void
+isc_log_setforcelog(bool v) {
+ forcelog = v;
+}
diff --git a/lib/isc/managers.c b/lib/isc/managers.c
new file mode 100644
index 0000000..5e9a6c3
--- /dev/null
+++ b/lib/isc/managers.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <isc/managers.h>
+#include <isc/util.h>
+
+#include "netmgr_p.h"
+#include "task_p.h"
+#include "timer_p.h"
+
+isc_result_t
+isc_managers_create(isc_mem_t *mctx, size_t workers, size_t quantum,
+ isc_nm_t **netmgrp, isc_taskmgr_t **taskmgrp,
+ isc_timermgr_t **timermgrp) {
+ isc_result_t result;
+ isc_nm_t *netmgr = NULL;
+ isc_taskmgr_t *taskmgr = NULL;
+ isc_timermgr_t *timermgr = NULL;
+
+ REQUIRE(netmgrp != NULL && *netmgrp == NULL);
+ isc__netmgr_create(mctx, workers, &netmgr);
+ *netmgrp = netmgr;
+ INSIST(netmgr != NULL);
+
+ REQUIRE(taskmgrp == NULL || *taskmgrp == NULL);
+ if (taskmgrp != NULL) {
+ INSIST(netmgr != NULL);
+ result = isc__taskmgr_create(mctx, quantum, netmgr, &taskmgr);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR("isc_taskmgr_create() failed: %s",
+ isc_result_totext(result));
+ goto fail;
+ }
+ *taskmgrp = taskmgr;
+ }
+
+ REQUIRE(timermgrp == NULL || *timermgrp == NULL);
+ if (timermgrp != NULL) {
+ result = isc__timermgr_create(mctx, &timermgr);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR("isc_timermgr_create() failed: %s",
+ isc_result_totext(result));
+ goto fail;
+ }
+ *timermgrp = timermgr;
+ }
+
+ return (ISC_R_SUCCESS);
+fail:
+ isc_managers_destroy(netmgrp, taskmgrp, timermgrp);
+
+ return (result);
+}
+
+void
+isc_managers_destroy(isc_nm_t **netmgrp, isc_taskmgr_t **taskmgrp,
+ isc_timermgr_t **timermgrp) {
+ /*
+ * If we have a taskmgr to clean up, then we must also have a netmgr.
+ */
+ REQUIRE(taskmgrp == NULL || netmgrp != NULL);
+
+ /*
+ * The sequence of operations here is important:
+ *
+ * 1. Initiate shutdown of the taskmgr, sending shutdown events to
+ * all tasks that are not already shutting down.
+ */
+ if (taskmgrp != NULL) {
+ INSIST(*taskmgrp != NULL);
+ isc__taskmgr_shutdown(*taskmgrp);
+ }
+
+ /*
+ * 2. Initiate shutdown of the network manager, freeing clients
+ * and other resources and preventing new connections, but do
+ * not stop processing of existing events.
+ */
+ if (netmgrp != NULL) {
+ INSIST(*netmgrp != NULL);
+ isc__netmgr_shutdown(*netmgrp);
+ }
+
+ /*
+ * 3. Finish destruction of the task manager when all tasks
+ * have completed.
+ */
+ if (taskmgrp != NULL) {
+ isc__taskmgr_destroy(taskmgrp);
+ }
+
+ /*
+ * 4. Finish destruction of the netmgr, and wait until all
+ * references have been released.
+ */
+ if (netmgrp != NULL) {
+ isc__netmgr_destroy(netmgrp);
+ }
+
+ /*
+ * 5. Clean up the remaining managers.
+ */
+ if (timermgrp != NULL) {
+ INSIST(*timermgrp != NULL);
+ isc__timermgr_destroy(timermgrp);
+ }
+}
diff --git a/lib/isc/md.c b/lib/isc/md.c
new file mode 100644
index 0000000..a8c6312
--- /dev/null
+++ b/lib/isc/md.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <stdio.h>
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/opensslv.h>
+
+#include <isc/md.h>
+#include <isc/util.h>
+
+#include "openssl_shim.h"
+
+isc_md_t *
+isc_md_new(void) {
+ isc_md_t *md = EVP_MD_CTX_new();
+ RUNTIME_CHECK(md != NULL);
+ return (md);
+}
+
+void
+isc_md_free(isc_md_t *md) {
+ if (md == NULL) {
+ return;
+ }
+
+ EVP_MD_CTX_free(md);
+}
+
+isc_result_t
+isc_md_init(isc_md_t *md, const isc_md_type_t *md_type) {
+ REQUIRE(md != NULL);
+
+ if (md_type == NULL) {
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+
+ if (EVP_DigestInit_ex(md, md_type, NULL) != 1) {
+ ERR_clear_error();
+ return (ISC_R_CRYPTOFAILURE);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_md_reset(isc_md_t *md) {
+ REQUIRE(md != NULL);
+
+ if (EVP_MD_CTX_reset(md) != 1) {
+ ERR_clear_error();
+ return (ISC_R_CRYPTOFAILURE);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_md_update(isc_md_t *md, const unsigned char *buf, const size_t len) {
+ REQUIRE(md != NULL);
+
+ if (buf == NULL || len == 0) {
+ return (ISC_R_SUCCESS);
+ }
+
+ if (EVP_DigestUpdate(md, buf, len) != 1) {
+ ERR_clear_error();
+ return (ISC_R_CRYPTOFAILURE);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_md_final(isc_md_t *md, unsigned char *digest, unsigned int *digestlen) {
+ REQUIRE(md != NULL);
+ REQUIRE(digest != NULL);
+
+ if (EVP_DigestFinal_ex(md, digest, digestlen) != 1) {
+ ERR_clear_error();
+ return (ISC_R_CRYPTOFAILURE);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+const isc_md_type_t *
+isc_md_get_md_type(isc_md_t *md) {
+ REQUIRE(md != NULL);
+
+ return (EVP_MD_CTX_get0_md(md));
+}
+
+size_t
+isc_md_get_size(isc_md_t *md) {
+ REQUIRE(md != NULL);
+
+ return (EVP_MD_CTX_size(md));
+}
+
+size_t
+isc_md_get_block_size(isc_md_t *md) {
+ REQUIRE(md != NULL);
+
+ return (EVP_MD_CTX_block_size(md));
+}
+
+size_t
+isc_md_type_get_size(const isc_md_type_t *md_type) {
+ STATIC_ASSERT(ISC_MAX_MD_SIZE >= EVP_MAX_MD_SIZE,
+ "Change ISC_MAX_MD_SIZE to be greater than or equal to "
+ "EVP_MAX_MD_SIZE");
+ if (md_type != NULL) {
+ return ((size_t)EVP_MD_size(md_type));
+ }
+
+ return (ISC_MAX_MD_SIZE);
+}
+
+size_t
+isc_md_type_get_block_size(const isc_md_type_t *md_type) {
+ STATIC_ASSERT(ISC_MAX_MD_SIZE >= EVP_MAX_MD_SIZE,
+ "Change ISC_MAX_MD_SIZE to be greater than or equal to "
+ "EVP_MAX_MD_SIZE");
+ if (md_type != NULL) {
+ return ((size_t)EVP_MD_block_size(md_type));
+ }
+
+ return (ISC_MAX_MD_SIZE);
+}
+
+isc_result_t
+isc_md(const isc_md_type_t *md_type, const unsigned char *buf, const size_t len,
+ unsigned char *digest, unsigned int *digestlen) {
+ isc_md_t *md;
+ isc_result_t res;
+
+ md = isc_md_new();
+
+ res = isc_md_init(md, md_type);
+ if (res != ISC_R_SUCCESS) {
+ goto end;
+ }
+
+ res = isc_md_update(md, buf, len);
+ if (res != ISC_R_SUCCESS) {
+ goto end;
+ }
+
+ res = isc_md_final(md, digest, digestlen);
+ if (res != ISC_R_SUCCESS) {
+ goto end;
+ }
+end:
+ isc_md_free(md);
+
+ return (res);
+}
+
+#define md_register_algorithm(alg) \
+ const isc_md_type_t *isc__md_##alg(void) { \
+ const isc_md_type_t *value = EVP_##alg(); \
+ if (value == NULL) { \
+ ERR_clear_error(); \
+ } \
+ return (value); \
+ }
+
+md_register_algorithm(md5);
+md_register_algorithm(sha1);
+md_register_algorithm(sha224);
+md_register_algorithm(sha256);
+md_register_algorithm(sha384);
+md_register_algorithm(sha512);
diff --git a/lib/isc/mem.c b/lib/isc/mem.c
new file mode 100644
index 0000000..61a66f6
--- /dev/null
+++ b/lib/isc/mem.c
@@ -0,0 +1,1908 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <isc/align.h>
+#include <isc/hash.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/mutex.h>
+#include <isc/once.h>
+#include <isc/os.h>
+#include <isc/print.h>
+#include <isc/refcount.h>
+#include <isc/string.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+#ifdef HAVE_LIBXML2
+#include <libxml/xmlwriter.h>
+#define ISC_XMLCHAR (const xmlChar *)
+#endif /* HAVE_LIBXML2 */
+
+#ifdef HAVE_JSON_C
+#include <json_object.h>
+#endif /* HAVE_JSON_C */
+
+/* On DragonFly BSD the header does not provide jemalloc API */
+#if defined(HAVE_MALLOC_NP_H) && !defined(__DragonFly__)
+#include <malloc_np.h>
+#define JEMALLOC_API_SUPPORTED 1
+#elif defined(HAVE_JEMALLOC)
+#include <jemalloc/jemalloc.h>
+#define JEMALLOC_API_SUPPORTED 1
+
+#if JEMALLOC_VERSION_MAJOR < 4
+#define sdallocx(ptr, size, flags) dallocx(ptr, flags)
+#define MALLOCX_TCACHE_NONE (0)
+#endif /* JEMALLOC_VERSION_MAJOR < 4 */
+
+#else
+#include "jemalloc_shim.h"
+#endif
+
+#include "mem_p.h"
+
+#define MCTXLOCK(m) LOCK(&m->lock)
+#define MCTXUNLOCK(m) UNLOCK(&m->lock)
+
+#ifndef ISC_MEM_DEBUGGING
+#define ISC_MEM_DEBUGGING 0
+#endif /* ifndef ISC_MEM_DEBUGGING */
+unsigned int isc_mem_debugging = ISC_MEM_DEBUGGING;
+unsigned int isc_mem_defaultflags = ISC_MEMFLAG_DEFAULT;
+
+#define ISC_MEM_ILLEGAL_ARENA (UINT_MAX)
+
+/*
+ * Constants.
+ */
+
+#define ZERO_ALLOCATION_SIZE sizeof(void *)
+#define ALIGNMENT 8U /*%< must be a power of 2 */
+#define ALIGNMENT_SIZE sizeof(size_info)
+#define DEBUG_TABLE_COUNT 512U
+#define STATS_BUCKETS 512U
+#define STATS_BUCKET_SIZE 32U
+
+/*
+ * Types.
+ */
+#if ISC_MEM_TRACKLINES
+typedef struct debuglink debuglink_t;
+struct debuglink {
+ ISC_LINK(debuglink_t) link;
+ const void *ptr;
+ size_t size;
+ const char *file;
+ unsigned int line;
+};
+
+typedef ISC_LIST(debuglink_t) debuglist_t;
+
+#define FLARG_PASS , file, line
+#define FLARG , const char *file, unsigned int line
+#else /* if ISC_MEM_TRACKLINES */
+#define FLARG_PASS
+#define FLARG
+#endif /* if ISC_MEM_TRACKLINES */
+
+typedef struct element element;
+struct element {
+ element *next;
+};
+
+struct stats {
+ atomic_size_t gets;
+ atomic_size_t totalgets;
+};
+
+#define MEM_MAGIC ISC_MAGIC('M', 'e', 'm', 'C')
+#define VALID_CONTEXT(c) ISC_MAGIC_VALID(c, MEM_MAGIC)
+
+/* List of all active memory contexts. */
+
+static ISC_LIST(isc_mem_t) contexts;
+
+static isc_once_t init_once = ISC_ONCE_INIT;
+static isc_once_t shut_once = ISC_ONCE_INIT;
+static isc_mutex_t contextslock;
+
+/*%
+ * Total size of lost memory due to a bug of external library.
+ * Locked by the global lock.
+ */
+static uint64_t totallost;
+
+struct isc_mem {
+ unsigned int magic;
+ unsigned int flags;
+ unsigned int jemalloc_flags;
+ unsigned int jemalloc_arena;
+ isc_mutex_t lock;
+ bool checkfree;
+ struct stats stats[STATS_BUCKETS + 1];
+ isc_refcount_t references;
+ char name[16];
+ atomic_size_t total;
+ atomic_size_t inuse;
+ atomic_size_t maxinuse;
+ atomic_size_t malloced;
+ atomic_size_t maxmalloced;
+ atomic_bool hi_called;
+ atomic_bool is_overmem;
+ isc_mem_water_t water;
+ void *water_arg;
+ atomic_size_t hi_water;
+ atomic_size_t lo_water;
+ ISC_LIST(isc_mempool_t) pools;
+ unsigned int poolcnt;
+
+#if ISC_MEM_TRACKLINES
+ debuglist_t *debuglist;
+ size_t debuglistcnt;
+#endif /* if ISC_MEM_TRACKLINES */
+
+ ISC_LINK(isc_mem_t) link;
+};
+
+#define MEMPOOL_MAGIC ISC_MAGIC('M', 'E', 'M', 'p')
+#define VALID_MEMPOOL(c) ISC_MAGIC_VALID(c, MEMPOOL_MAGIC)
+
+struct isc_mempool {
+ /* always unlocked */
+ unsigned int magic;
+ isc_mem_t *mctx; /*%< our memory context */
+ ISC_LINK(isc_mempool_t) link; /*%< next pool in this mem context */
+ element *items; /*%< low water item list */
+ size_t size; /*%< size of each item on this pool */
+ size_t allocated; /*%< # of items currently given out */
+ size_t freecount; /*%< # of items on reserved list */
+ size_t freemax; /*%< # of items allowed on free list */
+ size_t fillcount; /*%< # of items to fetch on each fill */
+ /*%< Stats only. */
+ size_t gets; /*%< # of requests to this pool */
+ /*%< Debugging only. */
+ char name[16]; /*%< printed name in stats reports */
+};
+
+/*
+ * Private Inline-able.
+ */
+
+#if !ISC_MEM_TRACKLINES
+#define ADD_TRACE(a, b, c, d, e)
+#define DELETE_TRACE(a, b, c, d, e)
+#define ISC_MEMFUNC_SCOPE
+#else /* if !ISC_MEM_TRACKLINES */
+#define TRACE_OR_RECORD (ISC_MEM_DEBUGTRACE | ISC_MEM_DEBUGRECORD)
+
+#define SHOULD_TRACE_OR_RECORD(ptr) \
+ ((isc_mem_debugging & TRACE_OR_RECORD) != 0 && ptr != NULL)
+
+#define ADD_TRACE(a, b, c, d, e) \
+ if (SHOULD_TRACE_OR_RECORD(b)) { \
+ add_trace_entry(a, b, c, d, e); \
+ }
+
+#define DELETE_TRACE(a, b, c, d, e) \
+ if (SHOULD_TRACE_OR_RECORD(b)) { \
+ delete_trace_entry(a, b, c, d, e); \
+ }
+
+static void
+print_active(isc_mem_t *ctx, FILE *out);
+#endif /* ISC_MEM_TRACKLINES */
+
+static size_t
+increment_malloced(isc_mem_t *ctx, size_t size) {
+ size_t malloced = atomic_fetch_add_relaxed(&ctx->malloced, size) + size;
+ size_t maxmalloced = atomic_load_relaxed(&ctx->maxmalloced);
+
+ if (malloced > maxmalloced) {
+ atomic_compare_exchange_strong(&ctx->maxmalloced, &maxmalloced,
+ malloced);
+ }
+
+ return (malloced);
+}
+
+static size_t
+decrement_malloced(isc_mem_t *ctx, size_t size) {
+ size_t malloced = atomic_fetch_sub_relaxed(&ctx->malloced, size) - size;
+
+ return (malloced);
+}
+
+#if ISC_MEM_TRACKLINES
+/*!
+ * mctx must not be locked.
+ */
+static void
+add_trace_entry(isc_mem_t *mctx, const void *ptr, size_t size FLARG) {
+ debuglink_t *dl = NULL;
+ uint32_t hash;
+ uint32_t idx;
+
+ MCTXLOCK(mctx);
+
+ if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) {
+ fprintf(stderr, "add %p size %zu file %s line %u mctx %p\n",
+ ptr, size, file, line, mctx);
+ }
+
+ if (mctx->debuglist == NULL) {
+ goto unlock;
+ }
+
+#ifdef __COVERITY__
+ /*
+ * Use simple conversion from pointer to hash to avoid
+ * tainting 'ptr' due to byte swap in isc_hash_function.
+ */
+ hash = (uintptr_t)ptr >> 3;
+#else
+ hash = isc_hash_function(&ptr, sizeof(ptr), true);
+#endif
+ idx = hash % DEBUG_TABLE_COUNT;
+
+ dl = mallocx(sizeof(*dl), mctx->jemalloc_flags);
+ INSIST(dl != NULL);
+ increment_malloced(mctx, sizeof(*dl));
+
+ ISC_LINK_INIT(dl, link);
+ dl->ptr = ptr;
+ dl->size = size;
+ dl->file = file;
+ dl->line = line;
+
+ ISC_LIST_PREPEND(mctx->debuglist[idx], dl, link);
+ mctx->debuglistcnt++;
+unlock:
+ MCTXUNLOCK(mctx);
+}
+
+static void
+delete_trace_entry(isc_mem_t *mctx, const void *ptr, size_t size,
+ const char *file, unsigned int line) {
+ debuglink_t *dl = NULL;
+ uint32_t hash;
+ uint32_t idx;
+
+ MCTXLOCK(mctx);
+
+ if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) {
+ fprintf(stderr, "del %p size %zu file %s line %u mctx %p\n",
+ ptr, size, file, line, mctx);
+ }
+
+ if (mctx->debuglist == NULL) {
+ goto unlock;
+ }
+
+#ifdef __COVERITY__
+ /*
+ * Use simple conversion from pointer to hash to avoid
+ * tainting 'ptr' due to byte swap in isc_hash_function.
+ */
+ hash = (uintptr_t)ptr >> 3;
+#else
+ hash = isc_hash_function(&ptr, sizeof(ptr), true);
+#endif
+ idx = hash % DEBUG_TABLE_COUNT;
+
+ dl = ISC_LIST_HEAD(mctx->debuglist[idx]);
+ while (dl != NULL) {
+ if (dl->ptr == ptr) {
+ ISC_LIST_UNLINK(mctx->debuglist[idx], dl, link);
+ decrement_malloced(mctx, sizeof(*dl));
+ sdallocx(dl, sizeof(*dl), mctx->jemalloc_flags);
+ goto unlock;
+ }
+ dl = ISC_LIST_NEXT(dl, link);
+ }
+
+ /*
+ * If we get here, we didn't find the item on the list. We're
+ * screwed.
+ */
+ UNREACHABLE();
+unlock:
+ MCTXUNLOCK(mctx);
+}
+#endif /* ISC_MEM_TRACKLINES */
+
+#define ADJUST_ZERO_ALLOCATION_SIZE(s) \
+ if (s == 0) { \
+ s = ZERO_ALLOCATION_SIZE; \
+ }
+
+#define MEM_ALIGN(a) ((a) ? MALLOCX_ALIGN(a) : 0)
+
+/*!
+ * Perform a malloc, doing memory filling and overrun detection as necessary.
+ */
+static void *
+mem_get(isc_mem_t *ctx, size_t size, int flags) {
+ char *ret = NULL;
+
+ ADJUST_ZERO_ALLOCATION_SIZE(size);
+
+ ret = mallocx(size, flags | ctx->jemalloc_flags);
+ INSIST(ret != NULL);
+
+ if ((ctx->flags & ISC_MEMFLAG_FILL) != 0) {
+ memset(ret, 0xbe, size); /* Mnemonic for "beef". */
+ }
+
+ return (ret);
+}
+
+/*!
+ * Perform a free, doing memory filling and overrun detection as necessary.
+ */
+/* coverity[+free : arg-1] */
+static void
+mem_put(isc_mem_t *ctx, void *mem, size_t size, int flags) {
+ ADJUST_ZERO_ALLOCATION_SIZE(size);
+
+ if ((ctx->flags & ISC_MEMFLAG_FILL) != 0) {
+ memset(mem, 0xde, size); /* Mnemonic for "dead". */
+ }
+ sdallocx(mem, size, flags | ctx->jemalloc_flags);
+}
+
+static void *
+mem_realloc(isc_mem_t *ctx, void *old_ptr, size_t old_size, size_t new_size,
+ int flags) {
+ void *new_ptr = NULL;
+
+ ADJUST_ZERO_ALLOCATION_SIZE(new_size);
+
+ new_ptr = rallocx(old_ptr, new_size, flags | ctx->jemalloc_flags);
+ INSIST(new_ptr != NULL);
+
+ if ((ctx->flags & ISC_MEMFLAG_FILL) != 0) {
+ ssize_t diff_size = new_size - old_size;
+ void *diff_ptr = (uint8_t *)new_ptr + old_size;
+ if (diff_size > 0) {
+ /* Mnemonic for "beef". */
+ memset(diff_ptr, 0xbe, diff_size);
+ }
+ }
+
+ return (new_ptr);
+}
+
+#define stats_bucket(ctx, size) \
+ ((size / STATS_BUCKET_SIZE) >= STATS_BUCKETS \
+ ? &ctx->stats[STATS_BUCKETS] \
+ : &ctx->stats[size / STATS_BUCKET_SIZE])
+
+/*!
+ * Update internal counters after a memory get.
+ */
+static void
+mem_getstats(isc_mem_t *ctx, size_t size) {
+ struct stats *stats = stats_bucket(ctx, size);
+
+ atomic_fetch_add_relaxed(&ctx->total, size);
+ atomic_fetch_add_release(&ctx->inuse, size);
+
+ atomic_fetch_add_relaxed(&stats->gets, 1);
+ atomic_fetch_add_relaxed(&stats->totalgets, 1);
+
+ increment_malloced(ctx, size);
+}
+
+/*!
+ * Update internal counters after a memory put.
+ */
+static void
+mem_putstats(isc_mem_t *ctx, void *ptr, size_t size) {
+ struct stats *stats = stats_bucket(ctx, size);
+ atomic_size_t s, g;
+
+ UNUSED(ptr);
+
+ s = atomic_fetch_sub_release(&ctx->inuse, size);
+ INSIST(s >= size);
+
+ g = atomic_fetch_sub_release(&stats->gets, 1);
+ INSIST(g >= 1);
+
+ decrement_malloced(ctx, size);
+}
+
+/*
+ * Private.
+ */
+
+static bool
+mem_jemalloc_arena_create(unsigned int *pnew_arenano) {
+ REQUIRE(pnew_arenano != NULL);
+
+#if defined(JEMALLOC_API_SUPPORTED) && JEMALLOC_VERSION_MAJOR >= 4
+ unsigned int arenano = 0;
+ size_t len = sizeof(arenano);
+ int res = 0;
+
+ res = mallctl("arenas.create", &arenano, &len, NULL, 0);
+ if (res != 0) {
+ return (false);
+ }
+
+ *pnew_arenano = arenano;
+
+ return (true);
+#else
+ *pnew_arenano = ISC_MEM_ILLEGAL_ARENA;
+ return (true);
+#endif /* defined(JEMALLOC_API_SUPPORTED) && JEMALLOC_VERSION_MAJOR >= 4 */
+}
+
+static bool
+mem_jemalloc_arena_destroy(unsigned int arenano) {
+#if defined(JEMALLOC_API_SUPPORTED) && JEMALLOC_VERSION_MAJOR >= 4
+ int res = 0;
+ char buf[256] = { 0 };
+
+ (void)snprintf(buf, sizeof(buf), "arena.%u.destroy", arenano);
+ res = mallctl(buf, NULL, NULL, NULL, 0);
+ if (res != 0) {
+ return (false);
+ }
+
+ return (true);
+#else
+ UNUSED(arenano);
+ return (true);
+#endif /* defined(JEMALLOC_API_SUPPORTED) && JEMALLOC_VERSION_MAJOR >= 4 */
+}
+
+static void
+mem_initialize(void) {
+ isc_mutex_init(&contextslock);
+ ISC_LIST_INIT(contexts);
+ totallost = 0;
+}
+
+void
+isc__mem_initialize(void) {
+ RUNTIME_CHECK(isc_once_do(&init_once, mem_initialize) == ISC_R_SUCCESS);
+}
+
+static void
+mem_shutdown(void) {
+ isc__mem_checkdestroyed();
+
+ isc_mutex_destroy(&contextslock);
+}
+
+void
+isc__mem_shutdown(void) {
+ RUNTIME_CHECK(isc_once_do(&shut_once, mem_shutdown) == ISC_R_SUCCESS);
+}
+
+static void
+mem_create(isc_mem_t **ctxp, unsigned int flags, unsigned int jemalloc_flags) {
+ isc_mem_t *ctx = NULL;
+
+ REQUIRE(ctxp != NULL && *ctxp == NULL);
+
+ ctx = mallocx(sizeof(*ctx),
+ MALLOCX_ALIGN(isc_os_cacheline()) | jemalloc_flags);
+ INSIST(ctx != NULL);
+
+ *ctx = (isc_mem_t){
+ .magic = MEM_MAGIC,
+ .flags = flags,
+ .jemalloc_flags = jemalloc_flags,
+ .jemalloc_arena = ISC_MEM_ILLEGAL_ARENA,
+ .checkfree = true,
+ };
+
+ isc_mutex_init(&ctx->lock);
+ isc_refcount_init(&ctx->references, 1);
+
+ atomic_init(&ctx->total, 0);
+ atomic_init(&ctx->inuse, 0);
+ atomic_init(&ctx->maxinuse, 0);
+ atomic_init(&ctx->malloced, sizeof(*ctx));
+ atomic_init(&ctx->maxmalloced, sizeof(*ctx));
+ atomic_init(&ctx->hi_water, 0);
+ atomic_init(&ctx->lo_water, 0);
+ atomic_init(&ctx->hi_called, false);
+ atomic_init(&ctx->is_overmem, false);
+
+ for (size_t i = 0; i < STATS_BUCKETS + 1; i++) {
+ atomic_init(&ctx->stats[i].gets, 0);
+ atomic_init(&ctx->stats[i].totalgets, 0);
+ }
+ ISC_LIST_INIT(ctx->pools);
+
+#if ISC_MEM_TRACKLINES
+ if ((isc_mem_debugging & ISC_MEM_DEBUGRECORD) != 0) {
+ unsigned int i;
+
+ ctx->debuglist =
+ mallocx((DEBUG_TABLE_COUNT * sizeof(debuglist_t)),
+ ctx->jemalloc_flags);
+ INSIST(ctx->debuglist != NULL);
+
+ for (i = 0; i < DEBUG_TABLE_COUNT; i++) {
+ ISC_LIST_INIT(ctx->debuglist[i]);
+ }
+ increment_malloced(ctx,
+ DEBUG_TABLE_COUNT * sizeof(debuglist_t));
+ }
+#endif /* if ISC_MEM_TRACKLINES */
+
+ LOCK(&contextslock);
+ ISC_LIST_INITANDAPPEND(contexts, ctx, link);
+ UNLOCK(&contextslock);
+
+ *ctxp = ctx;
+}
+
+/*
+ * Public.
+ */
+
+static void
+destroy(isc_mem_t *ctx) {
+ unsigned int i;
+ size_t malloced;
+ unsigned int arena_no;
+
+ LOCK(&contextslock);
+ ISC_LIST_UNLINK(contexts, ctx, link);
+ totallost += isc_mem_inuse(ctx);
+ UNLOCK(&contextslock);
+
+ ctx->magic = 0;
+
+ arena_no = ctx->jemalloc_arena;
+
+ INSIST(ISC_LIST_EMPTY(ctx->pools));
+
+#if ISC_MEM_TRACKLINES
+ if (ctx->debuglist != NULL) {
+ debuglink_t *dl;
+ for (i = 0; i < DEBUG_TABLE_COUNT; i++) {
+ for (dl = ISC_LIST_HEAD(ctx->debuglist[i]); dl != NULL;
+ dl = ISC_LIST_HEAD(ctx->debuglist[i]))
+ {
+ if (ctx->checkfree && dl->ptr != NULL) {
+ print_active(ctx, stderr);
+ }
+ INSIST(!ctx->checkfree || dl->ptr == NULL);
+
+ ISC_LIST_UNLINK(ctx->debuglist[i], dl, link);
+ sdallocx(dl, sizeof(*dl), ctx->jemalloc_flags);
+ decrement_malloced(ctx, sizeof(*dl));
+ }
+ }
+
+ sdallocx(ctx->debuglist,
+ (DEBUG_TABLE_COUNT * sizeof(debuglist_t)),
+ ctx->jemalloc_flags);
+ decrement_malloced(ctx,
+ DEBUG_TABLE_COUNT * sizeof(debuglist_t));
+ }
+#endif /* if ISC_MEM_TRACKLINES */
+
+ if (ctx->checkfree) {
+ for (i = 0; i <= STATS_BUCKETS; i++) {
+ struct stats *stats = &ctx->stats[i];
+ size_t gets = atomic_load_acquire(&stats->gets);
+ if (gets != 0U) {
+ fprintf(stderr,
+ "Failing assertion due to probable "
+ "leaked memory in context %p (\"%s\") "
+ "(stats[%u].gets == %zu).\n",
+ ctx, ctx->name, i, gets);
+#if ISC_MEM_TRACKLINES
+ print_active(ctx, stderr);
+#endif /* if ISC_MEM_TRACKLINES */
+ INSIST(gets == 0U);
+ }
+ }
+ }
+
+ isc_mutex_destroy(&ctx->lock);
+
+ malloced = decrement_malloced(ctx, sizeof(*ctx));
+
+ if (ctx->checkfree) {
+ INSIST(malloced == 0);
+ }
+ sdallocx(ctx, sizeof(*ctx),
+ MALLOCX_ALIGN(isc_os_cacheline()) | ctx->jemalloc_flags);
+
+ if (arena_no != ISC_MEM_ILLEGAL_ARENA) {
+ RUNTIME_CHECK(mem_jemalloc_arena_destroy(arena_no) == true);
+ }
+}
+
+void
+isc_mem_attach(isc_mem_t *source, isc_mem_t **targetp) {
+ REQUIRE(VALID_CONTEXT(source));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ isc_refcount_increment(&source->references);
+
+ *targetp = source;
+}
+
+void
+isc__mem_detach(isc_mem_t **ctxp FLARG) {
+ isc_mem_t *ctx = NULL;
+
+ REQUIRE(ctxp != NULL && VALID_CONTEXT(*ctxp));
+
+ ctx = *ctxp;
+ *ctxp = NULL;
+
+ if (isc_refcount_decrement(&ctx->references) == 1) {
+ isc_refcount_destroy(&ctx->references);
+#if ISC_MEM_TRACKLINES
+ if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) {
+ fprintf(stderr, "destroy mctx %p file %s line %u\n",
+ ctx, file, line);
+ }
+#endif
+ destroy(ctx);
+ }
+}
+
+/*
+ * isc_mem_putanddetach() is the equivalent of:
+ *
+ * mctx = NULL;
+ * isc_mem_attach(ptr->mctx, &mctx);
+ * isc_mem_detach(&ptr->mctx);
+ * isc_mem_put(mctx, ptr, sizeof(*ptr);
+ * isc_mem_detach(&mctx);
+ */
+
+void
+isc__mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size,
+ size_t alignment FLARG) {
+ isc_mem_t *ctx = NULL;
+
+ REQUIRE(ctxp != NULL && VALID_CONTEXT(*ctxp));
+ REQUIRE(ptr != NULL);
+ REQUIRE(size != 0);
+
+ ctx = *ctxp;
+ *ctxp = NULL;
+
+ DELETE_TRACE(ctx, ptr, size, file, line);
+
+ mem_putstats(ctx, ptr, size);
+ mem_put(ctx, ptr, size, MEM_ALIGN(alignment));
+
+ if (isc_refcount_decrement(&ctx->references) == 1) {
+ isc_refcount_destroy(&ctx->references);
+ destroy(ctx);
+ }
+}
+
+void
+isc__mem_destroy(isc_mem_t **ctxp FLARG) {
+ isc_mem_t *ctx = NULL;
+
+ /*
+ * This routine provides legacy support for callers who use mctxs
+ * without attaching/detaching.
+ */
+
+ REQUIRE(ctxp != NULL && VALID_CONTEXT(*ctxp));
+
+ ctx = *ctxp;
+ *ctxp = NULL;
+
+#if ISC_MEM_TRACKLINES
+ if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) {
+ fprintf(stderr, "destroy mctx %p file %s line %u\n", ctx, file,
+ line);
+ }
+
+ if (isc_refcount_decrement(&ctx->references) > 1) {
+ print_active(ctx, stderr);
+ }
+#else /* if ISC_MEM_TRACKLINES */
+ isc_refcount_decrementz(&ctx->references);
+#endif /* if ISC_MEM_TRACKLINES */
+ isc_refcount_destroy(&ctx->references);
+ destroy(ctx);
+
+ *ctxp = NULL;
+}
+
+#define CALL_HI_WATER(ctx) \
+ { \
+ if (ctx->water != NULL && hi_water(ctx)) { \
+ (ctx->water)(ctx->water_arg, ISC_MEM_HIWATER); \
+ } \
+ }
+
+#define CALL_LO_WATER(ctx) \
+ { \
+ if ((ctx->water != NULL) && lo_water(ctx)) { \
+ (ctx->water)(ctx->water_arg, ISC_MEM_LOWATER); \
+ } \
+ }
+
+static bool
+hi_water(isc_mem_t *ctx) {
+ size_t inuse;
+ size_t maxinuse;
+ size_t hiwater = atomic_load_relaxed(&ctx->hi_water);
+
+ if (hiwater == 0) {
+ return (false);
+ }
+
+ inuse = atomic_load_acquire(&ctx->inuse);
+ if (inuse <= hiwater) {
+ return (false);
+ }
+
+ maxinuse = atomic_load_acquire(&ctx->maxinuse);
+ if (inuse > maxinuse) {
+ (void)atomic_compare_exchange_strong(&ctx->maxinuse, &maxinuse,
+ inuse);
+
+ if ((isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0) {
+ fprintf(stderr, "maxinuse = %lu\n",
+ (unsigned long)inuse);
+ }
+ }
+
+ if (atomic_load_acquire(&ctx->hi_called)) {
+ return (false);
+ }
+
+ /* We are over water (for the first time) */
+ atomic_store_release(&ctx->is_overmem, true);
+
+ return (true);
+}
+
+static bool
+lo_water(isc_mem_t *ctx) {
+ size_t inuse;
+ size_t lowater = atomic_load_relaxed(&ctx->lo_water);
+
+ if (lowater == 0) {
+ return (false);
+ }
+
+ inuse = atomic_load_acquire(&ctx->inuse);
+ if (inuse >= lowater) {
+ return (false);
+ }
+
+ if (!atomic_load_acquire(&ctx->hi_called)) {
+ return (false);
+ }
+
+ /* We are no longer overmem */
+ atomic_store_release(&ctx->is_overmem, false);
+
+ return (true);
+}
+
+void *
+isc__mem_get(isc_mem_t *ctx, size_t size, size_t alignment FLARG) {
+ void *ptr = NULL;
+
+ REQUIRE(VALID_CONTEXT(ctx));
+
+ ptr = mem_get(ctx, size, MEM_ALIGN(alignment));
+
+ mem_getstats(ctx, size);
+ ADD_TRACE(ctx, ptr, size, file, line);
+
+ CALL_HI_WATER(ctx);
+
+ return (ptr);
+}
+
+void
+isc__mem_put(isc_mem_t *ctx, void *ptr, size_t size, size_t alignment FLARG) {
+ REQUIRE(VALID_CONTEXT(ctx));
+
+ DELETE_TRACE(ctx, ptr, size, file, line);
+
+ mem_putstats(ctx, ptr, size);
+ mem_put(ctx, ptr, size, MEM_ALIGN(alignment));
+
+ CALL_LO_WATER(ctx);
+}
+
+void
+isc_mem_waterack(isc_mem_t *ctx, int flag) {
+ REQUIRE(VALID_CONTEXT(ctx));
+
+ if (flag == ISC_MEM_LOWATER) {
+ atomic_store(&ctx->hi_called, false);
+ } else if (flag == ISC_MEM_HIWATER) {
+ atomic_store(&ctx->hi_called, true);
+ }
+}
+
+#if ISC_MEM_TRACKLINES
+static void
+print_active(isc_mem_t *mctx, FILE *out) {
+ if (mctx->debuglist != NULL) {
+ debuglink_t *dl;
+ unsigned int i;
+ bool found;
+
+ fprintf(out, "Dump of all outstanding memory "
+ "allocations:\n");
+ found = false;
+ for (i = 0; i < DEBUG_TABLE_COUNT; i++) {
+ dl = ISC_LIST_HEAD(mctx->debuglist[i]);
+
+ if (dl != NULL) {
+ found = true;
+ }
+
+ while (dl != NULL) {
+ if (dl->ptr != NULL) {
+ fprintf(out,
+ "\tptr %p size %zu "
+ "file %s "
+ "line %u\n",
+ dl->ptr, dl->size, dl->file,
+ dl->line);
+ }
+ dl = ISC_LIST_NEXT(dl, link);
+ }
+ }
+
+ if (!found) {
+ fprintf(out, "\tNone.\n");
+ }
+ }
+}
+#endif /* if ISC_MEM_TRACKLINES */
+
+/*
+ * Print the stats[] on the stream "out" with suitable formatting.
+ */
+void
+isc_mem_stats(isc_mem_t *ctx, FILE *out) {
+ isc_mempool_t *pool = NULL;
+
+ REQUIRE(VALID_CONTEXT(ctx));
+
+ MCTXLOCK(ctx);
+
+ for (size_t i = 0; i <= STATS_BUCKETS; i++) {
+ size_t totalgets;
+ size_t gets;
+ struct stats *stats = &ctx->stats[i];
+
+ totalgets = atomic_load_acquire(&stats->totalgets);
+ gets = atomic_load_acquire(&stats->gets);
+
+ if (totalgets != 0U && gets != 0U) {
+ fprintf(out, "%s%5zu: %11zu gets, %11zu rem",
+ (i == STATS_BUCKETS) ? ">=" : " ", i,
+ totalgets, gets);
+ fputc('\n', out);
+ }
+ }
+
+ /*
+ * Note that since a pool can be locked now, these stats might
+ * be somewhat off if the pool is in active use at the time the
+ * stats are dumped. The link fields are protected by the
+ * isc_mem_t's lock, however, so walking this list and
+ * extracting integers from stats fields is always safe.
+ */
+ pool = ISC_LIST_HEAD(ctx->pools);
+ if (pool != NULL) {
+ fprintf(out, "[Pool statistics]\n");
+ fprintf(out, "%15s %10s %10s %10s %10s %10s %10s %1s\n", "name",
+ "size", "allocated", "freecount", "freemax",
+ "fillcount", "gets", "L");
+ }
+ while (pool != NULL) {
+ fprintf(out,
+ "%15s %10zu %10zu %10zu %10zu %10zu %10zu %10zu %s\n",
+ pool->name, pool->size, (size_t)0, pool->allocated,
+ pool->freecount, pool->freemax, pool->fillcount,
+ pool->gets, "N");
+ pool = ISC_LIST_NEXT(pool, link);
+ }
+
+#if ISC_MEM_TRACKLINES
+ print_active(ctx, out);
+#endif /* if ISC_MEM_TRACKLINES */
+
+ MCTXUNLOCK(ctx);
+}
+
+void *
+isc__mem_allocate(isc_mem_t *ctx, size_t size FLARG) {
+ void *ptr = NULL;
+
+ REQUIRE(VALID_CONTEXT(ctx));
+
+ ptr = mem_get(ctx, size, 0);
+
+ /* Recalculate the real allocated size */
+ size = sallocx(ptr, ctx->jemalloc_flags);
+
+ mem_getstats(ctx, size);
+ ADD_TRACE(ctx, ptr, size, file, line);
+
+ CALL_HI_WATER(ctx);
+
+ return (ptr);
+}
+
+void *
+isc__mem_reget(isc_mem_t *ctx, void *old_ptr, size_t old_size, size_t new_size,
+ size_t alignment FLARG) {
+ void *new_ptr = NULL;
+
+ if (old_ptr == NULL) {
+ REQUIRE(old_size == 0);
+ new_ptr = isc__mem_get(ctx, new_size, alignment FLARG_PASS);
+ } else if (new_size == 0) {
+ isc__mem_put(ctx, old_ptr, old_size, alignment FLARG_PASS);
+ } else {
+ DELETE_TRACE(ctx, old_ptr, old_size, file, line);
+ mem_putstats(ctx, old_ptr, old_size);
+
+ new_ptr = mem_realloc(ctx, old_ptr, old_size, new_size,
+ MEM_ALIGN(alignment));
+
+ mem_getstats(ctx, new_size);
+ ADD_TRACE(ctx, new_ptr, new_size, file, line);
+
+ /*
+ * We want to postpone the call to water in edge case
+ * where the realloc will exactly hit on the boundary of
+ * the water and we would call water twice.
+ */
+ CALL_LO_WATER(ctx);
+ CALL_HI_WATER(ctx);
+ }
+
+ return (new_ptr);
+}
+
+void *
+isc__mem_reallocate(isc_mem_t *ctx, void *old_ptr, size_t new_size FLARG) {
+ void *new_ptr = NULL;
+
+ REQUIRE(VALID_CONTEXT(ctx));
+
+ if (old_ptr == NULL) {
+ new_ptr = isc__mem_allocate(ctx, new_size FLARG_PASS);
+ } else if (new_size == 0) {
+ isc__mem_free(ctx, old_ptr FLARG_PASS);
+ } else {
+ size_t old_size = sallocx(old_ptr, ctx->jemalloc_flags);
+
+ DELETE_TRACE(ctx, old_ptr, old_size, file, line);
+ mem_putstats(ctx, old_ptr, old_size);
+
+ new_ptr = mem_realloc(ctx, old_ptr, old_size, new_size, 0);
+
+ /* Recalculate the real allocated size */
+ new_size = sallocx(new_ptr, ctx->jemalloc_flags);
+
+ mem_getstats(ctx, new_size);
+ ADD_TRACE(ctx, new_ptr, new_size, file, line);
+
+ /*
+ * We want to postpone the call to water in edge case
+ * where the realloc will exactly hit on the boundary of
+ * the water and we would call water twice.
+ */
+ CALL_LO_WATER(ctx);
+ CALL_HI_WATER(ctx);
+ }
+
+ return (new_ptr);
+}
+
+void
+isc__mem_free(isc_mem_t *ctx, void *ptr FLARG) {
+ size_t size = 0;
+
+ REQUIRE(VALID_CONTEXT(ctx));
+
+ size = sallocx(ptr, ctx->jemalloc_flags);
+
+ DELETE_TRACE(ctx, ptr, size, file, line);
+
+ mem_putstats(ctx, ptr, size);
+ mem_put(ctx, ptr, size, 0);
+
+ CALL_LO_WATER(ctx);
+}
+
+/*
+ * Other useful things.
+ */
+
+char *
+isc__mem_strdup(isc_mem_t *mctx, const char *s FLARG) {
+ size_t len;
+ char *ns = NULL;
+
+ REQUIRE(VALID_CONTEXT(mctx));
+ REQUIRE(s != NULL);
+
+ len = strlen(s) + 1;
+
+ ns = isc__mem_allocate(mctx, len FLARG_PASS);
+
+ strlcpy(ns, s, len);
+
+ return (ns);
+}
+
+char *
+isc__mem_strndup(isc_mem_t *mctx, const char *s, size_t size FLARG) {
+ size_t len;
+ char *ns = NULL;
+
+ REQUIRE(VALID_CONTEXT(mctx));
+ REQUIRE(s != NULL);
+ REQUIRE(size != 0);
+
+ len = strlen(s) + 1;
+ if (len > size) {
+ len = size;
+ }
+
+ ns = isc__mem_allocate(mctx, len FLARG_PASS);
+
+ strlcpy(ns, s, len);
+
+ return (ns);
+}
+
+void
+isc_mem_setdestroycheck(isc_mem_t *ctx, bool flag) {
+ REQUIRE(VALID_CONTEXT(ctx));
+
+ MCTXLOCK(ctx);
+
+ ctx->checkfree = flag;
+
+ MCTXUNLOCK(ctx);
+}
+
+size_t
+isc_mem_inuse(isc_mem_t *ctx) {
+ REQUIRE(VALID_CONTEXT(ctx));
+
+ return (atomic_load_acquire(&ctx->inuse));
+}
+
+size_t
+isc_mem_maxinuse(isc_mem_t *ctx) {
+ REQUIRE(VALID_CONTEXT(ctx));
+
+ return (atomic_load_acquire(&ctx->maxinuse));
+}
+
+size_t
+isc_mem_total(isc_mem_t *ctx) {
+ REQUIRE(VALID_CONTEXT(ctx));
+
+ return (atomic_load_acquire(&ctx->total));
+}
+
+size_t
+isc_mem_malloced(isc_mem_t *ctx) {
+ REQUIRE(VALID_CONTEXT(ctx));
+
+ return (atomic_load_acquire(&ctx->malloced));
+}
+
+size_t
+isc_mem_maxmalloced(isc_mem_t *ctx) {
+ REQUIRE(VALID_CONTEXT(ctx));
+
+ return (atomic_load_acquire(&ctx->maxmalloced));
+}
+
+void
+isc_mem_clearwater(isc_mem_t *mctx) {
+ isc_mem_setwater(mctx, NULL, NULL, 0, 0);
+}
+
+void
+isc_mem_setwater(isc_mem_t *ctx, isc_mem_water_t water, void *water_arg,
+ size_t hiwater, size_t lowater) {
+ isc_mem_water_t oldwater;
+ void *oldwater_arg;
+
+ REQUIRE(VALID_CONTEXT(ctx));
+ REQUIRE(hiwater >= lowater);
+
+ oldwater = ctx->water;
+ oldwater_arg = ctx->water_arg;
+
+ /* No water was set and new water is also NULL */
+ if (oldwater == NULL && water == NULL) {
+ return;
+ }
+
+ /* The water function is being set for the first time */
+ if (oldwater == NULL) {
+ REQUIRE(water != NULL && lowater > 0);
+
+ INSIST(atomic_load(&ctx->hi_water) == 0);
+ INSIST(atomic_load(&ctx->lo_water) == 0);
+
+ ctx->water = water;
+ ctx->water_arg = water_arg;
+ atomic_store(&ctx->hi_water, hiwater);
+ atomic_store(&ctx->lo_water, lowater);
+
+ return;
+ }
+
+ REQUIRE((water == oldwater && water_arg == oldwater_arg) ||
+ (water == NULL && water_arg == NULL && hiwater == 0));
+
+ atomic_store(&ctx->hi_water, hiwater);
+ atomic_store(&ctx->lo_water, lowater);
+
+ if (atomic_load_acquire(&ctx->hi_called) &&
+ (atomic_load_acquire(&ctx->inuse) < lowater || lowater == 0U))
+ {
+ (oldwater)(oldwater_arg, ISC_MEM_LOWATER);
+ }
+}
+
+bool
+isc_mem_isovermem(isc_mem_t *ctx) {
+ REQUIRE(VALID_CONTEXT(ctx));
+
+ return (atomic_load_relaxed(&ctx->is_overmem));
+}
+
+void
+isc_mem_setname(isc_mem_t *ctx, const char *name) {
+ REQUIRE(VALID_CONTEXT(ctx));
+
+ LOCK(&ctx->lock);
+ strlcpy(ctx->name, name, sizeof(ctx->name));
+ UNLOCK(&ctx->lock);
+}
+
+const char *
+isc_mem_getname(isc_mem_t *ctx) {
+ REQUIRE(VALID_CONTEXT(ctx));
+
+ if (ctx->name[0] == 0) {
+ return ("");
+ }
+
+ return (ctx->name);
+}
+
+/*
+ * Memory pool stuff
+ */
+
+void
+isc__mempool_create(isc_mem_t *restrict mctx, const size_t element_size,
+ isc_mempool_t **restrict mpctxp FLARG) {
+ isc_mempool_t *restrict mpctx = NULL;
+ size_t size = element_size;
+
+ REQUIRE(VALID_CONTEXT(mctx));
+ REQUIRE(size > 0U);
+ REQUIRE(mpctxp != NULL && *mpctxp == NULL);
+
+ /*
+ * Mempools are stored as a linked list of element.
+ */
+ if (size < sizeof(element)) {
+ size = sizeof(element);
+ }
+
+ /*
+ * Allocate space for this pool, initialize values, and if all
+ * works well, attach to the memory context.
+ */
+ mpctx = isc_mem_get(mctx, sizeof(isc_mempool_t));
+
+ *mpctx = (isc_mempool_t){
+ .size = size,
+ .freemax = 1,
+ .fillcount = 1,
+ };
+
+#if ISC_MEM_TRACKLINES
+ if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) {
+ fprintf(stderr, "create pool %p file %s line %u mctx %p\n",
+ mpctx, file, line, mctx);
+ }
+#endif /* ISC_MEM_TRACKLINES */
+
+ isc_mem_attach(mctx, &mpctx->mctx);
+ mpctx->magic = MEMPOOL_MAGIC;
+
+ *mpctxp = (isc_mempool_t *)mpctx;
+
+ MCTXLOCK(mctx);
+ ISC_LIST_INITANDAPPEND(mctx->pools, mpctx, link);
+ mctx->poolcnt++;
+ MCTXUNLOCK(mctx);
+}
+
+void
+isc_mempool_setname(isc_mempool_t *restrict mpctx, const char *name) {
+ REQUIRE(VALID_MEMPOOL(mpctx));
+ REQUIRE(name != NULL);
+
+ strlcpy(mpctx->name, name, sizeof(mpctx->name));
+}
+
+void
+isc__mempool_destroy(isc_mempool_t **restrict mpctxp FLARG) {
+ isc_mempool_t *restrict mpctx = NULL;
+ isc_mem_t *mctx = NULL;
+ element *restrict item = NULL;
+
+ REQUIRE(mpctxp != NULL);
+ REQUIRE(VALID_MEMPOOL(*mpctxp));
+
+ mpctx = *mpctxp;
+ *mpctxp = NULL;
+
+ mctx = mpctx->mctx;
+
+#if ISC_MEM_TRACKLINES
+ if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) {
+ fprintf(stderr, "destroy pool %p file %s line %u mctx %p\n",
+ mpctx, file, line, mctx);
+ }
+#endif
+
+ if (mpctx->allocated > 0) {
+ UNEXPECTED_ERROR("mempool %s leaked memory", mpctx->name);
+ }
+ REQUIRE(mpctx->allocated == 0);
+
+ /*
+ * Return any items on the free list
+ */
+ while (mpctx->items != NULL) {
+ INSIST(mpctx->freecount > 0);
+ mpctx->freecount--;
+
+ item = mpctx->items;
+ mpctx->items = item->next;
+
+ mem_putstats(mctx, item, mpctx->size);
+ mem_put(mctx, item, mpctx->size, 0);
+ }
+
+ /*
+ * Remove our linked list entry from the memory context.
+ */
+ MCTXLOCK(mctx);
+ ISC_LIST_UNLINK(mctx->pools, mpctx, link);
+ mctx->poolcnt--;
+ MCTXUNLOCK(mctx);
+
+ mpctx->magic = 0;
+
+ isc_mem_putanddetach(&mpctx->mctx, mpctx, sizeof(isc_mempool_t));
+}
+
+void *
+isc__mempool_get(isc_mempool_t *restrict mpctx FLARG) {
+ element *restrict item = NULL;
+
+ REQUIRE(VALID_MEMPOOL(mpctx));
+
+ mpctx->allocated++;
+
+ if (mpctx->items == NULL) {
+ isc_mem_t *mctx = mpctx->mctx;
+#if !__SANITIZE_ADDRESS__
+ const size_t fillcount = mpctx->fillcount;
+#else
+ const size_t fillcount = 1;
+#endif
+ /*
+ * We need to dip into the well. Fill up our free list.
+ */
+ for (size_t i = 0; i < fillcount; i++) {
+ item = mem_get(mctx, mpctx->size, 0);
+ mem_getstats(mctx, mpctx->size);
+ item->next = mpctx->items;
+ mpctx->items = item;
+ mpctx->freecount++;
+ }
+ }
+
+ item = mpctx->items;
+ INSIST(item != NULL);
+
+ mpctx->items = item->next;
+
+ INSIST(mpctx->freecount > 0);
+ mpctx->freecount--;
+ mpctx->gets++;
+
+ ADD_TRACE(mpctx->mctx, item, mpctx->size, file, line);
+
+ return (item);
+}
+
+/* coverity[+free : arg-1] */
+void
+isc__mempool_put(isc_mempool_t *restrict mpctx, void *mem FLARG) {
+ element *restrict item = NULL;
+
+ REQUIRE(VALID_MEMPOOL(mpctx));
+ REQUIRE(mem != NULL);
+
+ isc_mem_t *mctx = mpctx->mctx;
+ const size_t freecount = mpctx->freecount;
+#if !__SANITIZE_ADDRESS__
+ const size_t freemax = mpctx->freemax;
+#else
+ const size_t freemax = 0;
+#endif
+
+ INSIST(mpctx->allocated > 0);
+ mpctx->allocated--;
+
+ DELETE_TRACE(mctx, mem, mpctx->size, file, line);
+
+ /*
+ * If our free list is full, return this to the mctx directly.
+ */
+ if (freecount >= freemax) {
+ mem_putstats(mctx, mem, mpctx->size);
+ mem_put(mctx, mem, mpctx->size, 0);
+ return;
+ }
+
+ /*
+ * Otherwise, attach it to our free list and bump the counter.
+ */
+ item = (element *)mem;
+ item->next = mpctx->items;
+ mpctx->items = item;
+ mpctx->freecount++;
+}
+
+/*
+ * Quotas
+ */
+
+void
+isc_mempool_setfreemax(isc_mempool_t *restrict mpctx,
+ const unsigned int limit) {
+ REQUIRE(VALID_MEMPOOL(mpctx));
+ mpctx->freemax = limit;
+}
+
+unsigned int
+isc_mempool_getfreemax(isc_mempool_t *restrict mpctx) {
+ REQUIRE(VALID_MEMPOOL(mpctx));
+
+ return (mpctx->freemax);
+}
+
+unsigned int
+isc_mempool_getfreecount(isc_mempool_t *restrict mpctx) {
+ REQUIRE(VALID_MEMPOOL(mpctx));
+
+ return (mpctx->freecount);
+}
+
+unsigned int
+isc_mempool_getallocated(isc_mempool_t *restrict mpctx) {
+ REQUIRE(VALID_MEMPOOL(mpctx));
+
+ return (mpctx->allocated);
+}
+
+void
+isc_mempool_setfillcount(isc_mempool_t *restrict mpctx,
+ unsigned int const limit) {
+ REQUIRE(VALID_MEMPOOL(mpctx));
+ REQUIRE(limit > 0);
+
+ mpctx->fillcount = limit;
+}
+
+unsigned int
+isc_mempool_getfillcount(isc_mempool_t *restrict mpctx) {
+ REQUIRE(VALID_MEMPOOL(mpctx));
+
+ return (mpctx->fillcount);
+}
+
+/*
+ * Requires contextslock to be held by caller.
+ */
+#if ISC_MEM_TRACKLINES
+static void
+print_contexts(FILE *file) {
+ isc_mem_t *ctx;
+
+ for (ctx = ISC_LIST_HEAD(contexts); ctx != NULL;
+ ctx = ISC_LIST_NEXT(ctx, link))
+ {
+ fprintf(file, "context: %p (%s): %" PRIuFAST32 " references\n",
+ ctx, ctx->name[0] == 0 ? "<unknown>" : ctx->name,
+ isc_refcount_current(&ctx->references));
+ print_active(ctx, file);
+ }
+ fflush(file);
+}
+#endif
+
+static atomic_uintptr_t checkdestroyed = 0;
+
+void
+isc_mem_checkdestroyed(FILE *file) {
+ atomic_store_release(&checkdestroyed, (uintptr_t)file);
+}
+
+void
+isc__mem_checkdestroyed(void) {
+ FILE *file = (FILE *)atomic_load_acquire(&checkdestroyed);
+
+ if (file == NULL) {
+ return;
+ }
+
+ LOCK(&contextslock);
+ if (!ISC_LIST_EMPTY(contexts)) {
+#if ISC_MEM_TRACKLINES
+ if ((isc_mem_debugging & TRACE_OR_RECORD) != 0) {
+ print_contexts(file);
+ }
+#endif /* if ISC_MEM_TRACKLINES */
+ UNREACHABLE();
+ }
+ UNLOCK(&contextslock);
+}
+
+unsigned int
+isc_mem_references(isc_mem_t *ctx) {
+ return (isc_refcount_current(&ctx->references));
+}
+
+typedef struct summarystat {
+ uint64_t total;
+ uint64_t inuse;
+ uint64_t malloced;
+ uint64_t contextsize;
+} summarystat_t;
+
+#ifdef HAVE_LIBXML2
+#define TRY0(a) \
+ do { \
+ xmlrc = (a); \
+ if (xmlrc < 0) \
+ goto error; \
+ } while (0)
+static int
+xml_renderctx(isc_mem_t *ctx, summarystat_t *summary, xmlTextWriterPtr writer) {
+ REQUIRE(VALID_CONTEXT(ctx));
+
+ int xmlrc;
+
+ MCTXLOCK(ctx);
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "context"));
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "id"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%p", ctx));
+ TRY0(xmlTextWriterEndElement(writer)); /* id */
+
+ if (ctx->name[0] != 0) {
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "name"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%s", ctx->name));
+ TRY0(xmlTextWriterEndElement(writer)); /* name */
+ }
+
+ summary->contextsize += sizeof(*ctx);
+#if ISC_MEM_TRACKLINES
+ if (ctx->debuglist != NULL) {
+ summary->contextsize += DEBUG_TABLE_COUNT *
+ sizeof(debuglist_t) +
+ ctx->debuglistcnt * sizeof(debuglink_t);
+ }
+#endif /* if ISC_MEM_TRACKLINES */
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "references"));
+ TRY0(xmlTextWriterWriteFormatString(
+ writer, "%" PRIuFAST32,
+ isc_refcount_current(&ctx->references)));
+ TRY0(xmlTextWriterEndElement(writer)); /* references */
+
+ summary->total += isc_mem_total(ctx);
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "total"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
+ (uint64_t)isc_mem_total(ctx)));
+ TRY0(xmlTextWriterEndElement(writer)); /* total */
+
+ summary->inuse += isc_mem_inuse(ctx);
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "inuse"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
+ (uint64_t)isc_mem_inuse(ctx)));
+ TRY0(xmlTextWriterEndElement(writer)); /* inuse */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "maxinuse"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
+ (uint64_t)isc_mem_maxinuse(ctx)));
+ TRY0(xmlTextWriterEndElement(writer)); /* maxinuse */
+
+ summary->malloced += isc_mem_malloced(ctx);
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "malloced"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
+ (uint64_t)isc_mem_malloced(ctx)));
+ TRY0(xmlTextWriterEndElement(writer)); /* malloced */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "maxmalloced"));
+ TRY0(xmlTextWriterWriteFormatString(
+ writer, "%" PRIu64 "", (uint64_t)isc_mem_maxmalloced(ctx)));
+ TRY0(xmlTextWriterEndElement(writer)); /* maxmalloced */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "pools"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%u", ctx->poolcnt));
+ TRY0(xmlTextWriterEndElement(writer)); /* pools */
+ summary->contextsize += ctx->poolcnt * sizeof(isc_mempool_t);
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "hiwater"));
+ TRY0(xmlTextWriterWriteFormatString(
+ writer, "%" PRIu64 "",
+ (uint64_t)atomic_load_relaxed(&ctx->hi_water)));
+ TRY0(xmlTextWriterEndElement(writer)); /* hiwater */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "lowater"));
+ TRY0(xmlTextWriterWriteFormatString(
+ writer, "%" PRIu64 "",
+ (uint64_t)atomic_load_relaxed(&ctx->lo_water)));
+ TRY0(xmlTextWriterEndElement(writer)); /* lowater */
+
+ TRY0(xmlTextWriterEndElement(writer)); /* context */
+
+error:
+ MCTXUNLOCK(ctx);
+
+ return (xmlrc);
+}
+
+int
+isc_mem_renderxml(void *writer0) {
+ isc_mem_t *ctx;
+ summarystat_t summary = { 0 };
+ uint64_t lost;
+ int xmlrc;
+ xmlTextWriterPtr writer = (xmlTextWriterPtr)writer0;
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "contexts"));
+
+ LOCK(&contextslock);
+ lost = totallost;
+ for (ctx = ISC_LIST_HEAD(contexts); ctx != NULL;
+ ctx = ISC_LIST_NEXT(ctx, link))
+ {
+ xmlrc = xml_renderctx(ctx, &summary, writer);
+ if (xmlrc < 0) {
+ UNLOCK(&contextslock);
+ goto error;
+ }
+ }
+ UNLOCK(&contextslock);
+
+ TRY0(xmlTextWriterEndElement(writer)); /* contexts */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "summary"));
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "TotalUse"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
+ summary.total));
+ TRY0(xmlTextWriterEndElement(writer)); /* TotalUse */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "InUse"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
+ summary.inuse));
+ TRY0(xmlTextWriterEndElement(writer)); /* InUse */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "Malloced"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
+ summary.malloced));
+ TRY0(xmlTextWriterEndElement(writer)); /* InUse */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "ContextSize"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
+ summary.contextsize));
+ TRY0(xmlTextWriterEndElement(writer)); /* ContextSize */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "Lost"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "", lost));
+ TRY0(xmlTextWriterEndElement(writer)); /* Lost */
+
+ TRY0(xmlTextWriterEndElement(writer)); /* summary */
+error:
+ return (xmlrc);
+}
+
+#endif /* HAVE_LIBXML2 */
+
+#ifdef HAVE_JSON_C
+#define CHECKMEM(m) RUNTIME_CHECK(m != NULL)
+
+static isc_result_t
+json_renderctx(isc_mem_t *ctx, summarystat_t *summary, json_object *array) {
+ REQUIRE(VALID_CONTEXT(ctx));
+ REQUIRE(summary != NULL);
+ REQUIRE(array != NULL);
+
+ json_object *ctxobj, *obj;
+ char buf[1024];
+
+ MCTXLOCK(ctx);
+
+ summary->contextsize += sizeof(*ctx);
+ summary->total += isc_mem_total(ctx);
+ summary->inuse += isc_mem_inuse(ctx);
+ summary->malloced += isc_mem_malloced(ctx);
+#if ISC_MEM_TRACKLINES
+ if (ctx->debuglist != NULL) {
+ summary->contextsize += DEBUG_TABLE_COUNT *
+ sizeof(debuglist_t) +
+ ctx->debuglistcnt * sizeof(debuglink_t);
+ }
+#endif /* if ISC_MEM_TRACKLINES */
+
+ ctxobj = json_object_new_object();
+ CHECKMEM(ctxobj);
+
+ snprintf(buf, sizeof(buf), "%p", ctx);
+ obj = json_object_new_string(buf);
+ CHECKMEM(obj);
+ json_object_object_add(ctxobj, "id", obj);
+
+ if (ctx->name[0] != 0) {
+ obj = json_object_new_string(ctx->name);
+ CHECKMEM(obj);
+ json_object_object_add(ctxobj, "name", obj);
+ }
+
+ obj = json_object_new_int64(isc_refcount_current(&ctx->references));
+ CHECKMEM(obj);
+ json_object_object_add(ctxobj, "references", obj);
+
+ obj = json_object_new_int64(isc_mem_total(ctx));
+ CHECKMEM(obj);
+ json_object_object_add(ctxobj, "total", obj);
+
+ obj = json_object_new_int64(isc_mem_inuse(ctx));
+ CHECKMEM(obj);
+ json_object_object_add(ctxobj, "inuse", obj);
+
+ obj = json_object_new_int64(isc_mem_maxinuse(ctx));
+ CHECKMEM(obj);
+ json_object_object_add(ctxobj, "maxinuse", obj);
+
+ obj = json_object_new_int64(isc_mem_malloced(ctx));
+ CHECKMEM(obj);
+ json_object_object_add(ctxobj, "malloced", obj);
+
+ obj = json_object_new_int64(isc_mem_maxmalloced(ctx));
+ CHECKMEM(obj);
+ json_object_object_add(ctxobj, "maxmalloced", obj);
+
+ obj = json_object_new_int64(ctx->poolcnt);
+ CHECKMEM(obj);
+ json_object_object_add(ctxobj, "pools", obj);
+
+ summary->contextsize += ctx->poolcnt * sizeof(isc_mempool_t);
+
+ obj = json_object_new_int64(atomic_load_relaxed(&ctx->hi_water));
+ CHECKMEM(obj);
+ json_object_object_add(ctxobj, "hiwater", obj);
+
+ obj = json_object_new_int64(atomic_load_relaxed(&ctx->lo_water));
+ CHECKMEM(obj);
+ json_object_object_add(ctxobj, "lowater", obj);
+
+ MCTXUNLOCK(ctx);
+ json_object_array_add(array, ctxobj);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_mem_renderjson(void *memobj0) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_mem_t *ctx;
+ summarystat_t summary = { 0 };
+ uint64_t lost;
+ json_object *ctxarray, *obj;
+ json_object *memobj = (json_object *)memobj0;
+
+ ctxarray = json_object_new_array();
+ CHECKMEM(ctxarray);
+
+ LOCK(&contextslock);
+ lost = totallost;
+ for (ctx = ISC_LIST_HEAD(contexts); ctx != NULL;
+ ctx = ISC_LIST_NEXT(ctx, link))
+ {
+ result = json_renderctx(ctx, &summary, ctxarray);
+ if (result != ISC_R_SUCCESS) {
+ UNLOCK(&contextslock);
+ goto error;
+ }
+ }
+ UNLOCK(&contextslock);
+
+ obj = json_object_new_int64(summary.total);
+ CHECKMEM(obj);
+ json_object_object_add(memobj, "TotalUse", obj);
+
+ obj = json_object_new_int64(summary.inuse);
+ CHECKMEM(obj);
+ json_object_object_add(memobj, "InUse", obj);
+
+ obj = json_object_new_int64(summary.malloced);
+ CHECKMEM(obj);
+ json_object_object_add(memobj, "Malloced", obj);
+
+ obj = json_object_new_int64(summary.contextsize);
+ CHECKMEM(obj);
+ json_object_object_add(memobj, "ContextSize", obj);
+
+ obj = json_object_new_int64(lost);
+ CHECKMEM(obj);
+ json_object_object_add(memobj, "Lost", obj);
+
+ json_object_object_add(memobj, "contexts", ctxarray);
+ return (ISC_R_SUCCESS);
+
+error:
+ if (ctxarray != NULL) {
+ json_object_put(ctxarray);
+ }
+ return (result);
+}
+#endif /* HAVE_JSON_C */
+
+void
+isc__mem_create(isc_mem_t **mctxp FLARG) {
+ mem_create(mctxp, isc_mem_defaultflags, 0);
+#if ISC_MEM_TRACKLINES
+ if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) {
+ fprintf(stderr, "create mctx %p file %s line %u\n", *mctxp,
+ file, line);
+ }
+#endif /* ISC_MEM_TRACKLINES */
+}
+
+void
+isc__mem_create_arena(isc_mem_t **mctxp FLARG) {
+ unsigned int arena_no = ISC_MEM_ILLEGAL_ARENA;
+
+ RUNTIME_CHECK(mem_jemalloc_arena_create(&arena_no));
+
+ /*
+ * We use MALLOCX_TCACHE_NONE to bypass the tcache and route
+ * allocations directly to the arena. That is a recommendation
+ * from jemalloc developers:
+ *
+ * https://github.com/jemalloc/jemalloc/issues/2483#issuecomment-1698173849
+ */
+ mem_create(mctxp, isc_mem_defaultflags,
+ arena_no == ISC_MEM_ILLEGAL_ARENA
+ ? 0
+ : MALLOCX_ARENA(arena_no) | MALLOCX_TCACHE_NONE);
+ (*mctxp)->jemalloc_arena = arena_no;
+#if ISC_MEM_TRACKLINES
+ if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) {
+ fprintf(stderr,
+ "create mctx %p file %s line %u for jemalloc arena "
+ "%u\n",
+ *mctxp, file, line, arena_no);
+ }
+#endif /* ISC_MEM_TRACKLINES */
+}
+
+#if defined(JEMALLOC_API_SUPPORTED) && JEMALLOC_VERSION_MAJOR >= 4
+static bool
+jemalloc_set_ssize_value(const char *valname, ssize_t newval) {
+ int ret;
+
+ ret = mallctl(valname, NULL, NULL, &newval, sizeof(newval));
+ return (ret == 0);
+}
+#endif /* defined(JEMALLOC_API_SUPPORTED) && JEMALLOC_VERSION_MAJOR >= 4 */
+
+static isc_result_t
+mem_set_arena_ssize_value(isc_mem_t *mctx, const char *arena_valname,
+ const ssize_t newval) {
+ REQUIRE(VALID_CONTEXT(mctx));
+#if defined(JEMALLOC_API_SUPPORTED) && JEMALLOC_VERSION_MAJOR >= 4
+ bool ret;
+ char buf[256] = { 0 };
+
+ if (mctx->jemalloc_arena == ISC_MEM_ILLEGAL_ARENA) {
+ return (ISC_R_UNEXPECTED);
+ }
+
+ (void)snprintf(buf, sizeof(buf), "arena.%u.%s", mctx->jemalloc_arena,
+ arena_valname);
+
+ ret = jemalloc_set_ssize_value(buf, newval);
+
+ if (!ret) {
+ return (ISC_R_FAILURE);
+ }
+
+ return (ISC_R_SUCCESS);
+#else
+ UNUSED(arena_valname);
+ UNUSED(newval);
+ return (ISC_R_NOTIMPLEMENTED);
+#endif /* defined(JEMALLOC_API_SUPPORTED) && JEMALLOC_VERSION_MAJOR >= 4 */
+}
+
+isc_result_t
+isc_mem_arena_set_muzzy_decay_ms(isc_mem_t *mctx, const ssize_t decay_ms) {
+ return (mem_set_arena_ssize_value(mctx, "muzzy_decay_ms", decay_ms));
+}
+
+isc_result_t
+isc_mem_arena_set_dirty_decay_ms(isc_mem_t *mctx, const ssize_t decay_ms) {
+ return (mem_set_arena_ssize_value(mctx, "dirty_decay_ms", decay_ms));
+}
+
+void
+isc__mem_printactive(isc_mem_t *ctx, FILE *file) {
+#if ISC_MEM_TRACKLINES
+ REQUIRE(VALID_CONTEXT(ctx));
+ REQUIRE(file != NULL);
+
+ print_active(ctx, file);
+#else /* if ISC_MEM_TRACKLINES */
+ UNUSED(ctx);
+ UNUSED(file);
+#endif /* if ISC_MEM_TRACKLINES */
+}
diff --git a/lib/isc/mem_p.h b/lib/isc/mem_p.h
new file mode 100644
index 0000000..611a025
--- /dev/null
+++ b/lib/isc/mem_p.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <stdio.h>
+
+#include <isc/mem.h>
+
+/*! \file */
+
+void
+isc__mem_printactive(isc_mem_t *mctx, FILE *file);
+/*%<
+ * For use by unit tests, prints active memory blocks for
+ * a single memory context.
+ */
+
+void
+isc__mem_checkdestroyed(void);
+
+void
+isc__mem_initialize(void);
+
+void
+isc__mem_shutdown(void);
diff --git a/lib/isc/meminfo.c b/lib/isc/meminfo.c
new file mode 100644
index 0000000..612ccea
--- /dev/null
+++ b/lib/isc/meminfo.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <inttypes.h>
+#include <unistd.h>
+
+#include <isc/meminfo.h>
+#if defined(HAVE_SYS_SYSCTL_H) && !defined(__linux__)
+#include <sys/sysctl.h>
+#endif /* if defined(HAVE_SYS_SYSCTL_H) && !defined(__linux__) */
+
+uint64_t
+isc_meminfo_totalphys(void) {
+#if defined(CTL_HW) && (defined(HW_PHYSMEM64) || defined(HW_MEMSIZE))
+ int mib[2];
+ mib[0] = CTL_HW;
+#if defined(HW_MEMSIZE)
+ mib[1] = HW_MEMSIZE;
+#elif defined(HW_PHYSMEM64)
+ mib[1] = HW_PHYSMEM64;
+#endif /* if defined(HW_MEMSIZE) */
+ uint64_t size = 0;
+ size_t len = sizeof(size);
+ if (sysctl(mib, 2, &size, &len, NULL, 0) == 0) {
+ return (size);
+ }
+#endif /* if defined(CTL_HW) && (defined(HW_PHYSMEM64) || defined(HW_MEMSIZE)) \
+ * */
+#if defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE)
+ long pages = sysconf(_SC_PHYS_PAGES);
+ long pagesize = sysconf(_SC_PAGESIZE);
+
+ if (pages == -1 || pagesize == -1) {
+ return (0);
+ }
+
+ return ((size_t)pages * pagesize);
+#endif /* if defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE) */
+ return (0);
+}
diff --git a/lib/isc/mutex.c b/lib/isc/mutex.c
new file mode 100644
index 0000000..65f1663
--- /dev/null
+++ b/lib/isc/mutex.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include <isc/mutex.h>
+#include <isc/once.h>
+#include <isc/print.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#ifdef HAVE_PTHREAD_MUTEX_ADAPTIVE_NP
+static bool attr_initialized = false;
+static pthread_mutexattr_t attr;
+static isc_once_t once_attr = ISC_ONCE_INIT;
+
+static void
+initialize_attr(void) {
+ RUNTIME_CHECK(pthread_mutexattr_init(&attr) == 0);
+ RUNTIME_CHECK(pthread_mutexattr_settype(
+ &attr, PTHREAD_MUTEX_ADAPTIVE_NP) == 0);
+ attr_initialized = true;
+}
+#endif /* HAVE_PTHREAD_MUTEX_ADAPTIVE_NP */
+
+int
+isc__mutex_init(isc_mutex_t *mp) {
+#ifdef HAVE_PTHREAD_MUTEX_ADAPTIVE_NP
+ isc_result_t result = ISC_R_SUCCESS;
+ result = isc_once_do(&once_attr, initialize_attr);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ return (pthread_mutex_init(mp, &attr));
+#else /* HAVE_PTHREAD_MUTEX_ADAPTIVE_NP */
+ return (pthread_mutex_init(mp, NULL));
+#endif /* HAVE_PTHREAD_MUTEX_ADAPTIVE_NP */
+}
diff --git a/lib/isc/mutexblock.c b/lib/isc/mutexblock.c
new file mode 100644
index 0000000..56a2985
--- /dev/null
+++ b/lib/isc/mutexblock.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <isc/mutexblock.h>
+#include <isc/util.h>
+
+void
+isc_mutexblock_init(isc_mutex_t *block, unsigned int count) {
+ unsigned int i;
+
+ for (i = 0; i < count; i++) {
+ isc_mutex_init(&block[i]);
+ }
+}
+
+void
+isc_mutexblock_destroy(isc_mutex_t *block, unsigned int count) {
+ unsigned int i;
+
+ for (i = 0; i < count; i++) {
+ isc_mutex_destroy(&block[i]);
+ }
+}
diff --git a/lib/isc/net.c b/lib/isc/net.c
new file mode 100644
index 0000000..e32a543
--- /dev/null
+++ b/lib/isc/net.c
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+#if defined(HAVE_SYS_SYSCTL_H) && !defined(__linux__)
+#if defined(HAVE_SYS_PARAM_H)
+#include <sys/param.h>
+#endif /* if defined(HAVE_SYS_PARAM_H) */
+#include <sys/sysctl.h>
+#endif /* if defined(HAVE_SYS_SYSCTL_H) && !defined(__linux__) */
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <isc/log.h>
+#include <isc/net.h>
+#include <isc/netdb.h>
+#include <isc/once.h>
+#include <isc/strerr.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#ifndef socklen_t
+#define socklen_t unsigned int
+#endif /* ifndef socklen_t */
+
+/*%
+ * Definitions about UDP port range specification. This is a total mess of
+ * portability variants: some use sysctl (but the sysctl names vary), some use
+ * system-specific interfaces, some have the same interface for IPv4 and IPv6,
+ * some separate them, etc...
+ */
+
+/*%
+ * The last resort defaults: use all non well known port space
+ */
+#ifndef ISC_NET_PORTRANGELOW
+#define ISC_NET_PORTRANGELOW 1024
+#endif /* ISC_NET_PORTRANGELOW */
+#ifndef ISC_NET_PORTRANGEHIGH
+#define ISC_NET_PORTRANGEHIGH 65535
+#endif /* ISC_NET_PORTRANGEHIGH */
+
+#ifdef HAVE_SYSCTLBYNAME
+
+/*%
+ * sysctl variants
+ */
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+#define USE_SYSCTL_PORTRANGE
+#define SYSCTL_V4PORTRANGE_LOW "net.inet.ip.portrange.hifirst"
+#define SYSCTL_V4PORTRANGE_HIGH "net.inet.ip.portrange.hilast"
+#define SYSCTL_V6PORTRANGE_LOW "net.inet.ip.portrange.hifirst"
+#define SYSCTL_V6PORTRANGE_HIGH "net.inet.ip.portrange.hilast"
+#endif /* if defined(__FreeBSD__) || defined(__APPLE__) || \
+ * defined(__DragonFly__) */
+
+#ifdef __NetBSD__
+#define USE_SYSCTL_PORTRANGE
+#define SYSCTL_V4PORTRANGE_LOW "net.inet.ip.anonportmin"
+#define SYSCTL_V4PORTRANGE_HIGH "net.inet.ip.anonportmax"
+#define SYSCTL_V6PORTRANGE_LOW "net.inet6.ip6.anonportmin"
+#define SYSCTL_V6PORTRANGE_HIGH "net.inet6.ip6.anonportmax"
+#endif /* ifdef __NetBSD__ */
+
+#else /* !HAVE_SYSCTLBYNAME */
+
+#ifdef __OpenBSD__
+#define USE_SYSCTL_PORTRANGE
+#define SYSCTL_V4PORTRANGE_LOW \
+ { \
+ CTL_NET, PF_INET, IPPROTO_IP, IPCTL_IPPORT_HIFIRSTAUTO \
+ }
+#define SYSCTL_V4PORTRANGE_HIGH \
+ { \
+ CTL_NET, PF_INET, IPPROTO_IP, IPCTL_IPPORT_HILASTAUTO \
+ }
+/* Same for IPv6 */
+#define SYSCTL_V6PORTRANGE_LOW SYSCTL_V4PORTRANGE_LOW
+#define SYSCTL_V6PORTRANGE_HIGH SYSCTL_V4PORTRANGE_HIGH
+#endif /* ifdef __OpenBSD__ */
+
+#endif /* HAVE_SYSCTLBYNAME */
+
+static isc_once_t once_ipv6only = ISC_ONCE_INIT;
+#ifdef __notyet__
+static isc_once_t once_ipv6pktinfo = ISC_ONCE_INIT;
+#endif /* ifdef __notyet__ */
+
+#ifndef ISC_CMSG_IP_TOS
+#ifdef __APPLE__
+#define ISC_CMSG_IP_TOS 0 /* As of 10.8.2. */
+#else /* ! __APPLE__ */
+#define ISC_CMSG_IP_TOS 1
+#endif /* ! __APPLE__ */
+#endif /* ! ISC_CMSG_IP_TOS */
+
+static isc_once_t once = ISC_ONCE_INIT;
+
+static isc_result_t ipv4_result = ISC_R_NOTFOUND;
+static isc_result_t ipv6_result = ISC_R_NOTFOUND;
+static isc_result_t unix_result = ISC_R_NOTFOUND;
+static isc_result_t ipv6only_result = ISC_R_NOTFOUND;
+static isc_result_t ipv6pktinfo_result = ISC_R_NOTFOUND;
+
+static isc_result_t
+try_proto(int domain) {
+ int s;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ s = socket(domain, SOCK_STREAM, 0);
+ if (s == -1) {
+ switch (errno) {
+#ifdef EAFNOSUPPORT
+ case EAFNOSUPPORT:
+#endif /* ifdef EAFNOSUPPORT */
+#ifdef EPFNOSUPPORT
+ case EPFNOSUPPORT:
+#endif /* ifdef EPFNOSUPPORT */
+#ifdef EPROTONOSUPPORT
+ case EPROTONOSUPPORT:
+#endif /* ifdef EPROTONOSUPPORT */
+#ifdef EINVAL
+ case EINVAL:
+#endif /* ifdef EINVAL */
+ return (ISC_R_NOTFOUND);
+ default:
+ UNEXPECTED_SYSERROR(errno, "socket()");
+ return (ISC_R_UNEXPECTED);
+ }
+ }
+
+ if (domain == PF_INET6) {
+ struct sockaddr_in6 sin6;
+ unsigned int len;
+
+ /*
+ * Check to see if IPv6 is broken, as is common on Linux.
+ */
+ len = sizeof(sin6);
+ if (getsockname(s, (struct sockaddr *)&sin6, (void *)&len) < 0)
+ {
+ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR,
+ "retrieving the address of an IPv6 "
+ "socket from the kernel failed.");
+ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR,
+ "IPv6 is not supported.");
+ result = ISC_R_NOTFOUND;
+ } else {
+ if (len == sizeof(struct sockaddr_in6)) {
+ result = ISC_R_SUCCESS;
+ } else {
+ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_SOCKET,
+ ISC_LOG_ERROR,
+ "IPv6 structures in kernel and "
+ "user space do not match.");
+ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_SOCKET,
+ ISC_LOG_ERROR,
+ "IPv6 is not supported.");
+ result = ISC_R_NOTFOUND;
+ }
+ }
+ }
+
+ (void)close(s);
+
+ return (result);
+}
+
+static void
+initialize_action(void) {
+ ipv4_result = try_proto(PF_INET);
+ ipv6_result = try_proto(PF_INET6);
+ unix_result = try_proto(PF_UNIX);
+}
+
+static void
+initialize(void) {
+ RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_net_probeipv4(void) {
+ initialize();
+ return (ipv4_result);
+}
+
+isc_result_t
+isc_net_probeipv6(void) {
+ initialize();
+ return (ipv6_result);
+}
+
+isc_result_t
+isc_net_probeunix(void) {
+ initialize();
+ return (unix_result);
+}
+
+static void
+try_ipv6only(void) {
+#ifdef IPV6_V6ONLY
+ int s, on;
+#endif /* ifdef IPV6_V6ONLY */
+ isc_result_t result;
+
+ result = isc_net_probeipv6();
+ if (result != ISC_R_SUCCESS) {
+ ipv6only_result = result;
+ return;
+ }
+
+#ifndef IPV6_V6ONLY
+ ipv6only_result = ISC_R_NOTFOUND;
+ return;
+#else /* ifndef IPV6_V6ONLY */
+ /* check for TCP sockets */
+ s = socket(PF_INET6, SOCK_STREAM, 0);
+ if (s == -1) {
+ UNEXPECTED_SYSERROR(errno, "socket()");
+ ipv6only_result = ISC_R_UNEXPECTED;
+ return;
+ }
+
+ on = 1;
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) {
+ ipv6only_result = ISC_R_NOTFOUND;
+ goto close;
+ }
+
+ close(s);
+
+ /* check for UDP sockets */
+ s = socket(PF_INET6, SOCK_DGRAM, 0);
+ if (s == -1) {
+ UNEXPECTED_SYSERROR(errno, "socket()");
+ ipv6only_result = ISC_R_UNEXPECTED;
+ return;
+ }
+
+ on = 1;
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) {
+ ipv6only_result = ISC_R_NOTFOUND;
+ goto close;
+ }
+
+ ipv6only_result = ISC_R_SUCCESS;
+
+close:
+ close(s);
+ return;
+#endif /* IPV6_V6ONLY */
+}
+
+static void
+initialize_ipv6only(void) {
+ RUNTIME_CHECK(isc_once_do(&once_ipv6only, try_ipv6only) ==
+ ISC_R_SUCCESS);
+}
+
+#ifdef __notyet__
+static void
+try_ipv6pktinfo(void) {
+ int s, on;
+ isc_result_t result;
+ int optname;
+
+ result = isc_net_probeipv6();
+ if (result != ISC_R_SUCCESS) {
+ ipv6pktinfo_result = result;
+ return;
+ }
+
+ /* we only use this for UDP sockets */
+ s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (s == -1) {
+ UNEXPECTED_SYSERROR(errno, "socket()");
+ ipv6pktinfo_result = ISC_R_UNEXPECTED;
+ return;
+ }
+
+#ifdef IPV6_RECVPKTINFO
+ optname = IPV6_RECVPKTINFO;
+#else /* ifdef IPV6_RECVPKTINFO */
+ optname = IPV6_PKTINFO;
+#endif /* ifdef IPV6_RECVPKTINFO */
+ on = 1;
+ if (setsockopt(s, IPPROTO_IPV6, optname, &on, sizeof(on)) < 0) {
+ ipv6pktinfo_result = ISC_R_NOTFOUND;
+ goto close;
+ }
+
+ ipv6pktinfo_result = ISC_R_SUCCESS;
+
+close:
+ close(s);
+ return;
+}
+
+static void
+initialize_ipv6pktinfo(void) {
+ RUNTIME_CHECK(isc_once_do(&once_ipv6pktinfo, try_ipv6pktinfo) ==
+ ISC_R_SUCCESS);
+}
+#endif /* ifdef __notyet__ */
+
+isc_result_t
+isc_net_probe_ipv6only(void) {
+ initialize_ipv6only();
+ return (ipv6only_result);
+}
+
+isc_result_t
+isc_net_probe_ipv6pktinfo(void) {
+/*
+ * XXXWPK if pktinfo were supported then we could listen on :: for ipv6 and get
+ * the information about the destination address from pktinfo structure passed
+ * in recvmsg but this method is not portable and libuv doesn't support it - so
+ * we need to listen on all interfaces.
+ * We should verify that this doesn't impact performance (we already do it for
+ * ipv4) and either remove all the ipv6pktinfo detection code from above
+ * or think of fixing libuv.
+ */
+#ifdef __notyet__
+ initialize_ipv6pktinfo();
+#endif /* ifdef __notyet__ */
+ return (ipv6pktinfo_result);
+}
+
+#if defined(USE_SYSCTL_PORTRANGE)
+#if defined(HAVE_SYSCTLBYNAME)
+static isc_result_t
+getudpportrange_sysctl(int af, in_port_t *low, in_port_t *high) {
+ int port_low, port_high;
+ size_t portlen;
+ const char *sysctlname_lowport, *sysctlname_hiport;
+
+ if (af == AF_INET) {
+ sysctlname_lowport = SYSCTL_V4PORTRANGE_LOW;
+ sysctlname_hiport = SYSCTL_V4PORTRANGE_HIGH;
+ } else {
+ sysctlname_lowport = SYSCTL_V6PORTRANGE_LOW;
+ sysctlname_hiport = SYSCTL_V6PORTRANGE_HIGH;
+ }
+ portlen = sizeof(port_low);
+ if (sysctlbyname(sysctlname_lowport, &port_low, &portlen, NULL, 0) < 0)
+ {
+ return (ISC_R_FAILURE);
+ }
+ portlen = sizeof(port_high);
+ if (sysctlbyname(sysctlname_hiport, &port_high, &portlen, NULL, 0) < 0)
+ {
+ return (ISC_R_FAILURE);
+ }
+ if ((port_low & ~0xffff) != 0 || (port_high & ~0xffff) != 0) {
+ return (ISC_R_RANGE);
+ }
+
+ *low = (in_port_t)port_low;
+ *high = (in_port_t)port_high;
+
+ return (ISC_R_SUCCESS);
+}
+#else /* !HAVE_SYSCTLBYNAME */
+static isc_result_t
+getudpportrange_sysctl(int af, in_port_t *low, in_port_t *high) {
+ int mib_lo4[4] = SYSCTL_V4PORTRANGE_LOW;
+ int mib_hi4[4] = SYSCTL_V4PORTRANGE_HIGH;
+ int mib_lo6[4] = SYSCTL_V6PORTRANGE_LOW;
+ int mib_hi6[4] = SYSCTL_V6PORTRANGE_HIGH;
+ int *mib_lo, *mib_hi, miblen;
+ int port_low, port_high;
+ size_t portlen;
+
+ if (af == AF_INET) {
+ mib_lo = mib_lo4;
+ mib_hi = mib_hi4;
+ miblen = sizeof(mib_lo4) / sizeof(mib_lo4[0]);
+ } else {
+ mib_lo = mib_lo6;
+ mib_hi = mib_hi6;
+ miblen = sizeof(mib_lo6) / sizeof(mib_lo6[0]);
+ }
+
+ portlen = sizeof(port_low);
+ if (sysctl(mib_lo, miblen, &port_low, &portlen, NULL, 0) < 0) {
+ return (ISC_R_FAILURE);
+ }
+
+ portlen = sizeof(port_high);
+ if (sysctl(mib_hi, miblen, &port_high, &portlen, NULL, 0) < 0) {
+ return (ISC_R_FAILURE);
+ }
+
+ if ((port_low & ~0xffff) != 0 || (port_high & ~0xffff) != 0) {
+ return (ISC_R_RANGE);
+ }
+
+ *low = (in_port_t)port_low;
+ *high = (in_port_t)port_high;
+
+ return (ISC_R_SUCCESS);
+}
+#endif /* HAVE_SYSCTLBYNAME */
+#endif /* USE_SYSCTL_PORTRANGE */
+
+isc_result_t
+isc_net_getudpportrange(int af, in_port_t *low, in_port_t *high) {
+ int result = ISC_R_FAILURE;
+#if !defined(USE_SYSCTL_PORTRANGE) && defined(__linux)
+ FILE *fp;
+#endif /* if !defined(USE_SYSCTL_PORTRANGE) && defined(__linux) */
+
+ REQUIRE(low != NULL && high != NULL);
+
+#if defined(USE_SYSCTL_PORTRANGE)
+ result = getudpportrange_sysctl(af, low, high);
+#elif defined(__linux)
+
+ UNUSED(af);
+
+ /*
+ * Linux local ports are address family agnostic.
+ */
+ fp = fopen("/proc/sys/net/ipv4/ip_local_port_range", "r");
+ if (fp != NULL) {
+ int n;
+ unsigned int l, h;
+
+ n = fscanf(fp, "%u %u", &l, &h);
+ if (n == 2 && (l & ~0xffff) == 0 && (h & ~0xffff) == 0) {
+ *low = l;
+ *high = h;
+ result = ISC_R_SUCCESS;
+ }
+ fclose(fp);
+ }
+#else /* if defined(USE_SYSCTL_PORTRANGE) */
+ UNUSED(af);
+#endif /* if defined(USE_SYSCTL_PORTRANGE) */
+
+ if (result != ISC_R_SUCCESS) {
+ *low = ISC_NET_PORTRANGELOW;
+ *high = ISC_NET_PORTRANGEHIGH;
+ }
+
+ return (ISC_R_SUCCESS); /* we currently never fail in this function */
+}
+
+void
+isc_net_disableipv4(void) {
+ initialize();
+ if (ipv4_result == ISC_R_SUCCESS) {
+ ipv4_result = ISC_R_DISABLED;
+ }
+}
+
+void
+isc_net_disableipv6(void) {
+ initialize();
+ if (ipv6_result == ISC_R_SUCCESS) {
+ ipv6_result = ISC_R_DISABLED;
+ }
+}
+
+void
+isc_net_enableipv4(void) {
+ initialize();
+ if (ipv4_result == ISC_R_DISABLED) {
+ ipv4_result = ISC_R_SUCCESS;
+ }
+}
+
+void
+isc_net_enableipv6(void) {
+ initialize();
+ if (ipv6_result == ISC_R_DISABLED) {
+ ipv6_result = ISC_R_SUCCESS;
+ }
+}
diff --git a/lib/isc/netaddr.c b/lib/isc/netaddr.c
new file mode 100644
index 0000000..c674d83
--- /dev/null
+++ b/lib/isc/netaddr.c
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <isc/buffer.h>
+#include <isc/net.h>
+#include <isc/netaddr.h>
+#include <isc/print.h>
+#include <isc/sockaddr.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+bool
+isc_netaddr_equal(const isc_netaddr_t *a, const isc_netaddr_t *b) {
+ REQUIRE(a != NULL && b != NULL);
+
+ if (a->family != b->family) {
+ return (false);
+ }
+
+ if (a->zone != b->zone) {
+ return (false);
+ }
+
+ switch (a->family) {
+ case AF_INET:
+ if (a->type.in.s_addr != b->type.in.s_addr) {
+ return (false);
+ }
+ break;
+ case AF_INET6:
+ if (memcmp(&a->type.in6, &b->type.in6, sizeof(a->type.in6)) !=
+ 0 ||
+ a->zone != b->zone)
+ {
+ return (false);
+ }
+ break;
+ case AF_UNIX:
+ if (strcmp(a->type.un, b->type.un) != 0) {
+ return (false);
+ }
+ break;
+ default:
+ return (false);
+ }
+ return (true);
+}
+
+bool
+isc_netaddr_eqprefix(const isc_netaddr_t *a, const isc_netaddr_t *b,
+ unsigned int prefixlen) {
+ const unsigned char *pa = NULL, *pb = NULL;
+ unsigned int ipabytes = 0; /* Length of whole IP address in bytes */
+ unsigned int nbytes; /* Number of significant whole bytes */
+ unsigned int nbits; /* Number of significant leftover bits */
+
+ REQUIRE(a != NULL && b != NULL);
+
+ if (a->family != b->family) {
+ return (false);
+ }
+
+ if (a->zone != b->zone && b->zone != 0) {
+ return (false);
+ }
+
+ switch (a->family) {
+ case AF_INET:
+ pa = (const unsigned char *)&a->type.in;
+ pb = (const unsigned char *)&b->type.in;
+ ipabytes = 4;
+ break;
+ case AF_INET6:
+ pa = (const unsigned char *)&a->type.in6;
+ pb = (const unsigned char *)&b->type.in6;
+ ipabytes = 16;
+ break;
+ default:
+ return (false);
+ }
+
+ /*
+ * Don't crash if we get a pattern like 10.0.0.1/9999999.
+ */
+ if (prefixlen > ipabytes * 8) {
+ prefixlen = ipabytes * 8;
+ }
+
+ nbytes = prefixlen / 8;
+ nbits = prefixlen % 8;
+
+ if (nbytes > 0) {
+ if (memcmp(pa, pb, nbytes) != 0) {
+ return (false);
+ }
+ }
+ if (nbits > 0) {
+ unsigned int bytea, byteb, mask;
+ INSIST(nbytes < ipabytes);
+ INSIST(nbits < 8);
+ bytea = pa[nbytes];
+ byteb = pb[nbytes];
+ mask = (0xFF << (8 - nbits)) & 0xFF;
+ if ((bytea & mask) != (byteb & mask)) {
+ return (false);
+ }
+ }
+ return (true);
+}
+
+isc_result_t
+isc_netaddr_totext(const isc_netaddr_t *netaddr, isc_buffer_t *target) {
+ char abuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")];
+ char zbuf[sizeof("%4294967295")];
+ unsigned int alen;
+ int zlen;
+ const char *r;
+ const void *type;
+
+ REQUIRE(netaddr != NULL);
+
+ switch (netaddr->family) {
+ case AF_INET:
+ type = &netaddr->type.in;
+ break;
+ case AF_INET6:
+ type = &netaddr->type.in6;
+ break;
+ case AF_UNIX:
+ alen = strlen(netaddr->type.un);
+ if (alen > isc_buffer_availablelength(target)) {
+ return (ISC_R_NOSPACE);
+ }
+ isc_buffer_putmem(target,
+ (const unsigned char *)(netaddr->type.un),
+ alen);
+ return (ISC_R_SUCCESS);
+ default:
+ return (ISC_R_FAILURE);
+ }
+ r = inet_ntop(netaddr->family, type, abuf, sizeof(abuf));
+ if (r == NULL) {
+ return (ISC_R_FAILURE);
+ }
+
+ alen = strlen(abuf);
+ INSIST(alen < sizeof(abuf));
+
+ zlen = 0;
+ if (netaddr->family == AF_INET6 && netaddr->zone != 0) {
+ zlen = snprintf(zbuf, sizeof(zbuf), "%%%u", netaddr->zone);
+ if (zlen < 0) {
+ return (ISC_R_FAILURE);
+ }
+ INSIST((unsigned int)zlen < sizeof(zbuf));
+ }
+
+ if (alen + zlen > isc_buffer_availablelength(target)) {
+ return (ISC_R_NOSPACE);
+ }
+
+ isc_buffer_putmem(target, (unsigned char *)abuf, alen);
+ isc_buffer_putmem(target, (unsigned char *)zbuf, (unsigned int)zlen);
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_netaddr_format(const isc_netaddr_t *na, char *array, unsigned int size) {
+ isc_result_t result;
+ isc_buffer_t buf;
+
+ isc_buffer_init(&buf, array, size);
+ result = isc_netaddr_totext(na, &buf);
+
+ if (size == 0) {
+ return;
+ }
+
+ /*
+ * Null terminate.
+ */
+ if (result == ISC_R_SUCCESS) {
+ if (isc_buffer_availablelength(&buf) >= 1) {
+ isc_buffer_putuint8(&buf, 0);
+ } else {
+ result = ISC_R_NOSPACE;
+ }
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ snprintf(array, size, "<unknown address, family %u>",
+ na->family);
+ array[size - 1] = '\0';
+ }
+}
+
+isc_result_t
+isc_netaddr_prefixok(const isc_netaddr_t *na, unsigned int prefixlen) {
+ static const unsigned char zeros[16];
+ unsigned int nbits, nbytes, ipbytes = 0;
+ const unsigned char *p;
+
+ switch (na->family) {
+ case AF_INET:
+ p = (const unsigned char *)&na->type.in;
+ ipbytes = 4;
+ if (prefixlen > 32) {
+ return (ISC_R_RANGE);
+ }
+ break;
+ case AF_INET6:
+ p = (const unsigned char *)&na->type.in6;
+ ipbytes = 16;
+ if (prefixlen > 128) {
+ return (ISC_R_RANGE);
+ }
+ break;
+ default:
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+ nbytes = prefixlen / 8;
+ nbits = prefixlen % 8;
+ if (nbits != 0) {
+ INSIST(nbytes < ipbytes);
+ if ((p[nbytes] & (0xff >> nbits)) != 0U) {
+ return (ISC_R_FAILURE);
+ }
+ nbytes++;
+ }
+ if (nbytes < ipbytes &&
+ memcmp(p + nbytes, zeros, ipbytes - nbytes) != 0)
+ {
+ return (ISC_R_FAILURE);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_netaddr_masktoprefixlen(const isc_netaddr_t *s, unsigned int *lenp) {
+ unsigned int nbits = 0, nbytes = 0, ipbytes = 0, i;
+ const unsigned char *p;
+
+ switch (s->family) {
+ case AF_INET:
+ p = (const unsigned char *)&s->type.in;
+ ipbytes = 4;
+ break;
+ case AF_INET6:
+ p = (const unsigned char *)&s->type.in6;
+ ipbytes = 16;
+ break;
+ default:
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+ for (i = 0; i < ipbytes; i++) {
+ if (p[i] != 0xFF) {
+ break;
+ }
+ }
+ nbytes = i;
+ if (i < ipbytes) {
+ unsigned int c = p[nbytes];
+ while ((c & 0x80) != 0 && nbits < 8) {
+ c <<= 1;
+ nbits++;
+ }
+ if ((c & 0xFF) != 0) {
+ return (ISC_R_MASKNONCONTIG);
+ }
+ i++;
+ }
+ for (; i < ipbytes; i++) {
+ if (p[i] != 0) {
+ return (ISC_R_MASKNONCONTIG);
+ }
+ }
+ *lenp = nbytes * 8 + nbits;
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_netaddr_fromin(isc_netaddr_t *netaddr, const struct in_addr *ina) {
+ memset(netaddr, 0, sizeof(*netaddr));
+ netaddr->family = AF_INET;
+ netaddr->type.in = *ina;
+}
+
+void
+isc_netaddr_fromin6(isc_netaddr_t *netaddr, const struct in6_addr *ina6) {
+ memset(netaddr, 0, sizeof(*netaddr));
+ netaddr->family = AF_INET6;
+ netaddr->type.in6 = *ina6;
+}
+
+isc_result_t
+isc_netaddr_frompath(isc_netaddr_t *netaddr, const char *path) {
+ if (strlen(path) > sizeof(netaddr->type.un) - 1) {
+ return (ISC_R_NOSPACE);
+ }
+
+ memset(netaddr, 0, sizeof(*netaddr));
+ netaddr->family = AF_UNIX;
+ strlcpy(netaddr->type.un, path, sizeof(netaddr->type.un));
+ netaddr->zone = 0;
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_netaddr_setzone(isc_netaddr_t *netaddr, uint32_t zone) {
+ /* we currently only support AF_INET6. */
+ REQUIRE(netaddr->family == AF_INET6);
+
+ netaddr->zone = zone;
+}
+
+uint32_t
+isc_netaddr_getzone(const isc_netaddr_t *netaddr) {
+ return (netaddr->zone);
+}
+
+void
+isc_netaddr_fromsockaddr(isc_netaddr_t *t, const isc_sockaddr_t *s) {
+ int family = s->type.sa.sa_family;
+ t->family = family;
+ switch (family) {
+ case AF_INET:
+ t->type.in = s->type.sin.sin_addr;
+ t->zone = 0;
+ break;
+ case AF_INET6:
+ memmove(&t->type.in6, &s->type.sin6.sin6_addr, 16);
+ t->zone = s->type.sin6.sin6_scope_id;
+ break;
+ case AF_UNIX:
+ memmove(t->type.un, s->type.sunix.sun_path, sizeof(t->type.un));
+ t->zone = 0;
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+void
+isc_netaddr_any(isc_netaddr_t *netaddr) {
+ memset(netaddr, 0, sizeof(*netaddr));
+ netaddr->family = AF_INET;
+ netaddr->type.in.s_addr = INADDR_ANY;
+}
+
+void
+isc_netaddr_any6(isc_netaddr_t *netaddr) {
+ memset(netaddr, 0, sizeof(*netaddr));
+ netaddr->family = AF_INET6;
+ netaddr->type.in6 = in6addr_any;
+}
+
+void
+isc_netaddr_unspec(isc_netaddr_t *netaddr) {
+ memset(netaddr, 0, sizeof(*netaddr));
+ netaddr->family = AF_UNSPEC;
+}
+
+bool
+isc_netaddr_ismulticast(const isc_netaddr_t *na) {
+ switch (na->family) {
+ case AF_INET:
+ return (ISC_IPADDR_ISMULTICAST(na->type.in.s_addr));
+ case AF_INET6:
+ return (IN6_IS_ADDR_MULTICAST(&na->type.in6));
+ default:
+ return (false); /* XXXMLG ? */
+ }
+}
+
+bool
+isc_netaddr_isexperimental(const isc_netaddr_t *na) {
+ switch (na->family) {
+ case AF_INET:
+ return (ISC_IPADDR_ISEXPERIMENTAL(na->type.in.s_addr));
+ default:
+ return (false); /* XXXMLG ? */
+ }
+}
+
+bool
+isc_netaddr_islinklocal(const isc_netaddr_t *na) {
+ switch (na->family) {
+ case AF_INET:
+ return (false);
+ case AF_INET6:
+ return (IN6_IS_ADDR_LINKLOCAL(&na->type.in6));
+ default:
+ return (false);
+ }
+}
+
+bool
+isc_netaddr_issitelocal(const isc_netaddr_t *na) {
+ switch (na->family) {
+ case AF_INET:
+ return (false);
+ case AF_INET6:
+ return (IN6_IS_ADDR_SITELOCAL(&na->type.in6));
+ default:
+ return (false);
+ }
+}
+
+#define ISC_IPADDR_ISNETZERO(i) \
+ (((uint32_t)(i)&ISC__IPADDR(0xff000000)) == ISC__IPADDR(0x00000000))
+
+bool
+isc_netaddr_isnetzero(const isc_netaddr_t *na) {
+ switch (na->family) {
+ case AF_INET:
+ return (ISC_IPADDR_ISNETZERO(na->type.in.s_addr));
+ case AF_INET6:
+ return (false);
+ default:
+ return (false);
+ }
+}
+
+void
+isc_netaddr_fromv4mapped(isc_netaddr_t *t, const isc_netaddr_t *s) {
+ isc_netaddr_t *src;
+
+ DE_CONST(s, src); /* Must come before IN6_IS_ADDR_V4MAPPED. */
+
+ REQUIRE(s->family == AF_INET6);
+ REQUIRE(IN6_IS_ADDR_V4MAPPED(&src->type.in6));
+
+ memset(t, 0, sizeof(*t));
+ t->family = AF_INET;
+ memmove(&t->type.in, (char *)&src->type.in6 + 12, 4);
+ return;
+}
+
+bool
+isc_netaddr_isloopback(const isc_netaddr_t *na) {
+ switch (na->family) {
+ case AF_INET:
+ return (((ntohl(na->type.in.s_addr) & 0xff000000U) ==
+ 0x7f000000U));
+ case AF_INET6:
+ return (IN6_IS_ADDR_LOOPBACK(&na->type.in6));
+ default:
+ return (false);
+ }
+}
diff --git a/lib/isc/netmgr/http.c b/lib/isc/netmgr/http.c
new file mode 100644
index 0000000..f2d3e2d
--- /dev/null
+++ b/lib/isc/netmgr/http.c
@@ -0,0 +1,3755 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <nghttp2/nghttp2.h>
+#include <signal.h>
+#include <string.h>
+
+#include <isc/base64.h>
+#include <isc/log.h>
+#include <isc/netmgr.h>
+#include <isc/print.h>
+#include <isc/sockaddr.h>
+#include <isc/tls.h>
+#include <isc/url.h>
+#include <isc/util.h>
+
+#include "netmgr-int.h"
+
+#define AUTHEXTRA 7
+
+#define MAX_DNS_MESSAGE_SIZE (UINT16_MAX)
+
+#define DNS_MEDIA_TYPE "application/dns-message"
+
+/*
+ * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
+ * for additional details. Basically it means "avoid caching by any
+ * means."
+ */
+#define DEFAULT_CACHE_CONTROL "no-cache, no-store, must-revalidate"
+
+/*
+ * If server during request processing surpasses any of the limits
+ * below, it will just reset the stream without returning any error
+ * codes in a response. Ideally, these parameters should be
+ * configurable both globally and per every HTTP endpoint description
+ * in the configuration file, but for now it should be enough.
+ */
+
+/*
+ * 128K should be enough to encode 64K of data into base64url inside GET
+ * request and have extra space for other headers
+ */
+#define MAX_ALLOWED_DATA_IN_HEADERS (MAX_DNS_MESSAGE_SIZE * 2)
+
+#define MAX_ALLOWED_DATA_IN_POST \
+ (MAX_DNS_MESSAGE_SIZE + MAX_DNS_MESSAGE_SIZE / 2)
+
+#define HEADER_MATCH(header, name, namelen) \
+ (((namelen) == sizeof(header) - 1) && \
+ (strncasecmp((header), (const char *)(name), (namelen)) == 0))
+
+#define MIN_SUCCESSFUL_HTTP_STATUS (200)
+#define MAX_SUCCESSFUL_HTTP_STATUS (299)
+
+/* This definition sets the upper limit of pending write buffer to an
+ * adequate enough value. That is done mostly to fight a limitation
+ * for a max TLS record size in flamethrower (2K). In a perfect world
+ * this constant should not be required, if we ever move closer to
+ * that state, the constant, and corresponding code, should be
+ * removed. For now the limit seems adequate enough to fight
+ * "tinygrams" problem. */
+#define FLUSH_HTTP_WRITE_BUFFER_AFTER (1536)
+
+/* This switch is here mostly to test the code interoperability with
+ * buggy implementations */
+#define ENABLE_HTTP_WRITE_BUFFERING 1
+
+#define SUCCESSFUL_HTTP_STATUS(code) \
+ ((code) >= MIN_SUCCESSFUL_HTTP_STATUS && \
+ (code) <= MAX_SUCCESSFUL_HTTP_STATUS)
+
+#define INITIAL_DNS_MESSAGE_BUFFER_SIZE (512)
+
+typedef struct isc_nm_http_response_status {
+ size_t code;
+ size_t content_length;
+ bool content_type_valid;
+} isc_nm_http_response_status_t;
+
+typedef struct http_cstream {
+ isc_nm_recv_cb_t read_cb;
+ void *read_cbarg;
+ isc_nm_cb_t connect_cb;
+ void *connect_cbarg;
+
+ bool sending;
+ bool reading;
+
+ char *uri;
+ isc_url_parser_t up;
+
+ char *authority;
+ size_t authoritylen;
+ char *path;
+
+ isc_buffer_t *rbuf;
+
+ size_t pathlen;
+ int32_t stream_id;
+
+ bool post; /* POST or GET */
+ isc_buffer_t *postdata;
+ char *GET_path;
+ size_t GET_path_len;
+
+ isc_nm_http_response_status_t response_status;
+ isc_nmsocket_t *httpsock;
+ LINK(struct http_cstream) link;
+} http_cstream_t;
+
+#define HTTP2_SESSION_MAGIC ISC_MAGIC('H', '2', 'S', 'S')
+#define VALID_HTTP2_SESSION(t) ISC_MAGIC_VALID(t, HTTP2_SESSION_MAGIC)
+
+typedef ISC_LIST(isc__nm_uvreq_t) isc__nm_http_pending_callbacks_t;
+
+struct isc_nm_http_session {
+ unsigned int magic;
+ isc_refcount_t references;
+ isc_mem_t *mctx;
+
+ size_t sending;
+ bool reading;
+ bool closed;
+ bool closing;
+
+ nghttp2_session *ngsession;
+ bool client;
+
+ ISC_LIST(http_cstream_t) cstreams;
+ ISC_LIST(isc_nmsocket_h2_t) sstreams;
+ size_t nsstreams;
+
+ isc_nmhandle_t *handle;
+ isc_nmhandle_t *client_httphandle;
+ isc_nmsocket_t *serversocket;
+
+ isc_buffer_t *buf;
+
+ isc_tlsctx_t *tlsctx;
+ uint32_t max_concurrent_streams;
+
+ isc__nm_http_pending_callbacks_t pending_write_callbacks;
+ isc_buffer_t *pending_write_data;
+};
+
+typedef enum isc_http_error_responses {
+ ISC_HTTP_ERROR_SUCCESS, /* 200 */
+ ISC_HTTP_ERROR_NOT_FOUND, /* 404 */
+ ISC_HTTP_ERROR_PAYLOAD_TOO_LARGE, /* 413 */
+ ISC_HTTP_ERROR_URI_TOO_LONG, /* 414 */
+ ISC_HTTP_ERROR_UNSUPPORTED_MEDIA_TYPE, /* 415 */
+ ISC_HTTP_ERROR_BAD_REQUEST, /* 400 */
+ ISC_HTTP_ERROR_NOT_IMPLEMENTED, /* 501 */
+ ISC_HTTP_ERROR_GENERIC, /* 500 Internal Server Error */
+ ISC_HTTP_ERROR_MAX
+} isc_http_error_responses_t;
+
+typedef struct isc_http_send_req {
+ isc_nm_http_session_t *session;
+ isc_nmhandle_t *transphandle;
+ isc_nmhandle_t *httphandle;
+ isc_nm_cb_t cb;
+ void *cbarg;
+ isc_buffer_t *pending_write_data;
+ isc__nm_http_pending_callbacks_t pending_write_callbacks;
+} isc_http_send_req_t;
+
+#define HTTP_ENDPOINTS_MAGIC ISC_MAGIC('H', 'T', 'E', 'P')
+#define VALID_HTTP_ENDPOINTS(t) ISC_MAGIC_VALID(t, HTTP_ENDPOINTS_MAGIC)
+
+static bool
+http_send_outgoing(isc_nm_http_session_t *session, isc_nmhandle_t *httphandle,
+ isc_nm_cb_t cb, void *cbarg);
+
+static void
+http_do_bio(isc_nm_http_session_t *session, isc_nmhandle_t *send_httphandle,
+ isc_nm_cb_t send_cb, void *send_cbarg);
+
+static void
+failed_httpstream_read_cb(isc_nmsocket_t *sock, isc_result_t result,
+ isc_nm_http_session_t *session);
+
+static void
+client_call_failed_read_cb(isc_result_t result, isc_nm_http_session_t *session);
+
+static void
+server_call_failed_read_cb(isc_result_t result, isc_nm_http_session_t *session);
+
+static void
+failed_read_cb(isc_result_t result, isc_nm_http_session_t *session);
+
+static isc_result_t
+server_send_error_response(const isc_http_error_responses_t error,
+ nghttp2_session *ngsession, isc_nmsocket_t *socket);
+
+static isc_result_t
+client_send(isc_nmhandle_t *handle, const isc_region_t *region);
+
+static void
+finish_http_session(isc_nm_http_session_t *session);
+
+static void
+http_transpost_tcp_nodelay(isc_nmhandle_t *transphandle);
+
+static void
+call_pending_callbacks(isc__nm_http_pending_callbacks_t pending_callbacks,
+ isc_result_t result);
+
+static void
+server_call_cb(isc_nmsocket_t *socket, isc_nm_http_session_t *session,
+ const isc_result_t result, isc_region_t *data);
+
+static isc_nm_httphandler_t *
+http_endpoints_find(const char *request_path,
+ const isc_nm_http_endpoints_t *restrict eps);
+
+static void
+http_init_listener_endpoints(isc_nmsocket_t *listener,
+ isc_nm_http_endpoints_t *epset);
+
+static void
+http_cleanup_listener_endpoints(isc_nmsocket_t *listener);
+
+static isc_nm_http_endpoints_t *
+http_get_listener_endpoints(isc_nmsocket_t *listener, const int tid);
+
+static bool
+http_session_active(isc_nm_http_session_t *session) {
+ REQUIRE(VALID_HTTP2_SESSION(session));
+ return (!session->closed && !session->closing);
+}
+
+static void *
+http_malloc(size_t sz, isc_mem_t *mctx) {
+ return (isc_mem_allocate(mctx, sz));
+}
+
+static void *
+http_calloc(size_t n, size_t sz, isc_mem_t *mctx) {
+ const size_t msize = n * sz;
+ void *data = isc_mem_allocate(mctx, msize);
+
+ memset(data, 0, msize);
+ return (data);
+}
+
+static void *
+http_realloc(void *p, size_t newsz, isc_mem_t *mctx) {
+ return (isc_mem_reallocate(mctx, p, newsz));
+}
+
+static void
+http_free(void *p, isc_mem_t *mctx) {
+ if (p == NULL) { /* as standard free() behaves */
+ return;
+ }
+ isc_mem_free(mctx, p);
+}
+
+static void
+init_nghttp2_mem(isc_mem_t *mctx, nghttp2_mem *mem) {
+ *mem = (nghttp2_mem){ .malloc = (nghttp2_malloc)http_malloc,
+ .calloc = (nghttp2_calloc)http_calloc,
+ .realloc = (nghttp2_realloc)http_realloc,
+ .free = (nghttp2_free)http_free,
+ .mem_user_data = mctx };
+}
+
+static void
+new_session(isc_mem_t *mctx, isc_tlsctx_t *tctx,
+ isc_nm_http_session_t **sessionp) {
+ isc_nm_http_session_t *session = NULL;
+
+ REQUIRE(sessionp != NULL && *sessionp == NULL);
+ REQUIRE(mctx != NULL);
+
+ session = isc_mem_get(mctx, sizeof(isc_nm_http_session_t));
+ *session = (isc_nm_http_session_t){ .magic = HTTP2_SESSION_MAGIC,
+ .tlsctx = tctx };
+ isc_refcount_init(&session->references, 1);
+ isc_mem_attach(mctx, &session->mctx);
+ ISC_LIST_INIT(session->cstreams);
+ ISC_LIST_INIT(session->sstreams);
+ ISC_LIST_INIT(session->pending_write_callbacks);
+
+ *sessionp = session;
+}
+
+void
+isc__nm_httpsession_attach(isc_nm_http_session_t *source,
+ isc_nm_http_session_t **targetp) {
+ REQUIRE(VALID_HTTP2_SESSION(source));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ isc_refcount_increment(&source->references);
+
+ *targetp = source;
+}
+
+void
+isc__nm_httpsession_detach(isc_nm_http_session_t **sessionp) {
+ isc_nm_http_session_t *session = NULL;
+
+ REQUIRE(sessionp != NULL);
+
+ session = *sessionp;
+ *sessionp = NULL;
+
+ REQUIRE(VALID_HTTP2_SESSION(session));
+
+ if (isc_refcount_decrement(&session->references) > 1) {
+ return;
+ }
+
+ finish_http_session(session);
+
+ INSIST(ISC_LIST_EMPTY(session->sstreams));
+ INSIST(ISC_LIST_EMPTY(session->cstreams));
+
+ if (session->ngsession != NULL) {
+ nghttp2_session_del(session->ngsession);
+ session->ngsession = NULL;
+ }
+
+ if (session->buf != NULL) {
+ isc_buffer_free(&session->buf);
+ }
+
+ /* We need an acquire memory barrier here */
+ (void)isc_refcount_current(&session->references);
+
+ session->magic = 0;
+ isc_mem_putanddetach(&session->mctx, session,
+ sizeof(isc_nm_http_session_t));
+}
+
+static http_cstream_t *
+find_http_cstream(int32_t stream_id, isc_nm_http_session_t *session) {
+ http_cstream_t *cstream = NULL;
+ REQUIRE(VALID_HTTP2_SESSION(session));
+
+ if (ISC_LIST_EMPTY(session->cstreams)) {
+ return (NULL);
+ }
+
+ for (cstream = ISC_LIST_HEAD(session->cstreams); cstream != NULL;
+ cstream = ISC_LIST_NEXT(cstream, link))
+ {
+ if (cstream->stream_id == stream_id) {
+ break;
+ }
+ }
+
+ /* LRU-like behaviour */
+ if (cstream && ISC_LIST_HEAD(session->cstreams) != cstream) {
+ ISC_LIST_UNLINK(session->cstreams, cstream, link);
+ ISC_LIST_PREPEND(session->cstreams, cstream, link);
+ }
+
+ return (cstream);
+}
+
+static isc_result_t
+new_http_cstream(isc_nmsocket_t *sock, http_cstream_t **streamp) {
+ isc_mem_t *mctx = sock->mgr->mctx;
+ const char *uri = NULL;
+ bool post;
+ http_cstream_t *stream = NULL;
+ isc_result_t result;
+
+ uri = sock->h2.session->handle->sock->h2.connect.uri;
+ post = sock->h2.session->handle->sock->h2.connect.post;
+
+ stream = isc_mem_get(mctx, sizeof(http_cstream_t));
+ *stream = (http_cstream_t){ .stream_id = -1,
+ .post = post,
+ .uri = isc_mem_strdup(mctx, uri) };
+ ISC_LINK_INIT(stream, link);
+
+ result = isc_url_parse(stream->uri, strlen(stream->uri), 0,
+ &stream->up);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_free(mctx, stream->uri);
+ isc_mem_put(mctx, stream, sizeof(http_cstream_t));
+ return (result);
+ }
+
+ isc__nmsocket_attach(sock, &stream->httpsock);
+ stream->authoritylen = stream->up.field_data[ISC_UF_HOST].len;
+ stream->authority = isc_mem_get(mctx, stream->authoritylen + AUTHEXTRA);
+ memmove(stream->authority, &uri[stream->up.field_data[ISC_UF_HOST].off],
+ stream->up.field_data[ISC_UF_HOST].len);
+
+ if (stream->up.field_set & (1 << ISC_UF_PORT)) {
+ stream->authoritylen += (size_t)snprintf(
+ stream->authority +
+ stream->up.field_data[ISC_UF_HOST].len,
+ AUTHEXTRA, ":%u", stream->up.port);
+ }
+
+ /* If we don't have path in URI, we use "/" as path. */
+ stream->pathlen = 1;
+ if (stream->up.field_set & (1 << ISC_UF_PATH)) {
+ stream->pathlen = stream->up.field_data[ISC_UF_PATH].len;
+ }
+ if (stream->up.field_set & (1 << ISC_UF_QUERY)) {
+ /* +1 for '?' character */
+ stream->pathlen +=
+ (size_t)(stream->up.field_data[ISC_UF_QUERY].len + 1);
+ }
+
+ stream->path = isc_mem_get(mctx, stream->pathlen);
+ if (stream->up.field_set & (1 << ISC_UF_PATH)) {
+ memmove(stream->path,
+ &uri[stream->up.field_data[ISC_UF_PATH].off],
+ stream->up.field_data[ISC_UF_PATH].len);
+ } else {
+ stream->path[0] = '/';
+ }
+
+ if (stream->up.field_set & (1 << ISC_UF_QUERY)) {
+ stream->path[stream->pathlen -
+ stream->up.field_data[ISC_UF_QUERY].len - 1] = '?';
+ memmove(stream->path + stream->pathlen -
+ stream->up.field_data[ISC_UF_QUERY].len,
+ &uri[stream->up.field_data[ISC_UF_QUERY].off],
+ stream->up.field_data[ISC_UF_QUERY].len);
+ }
+
+ isc_buffer_allocate(mctx, &stream->rbuf,
+ INITIAL_DNS_MESSAGE_BUFFER_SIZE);
+ isc_buffer_setautorealloc(stream->rbuf, true);
+
+ ISC_LIST_PREPEND(sock->h2.session->cstreams, stream, link);
+ *streamp = stream;
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+put_http_cstream(isc_mem_t *mctx, http_cstream_t *stream) {
+ isc_mem_put(mctx, stream->path, stream->pathlen);
+ isc_mem_put(mctx, stream->authority,
+ stream->up.field_data[ISC_UF_HOST].len + AUTHEXTRA);
+ isc_mem_free(mctx, stream->uri);
+ if (stream->GET_path != NULL) {
+ isc_mem_free(mctx, stream->GET_path);
+ stream->GET_path = NULL;
+ stream->GET_path_len = 0;
+ }
+
+ if (stream->postdata != NULL) {
+ INSIST(stream->post);
+ isc_buffer_free(&stream->postdata);
+ }
+
+ if (stream == stream->httpsock->h2.connect.cstream) {
+ stream->httpsock->h2.connect.cstream = NULL;
+ }
+ if (ISC_LINK_LINKED(stream, link)) {
+ ISC_LIST_UNLINK(stream->httpsock->h2.session->cstreams, stream,
+ link);
+ }
+ isc__nmsocket_detach(&stream->httpsock);
+
+ isc_buffer_free(&stream->rbuf);
+ isc_mem_put(mctx, stream, sizeof(http_cstream_t));
+}
+
+static void
+finish_http_session(isc_nm_http_session_t *session) {
+ if (session->closed) {
+ return;
+ }
+
+ if (session->handle != NULL) {
+ if (!session->closed) {
+ session->closed = true;
+ isc_nm_cancelread(session->handle);
+ }
+
+ if (session->client) {
+ client_call_failed_read_cb(ISC_R_UNEXPECTED, session);
+ } else {
+ server_call_failed_read_cb(ISC_R_UNEXPECTED, session);
+ }
+
+ call_pending_callbacks(session->pending_write_callbacks,
+ ISC_R_UNEXPECTED);
+ ISC_LIST_INIT(session->pending_write_callbacks);
+
+ if (session->pending_write_data != NULL) {
+ isc_buffer_free(&session->pending_write_data);
+ }
+
+ isc_nmhandle_detach(&session->handle);
+ }
+
+ if (session->client_httphandle != NULL) {
+ isc_nmhandle_detach(&session->client_httphandle);
+ }
+
+ INSIST(ISC_LIST_EMPTY(session->cstreams));
+
+ /* detach from server socket */
+ if (session->serversocket != NULL) {
+ isc__nmsocket_detach(&session->serversocket);
+ }
+ session->closed = true;
+}
+
+static int
+on_client_data_chunk_recv_callback(int32_t stream_id, const uint8_t *data,
+ size_t len, isc_nm_http_session_t *session) {
+ http_cstream_t *cstream = find_http_cstream(stream_id, session);
+
+ if (cstream != NULL) {
+ size_t new_rbufsize = len;
+ INSIST(cstream->rbuf != NULL);
+ new_rbufsize += isc_buffer_usedlength(cstream->rbuf);
+ if (new_rbufsize <= MAX_DNS_MESSAGE_SIZE &&
+ new_rbufsize <= cstream->response_status.content_length)
+ {
+ isc_buffer_putmem(cstream->rbuf, data, len);
+ } else {
+ return (NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE);
+ }
+ } else {
+ return (NGHTTP2_ERR_CALLBACK_FAILURE);
+ }
+
+ return (0);
+}
+
+static int
+on_server_data_chunk_recv_callback(int32_t stream_id, const uint8_t *data,
+ size_t len, isc_nm_http_session_t *session) {
+ isc_nmsocket_h2_t *h2 = ISC_LIST_HEAD(session->sstreams);
+ while (h2 != NULL) {
+ if (stream_id == h2->stream_id) {
+ if (isc_buffer_base(&h2->rbuf) == NULL) {
+ isc_buffer_init(
+ &h2->rbuf,
+ isc_mem_allocate(session->mctx,
+ h2->content_length),
+ MAX_DNS_MESSAGE_SIZE);
+ }
+ size_t new_bufsize = isc_buffer_usedlength(&h2->rbuf) +
+ len;
+ if (new_bufsize <= MAX_DNS_MESSAGE_SIZE &&
+ new_bufsize <= h2->content_length)
+ {
+ isc_buffer_putmem(&h2->rbuf, data, len);
+ break;
+ }
+
+ return (NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE);
+ }
+ h2 = ISC_LIST_NEXT(h2, link);
+ }
+ if (h2 == NULL) {
+ return (NGHTTP2_ERR_CALLBACK_FAILURE);
+ }
+
+ return (0);
+}
+
+static int
+on_data_chunk_recv_callback(nghttp2_session *ngsession, uint8_t flags,
+ int32_t stream_id, const uint8_t *data, size_t len,
+ void *user_data) {
+ isc_nm_http_session_t *session = (isc_nm_http_session_t *)user_data;
+ int rv;
+
+ UNUSED(ngsession);
+ UNUSED(flags);
+
+ if (session->client) {
+ rv = on_client_data_chunk_recv_callback(stream_id, data, len,
+ session);
+ } else {
+ rv = on_server_data_chunk_recv_callback(stream_id, data, len,
+ session);
+ }
+
+ return (rv);
+}
+
+static void
+call_unlink_cstream_readcb(http_cstream_t *cstream,
+ isc_nm_http_session_t *session,
+ isc_result_t result) {
+ isc_region_t read_data;
+ REQUIRE(VALID_HTTP2_SESSION(session));
+ REQUIRE(cstream != NULL);
+ ISC_LIST_UNLINK(session->cstreams, cstream, link);
+ INSIST(VALID_NMHANDLE(session->client_httphandle));
+ isc_buffer_usedregion(cstream->rbuf, &read_data);
+ cstream->read_cb(session->client_httphandle, result, &read_data,
+ cstream->read_cbarg);
+ put_http_cstream(session->mctx, cstream);
+}
+
+static int
+on_client_stream_close_callback(int32_t stream_id,
+ isc_nm_http_session_t *session) {
+ http_cstream_t *cstream = find_http_cstream(stream_id, session);
+
+ if (cstream != NULL) {
+ isc_result_t result =
+ SUCCESSFUL_HTTP_STATUS(cstream->response_status.code)
+ ? ISC_R_SUCCESS
+ : ISC_R_FAILURE;
+ call_unlink_cstream_readcb(cstream, session, result);
+ if (ISC_LIST_EMPTY(session->cstreams)) {
+ int rv = 0;
+ rv = nghttp2_session_terminate_session(
+ session->ngsession, NGHTTP2_NO_ERROR);
+ if (rv != 0) {
+ return (rv);
+ }
+ /* Mark the session as closing one to finish it on a
+ * subsequent call to http_do_bio() */
+ session->closing = true;
+ }
+ } else {
+ return (NGHTTP2_ERR_CALLBACK_FAILURE);
+ }
+
+ return (0);
+}
+
+static int
+on_server_stream_close_callback(int32_t stream_id,
+ isc_nm_http_session_t *session) {
+ isc_nmsocket_t *sock = nghttp2_session_get_stream_user_data(
+ session->ngsession, stream_id);
+ int rv = 0;
+
+ ISC_LIST_UNLINK(session->sstreams, &sock->h2, link);
+ session->nsstreams--;
+
+ /*
+ * By making a call to isc__nmsocket_prep_destroy(), we ensure that
+ * the socket gets marked as inactive, allowing the HTTP/2 data
+ * associated with it to be properly disposed of eventually.
+ *
+ * An HTTP/2 stream socket will normally be marked as inactive in
+ * the normal course of operation. However, when browsers terminate
+ * HTTP/2 streams prematurely (e.g. by sending RST_STREAM),
+ * corresponding sockets can remain marked as active, retaining
+ * references to the HTTP/2 data (most notably the session objects),
+ * preventing them from being correctly freed and leading to BIND
+ * hanging on shutdown. Calling isc__nmsocket_prep_destroy()
+ * ensures that this will not happen.
+ */
+ isc__nmsocket_prep_destroy(sock);
+ isc__nmsocket_detach(&sock);
+ return (rv);
+}
+
+static int
+on_stream_close_callback(nghttp2_session *ngsession, int32_t stream_id,
+ uint32_t error_code, void *user_data) {
+ isc_nm_http_session_t *session = (isc_nm_http_session_t *)user_data;
+ int rv = 0;
+
+ REQUIRE(VALID_HTTP2_SESSION(session));
+ REQUIRE(session->ngsession == ngsession);
+
+ UNUSED(error_code);
+
+ if (session->client) {
+ rv = on_client_stream_close_callback(stream_id, session);
+ } else {
+ rv = on_server_stream_close_callback(stream_id, session);
+ }
+
+ return (rv);
+}
+
+static bool
+client_handle_status_header(http_cstream_t *cstream, const uint8_t *value,
+ const size_t valuelen) {
+ char tmp[32] = { 0 };
+ const size_t tmplen = sizeof(tmp) - 1;
+
+ strncpy(tmp, (const char *)value, ISC_MIN(tmplen, valuelen));
+ cstream->response_status.code = strtoul(tmp, NULL, 10);
+
+ if (SUCCESSFUL_HTTP_STATUS(cstream->response_status.code)) {
+ return (true);
+ }
+
+ return (false);
+}
+
+static bool
+client_handle_content_length_header(http_cstream_t *cstream,
+ const uint8_t *value,
+ const size_t valuelen) {
+ char tmp[32] = { 0 };
+ const size_t tmplen = sizeof(tmp) - 1;
+
+ strncpy(tmp, (const char *)value, ISC_MIN(tmplen, valuelen));
+ cstream->response_status.content_length = strtoul(tmp, NULL, 10);
+
+ if (cstream->response_status.content_length == 0 ||
+ cstream->response_status.content_length > MAX_DNS_MESSAGE_SIZE)
+ {
+ return (false);
+ }
+
+ return (true);
+}
+
+static bool
+client_handle_content_type_header(http_cstream_t *cstream, const uint8_t *value,
+ const size_t valuelen) {
+ const char type_dns_message[] = DNS_MEDIA_TYPE;
+ const size_t len = sizeof(type_dns_message) - 1;
+
+ UNUSED(valuelen);
+
+ if (strncasecmp((const char *)value, type_dns_message, len) == 0) {
+ cstream->response_status.content_type_valid = true;
+ return (true);
+ }
+
+ return (false);
+}
+
+static int
+client_on_header_callback(nghttp2_session *ngsession,
+ const nghttp2_frame *frame, const uint8_t *name,
+ size_t namelen, const uint8_t *value, size_t valuelen,
+ uint8_t flags, void *user_data) {
+ isc_nm_http_session_t *session = (isc_nm_http_session_t *)user_data;
+ http_cstream_t *cstream = NULL;
+ const char status[] = ":status";
+ const char content_length[] = "Content-Length";
+ const char content_type[] = "Content-Type";
+ bool header_ok = true;
+
+ REQUIRE(VALID_HTTP2_SESSION(session));
+ REQUIRE(session->client);
+
+ UNUSED(flags);
+ UNUSED(ngsession);
+
+ cstream = find_http_cstream(frame->hd.stream_id, session);
+ if (cstream == NULL) {
+ /*
+ * This could happen in two cases:
+ * - the server sent us bad data, or
+ * - we closed the session prematurely before receiving all
+ * responses (i.e., because of a belated or partial response).
+ */
+ return (NGHTTP2_ERR_CALLBACK_FAILURE);
+ }
+
+ INSIST(!ISC_LIST_EMPTY(session->cstreams));
+
+ switch (frame->hd.type) {
+ case NGHTTP2_HEADERS:
+ if (frame->headers.cat != NGHTTP2_HCAT_RESPONSE) {
+ break;
+ }
+
+ if (HEADER_MATCH(status, name, namelen)) {
+ header_ok = client_handle_status_header(cstream, value,
+ valuelen);
+ } else if (HEADER_MATCH(content_length, name, namelen)) {
+ header_ok = client_handle_content_length_header(
+ cstream, value, valuelen);
+ } else if (HEADER_MATCH(content_type, name, namelen)) {
+ header_ok = client_handle_content_type_header(
+ cstream, value, valuelen);
+ }
+ break;
+ }
+
+ if (!header_ok) {
+ return (NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE);
+ }
+
+ return (0);
+}
+
+static void
+initialize_nghttp2_client_session(isc_nm_http_session_t *session) {
+ nghttp2_session_callbacks *callbacks = NULL;
+ nghttp2_option *option = NULL;
+ nghttp2_mem mem;
+
+ init_nghttp2_mem(session->mctx, &mem);
+ RUNTIME_CHECK(nghttp2_session_callbacks_new(&callbacks) == 0);
+ RUNTIME_CHECK(nghttp2_option_new(&option) == 0);
+
+#if NGHTTP2_VERSION_NUM >= (0x010c00)
+ nghttp2_option_set_max_send_header_block_length(
+ option, MAX_ALLOWED_DATA_IN_HEADERS);
+#endif
+
+ nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
+ callbacks, on_data_chunk_recv_callback);
+
+ nghttp2_session_callbacks_set_on_stream_close_callback(
+ callbacks, on_stream_close_callback);
+
+ nghttp2_session_callbacks_set_on_header_callback(
+ callbacks, client_on_header_callback);
+
+ RUNTIME_CHECK(nghttp2_session_client_new3(&session->ngsession,
+ callbacks, session, option,
+ &mem) == 0);
+
+ nghttp2_option_del(option);
+ nghttp2_session_callbacks_del(callbacks);
+}
+
+static bool
+send_client_connection_header(isc_nm_http_session_t *session) {
+ nghttp2_settings_entry iv[] = { { NGHTTP2_SETTINGS_ENABLE_PUSH, 0 } };
+ int rv;
+
+ rv = nghttp2_submit_settings(session->ngsession, NGHTTP2_FLAG_NONE, iv,
+ sizeof(iv) / sizeof(iv[0]));
+ if (rv != 0) {
+ return (false);
+ }
+
+ return (true);
+}
+
+#define MAKE_NV(NAME, VALUE, VALUELEN) \
+ { \
+ (uint8_t *)(uintptr_t)(NAME), (uint8_t *)(uintptr_t)(VALUE), \
+ sizeof(NAME) - 1, VALUELEN, NGHTTP2_NV_FLAG_NONE \
+ }
+
+#define MAKE_NV2(NAME, VALUE) \
+ { \
+ (uint8_t *)(uintptr_t)(NAME), (uint8_t *)(uintptr_t)(VALUE), \
+ sizeof(NAME) - 1, sizeof(VALUE) - 1, \
+ NGHTTP2_NV_FLAG_NONE \
+ }
+
+static ssize_t
+client_read_callback(nghttp2_session *ngsession, int32_t stream_id,
+ uint8_t *buf, size_t length, uint32_t *data_flags,
+ nghttp2_data_source *source, void *user_data) {
+ isc_nm_http_session_t *session = (isc_nm_http_session_t *)user_data;
+ http_cstream_t *cstream = NULL;
+
+ REQUIRE(session->client);
+ REQUIRE(!ISC_LIST_EMPTY(session->cstreams));
+
+ UNUSED(ngsession);
+ UNUSED(source);
+
+ cstream = find_http_cstream(stream_id, session);
+ if (!cstream || cstream->stream_id != stream_id) {
+ /* We haven't found the stream, so we are not reading */
+ return (NGHTTP2_ERR_CALLBACK_FAILURE);
+ }
+
+ if (cstream->post) {
+ size_t len = isc_buffer_remaininglength(cstream->postdata);
+
+ if (len > length) {
+ len = length;
+ }
+
+ if (len > 0) {
+ memmove(buf, isc_buffer_current(cstream->postdata),
+ len);
+ isc_buffer_forward(cstream->postdata, len);
+ }
+
+ if (isc_buffer_remaininglength(cstream->postdata) == 0) {
+ *data_flags |= NGHTTP2_DATA_FLAG_EOF;
+ }
+
+ return (len);
+ } else {
+ *data_flags |= NGHTTP2_DATA_FLAG_EOF;
+ return (0);
+ }
+
+ return (0);
+}
+
+/*
+ * Send HTTP request to the remote peer.
+ */
+static isc_result_t
+client_submit_request(isc_nm_http_session_t *session, http_cstream_t *stream) {
+ int32_t stream_id;
+ char *uri = stream->uri;
+ isc_url_parser_t *up = &stream->up;
+ nghttp2_data_provider dp;
+
+ if (stream->post) {
+ char p[64];
+ snprintf(p, sizeof(p), "%u",
+ isc_buffer_usedlength(stream->postdata));
+ nghttp2_nv hdrs[] = {
+ MAKE_NV2(":method", "POST"),
+ MAKE_NV(":scheme",
+ &uri[up->field_data[ISC_UF_SCHEMA].off],
+ up->field_data[ISC_UF_SCHEMA].len),
+ MAKE_NV(":authority", stream->authority,
+ stream->authoritylen),
+ MAKE_NV(":path", stream->path, stream->pathlen),
+ MAKE_NV2("content-type", DNS_MEDIA_TYPE),
+ MAKE_NV2("accept", DNS_MEDIA_TYPE),
+ MAKE_NV("content-length", p, strlen(p)),
+ MAKE_NV2("cache-control", DEFAULT_CACHE_CONTROL)
+ };
+
+ dp = (nghttp2_data_provider){ .read_callback =
+ client_read_callback };
+ stream_id = nghttp2_submit_request(
+ session->ngsession, NULL, hdrs,
+ sizeof(hdrs) / sizeof(hdrs[0]), &dp, stream);
+ } else {
+ INSIST(stream->GET_path != NULL);
+ INSIST(stream->GET_path_len != 0);
+ nghttp2_nv hdrs[] = {
+ MAKE_NV2(":method", "GET"),
+ MAKE_NV(":scheme",
+ &uri[up->field_data[ISC_UF_SCHEMA].off],
+ up->field_data[ISC_UF_SCHEMA].len),
+ MAKE_NV(":authority", stream->authority,
+ stream->authoritylen),
+ MAKE_NV(":path", stream->GET_path,
+ stream->GET_path_len),
+ MAKE_NV2("accept", DNS_MEDIA_TYPE),
+ MAKE_NV2("cache-control", DEFAULT_CACHE_CONTROL)
+ };
+
+ dp = (nghttp2_data_provider){ .read_callback =
+ client_read_callback };
+ stream_id = nghttp2_submit_request(
+ session->ngsession, NULL, hdrs,
+ sizeof(hdrs) / sizeof(hdrs[0]), &dp, stream);
+ }
+ if (stream_id < 0) {
+ return (ISC_R_FAILURE);
+ }
+
+ stream->stream_id = stream_id;
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Read callback from TLS socket.
+ */
+static void
+http_readcb(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *region,
+ void *data) {
+ isc_nm_http_session_t *session = (isc_nm_http_session_t *)data;
+ ssize_t readlen;
+
+ REQUIRE(VALID_HTTP2_SESSION(session));
+
+ UNUSED(handle);
+
+ if (result != ISC_R_SUCCESS) {
+ if (result != ISC_R_TIMEDOUT) {
+ session->reading = false;
+ }
+ failed_read_cb(result, session);
+ return;
+ }
+
+ readlen = nghttp2_session_mem_recv(session->ngsession, region->base,
+ region->length);
+ if (readlen < 0) {
+ failed_read_cb(ISC_R_UNEXPECTED, session);
+ return;
+ }
+
+ if ((size_t)readlen < region->length) {
+ size_t unread_size = region->length - readlen;
+ if (session->buf == NULL) {
+ isc_buffer_allocate(session->mctx, &session->buf,
+ unread_size);
+ isc_buffer_setautorealloc(session->buf, true);
+ }
+ isc_buffer_putmem(session->buf, region->base + readlen,
+ unread_size);
+ isc_nm_pauseread(session->handle);
+ }
+
+ /* We might have something to receive or send, do IO */
+ http_do_bio(session, NULL, NULL, NULL);
+}
+
+static void
+call_pending_callbacks(isc__nm_http_pending_callbacks_t pending_callbacks,
+ isc_result_t result) {
+ isc__nm_uvreq_t *cbreq = ISC_LIST_HEAD(pending_callbacks);
+ while (cbreq != NULL) {
+ isc__nm_uvreq_t *next = ISC_LIST_NEXT(cbreq, link);
+ ISC_LIST_UNLINK(pending_callbacks, cbreq, link);
+ isc__nm_sendcb(cbreq->handle->sock, cbreq, result, false);
+ cbreq = next;
+ }
+}
+
+static void
+http_writecb(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
+ isc_http_send_req_t *req = (isc_http_send_req_t *)arg;
+ isc_nm_http_session_t *session = req->session;
+ isc_nmhandle_t *transphandle = req->transphandle;
+
+ REQUIRE(VALID_HTTP2_SESSION(session));
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ if (http_session_active(session)) {
+ INSIST(session->handle == handle);
+ }
+
+ call_pending_callbacks(req->pending_write_callbacks, result);
+
+ if (req->cb != NULL) {
+ req->cb(req->httphandle, result, req->cbarg);
+ isc_nmhandle_detach(&req->httphandle);
+ }
+
+ isc_buffer_free(&req->pending_write_data);
+ isc_mem_put(session->mctx, req, sizeof(*req));
+
+ session->sending--;
+ http_do_bio(session, NULL, NULL, NULL);
+ isc_nmhandle_detach(&transphandle);
+ if (result != ISC_R_SUCCESS && session->sending == 0) {
+ finish_http_session(session);
+ }
+ isc__nm_httpsession_detach(&session);
+}
+
+static void
+move_pending_send_callbacks(isc_nm_http_session_t *session,
+ isc_http_send_req_t *send) {
+ STATIC_ASSERT(
+ sizeof(session->pending_write_callbacks) ==
+ sizeof(send->pending_write_callbacks),
+ "size of pending writes requests callbacks lists differs");
+ memmove(&send->pending_write_callbacks,
+ &session->pending_write_callbacks,
+ sizeof(session->pending_write_callbacks));
+ ISC_LIST_INIT(session->pending_write_callbacks);
+}
+
+static bool
+http_send_outgoing(isc_nm_http_session_t *session, isc_nmhandle_t *httphandle,
+ isc_nm_cb_t cb, void *cbarg) {
+ isc_http_send_req_t *send = NULL;
+ size_t total = 0;
+ isc_region_t send_data = { 0 };
+ isc_nmhandle_t *transphandle = NULL;
+#ifdef ENABLE_HTTP_WRITE_BUFFERING
+ size_t max_total_write_size = 0;
+#endif /* ENABLE_HTTP_WRITE_BUFFERING */
+
+ if (!http_session_active(session) ||
+ (!nghttp2_session_want_write(session->ngsession) &&
+ session->pending_write_data == NULL))
+ {
+ return (false);
+ }
+
+ /* We need to attach to the session->handle earlier because as an
+ * indirect result of the nghttp2_session_mem_send() the session
+ * might get closed and the handle detached. However, there is
+ * still some outgoing data to handle and we need to call it
+ * anyway if only to get the write callback passed here to get
+ * called properly. */
+ isc_nmhandle_attach(session->handle, &transphandle);
+
+ while (nghttp2_session_want_write(session->ngsession)) {
+ const uint8_t *data = NULL;
+ const size_t pending =
+ nghttp2_session_mem_send(session->ngsession, &data);
+ const size_t new_total = total + pending;
+
+ /* Sometimes nghttp2_session_mem_send() does not return any
+ * data to send even though nghttp2_session_want_write()
+ * returns success. */
+ if (pending == 0 || data == NULL) {
+ break;
+ }
+
+ /* reallocate buffer if required */
+ if (session->pending_write_data == NULL) {
+ isc_buffer_allocate(session->mctx,
+ &session->pending_write_data,
+ INITIAL_DNS_MESSAGE_BUFFER_SIZE);
+ isc_buffer_setautorealloc(session->pending_write_data,
+ true);
+ }
+ isc_buffer_putmem(session->pending_write_data, data, pending);
+ total = new_total;
+ }
+
+#ifdef ENABLE_HTTP_WRITE_BUFFERING
+ if (session->pending_write_data != NULL) {
+ max_total_write_size =
+ isc_buffer_usedlength(session->pending_write_data);
+ }
+
+ /* Here we are trying to flush the pending writes buffer earlier
+ * to avoid hitting unnecessary limitations on a TLS record size
+ * within some tools (e.g. flamethrower). */
+ if (max_total_write_size >= FLUSH_HTTP_WRITE_BUFFER_AFTER) {
+ /* Case 1: We have equal or more than
+ * FLUSH_HTTP_WRITE_BUFFER_AFTER bytes to send. Let's flush it.
+ */
+ total = max_total_write_size;
+ } else if (session->sending > 0 && total > 0) {
+ /* Case 2: There is one or more write requests in flight and
+ * we have some new data form nghttp2 to send. Let's put the
+ * write callback (if any) into the pending write callbacks
+ * list. Then let's return from the function: as soon as the
+ * "in-flight" write callback get's called or we have reached
+ * FLUSH_HTTP_WRITE_BUFFER_AFTER bytes in the write buffer, we
+ * will flush the buffer. */
+ if (cb != NULL) {
+ isc__nm_uvreq_t *newcb = isc__nm_uvreq_get(
+ httphandle->sock->mgr, httphandle->sock);
+
+ INSIST(VALID_NMHANDLE(httphandle));
+ newcb->cb.send = cb;
+ newcb->cbarg = cbarg;
+ isc_nmhandle_attach(httphandle, &newcb->handle);
+ ISC_LIST_APPEND(session->pending_write_callbacks, newcb,
+ link);
+ }
+ goto nothing_to_send;
+ } else if (session->sending == 0 && total == 0 &&
+ session->pending_write_data != NULL)
+ {
+ /* Case 3: There is no write in flight and we haven't got
+ * anything new from nghttp2, but there is some data pending
+ * in the write buffer. Let's flush the buffer. */
+ isc_region_t region = { 0 };
+ total = isc_buffer_usedlength(session->pending_write_data);
+ INSIST(total > 0);
+ isc_buffer_usedregion(session->pending_write_data, &region);
+ INSIST(total == region.length);
+ } else {
+ /* The other cases are, uninteresting, fall-through ones. */
+ /* In the following cases (4-6) we will just bail out. */
+ /* Case 4: There is nothing new to send, nor anything in the
+ * write buffer. */
+ /* Case 5: There is nothing new to send and there is write
+ * request(s) in flight. */
+ /* Case 6: There is nothing new to send nor there are any
+ * write requests in flight. */
+
+ /* Case 7: There is some new data to send and there are no any
+ * write requests in flight: Let's send the data.*/
+ INSIST((total == 0 && session->pending_write_data == NULL) ||
+ (total == 0 && session->sending > 0) ||
+ (total == 0 && session->sending == 0) ||
+ (total > 0 && session->sending == 0));
+ }
+#else
+ INSIST(ISC_LIST_EMPTY(session->pending_write_callbacks));
+#endif /* ENABLE_HTTP_WRITE_BUFFERING */
+
+ if (total == 0) {
+ /* No data returned */
+ goto nothing_to_send;
+ }
+
+ /* If we have reached the point it means that we need to send some
+ * data and flush the outgoing buffer. The code below does that. */
+ send = isc_mem_get(session->mctx, sizeof(*send));
+
+ *send = (isc_http_send_req_t){ .pending_write_data =
+ session->pending_write_data,
+ .cb = cb,
+ .cbarg = cbarg };
+ session->pending_write_data = NULL;
+ move_pending_send_callbacks(session, send);
+
+ send->transphandle = transphandle;
+ isc__nm_httpsession_attach(session, &send->session);
+
+ if (cb != NULL) {
+ INSIST(VALID_NMHANDLE(httphandle));
+ isc_nmhandle_attach(httphandle, &send->httphandle);
+ }
+
+ session->sending++;
+ isc_buffer_usedregion(send->pending_write_data, &send_data);
+ isc_nm_send(transphandle, &send_data, http_writecb, send);
+ return (true);
+nothing_to_send:
+ isc_nmhandle_detach(&transphandle);
+ return (false);
+}
+
+static void
+http_do_bio(isc_nm_http_session_t *session, isc_nmhandle_t *send_httphandle,
+ isc_nm_cb_t send_cb, void *send_cbarg) {
+ REQUIRE(VALID_HTTP2_SESSION(session));
+
+ if (session->closed) {
+ return;
+ } else if (session->closing) {
+ /*
+ * There might be leftover callbacks waiting to be received
+ */
+ if (session->sending == 0) {
+ finish_http_session(session);
+ }
+ return;
+ } else if (nghttp2_session_want_read(session->ngsession) == 0 &&
+ nghttp2_session_want_write(session->ngsession) == 0 &&
+ session->pending_write_data == NULL)
+ {
+ session->closing = true;
+ return;
+ }
+
+ if (nghttp2_session_want_read(session->ngsession) != 0) {
+ if (!session->reading) {
+ /* We have not yet started reading from this handle */
+ isc_nm_read(session->handle, http_readcb, session);
+ session->reading = true;
+ } else if (session->buf != NULL) {
+ size_t remaining =
+ isc_buffer_remaininglength(session->buf);
+ /* Leftover data in the buffer, use it */
+ size_t readlen = nghttp2_session_mem_recv(
+ session->ngsession,
+ isc_buffer_current(session->buf), remaining);
+
+ if (readlen == remaining) {
+ isc_buffer_free(&session->buf);
+ } else {
+ isc_buffer_forward(session->buf, readlen);
+ }
+
+ http_do_bio(session, send_httphandle, send_cb,
+ send_cbarg);
+ return;
+ } else {
+ /* Resume reading, it's idempotent, wait for more */
+ isc_nm_resumeread(session->handle);
+ }
+ } else {
+ /* We don't want more data, stop reading for now */
+ isc_nm_pauseread(session->handle);
+ }
+
+ if (send_cb != NULL) {
+ INSIST(VALID_NMHANDLE(send_httphandle));
+ (void)http_send_outgoing(session, send_httphandle, send_cb,
+ send_cbarg);
+ } else {
+ INSIST(send_httphandle == NULL);
+ INSIST(send_cb == NULL);
+ INSIST(send_cbarg == NULL);
+ (void)http_send_outgoing(session, NULL, NULL, NULL);
+ }
+
+ return;
+}
+
+static isc_result_t
+get_http_cstream(isc_nmsocket_t *sock, http_cstream_t **streamp) {
+ http_cstream_t *cstream = sock->h2.connect.cstream;
+ isc_result_t result;
+
+ REQUIRE(streamp != NULL && *streamp == NULL);
+
+ sock->h2.connect.cstream = NULL;
+
+ if (cstream == NULL) {
+ result = new_http_cstream(sock, &cstream);
+ if (result != ISC_R_SUCCESS) {
+ INSIST(cstream == NULL);
+ return (result);
+ }
+ }
+
+ *streamp = cstream;
+ return (ISC_R_SUCCESS);
+}
+
+static void
+http_call_connect_cb(isc_nmsocket_t *sock, isc_nm_http_session_t *session,
+ isc_result_t result) {
+ isc__nm_uvreq_t *req = NULL;
+ isc_nmhandle_t *httphandle = isc__nmhandle_get(sock, &sock->peer,
+ &sock->iface);
+
+ REQUIRE(sock->connect_cb != NULL);
+
+ if (result == ISC_R_SUCCESS) {
+ req = isc__nm_uvreq_get(sock->mgr, sock);
+ req->cb.connect = sock->connect_cb;
+ req->cbarg = sock->connect_cbarg;
+ if (session != NULL) {
+ session->client_httphandle = httphandle;
+ req->handle = NULL;
+ isc_nmhandle_attach(httphandle, &req->handle);
+ } else {
+ req->handle = httphandle;
+ }
+
+ isc__nmsocket_clearcb(sock);
+ isc__nm_connectcb(sock, req, result, true);
+ } else {
+ void *cbarg = sock->connect_cbarg;
+ isc_nm_cb_t connect_cb = sock->connect_cb;
+
+ isc__nmsocket_clearcb(sock);
+ connect_cb(httphandle, result, cbarg);
+ isc_nmhandle_detach(&httphandle);
+ }
+}
+
+static void
+transport_connect_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
+ isc_nmsocket_t *http_sock = (isc_nmsocket_t *)cbarg;
+ isc_nmsocket_t *transp_sock = NULL;
+ isc_nm_http_session_t *session = NULL;
+ http_cstream_t *cstream = NULL;
+ isc_mem_t *mctx = NULL;
+
+ REQUIRE(VALID_NMSOCK(http_sock));
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ transp_sock = handle->sock;
+
+ REQUIRE(VALID_NMSOCK(transp_sock));
+
+ mctx = transp_sock->mgr->mctx;
+
+ INSIST(http_sock->h2.connect.uri != NULL);
+
+ http_sock->tid = transp_sock->tid;
+ http_sock->h2.connect.tls_peer_verify_string =
+ isc_nm_verify_tls_peer_result_string(handle);
+ if (result != ISC_R_SUCCESS) {
+ goto error;
+ }
+
+ new_session(mctx, http_sock->h2.connect.tlsctx, &session);
+ session->client = true;
+ transp_sock->h2.session = session;
+ http_sock->h2.connect.tlsctx = NULL;
+ /* otherwise we will get some garbage output in DIG */
+ http_sock->iface = handle->sock->iface;
+ http_sock->peer = handle->sock->peer;
+
+ transp_sock->h2.connect.post = http_sock->h2.connect.post;
+ transp_sock->h2.connect.uri = http_sock->h2.connect.uri;
+ http_sock->h2.connect.uri = NULL;
+ isc__nm_httpsession_attach(session, &http_sock->h2.session);
+
+ if (session->tlsctx != NULL) {
+ const unsigned char *alpn = NULL;
+ unsigned int alpnlen = 0;
+
+ INSIST(transp_sock->type == isc_nm_tlssocket);
+
+ isc_tls_get_selected_alpn(transp_sock->tlsstream.tls, &alpn,
+ &alpnlen);
+ if (alpn == NULL || alpnlen != NGHTTP2_PROTO_VERSION_ID_LEN ||
+ memcmp(NGHTTP2_PROTO_VERSION_ID, alpn,
+ NGHTTP2_PROTO_VERSION_ID_LEN) != 0)
+ {
+ /*
+ * HTTP/2 negotiation error. Any sensible DoH
+ * client will fail if HTTP/2 cannot be
+ * negotiated via ALPN.
+ */
+ result = ISC_R_HTTP2ALPNERROR;
+ goto error;
+ }
+ }
+
+ isc_nmhandle_attach(handle, &session->handle);
+
+ initialize_nghttp2_client_session(session);
+ if (!send_client_connection_header(session)) {
+ goto error;
+ }
+
+ result = get_http_cstream(http_sock, &cstream);
+ http_sock->h2.connect.cstream = cstream;
+ if (result != ISC_R_SUCCESS) {
+ goto error;
+ }
+
+ http_transpost_tcp_nodelay(handle);
+
+ http_call_connect_cb(http_sock, session, result);
+
+ http_do_bio(session, NULL, NULL, NULL);
+ isc__nmsocket_detach(&http_sock);
+ return;
+
+error:
+ http_call_connect_cb(http_sock, session, result);
+
+ if (http_sock->h2.connect.uri != NULL) {
+ isc_mem_free(mctx, http_sock->h2.connect.uri);
+ }
+
+ isc__nmsocket_prep_destroy(http_sock);
+ isc__nmsocket_detach(&http_sock);
+}
+
+void
+isc_nm_httpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
+ const char *uri, bool post, isc_nm_cb_t cb, void *cbarg,
+ isc_tlsctx_t *tlsctx,
+ isc_tlsctx_client_session_cache_t *client_sess_cache,
+ unsigned int timeout, size_t extrahandlesize) {
+ isc_sockaddr_t local_interface;
+ isc_nmsocket_t *sock = NULL;
+
+ REQUIRE(VALID_NM(mgr));
+ REQUIRE(cb != NULL);
+ REQUIRE(peer != NULL);
+ REQUIRE(uri != NULL);
+ REQUIRE(*uri != '\0');
+
+ if (local == NULL) {
+ isc_sockaddr_anyofpf(&local_interface, peer->type.sa.sa_family);
+ local = &local_interface;
+ }
+
+ sock = isc_mem_get(mgr->mctx, sizeof(*sock));
+ isc__nmsocket_init(sock, mgr, isc_nm_httpsocket, local);
+
+ sock->extrahandlesize = extrahandlesize;
+ sock->connect_timeout = timeout;
+ sock->result = ISC_R_UNSET;
+ sock->connect_cb = cb;
+ sock->connect_cbarg = cbarg;
+ atomic_init(&sock->client, true);
+
+ if (isc__nm_closing(sock)) {
+ isc__nm_uvreq_t *req = isc__nm_uvreq_get(mgr, sock);
+
+ req->cb.connect = cb;
+ req->cbarg = cbarg;
+ req->peer = *peer;
+ req->local = *local;
+ req->handle = isc__nmhandle_get(sock, &req->peer, &sock->iface);
+
+ if (isc__nm_in_netthread()) {
+ sock->tid = isc_nm_tid();
+ }
+
+ isc__nmsocket_clearcb(sock);
+ isc__nm_connectcb(sock, req, ISC_R_SHUTTINGDOWN, true);
+ isc__nmsocket_prep_destroy(sock);
+ isc__nmsocket_detach(&sock);
+ return;
+ }
+
+ sock->h2 = (isc_nmsocket_h2_t){ .connect.uri = isc_mem_strdup(mgr->mctx,
+ uri),
+ .connect.post = post,
+ .connect.tlsctx = tlsctx };
+ ISC_LINK_INIT(&sock->h2, link);
+
+ /*
+ * We need to prevent the interface object data from going out of
+ * scope too early.
+ */
+ if (local == &local_interface) {
+ sock->h2.connect.local_interface = local_interface;
+ sock->iface = sock->h2.connect.local_interface;
+ }
+
+ if (tlsctx != NULL) {
+ isc_nm_tlsconnect(mgr, local, peer, transport_connect_cb, sock,
+ tlsctx, client_sess_cache, timeout, 0);
+ } else {
+ isc_nm_tcpconnect(mgr, local, peer, transport_connect_cb, sock,
+ timeout, 0);
+ }
+}
+
+static isc_result_t
+client_send(isc_nmhandle_t *handle, const isc_region_t *region) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_nmsocket_t *sock = handle->sock;
+ isc_mem_t *mctx = sock->mgr->mctx;
+ isc_nm_http_session_t *session = sock->h2.session;
+ http_cstream_t *cstream = sock->h2.connect.cstream;
+
+ REQUIRE(VALID_HTTP2_SESSION(handle->sock->h2.session));
+ REQUIRE(session->client);
+ REQUIRE(region != NULL);
+ REQUIRE(region->base != NULL);
+ REQUIRE(region->length <= MAX_DNS_MESSAGE_SIZE);
+
+ if (session->closed) {
+ return (ISC_R_CANCELED);
+ }
+
+ INSIST(cstream != NULL);
+
+ if (cstream->post) {
+ /* POST */
+ isc_buffer_allocate(mctx, &cstream->postdata, region->length);
+ isc_buffer_putmem(cstream->postdata, region->base,
+ region->length);
+ } else {
+ /* GET */
+ size_t path_size = 0;
+ char *base64url_data = NULL;
+ size_t base64url_data_len = 0;
+ isc_buffer_t *buf = NULL;
+ isc_region_t data = *region;
+ isc_region_t base64_region;
+ size_t base64_len = ((4 * data.length / 3) + 3) & ~3;
+
+ isc_buffer_allocate(mctx, &buf, base64_len);
+
+ result = isc_base64_totext(&data, -1, "", buf);
+ if (result != ISC_R_SUCCESS) {
+ isc_buffer_free(&buf);
+ goto error;
+ }
+
+ isc_buffer_usedregion(buf, &base64_region);
+ INSIST(base64_region.length == base64_len);
+
+ base64url_data = isc__nm_base64_to_base64url(
+ mctx, (const char *)base64_region.base,
+ base64_region.length, &base64url_data_len);
+ isc_buffer_free(&buf);
+ if (base64url_data == NULL) {
+ goto error;
+ }
+
+ /* len("?dns=") + len(path) + len(base64url) + len("\0") */
+ path_size = cstream->pathlen + base64url_data_len + 5 + 1;
+ cstream->GET_path = isc_mem_allocate(mctx, path_size);
+ cstream->GET_path_len = (size_t)snprintf(
+ cstream->GET_path, path_size, "%.*s?dns=%s",
+ (int)cstream->pathlen, cstream->path, base64url_data);
+
+ INSIST(cstream->GET_path_len == (path_size - 1));
+ isc_mem_free(mctx, base64url_data);
+ }
+
+ cstream->sending = true;
+
+ sock->h2.connect.cstream = NULL;
+ result = client_submit_request(session, cstream);
+ if (result != ISC_R_SUCCESS) {
+ put_http_cstream(session->mctx, cstream);
+ goto error;
+ }
+
+error:
+ return (result);
+}
+
+isc_result_t
+isc__nm_http_request(isc_nmhandle_t *handle, isc_region_t *region,
+ isc_nm_recv_cb_t cb, void *cbarg) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_nmsocket_t *sock = NULL;
+ http_cstream_t *cstream = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+ REQUIRE(handle->sock->tid == isc_nm_tid());
+ REQUIRE(atomic_load(&handle->sock->client));
+
+ REQUIRE(cb != NULL);
+
+ sock = handle->sock;
+
+ isc__nm_http_read(handle, cb, cbarg);
+ if (!http_session_active(handle->sock->h2.session)) {
+ /* the callback was called by isc__nm_http_read() */
+ return (ISC_R_CANCELED);
+ }
+ result = client_send(handle, region);
+ if (result != ISC_R_SUCCESS) {
+ goto error;
+ }
+
+ return (ISC_R_SUCCESS);
+
+error:
+ cstream = sock->h2.connect.cstream;
+ if (cstream->read_cb != NULL) {
+ cstream->read_cb(handle, result, NULL, cstream->read_cbarg);
+ }
+ return (result);
+}
+
+static int
+server_on_begin_headers_callback(nghttp2_session *ngsession,
+ const nghttp2_frame *frame, void *user_data) {
+ isc_nm_http_session_t *session = (isc_nm_http_session_t *)user_data;
+ isc_nmsocket_t *socket = NULL;
+
+ if (frame->hd.type != NGHTTP2_HEADERS ||
+ frame->headers.cat != NGHTTP2_HCAT_REQUEST)
+ {
+ return (0);
+ } else if (frame->hd.length > MAX_ALLOWED_DATA_IN_HEADERS) {
+ return (NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE);
+ }
+
+ if (session->nsstreams >= session->max_concurrent_streams) {
+ return (NGHTTP2_ERR_CALLBACK_FAILURE);
+ }
+
+ socket = isc_mem_get(session->mctx, sizeof(isc_nmsocket_t));
+ isc__nmsocket_init(socket, session->serversocket->mgr,
+ isc_nm_httpsocket,
+ (isc_sockaddr_t *)&session->handle->sock->iface);
+ socket->peer = session->handle->sock->peer;
+ socket->h2 = (isc_nmsocket_h2_t){
+ .psock = socket,
+ .stream_id = frame->hd.stream_id,
+ .headers_error_code = ISC_HTTP_ERROR_SUCCESS,
+ .request_type = ISC_HTTP_REQ_UNSUPPORTED,
+ .request_scheme = ISC_HTTP_SCHEME_UNSUPPORTED
+ };
+ isc_buffer_initnull(&socket->h2.rbuf);
+ isc_buffer_initnull(&socket->h2.wbuf);
+ session->nsstreams++;
+ isc__nm_httpsession_attach(session, &socket->h2.session);
+ socket->tid = session->handle->sock->tid;
+ ISC_LINK_INIT(&socket->h2, link);
+ ISC_LIST_APPEND(session->sstreams, &socket->h2, link);
+
+ nghttp2_session_set_stream_user_data(ngsession, frame->hd.stream_id,
+ socket);
+ return (0);
+}
+
+static isc_nm_httphandler_t *
+find_server_request_handler(const char *request_path,
+ isc_nmsocket_t *serversocket, const int tid) {
+ isc_nm_httphandler_t *handler = NULL;
+
+ REQUIRE(VALID_NMSOCK(serversocket));
+
+ if (atomic_load(&serversocket->listening)) {
+ handler = http_endpoints_find(
+ request_path,
+ http_get_listener_endpoints(serversocket, tid));
+ }
+ return (handler);
+}
+
+static isc_http_error_responses_t
+server_handle_path_header(isc_nmsocket_t *socket, const uint8_t *value,
+ const size_t valuelen) {
+ isc_nm_httphandler_t *handler = NULL;
+ const uint8_t *qstr = NULL;
+ size_t vlen = valuelen;
+
+ qstr = memchr(value, '?', valuelen);
+ if (qstr != NULL) {
+ vlen = qstr - value;
+ }
+
+ if (socket->h2.request_path != NULL) {
+ isc_mem_free(socket->mgr->mctx, socket->h2.request_path);
+ }
+ socket->h2.request_path = isc_mem_strndup(
+ socket->mgr->mctx, (const char *)value, vlen + 1);
+
+ if (!isc_nm_http_path_isvalid(socket->h2.request_path)) {
+ isc_mem_free(socket->mgr->mctx, socket->h2.request_path);
+ socket->h2.request_path = NULL;
+ return (ISC_HTTP_ERROR_BAD_REQUEST);
+ }
+
+ handler = find_server_request_handler(socket->h2.request_path,
+ socket->h2.session->serversocket,
+ socket->tid);
+ if (handler != NULL) {
+ socket->h2.cb = handler->cb;
+ socket->h2.cbarg = handler->cbarg;
+ socket->extrahandlesize = handler->extrahandlesize;
+ } else {
+ isc_mem_free(socket->mgr->mctx, socket->h2.request_path);
+ socket->h2.request_path = NULL;
+ return (ISC_HTTP_ERROR_NOT_FOUND);
+ }
+
+ if (qstr != NULL) {
+ const char *dns_value = NULL;
+ size_t dns_value_len = 0;
+
+ if (isc__nm_parse_httpquery((const char *)qstr, &dns_value,
+ &dns_value_len))
+ {
+ const size_t decoded_size = dns_value_len / 4 * 3;
+ if (decoded_size <= MAX_DNS_MESSAGE_SIZE) {
+ if (socket->h2.query_data != NULL) {
+ isc_mem_free(socket->mgr->mctx,
+ socket->h2.query_data);
+ }
+ socket->h2.query_data =
+ isc__nm_base64url_to_base64(
+ socket->mgr->mctx, dns_value,
+ dns_value_len,
+ &socket->h2.query_data_len);
+ } else {
+ socket->h2.query_too_large = true;
+ return (ISC_HTTP_ERROR_PAYLOAD_TOO_LARGE);
+ }
+ } else {
+ return (ISC_HTTP_ERROR_BAD_REQUEST);
+ }
+ }
+ return (ISC_HTTP_ERROR_SUCCESS);
+}
+
+static isc_http_error_responses_t
+server_handle_method_header(isc_nmsocket_t *socket, const uint8_t *value,
+ const size_t valuelen) {
+ const char get[] = "GET";
+ const char post[] = "POST";
+
+ if (HEADER_MATCH(get, value, valuelen)) {
+ socket->h2.request_type = ISC_HTTP_REQ_GET;
+ } else if (HEADER_MATCH(post, value, valuelen)) {
+ socket->h2.request_type = ISC_HTTP_REQ_POST;
+ } else {
+ return (ISC_HTTP_ERROR_NOT_IMPLEMENTED);
+ }
+ return (ISC_HTTP_ERROR_SUCCESS);
+}
+
+static isc_http_error_responses_t
+server_handle_scheme_header(isc_nmsocket_t *socket, const uint8_t *value,
+ const size_t valuelen) {
+ const char http[] = "http";
+ const char http_secure[] = "https";
+
+ if (HEADER_MATCH(http_secure, value, valuelen)) {
+ socket->h2.request_scheme = ISC_HTTP_SCHEME_HTTP_SECURE;
+ } else if (HEADER_MATCH(http, value, valuelen)) {
+ socket->h2.request_scheme = ISC_HTTP_SCHEME_HTTP;
+ } else {
+ return (ISC_HTTP_ERROR_BAD_REQUEST);
+ }
+ return (ISC_HTTP_ERROR_SUCCESS);
+}
+
+static isc_http_error_responses_t
+server_handle_content_length_header(isc_nmsocket_t *socket,
+ const uint8_t *value,
+ const size_t valuelen) {
+ char tmp[32] = { 0 };
+ const size_t tmplen = sizeof(tmp) - 1;
+
+ strncpy(tmp, (const char *)value,
+ valuelen > tmplen ? tmplen : valuelen);
+ socket->h2.content_length = strtoul(tmp, NULL, 10);
+ if (socket->h2.content_length > MAX_DNS_MESSAGE_SIZE) {
+ return (ISC_HTTP_ERROR_PAYLOAD_TOO_LARGE);
+ } else if (socket->h2.content_length == 0) {
+ return (ISC_HTTP_ERROR_BAD_REQUEST);
+ }
+ return (ISC_HTTP_ERROR_SUCCESS);
+}
+
+static isc_http_error_responses_t
+server_handle_content_type_header(isc_nmsocket_t *socket, const uint8_t *value,
+ const size_t valuelen) {
+ const char type_dns_message[] = DNS_MEDIA_TYPE;
+ isc_http_error_responses_t resp = ISC_HTTP_ERROR_SUCCESS;
+
+ UNUSED(socket);
+
+ if (!HEADER_MATCH(type_dns_message, value, valuelen)) {
+ resp = ISC_HTTP_ERROR_UNSUPPORTED_MEDIA_TYPE;
+ }
+ return (resp);
+}
+
+static isc_http_error_responses_t
+server_handle_header(isc_nmsocket_t *socket, const uint8_t *name,
+ size_t namelen, const uint8_t *value,
+ const size_t valuelen) {
+ isc_http_error_responses_t code = ISC_HTTP_ERROR_SUCCESS;
+ bool was_error;
+ const char path[] = ":path";
+ const char method[] = ":method";
+ const char scheme[] = ":scheme";
+ const char content_length[] = "Content-Length";
+ const char content_type[] = "Content-Type";
+
+ was_error = socket->h2.headers_error_code != ISC_HTTP_ERROR_SUCCESS;
+ /*
+ * process Content-Length even when there was an error,
+ * to drop the connection earlier if required.
+ */
+ if (HEADER_MATCH(content_length, name, namelen)) {
+ code = server_handle_content_length_header(socket, value,
+ valuelen);
+ } else if (!was_error && HEADER_MATCH(path, name, namelen)) {
+ code = server_handle_path_header(socket, value, valuelen);
+ } else if (!was_error && HEADER_MATCH(method, name, namelen)) {
+ code = server_handle_method_header(socket, value, valuelen);
+ } else if (!was_error && HEADER_MATCH(scheme, name, namelen)) {
+ code = server_handle_scheme_header(socket, value, valuelen);
+ } else if (!was_error && HEADER_MATCH(content_type, name, namelen)) {
+ code = server_handle_content_type_header(socket, value,
+ valuelen);
+ }
+
+ return (code);
+}
+
+static int
+server_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) {
+ isc_nmsocket_t *socket = NULL;
+ isc_http_error_responses_t code = ISC_HTTP_ERROR_SUCCESS;
+
+ UNUSED(flags);
+ UNUSED(user_data);
+
+ socket = nghttp2_session_get_stream_user_data(session,
+ frame->hd.stream_id);
+ if (socket == NULL) {
+ return (NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE);
+ }
+
+ socket->h2.headers_data_processed += (namelen + valuelen);
+
+ switch (frame->hd.type) {
+ case NGHTTP2_HEADERS:
+ if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
+ break;
+ }
+ code = server_handle_header(socket, name, namelen, value,
+ valuelen);
+ break;
+ }
+
+ INSIST(socket != NULL);
+
+ if (socket->h2.headers_data_processed > MAX_ALLOWED_DATA_IN_HEADERS) {
+ return (NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE);
+ } else if (socket->h2.content_length > MAX_ALLOWED_DATA_IN_POST) {
+ return (NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE);
+ }
+
+ if (code == ISC_HTTP_ERROR_SUCCESS) {
+ return (0);
+ } else {
+ socket->h2.headers_error_code = code;
+ }
+
+ return (0);
+}
+
+static ssize_t
+server_read_callback(nghttp2_session *ngsession, int32_t stream_id,
+ uint8_t *buf, size_t length, uint32_t *data_flags,
+ nghttp2_data_source *source, void *user_data) {
+ isc_nm_http_session_t *session = (isc_nm_http_session_t *)user_data;
+ isc_nmsocket_t *socket = (isc_nmsocket_t *)source->ptr;
+ size_t buflen;
+
+ REQUIRE(socket->h2.stream_id == stream_id);
+
+ UNUSED(ngsession);
+ UNUSED(session);
+
+ buflen = isc_buffer_remaininglength(&socket->h2.wbuf);
+ if (buflen > length) {
+ buflen = length;
+ }
+
+ if (buflen > 0) {
+ (void)memmove(buf, isc_buffer_current(&socket->h2.wbuf),
+ buflen);
+ isc_buffer_forward(&socket->h2.wbuf, buflen);
+ }
+
+ if (isc_buffer_remaininglength(&socket->h2.wbuf) == 0) {
+ *data_flags |= NGHTTP2_DATA_FLAG_EOF;
+ }
+
+ return (buflen);
+}
+
+static isc_result_t
+server_send_response(nghttp2_session *ngsession, int32_t stream_id,
+ const nghttp2_nv *nva, size_t nvlen,
+ isc_nmsocket_t *socket) {
+ nghttp2_data_provider data_prd;
+ int rv;
+
+ if (socket->h2.response_submitted) {
+ /* NGHTTP2 will gladly accept new response (write request)
+ * from us even though we cannot send more than one over the
+ * same HTTP/2 stream. Thus, we need to handle this case
+ * manually. We will return failure code so that it will be
+ * passed to the write callback. */
+ return (ISC_R_FAILURE);
+ }
+
+ data_prd.source.ptr = socket;
+ data_prd.read_callback = server_read_callback;
+
+ rv = nghttp2_submit_response(ngsession, stream_id, nva, nvlen,
+ &data_prd);
+ if (rv != 0) {
+ return (ISC_R_FAILURE);
+ }
+
+ socket->h2.response_submitted = true;
+ return (ISC_R_SUCCESS);
+}
+
+#define MAKE_ERROR_REPLY(tag, code, desc) \
+ { \
+ tag, MAKE_NV2(":status", #code), desc \
+ }
+
+/*
+ * Here we use roughly the same error codes that Unbound uses.
+ * (https://blog.nlnetlabs.nl/dns-over-https-in-unbound/)
+ */
+
+static struct http_error_responses {
+ const isc_http_error_responses_t type;
+ const nghttp2_nv header;
+ const char *desc;
+} error_responses[] = {
+ MAKE_ERROR_REPLY(ISC_HTTP_ERROR_BAD_REQUEST, 400, "Bad Request"),
+ MAKE_ERROR_REPLY(ISC_HTTP_ERROR_NOT_FOUND, 404, "Not Found"),
+ MAKE_ERROR_REPLY(ISC_HTTP_ERROR_PAYLOAD_TOO_LARGE, 413,
+ "Payload Too Large"),
+ MAKE_ERROR_REPLY(ISC_HTTP_ERROR_URI_TOO_LONG, 414, "URI Too Long"),
+ MAKE_ERROR_REPLY(ISC_HTTP_ERROR_UNSUPPORTED_MEDIA_TYPE, 415,
+ "Unsupported Media Type"),
+ MAKE_ERROR_REPLY(ISC_HTTP_ERROR_GENERIC, 500, "Internal Server Error"),
+ MAKE_ERROR_REPLY(ISC_HTTP_ERROR_NOT_IMPLEMENTED, 501, "Not Implemented")
+};
+
+static void
+log_server_error_response(const isc_nmsocket_t *socket,
+ const struct http_error_responses *response) {
+ const int log_level = ISC_LOG_DEBUG(1);
+ isc_sockaddr_t client_addr;
+ isc_sockaddr_t local_addr;
+ char client_sabuf[ISC_SOCKADDR_FORMATSIZE];
+ char local_sabuf[ISC_SOCKADDR_FORMATSIZE];
+
+ if (!isc_log_wouldlog(isc_lctx, log_level)) {
+ return;
+ }
+
+ client_addr = isc_nmhandle_peeraddr(socket->h2.session->handle);
+ local_addr = isc_nmhandle_localaddr(socket->h2.session->handle);
+ isc_sockaddr_format(&client_addr, client_sabuf, sizeof(client_sabuf));
+ isc_sockaddr_format(&local_addr, local_sabuf, sizeof(local_sabuf));
+ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_NETMGR,
+ log_level, "HTTP/2 request from %s (on %s) failed: %s %s",
+ client_sabuf, local_sabuf, response->header.value,
+ response->desc);
+}
+
+static isc_result_t
+server_send_error_response(const isc_http_error_responses_t error,
+ nghttp2_session *ngsession, isc_nmsocket_t *socket) {
+ void *base;
+
+ REQUIRE(error != ISC_HTTP_ERROR_SUCCESS);
+
+ base = isc_buffer_base(&socket->h2.rbuf);
+ if (base != NULL) {
+ isc_mem_free(socket->h2.session->mctx, base);
+ isc_buffer_initnull(&socket->h2.rbuf);
+ }
+
+ /* We do not want the error response to be cached anywhere. */
+ socket->h2.min_ttl = 0;
+
+ for (size_t i = 0;
+ i < sizeof(error_responses) / sizeof(error_responses[0]); i++)
+ {
+ if (error_responses[i].type == error) {
+ log_server_error_response(socket, &error_responses[i]);
+ return (server_send_response(
+ ngsession, socket->h2.stream_id,
+ &error_responses[i].header, 1, socket));
+ }
+ }
+
+ return (server_send_error_response(ISC_HTTP_ERROR_GENERIC, ngsession,
+ socket));
+}
+
+static void
+server_call_cb(isc_nmsocket_t *socket, isc_nm_http_session_t *session,
+ const isc_result_t result, isc_region_t *data) {
+ isc_sockaddr_t addr;
+ isc_nmhandle_t *handle = NULL;
+
+ REQUIRE(VALID_NMSOCK(socket));
+ REQUIRE(VALID_HTTP2_SESSION(session));
+ REQUIRE(socket->h2.cb != NULL);
+
+ addr = isc_nmhandle_peeraddr(session->handle);
+ handle = isc__nmhandle_get(socket, &addr, NULL);
+ socket->h2.cb(handle, result, data, socket->h2.cbarg);
+ isc_nmhandle_detach(&handle);
+}
+
+void
+isc__nm_http_bad_request(isc_nmhandle_t *handle) {
+ isc_nmsocket_t *sock = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+ sock = handle->sock;
+ REQUIRE(sock->type == isc_nm_httpsocket);
+ REQUIRE(!atomic_load(&sock->client));
+ REQUIRE(VALID_HTTP2_SESSION(sock->h2.session));
+
+ (void)server_send_error_response(ISC_HTTP_ERROR_BAD_REQUEST,
+ sock->h2.session->ngsession, sock);
+}
+
+static int
+server_on_request_recv(nghttp2_session *ngsession,
+ isc_nm_http_session_t *session, isc_nmsocket_t *socket) {
+ isc_result_t result;
+ isc_http_error_responses_t code = ISC_HTTP_ERROR_SUCCESS;
+ isc_region_t data;
+ uint8_t tmp_buf[MAX_DNS_MESSAGE_SIZE];
+
+ code = socket->h2.headers_error_code;
+ if (code != ISC_HTTP_ERROR_SUCCESS) {
+ goto error;
+ }
+
+ if (socket->h2.request_path == NULL || socket->h2.cb == NULL) {
+ code = ISC_HTTP_ERROR_NOT_FOUND;
+ } else if (socket->h2.request_type == ISC_HTTP_REQ_POST &&
+ socket->h2.content_length == 0)
+ {
+ code = ISC_HTTP_ERROR_BAD_REQUEST;
+ } else if (socket->h2.request_type == ISC_HTTP_REQ_POST &&
+ isc_buffer_usedlength(&socket->h2.rbuf) >
+ socket->h2.content_length)
+ {
+ code = ISC_HTTP_ERROR_PAYLOAD_TOO_LARGE;
+ } else if (socket->h2.request_type == ISC_HTTP_REQ_POST &&
+ isc_buffer_usedlength(&socket->h2.rbuf) !=
+ socket->h2.content_length)
+ {
+ code = ISC_HTTP_ERROR_BAD_REQUEST;
+ } else if (socket->h2.request_type == ISC_HTTP_REQ_POST &&
+ socket->h2.query_data != NULL)
+ {
+ /* The spec does not mention which value the query string for
+ * POST should have. For GET we use its value to decode a DNS
+ * message from it, for POST the message is transferred in the
+ * body of the request. Taking it into account, it is much safer
+ * to treat POST
+ * requests with query strings as malformed ones. */
+ code = ISC_HTTP_ERROR_BAD_REQUEST;
+ } else if (socket->h2.request_type == ISC_HTTP_REQ_GET &&
+ socket->h2.content_length > 0)
+ {
+ code = ISC_HTTP_ERROR_BAD_REQUEST;
+ } else if (socket->h2.request_type == ISC_HTTP_REQ_GET &&
+ socket->h2.query_data == NULL)
+ {
+ /* A GET request without any query data - there is nothing to
+ * decode. */
+ INSIST(socket->h2.query_data_len == 0);
+ code = ISC_HTTP_ERROR_BAD_REQUEST;
+ }
+
+ if (code != ISC_HTTP_ERROR_SUCCESS) {
+ goto error;
+ }
+
+ if (socket->h2.request_type == ISC_HTTP_REQ_GET) {
+ isc_buffer_t decoded_buf;
+ isc_buffer_init(&decoded_buf, tmp_buf, sizeof(tmp_buf));
+ if (isc_base64_decodestring(socket->h2.query_data,
+ &decoded_buf) != ISC_R_SUCCESS)
+ {
+ code = ISC_HTTP_ERROR_BAD_REQUEST;
+ goto error;
+ }
+ isc_buffer_usedregion(&decoded_buf, &data);
+ } else if (socket->h2.request_type == ISC_HTTP_REQ_POST) {
+ INSIST(socket->h2.content_length > 0);
+ isc_buffer_usedregion(&socket->h2.rbuf, &data);
+ } else {
+ UNREACHABLE();
+ }
+
+ server_call_cb(socket, session, ISC_R_SUCCESS, &data);
+
+ return (0);
+
+error:
+ result = server_send_error_response(code, ngsession, socket);
+ if (result != ISC_R_SUCCESS) {
+ return (NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE);
+ }
+ return (0);
+}
+
+void
+isc__nm_http_send(isc_nmhandle_t *handle, const isc_region_t *region,
+ isc_nm_cb_t cb, void *cbarg) {
+ isc_nmsocket_t *sock = NULL;
+ isc__netievent_httpsend_t *ievent = NULL;
+ isc__nm_uvreq_t *uvreq = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ sock = handle->sock;
+
+ REQUIRE(VALID_NMSOCK(sock));
+
+ uvreq = isc__nm_uvreq_get(sock->mgr, sock);
+ isc_nmhandle_attach(handle, &uvreq->handle);
+ uvreq->cb.send = cb;
+ uvreq->cbarg = cbarg;
+
+ uvreq->uvbuf.base = (char *)region->base;
+ uvreq->uvbuf.len = region->length;
+
+ ievent = isc__nm_get_netievent_httpsend(sock->mgr, sock, uvreq);
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+}
+
+static void
+failed_send_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req,
+ isc_result_t eresult) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(VALID_UVREQ(req));
+
+ if (req->cb.send != NULL) {
+ isc__nm_sendcb(sock, req, eresult, true);
+ } else {
+ isc__nm_uvreq_put(&req, sock);
+ }
+}
+
+static void
+client_httpsend(isc_nmhandle_t *handle, isc_nmsocket_t *sock,
+ isc__nm_uvreq_t *req) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_nm_cb_t cb = req->cb.send;
+ void *cbarg = req->cbarg;
+
+ result = client_send(
+ handle,
+ &(isc_region_t){ (uint8_t *)req->uvbuf.base, req->uvbuf.len });
+ if (result != ISC_R_SUCCESS) {
+ failed_send_cb(sock, req, result);
+ return;
+ }
+
+ http_do_bio(sock->h2.session, handle, cb, cbarg);
+ isc__nm_uvreq_put(&req, sock);
+}
+
+static void
+server_httpsend(isc_nmhandle_t *handle, isc_nmsocket_t *sock,
+ isc__nm_uvreq_t *req) {
+ size_t content_len_buf_len, cache_control_buf_len;
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_nm_cb_t cb = req->cb.send;
+ void *cbarg = req->cbarg;
+ if (isc__nmsocket_closing(sock) ||
+ !http_session_active(handle->httpsession))
+ {
+ failed_send_cb(sock, req, ISC_R_CANCELED);
+ return;
+ }
+
+ INSIST(handle->httpsession->handle->sock->tid == isc_nm_tid());
+ INSIST(VALID_NMHANDLE(handle->httpsession->handle));
+ INSIST(VALID_NMSOCK(handle->httpsession->handle->sock));
+
+ isc_buffer_init(&sock->h2.wbuf, req->uvbuf.base, req->uvbuf.len);
+ isc_buffer_add(&sock->h2.wbuf, req->uvbuf.len);
+
+ content_len_buf_len = snprintf(sock->h2.clenbuf,
+ sizeof(sock->h2.clenbuf), "%lu",
+ (unsigned long)req->uvbuf.len);
+ if (sock->h2.min_ttl == 0) {
+ cache_control_buf_len =
+ snprintf(sock->h2.cache_control_buf,
+ sizeof(sock->h2.cache_control_buf), "%s",
+ DEFAULT_CACHE_CONTROL);
+ } else {
+ cache_control_buf_len =
+ snprintf(sock->h2.cache_control_buf,
+ sizeof(sock->h2.cache_control_buf),
+ "max-age=%" PRIu32, sock->h2.min_ttl);
+ }
+ const nghttp2_nv hdrs[] = { MAKE_NV2(":status", "200"),
+ MAKE_NV2("Content-Type", DNS_MEDIA_TYPE),
+ MAKE_NV("Content-Length", sock->h2.clenbuf,
+ content_len_buf_len),
+ MAKE_NV("Cache-Control",
+ sock->h2.cache_control_buf,
+ cache_control_buf_len) };
+
+ result = server_send_response(handle->httpsession->ngsession,
+ sock->h2.stream_id, hdrs,
+ sizeof(hdrs) / sizeof(nghttp2_nv), sock);
+
+ if (result == ISC_R_SUCCESS) {
+ http_do_bio(handle->httpsession, handle, cb, cbarg);
+ } else {
+ cb(handle, result, cbarg);
+ }
+ isc__nm_uvreq_put(&req, sock);
+}
+
+void
+isc__nm_async_httpsend(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_httpsend_t *ievent = (isc__netievent_httpsend_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+ isc__nm_uvreq_t *req = ievent->req;
+ isc_nmhandle_t *handle = NULL;
+ isc_nm_http_session_t *session = NULL;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(VALID_UVREQ(req));
+ REQUIRE(VALID_HTTP2_SESSION(sock->h2.session));
+
+ ievent->req = NULL;
+ handle = req->handle;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ session = sock->h2.session;
+ if (session != NULL && session->client) {
+ client_httpsend(handle, sock, req);
+ } else {
+ server_httpsend(handle, sock, req);
+ }
+}
+
+void
+isc__nm_http_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
+ isc_result_t result;
+ http_cstream_t *cstream = NULL;
+ isc_nm_http_session_t *session = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ session = handle->sock->h2.session;
+ if (!http_session_active(session)) {
+ cb(handle, ISC_R_CANCELED, NULL, cbarg);
+ return;
+ }
+
+ result = get_http_cstream(handle->sock, &cstream);
+ if (result != ISC_R_SUCCESS) {
+ return;
+ }
+
+ handle->sock->h2.connect.cstream = cstream;
+ cstream->read_cb = cb;
+ cstream->read_cbarg = cbarg;
+ cstream->reading = true;
+
+ if (cstream->sending) {
+ result = client_submit_request(session, cstream);
+ if (result != ISC_R_SUCCESS) {
+ put_http_cstream(session->mctx, cstream);
+ return;
+ }
+
+ http_do_bio(session, NULL, NULL, NULL);
+ }
+}
+
+static int
+server_on_frame_recv_callback(nghttp2_session *ngsession,
+ const nghttp2_frame *frame, void *user_data) {
+ isc_nm_http_session_t *session = (isc_nm_http_session_t *)user_data;
+ isc_nmsocket_t *socket = NULL;
+
+ switch (frame->hd.type) {
+ case NGHTTP2_DATA:
+ case NGHTTP2_HEADERS:
+ /* Check that the client request has finished */
+ if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
+ socket = nghttp2_session_get_stream_user_data(
+ ngsession, frame->hd.stream_id);
+
+ /*
+ * For DATA and HEADERS frame, this callback may be
+ * called after on_stream_close_callback. Check that
+ * the stream is still alive.
+ */
+ if (socket == NULL) {
+ return (0);
+ }
+
+ return (server_on_request_recv(ngsession, session,
+ socket));
+ }
+ break;
+ default:
+ break;
+ }
+ return (0);
+}
+
+static void
+initialize_nghttp2_server_session(isc_nm_http_session_t *session) {
+ nghttp2_session_callbacks *callbacks = NULL;
+ nghttp2_mem mem;
+
+ init_nghttp2_mem(session->mctx, &mem);
+
+ RUNTIME_CHECK(nghttp2_session_callbacks_new(&callbacks) == 0);
+
+ nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
+ callbacks, on_data_chunk_recv_callback);
+
+ nghttp2_session_callbacks_set_on_stream_close_callback(
+ callbacks, on_stream_close_callback);
+
+ nghttp2_session_callbacks_set_on_header_callback(
+ callbacks, server_on_header_callback);
+
+ nghttp2_session_callbacks_set_on_begin_headers_callback(
+ callbacks, server_on_begin_headers_callback);
+
+ nghttp2_session_callbacks_set_on_frame_recv_callback(
+ callbacks, server_on_frame_recv_callback);
+
+ RUNTIME_CHECK(nghttp2_session_server_new3(&session->ngsession,
+ callbacks, session, NULL,
+ &mem) == 0);
+
+ nghttp2_session_callbacks_del(callbacks);
+}
+
+static int
+server_send_connection_header(isc_nm_http_session_t *session) {
+ nghttp2_settings_entry iv[1] = {
+ { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS,
+ session->max_concurrent_streams }
+ };
+ int rv;
+
+ rv = nghttp2_submit_settings(session->ngsession, NGHTTP2_FLAG_NONE, iv,
+ 1);
+ if (rv != 0) {
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * It is advisable to disable Nagle's algorithm for HTTP/2
+ * connections because multiple HTTP/2 streams could be multiplexed
+ * over one transport connection. Thus, delays when delivering small
+ * packets could bring down performance for the whole session.
+ * HTTP/2 is meant to be used this way.
+ */
+static void
+http_transpost_tcp_nodelay(isc_nmhandle_t *transphandle) {
+ isc_nmsocket_t *tcpsock = NULL;
+ uv_os_fd_t tcp_fd = (uv_os_fd_t)-1;
+
+ if (transphandle->sock->type == isc_nm_tlssocket) {
+ tcpsock = transphandle->sock->outerhandle->sock;
+ } else {
+ tcpsock = transphandle->sock;
+ }
+
+ (void)uv_fileno((uv_handle_t *)&tcpsock->uv_handle.tcp, &tcp_fd);
+ RUNTIME_CHECK(tcp_fd != (uv_os_fd_t)-1);
+ (void)isc__nm_socket_tcp_nodelay((uv_os_sock_t)tcp_fd);
+}
+
+static isc_result_t
+httplisten_acceptcb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
+ isc_nmsocket_t *httplistensock = (isc_nmsocket_t *)cbarg;
+ isc_nm_http_session_t *session = NULL;
+ isc_nmsocket_t *listener = NULL, *httpserver = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ if (handle->sock->type == isc_nm_tlssocket) {
+ REQUIRE(VALID_NMSOCK(handle->sock->listener));
+ listener = handle->sock->listener;
+ httpserver = listener->h2.httpserver;
+ } else {
+ REQUIRE(VALID_NMSOCK(handle->sock->server));
+ listener = handle->sock->server;
+ REQUIRE(VALID_NMSOCK(listener->parent));
+ httpserver = listener->parent->h2.httpserver;
+ }
+
+ /*
+ * NOTE: HTTP listener socket might be destroyed by the time this
+ * function gets invoked, so we need to do extra sanity checks to
+ * detect this case.
+ */
+ if (isc__nmsocket_closing(handle->sock) || httpserver == NULL) {
+ return (ISC_R_CANCELED);
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ /* XXXWPK do nothing? */
+ return (result);
+ }
+
+ REQUIRE(VALID_NMSOCK(httplistensock));
+ INSIST(httplistensock == httpserver);
+
+ if (isc__nmsocket_closing(httplistensock) ||
+ !atomic_load(&httplistensock->listening))
+ {
+ return (ISC_R_CANCELED);
+ }
+
+ http_transpost_tcp_nodelay(handle);
+
+ new_session(httplistensock->mgr->mctx, NULL, &session);
+ session->max_concurrent_streams =
+ atomic_load(&httplistensock->h2.max_concurrent_streams);
+ initialize_nghttp2_server_session(session);
+ handle->sock->h2.session = session;
+
+ isc_nmhandle_attach(handle, &session->handle);
+ isc__nmsocket_attach(httplistensock, &session->serversocket);
+ server_send_connection_header(session);
+
+ /* TODO H2 */
+ http_do_bio(session, NULL, NULL, NULL);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_nm_listenhttp(isc_nm_t *mgr, isc_sockaddr_t *iface, int backlog,
+ isc_quota_t *quota, isc_tlsctx_t *ctx,
+ isc_nm_http_endpoints_t *eps, uint32_t max_concurrent_streams,
+ isc_nmsocket_t **sockp) {
+ isc_nmsocket_t *sock = NULL;
+ isc_result_t result;
+
+ REQUIRE(!ISC_LIST_EMPTY(eps->handlers));
+ REQUIRE(!ISC_LIST_EMPTY(eps->handler_cbargs));
+ REQUIRE(atomic_load(&eps->in_use) == false);
+
+ sock = isc_mem_get(mgr->mctx, sizeof(*sock));
+ isc__nmsocket_init(sock, mgr, isc_nm_httplistener, iface);
+ atomic_init(&sock->h2.max_concurrent_streams,
+ NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS);
+
+ isc_nmsocket_set_max_streams(sock, max_concurrent_streams);
+
+ atomic_store(&eps->in_use, true);
+ http_init_listener_endpoints(sock, eps);
+
+ if (ctx != NULL) {
+ result = isc_nm_listentls(mgr, iface, httplisten_acceptcb, sock,
+ sizeof(isc_nm_http_session_t),
+ backlog, quota, ctx, &sock->outer);
+ } else {
+ result = isc_nm_listentcp(mgr, iface, httplisten_acceptcb, sock,
+ sizeof(isc_nm_http_session_t),
+ backlog, quota, &sock->outer);
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ atomic_store(&sock->closed, true);
+ isc__nmsocket_detach(&sock);
+ return (result);
+ }
+
+ isc__nmsocket_attach(sock, &sock->outer->h2.httpserver);
+
+ sock->nchildren = sock->outer->nchildren;
+ sock->result = ISC_R_UNSET;
+ sock->tid = 0;
+ sock->fd = (uv_os_sock_t)-1;
+
+ isc__nmsocket_barrier_init(sock);
+ atomic_init(&sock->rchildren, sock->nchildren);
+
+ atomic_store(&sock->listening, true);
+ *sockp = sock;
+ return (ISC_R_SUCCESS);
+}
+
+isc_nm_http_endpoints_t *
+isc_nm_http_endpoints_new(isc_mem_t *mctx) {
+ isc_nm_http_endpoints_t *restrict eps;
+ REQUIRE(mctx != NULL);
+
+ eps = isc_mem_get(mctx, sizeof(*eps));
+ *eps = (isc_nm_http_endpoints_t){ .mctx = NULL };
+
+ isc_mem_attach(mctx, &eps->mctx);
+ ISC_LIST_INIT(eps->handler_cbargs);
+ ISC_LIST_INIT(eps->handlers);
+ isc_refcount_init(&eps->references, 1);
+ atomic_init(&eps->in_use, false);
+ eps->magic = HTTP_ENDPOINTS_MAGIC;
+
+ return eps;
+}
+
+void
+isc_nm_http_endpoints_detach(isc_nm_http_endpoints_t **restrict epsp) {
+ isc_nm_http_endpoints_t *restrict eps;
+ isc_mem_t *mctx;
+ isc_nm_httphandler_t *handler = NULL;
+ isc_nm_httpcbarg_t *httpcbarg = NULL;
+
+ REQUIRE(epsp != NULL);
+ eps = *epsp;
+ REQUIRE(VALID_HTTP_ENDPOINTS(eps));
+
+ if (isc_refcount_decrement(&eps->references) > 1) {
+ *epsp = NULL;
+ return;
+ }
+
+ mctx = eps->mctx;
+
+ /* Delete all handlers */
+ handler = ISC_LIST_HEAD(eps->handlers);
+ while (handler != NULL) {
+ isc_nm_httphandler_t *next = NULL;
+
+ next = ISC_LIST_NEXT(handler, link);
+ ISC_LIST_DEQUEUE(eps->handlers, handler, link);
+ isc_mem_free(mctx, handler->path);
+ isc_mem_put(mctx, handler, sizeof(*handler));
+ handler = next;
+ }
+
+ httpcbarg = ISC_LIST_HEAD(eps->handler_cbargs);
+ while (httpcbarg != NULL) {
+ isc_nm_httpcbarg_t *next = NULL;
+
+ next = ISC_LIST_NEXT(httpcbarg, link);
+ ISC_LIST_DEQUEUE(eps->handler_cbargs, httpcbarg, link);
+ isc_mem_put(mctx, httpcbarg, sizeof(isc_nm_httpcbarg_t));
+ httpcbarg = next;
+ }
+
+ eps->magic = 0;
+
+ isc_mem_putanddetach(&mctx, eps, sizeof(*eps));
+ *epsp = NULL;
+}
+
+void
+isc_nm_http_endpoints_attach(isc_nm_http_endpoints_t *source,
+ isc_nm_http_endpoints_t **targetp) {
+ REQUIRE(VALID_HTTP_ENDPOINTS(source));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ isc_refcount_increment(&source->references);
+
+ *targetp = source;
+}
+
+static isc_nm_httphandler_t *
+http_endpoints_find(const char *request_path,
+ const isc_nm_http_endpoints_t *restrict eps) {
+ isc_nm_httphandler_t *handler = NULL;
+
+ REQUIRE(VALID_HTTP_ENDPOINTS(eps));
+
+ if (request_path == NULL || *request_path == '\0') {
+ return (NULL);
+ }
+
+ for (handler = ISC_LIST_HEAD(eps->handlers); handler != NULL;
+ handler = ISC_LIST_NEXT(handler, link))
+ {
+ if (!strcmp(request_path, handler->path)) {
+ break;
+ }
+ }
+
+ return (handler);
+}
+
+/*
+ * In DoH we just need to intercept the request - the response can be sent
+ * to the client code via the nmhandle directly as it's always just the
+ * http content.
+ */
+static void
+http_callback(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *data,
+ void *arg) {
+ isc_nm_httpcbarg_t *httpcbarg = arg;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ if (result != ISC_R_SUCCESS) {
+ /* Shut down the client, then ourselves */
+ httpcbarg->cb(handle, result, NULL, httpcbarg->cbarg);
+ /* XXXWPK FREE */
+ return;
+ }
+ httpcbarg->cb(handle, result, data, httpcbarg->cbarg);
+}
+
+isc_result_t
+isc_nm_http_endpoints_add(isc_nm_http_endpoints_t *restrict eps,
+ const char *uri, const isc_nm_recv_cb_t cb,
+ void *cbarg, const size_t extrahandlesize) {
+ isc_mem_t *mctx;
+ isc_nm_httphandler_t *restrict handler = NULL;
+ isc_nm_httpcbarg_t *restrict httpcbarg = NULL;
+ bool newhandler = false;
+
+ REQUIRE(VALID_HTTP_ENDPOINTS(eps));
+ REQUIRE(isc_nm_http_path_isvalid(uri));
+ REQUIRE(atomic_load(&eps->in_use) == false);
+
+ mctx = eps->mctx;
+
+ httpcbarg = isc_mem_get(mctx, sizeof(isc_nm_httpcbarg_t));
+ *httpcbarg = (isc_nm_httpcbarg_t){ .cb = cb, .cbarg = cbarg };
+ ISC_LINK_INIT(httpcbarg, link);
+
+ if (http_endpoints_find(uri, eps) == NULL) {
+ handler = isc_mem_get(mctx, sizeof(*handler));
+ *handler = (isc_nm_httphandler_t){
+ .cb = http_callback,
+ .cbarg = httpcbarg,
+ .extrahandlesize = extrahandlesize,
+ .path = isc_mem_strdup(mctx, uri)
+ };
+ ISC_LINK_INIT(handler, link);
+
+ newhandler = true;
+ }
+
+ if (newhandler) {
+ ISC_LIST_APPEND(eps->handlers, handler, link);
+ }
+ ISC_LIST_APPEND(eps->handler_cbargs, httpcbarg, link);
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc__nm_http_stoplistening(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_httplistener);
+
+ isc__nmsocket_stop(sock);
+}
+
+static void
+http_close_direct(isc_nmsocket_t *sock) {
+ isc_nm_http_session_t *session = NULL;
+
+ REQUIRE(VALID_NMSOCK(sock));
+
+ atomic_store(&sock->closed, true);
+ atomic_store(&sock->active, false);
+ session = sock->h2.session;
+
+ if (session != NULL && session->sending == 0 && !session->reading) {
+ /*
+ * The socket is going to be closed too early without been
+ * used even once (might happen in a case of low level
+ * error).
+ */
+ finish_http_session(session);
+ } else if (session != NULL && session->handle) {
+ http_do_bio(session, NULL, NULL, NULL);
+ }
+}
+
+void
+isc__nm_http_close(isc_nmsocket_t *sock) {
+ bool destroy = false;
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_httpsocket);
+ REQUIRE(!isc__nmsocket_active(sock));
+
+ if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false },
+ true))
+ {
+ return;
+ }
+
+ if (sock->h2.session != NULL && sock->h2.session->closed &&
+ sock->tid == isc_nm_tid())
+ {
+ isc__nm_httpsession_detach(&sock->h2.session);
+ destroy = true;
+ } else if (sock->h2.session == NULL && sock->tid == isc_nm_tid()) {
+ destroy = true;
+ }
+
+ if (destroy) {
+ http_close_direct(sock);
+ isc__nmsocket_prep_destroy(sock);
+ return;
+ }
+
+ isc__netievent_httpclose_t *ievent =
+ isc__nm_get_netievent_httpclose(sock->mgr, sock);
+
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+}
+
+void
+isc__nm_async_httpclose(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_httpclose_t *ievent = (isc__netievent_httpclose_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ UNUSED(worker);
+
+ http_close_direct(sock);
+}
+
+static void
+failed_httpstream_read_cb(isc_nmsocket_t *sock, isc_result_t result,
+ isc_nm_http_session_t *session) {
+ isc_region_t data;
+ REQUIRE(VALID_NMSOCK(sock));
+ INSIST(sock->type == isc_nm_httpsocket);
+
+ if (sock->h2.request_path == NULL) {
+ return;
+ }
+
+ INSIST(sock->h2.cbarg != NULL);
+
+ (void)nghttp2_submit_rst_stream(
+ session->ngsession, NGHTTP2_FLAG_END_STREAM, sock->h2.stream_id,
+ NGHTTP2_REFUSED_STREAM);
+ isc_buffer_usedregion(&sock->h2.rbuf, &data);
+ server_call_cb(sock, session, result, &data);
+}
+
+static void
+client_call_failed_read_cb(isc_result_t result,
+ isc_nm_http_session_t *session) {
+ http_cstream_t *cstream = NULL;
+
+ REQUIRE(VALID_HTTP2_SESSION(session));
+ REQUIRE(result != ISC_R_SUCCESS);
+
+ cstream = ISC_LIST_HEAD(session->cstreams);
+ while (cstream != NULL) {
+ http_cstream_t *next = ISC_LIST_NEXT(cstream, link);
+
+ /*
+ * read_cb could be NULL if cstream was allocated and added
+ * to the tracking list, but was not properly initialized due
+ * to a low-level error. It is safe to get rid of the object
+ * in such a case.
+ */
+ if (cstream->read_cb != NULL) {
+ isc_region_t read_data;
+ isc_buffer_usedregion(cstream->rbuf, &read_data);
+ cstream->read_cb(session->client_httphandle, result,
+ &read_data, cstream->read_cbarg);
+ }
+
+ if (result != ISC_R_TIMEDOUT || cstream->read_cb == NULL ||
+ !isc__nmsocket_timer_running(session->handle->sock))
+ {
+ ISC_LIST_DEQUEUE(session->cstreams, cstream, link);
+ put_http_cstream(session->mctx, cstream);
+ }
+
+ cstream = next;
+ }
+}
+
+static void
+server_call_failed_read_cb(isc_result_t result,
+ isc_nm_http_session_t *session) {
+ isc_nmsocket_h2_t *h2data = NULL; /* stream socket */
+
+ REQUIRE(VALID_HTTP2_SESSION(session));
+ REQUIRE(result != ISC_R_SUCCESS);
+
+ for (h2data = ISC_LIST_HEAD(session->sstreams); h2data != NULL;
+ h2data = ISC_LIST_NEXT(h2data, link))
+ {
+ failed_httpstream_read_cb(h2data->psock, result, session);
+ }
+
+ h2data = ISC_LIST_HEAD(session->sstreams);
+ while (h2data != NULL) {
+ isc_nmsocket_h2_t *next = ISC_LIST_NEXT(h2data, link);
+ ISC_LIST_DEQUEUE(session->sstreams, h2data, link);
+ /* Cleanup socket in place */
+ atomic_store(&h2data->psock->active, false);
+ atomic_store(&h2data->psock->closed, true);
+ isc__nmsocket_detach(&h2data->psock);
+
+ h2data = next;
+ }
+}
+
+static void
+failed_read_cb(isc_result_t result, isc_nm_http_session_t *session) {
+ if (session->client) {
+ client_call_failed_read_cb(result, session);
+ /*
+ * If result was ISC_R_TIMEDOUT and the timer was reset,
+ * then we still have active streams and should not close
+ * the session.
+ */
+ if (ISC_LIST_EMPTY(session->cstreams)) {
+ finish_http_session(session);
+ }
+ } else {
+ server_call_failed_read_cb(result, session);
+ /*
+ * All streams are now destroyed; close the session.
+ */
+ finish_http_session(session);
+ }
+}
+
+void
+isc__nm_http_set_maxage(isc_nmhandle_t *handle, const uint32_t ttl) {
+ isc_nm_http_session_t *session;
+ isc_nmsocket_t *sock;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ sock = handle->sock;
+ session = sock->h2.session;
+
+ INSIST(VALID_HTTP2_SESSION(session));
+ INSIST(!session->client);
+
+ sock->h2.min_ttl = ttl;
+}
+
+bool
+isc__nm_http_has_encryption(const isc_nmhandle_t *handle) {
+ isc_nm_http_session_t *session;
+ isc_nmsocket_t *sock;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ sock = handle->sock;
+ session = sock->h2.session;
+
+ INSIST(VALID_HTTP2_SESSION(session));
+
+ return (isc_nm_socket_type(session->handle) == isc_nm_tlssocket);
+}
+
+const char *
+isc__nm_http_verify_tls_peer_result_string(const isc_nmhandle_t *handle) {
+ isc_nmsocket_t *sock = NULL;
+ isc_nm_http_session_t *session;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+ REQUIRE(handle->sock->type == isc_nm_httpsocket);
+
+ sock = handle->sock;
+ session = sock->h2.session;
+
+ /*
+ * In the case of a low-level error the session->handle is not
+ * attached nor session object is created.
+ */
+ if (session == NULL && sock->h2.connect.tls_peer_verify_string != NULL)
+ {
+ return (sock->h2.connect.tls_peer_verify_string);
+ }
+
+ if (session == NULL) {
+ return (NULL);
+ }
+
+ INSIST(VALID_HTTP2_SESSION(session));
+
+ return (isc_nm_verify_tls_peer_result_string(session->handle));
+}
+
+void
+isc__nm_http_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx) {
+ REQUIRE(VALID_NMSOCK(listener));
+ REQUIRE(listener->type == isc_nm_httplistener);
+
+ isc_nmsocket_set_tlsctx(listener->outer, tlsctx);
+}
+
+void
+isc__nm_http_set_max_streams(isc_nmsocket_t *listener,
+ const uint32_t max_concurrent_streams) {
+ uint32_t max_streams = NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS;
+
+ REQUIRE(VALID_NMSOCK(listener));
+ REQUIRE(listener->type == isc_nm_httplistener);
+
+ if (max_concurrent_streams > 0 &&
+ max_concurrent_streams < NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS)
+ {
+ max_streams = max_concurrent_streams;
+ }
+
+ atomic_store(&listener->h2.max_concurrent_streams, max_streams);
+}
+
+void
+isc_nm_http_set_endpoints(isc_nmsocket_t *listener,
+ isc_nm_http_endpoints_t *eps) {
+ size_t nworkers;
+
+ REQUIRE(VALID_NMSOCK(listener));
+ REQUIRE(listener->type == isc_nm_httplistener);
+ REQUIRE(VALID_HTTP_ENDPOINTS(eps));
+
+ atomic_store(&eps->in_use, true);
+
+ nworkers = (size_t)listener->mgr->nworkers;
+ for (size_t i = 0; i < nworkers; i++) {
+ isc__netievent__http_eps_t *ievent =
+ isc__nm_get_netievent_httpendpoints(listener->mgr,
+ listener, eps);
+ isc__nm_enqueue_ievent(&listener->mgr->workers[i],
+ (isc__netievent_t *)ievent);
+ }
+}
+
+void
+isc__nm_async_httpendpoints(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent__http_eps_t *ievent = (isc__netievent__http_eps_t *)ev0;
+ const int tid = isc_nm_tid();
+ isc_nmsocket_t *listener = ievent->sock;
+ isc_nm_http_endpoints_t *eps = ievent->endpoints;
+ UNUSED(worker);
+
+ isc_nm_http_endpoints_detach(&listener->h2.listener_endpoints[tid]);
+ isc_nm_http_endpoints_attach(eps,
+ &listener->h2.listener_endpoints[tid]);
+}
+
+static void
+http_init_listener_endpoints(isc_nmsocket_t *listener,
+ isc_nm_http_endpoints_t *epset) {
+ size_t nworkers;
+
+ REQUIRE(VALID_NMSOCK(listener));
+ REQUIRE(VALID_NM(listener->mgr));
+ REQUIRE(VALID_HTTP_ENDPOINTS(epset));
+
+ nworkers = (size_t)listener->mgr->nworkers;
+ INSIST(nworkers > 0);
+
+ listener->h2.listener_endpoints =
+ isc_mem_get(listener->mgr->mctx,
+ sizeof(isc_nm_http_endpoints_t *) * nworkers);
+ listener->h2.n_listener_endpoints = nworkers;
+ for (size_t i = 0; i < nworkers; i++) {
+ listener->h2.listener_endpoints[i] = NULL;
+ isc_nm_http_endpoints_attach(
+ epset, &listener->h2.listener_endpoints[i]);
+ }
+}
+
+static void
+http_cleanup_listener_endpoints(isc_nmsocket_t *listener) {
+ REQUIRE(VALID_NM(listener->mgr));
+
+ if (listener->h2.listener_endpoints == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < listener->h2.n_listener_endpoints; i++) {
+ isc_nm_http_endpoints_detach(
+ &listener->h2.listener_endpoints[i]);
+ }
+ isc_mem_put(listener->mgr->mctx, listener->h2.listener_endpoints,
+ sizeof(isc_nm_http_endpoints_t *) *
+ listener->h2.n_listener_endpoints);
+ listener->h2.n_listener_endpoints = 0;
+}
+
+static isc_nm_http_endpoints_t *
+http_get_listener_endpoints(isc_nmsocket_t *listener, const int tid) {
+ isc_nm_http_endpoints_t *eps;
+ REQUIRE(VALID_NMSOCK(listener));
+ REQUIRE(tid >= 0);
+ REQUIRE((size_t)tid < listener->h2.n_listener_endpoints);
+
+ eps = listener->h2.listener_endpoints[tid];
+ INSIST(eps != NULL);
+ return (eps);
+}
+
+static const bool base64url_validation_table[256] = {
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, true, false, false, true, true,
+ true, true, true, true, true, true, true, true, false, false,
+ false, false, false, false, false, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true,
+ true, false, false, false, false, true, false, true, true, true,
+ true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true,
+ true, true, true, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false
+};
+
+char *
+isc__nm_base64url_to_base64(isc_mem_t *mem, const char *base64url,
+ const size_t base64url_len, size_t *res_len) {
+ char *res = NULL;
+ size_t i, k, len;
+
+ if (mem == NULL || base64url == NULL || base64url_len == 0) {
+ return (NULL);
+ }
+
+ len = base64url_len % 4 ? base64url_len + (4 - base64url_len % 4)
+ : base64url_len;
+ res = isc_mem_allocate(mem, len + 1); /* '\0' */
+
+ for (i = 0; i < base64url_len; i++) {
+ switch (base64url[i]) {
+ case '-':
+ res[i] = '+';
+ break;
+ case '_':
+ res[i] = '/';
+ break;
+ default:
+ if (base64url_validation_table[(size_t)base64url[i]]) {
+ res[i] = base64url[i];
+ } else {
+ isc_mem_free(mem, res);
+ return (NULL);
+ }
+ break;
+ }
+ }
+
+ if (base64url_len % 4 != 0) {
+ for (k = 0; k < (4 - base64url_len % 4); k++, i++) {
+ res[i] = '=';
+ }
+ }
+
+ INSIST(i == len);
+
+ if (res_len != NULL) {
+ *res_len = len;
+ }
+
+ res[len] = '\0';
+
+ return (res);
+}
+
+char *
+isc__nm_base64_to_base64url(isc_mem_t *mem, const char *base64,
+ const size_t base64_len, size_t *res_len) {
+ char *res = NULL;
+ size_t i;
+
+ if (mem == NULL || base64 == NULL || base64_len == 0) {
+ return (NULL);
+ }
+
+ res = isc_mem_allocate(mem, base64_len + 1); /* '\0' */
+
+ for (i = 0; i < base64_len; i++) {
+ switch (base64[i]) {
+ case '+':
+ res[i] = '-';
+ break;
+ case '/':
+ res[i] = '_';
+ break;
+ case '=':
+ goto end;
+ break;
+ default:
+ /*
+ * All other characters from the alphabet are the same
+ * for both base64 and base64url, so we can reuse the
+ * validation table for the rest of the characters.
+ */
+ if (base64[i] != '-' && base64[i] != '_' &&
+ base64url_validation_table[(size_t)base64[i]])
+ {
+ res[i] = base64[i];
+ } else {
+ isc_mem_free(mem, res);
+ return (NULL);
+ }
+ break;
+ }
+ }
+end:
+ if (res_len) {
+ *res_len = i;
+ }
+
+ res[i] = '\0';
+
+ return (res);
+}
+
+void
+isc__nm_http_initsocket(isc_nmsocket_t *sock) {
+ REQUIRE(sock != NULL);
+
+ sock->h2 = (isc_nmsocket_h2_t){
+ .request_type = ISC_HTTP_REQ_UNSUPPORTED,
+ .request_scheme = ISC_HTTP_SCHEME_UNSUPPORTED,
+ };
+}
+
+void
+isc__nm_http_cleanup_data(isc_nmsocket_t *sock) {
+ if ((sock->type == isc_nm_tcplistener ||
+ sock->type == isc_nm_tlslistener) &&
+ sock->h2.httpserver != NULL)
+ {
+ isc__nmsocket_detach(&sock->h2.httpserver);
+ }
+
+ if (sock->type == isc_nm_httplistener ||
+ sock->type == isc_nm_httpsocket)
+ {
+ if (sock->type == isc_nm_httplistener &&
+ sock->h2.listener_endpoints != NULL)
+ {
+ /* Delete all handlers */
+ http_cleanup_listener_endpoints(sock);
+ }
+
+ if (sock->h2.request_path != NULL) {
+ isc_mem_free(sock->mgr->mctx, sock->h2.request_path);
+ sock->h2.request_path = NULL;
+ }
+
+ if (sock->h2.query_data != NULL) {
+ isc_mem_free(sock->mgr->mctx, sock->h2.query_data);
+ sock->h2.query_data = NULL;
+ }
+
+ INSIST(sock->h2.connect.cstream == NULL);
+
+ if (isc_buffer_base(&sock->h2.rbuf) != NULL) {
+ void *base = isc_buffer_base(&sock->h2.rbuf);
+ isc_mem_free(sock->mgr->mctx, base);
+ isc_buffer_initnull(&sock->h2.rbuf);
+ }
+ }
+
+ if ((sock->type == isc_nm_httplistener ||
+ sock->type == isc_nm_httpsocket ||
+ sock->type == isc_nm_tcpsocket ||
+ sock->type == isc_nm_tlssocket) &&
+ sock->h2.session != NULL)
+ {
+ if (sock->h2.connect.uri != NULL) {
+ isc_mem_free(sock->mgr->mctx, sock->h2.connect.uri);
+ sock->h2.connect.uri = NULL;
+ }
+ isc__nm_httpsession_detach(&sock->h2.session);
+ }
+}
+
+void
+isc__nm_http_cleartimeout(isc_nmhandle_t *handle) {
+ isc_nmsocket_t *sock = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+ REQUIRE(handle->sock->type == isc_nm_httpsocket);
+
+ sock = handle->sock;
+ if (sock->h2.session != NULL && sock->h2.session->handle != NULL) {
+ INSIST(VALID_HTTP2_SESSION(sock->h2.session));
+ INSIST(VALID_NMHANDLE(sock->h2.session->handle));
+ isc_nmhandle_cleartimeout(sock->h2.session->handle);
+ }
+}
+
+void
+isc__nm_http_settimeout(isc_nmhandle_t *handle, uint32_t timeout) {
+ isc_nmsocket_t *sock = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+ REQUIRE(handle->sock->type == isc_nm_httpsocket);
+
+ sock = handle->sock;
+ if (sock->h2.session != NULL && sock->h2.session->handle != NULL) {
+ INSIST(VALID_HTTP2_SESSION(sock->h2.session));
+ INSIST(VALID_NMHANDLE(sock->h2.session->handle));
+ isc_nmhandle_settimeout(sock->h2.session->handle, timeout);
+ }
+}
+
+void
+isc__nmhandle_http_keepalive(isc_nmhandle_t *handle, bool value) {
+ isc_nmsocket_t *sock = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+ REQUIRE(handle->sock->type == isc_nm_httpsocket);
+
+ sock = handle->sock;
+ if (sock->h2.session != NULL && sock->h2.session->handle) {
+ INSIST(VALID_HTTP2_SESSION(sock->h2.session));
+ INSIST(VALID_NMHANDLE(sock->h2.session->handle));
+
+ isc_nmhandle_keepalive(sock->h2.session->handle, value);
+ }
+}
+
+void
+isc_nm_http_makeuri(const bool https, const isc_sockaddr_t *sa,
+ const char *hostname, const uint16_t http_port,
+ const char *abs_path, char *outbuf,
+ const size_t outbuf_len) {
+ char saddr[INET6_ADDRSTRLEN] = { 0 };
+ int family;
+ bool ipv6_addr = false;
+ struct sockaddr_in6 sa6;
+ uint16_t host_port = http_port;
+ const char *host = NULL;
+
+ REQUIRE(outbuf != NULL);
+ REQUIRE(outbuf_len != 0);
+ REQUIRE(isc_nm_http_path_isvalid(abs_path));
+
+ /* If hostname is specified, use that. */
+ if (hostname != NULL && hostname[0] != '\0') {
+ /*
+ * The host name could be an IPv6 address. If so,
+ * wrap it between [ and ].
+ */
+ if (inet_pton(AF_INET6, hostname, &sa6) == 1 &&
+ hostname[0] != '[')
+ {
+ ipv6_addr = true;
+ }
+ host = hostname;
+ } else {
+ /*
+ * A hostname was not specified; build one from
+ * the given IP address.
+ */
+ INSIST(sa != NULL);
+ family = ((const struct sockaddr *)&sa->type.sa)->sa_family;
+ host_port = ntohs(family == AF_INET ? sa->type.sin.sin_port
+ : sa->type.sin6.sin6_port);
+ ipv6_addr = family == AF_INET6;
+ (void)inet_ntop(
+ family,
+ family == AF_INET
+ ? (const struct sockaddr *)&sa->type.sin.sin_addr
+ : (const struct sockaddr *)&sa->type.sin6
+ .sin6_addr,
+ saddr, sizeof(saddr));
+ host = saddr;
+ }
+
+ /*
+ * If the port number was not specified, the default
+ * depends on whether we're using encryption or not.
+ */
+ if (host_port == 0) {
+ host_port = https ? 443 : 80;
+ }
+
+ (void)snprintf(outbuf, outbuf_len, "%s://%s%s%s:%u%s",
+ https ? "https" : "http", ipv6_addr ? "[" : "", host,
+ ipv6_addr ? "]" : "", host_port, abs_path);
+}
+
+/*
+ * DoH GET Query String Scanner-less Recursive Descent Parser/Verifier
+ *
+ * It is based on the following grammar (using WSN/EBNF):
+ *
+ * S = query-string.
+ * query-string = ['?'] { key-value-pair } EOF.
+ * key-value-pair = key '=' value [ '&' ].
+ * key = ('_' | alpha) { '_' | alnum}.
+ * value = value-char {value-char}.
+ * value-char = unreserved-char | percent-charcode.
+ * unreserved-char = alnum |'_' | '.' | '-' | '~'. (* RFC3986, Section 2.3 *)
+ * percent-charcode = '%' hexdigit hexdigit.
+ * ...
+ *
+ * Should be good enough.
+ */
+typedef struct isc_httpparser_state {
+ const char *str;
+
+ const char *last_key;
+ size_t last_key_len;
+
+ const char *last_value;
+ size_t last_value_len;
+
+ bool query_found;
+ const char *query;
+ size_t query_len;
+} isc_httpparser_state_t;
+
+#define MATCH(ch) (st->str[0] == (ch))
+#define MATCH_ALPHA() isalpha((unsigned char)(st->str[0]))
+#define MATCH_DIGIT() isdigit((unsigned char)(st->str[0]))
+#define MATCH_ALNUM() isalnum((unsigned char)(st->str[0]))
+#define MATCH_XDIGIT() isxdigit((unsigned char)(st->str[0]))
+#define ADVANCE() st->str++
+#define GETP() (st->str)
+
+static bool
+rule_query_string(isc_httpparser_state_t *st);
+
+bool
+isc__nm_parse_httpquery(const char *query_string, const char **start,
+ size_t *len) {
+ isc_httpparser_state_t state;
+
+ REQUIRE(start != NULL);
+ REQUIRE(len != NULL);
+
+ if (query_string == NULL || query_string[0] == '\0') {
+ return (false);
+ }
+
+ state = (isc_httpparser_state_t){ .str = query_string };
+ if (!rule_query_string(&state)) {
+ return (false);
+ }
+
+ if (!state.query_found) {
+ return (false);
+ }
+
+ *start = state.query;
+ *len = state.query_len;
+
+ return (true);
+}
+
+static bool
+rule_key_value_pair(isc_httpparser_state_t *st);
+
+static bool
+rule_key(isc_httpparser_state_t *st);
+
+static bool
+rule_value(isc_httpparser_state_t *st);
+
+static bool
+rule_value_char(isc_httpparser_state_t *st);
+
+static bool
+rule_percent_charcode(isc_httpparser_state_t *st);
+
+static bool
+rule_unreserved_char(isc_httpparser_state_t *st);
+
+static bool
+rule_query_string(isc_httpparser_state_t *st) {
+ if (MATCH('?')) {
+ ADVANCE();
+ }
+
+ while (rule_key_value_pair(st)) {
+ /* skip */;
+ }
+
+ if (!MATCH('\0')) {
+ return (false);
+ }
+
+ ADVANCE();
+ return (true);
+}
+
+static bool
+rule_key_value_pair(isc_httpparser_state_t *st) {
+ if (!rule_key(st)) {
+ return (false);
+ }
+
+ if (MATCH('=')) {
+ ADVANCE();
+ } else {
+ return (false);
+ }
+
+ if (rule_value(st)) {
+ const char dns[] = "dns";
+ if (st->last_key_len == sizeof(dns) - 1 &&
+ memcmp(st->last_key, dns, sizeof(dns) - 1) == 0)
+ {
+ st->query_found = true;
+ st->query = st->last_value;
+ st->query_len = st->last_value_len;
+ }
+ } else {
+ return (false);
+ }
+
+ if (MATCH('&')) {
+ ADVANCE();
+ }
+
+ return (true);
+}
+
+static bool
+rule_key(isc_httpparser_state_t *st) {
+ if (MATCH('_') || MATCH_ALPHA()) {
+ st->last_key = GETP();
+ ADVANCE();
+ } else {
+ return (false);
+ }
+
+ while (MATCH('_') || MATCH_ALNUM()) {
+ ADVANCE();
+ }
+
+ st->last_key_len = GETP() - st->last_key;
+ return (true);
+}
+
+static bool
+rule_value(isc_httpparser_state_t *st) {
+ const char *s = GETP();
+ if (!rule_value_char(st)) {
+ return (false);
+ }
+
+ st->last_value = s;
+ while (rule_value_char(st)) {
+ /* skip */;
+ }
+ st->last_value_len = GETP() - st->last_value;
+ return (true);
+}
+
+static bool
+rule_value_char(isc_httpparser_state_t *st) {
+ if (rule_unreserved_char(st)) {
+ return (true);
+ }
+
+ return (rule_percent_charcode(st));
+}
+
+static bool
+rule_unreserved_char(isc_httpparser_state_t *st) {
+ if (MATCH_ALNUM() || MATCH('_') || MATCH('.') || MATCH('-') ||
+ MATCH('~'))
+ {
+ ADVANCE();
+ return (true);
+ }
+ return (false);
+}
+
+static bool
+rule_percent_charcode(isc_httpparser_state_t *st) {
+ if (MATCH('%')) {
+ ADVANCE();
+ } else {
+ return (false);
+ }
+
+ if (!MATCH_XDIGIT()) {
+ return (false);
+ }
+ ADVANCE();
+
+ if (!MATCH_XDIGIT()) {
+ return (false);
+ }
+ ADVANCE();
+
+ return (true);
+}
+
+/*
+ * DoH URL Location Verifier. Based on the following grammar (EBNF/WSN
+ * notation):
+ *
+ * S = path_absolute.
+ * path_absolute = '/' [ segments ] '\0'.
+ * segments = segment_nz { slash_segment }.
+ * slash_segment = '/' segment.
+ * segment = { pchar }.
+ * segment_nz = pchar { pchar }.
+ * pchar = unreserved | pct_encoded | sub_delims | ':' | '@'.
+ * unreserved = ALPHA | DIGIT | '-' | '.' | '_' | '~'.
+ * pct_encoded = '%' XDIGIT XDIGIT.
+ * sub_delims = '!' | '$' | '&' | '\'' | '(' | ')' | '*' | '+' |
+ * ',' | ';' | '='.
+ *
+ * The grammar is extracted from RFC 3986. It is slightly modified to
+ * aid in parser creation, but the end result is the same
+ * (path_absolute is defined slightly differently - split into
+ * multiple productions).
+ *
+ * https://datatracker.ietf.org/doc/html/rfc3986#appendix-A
+ */
+
+typedef struct isc_http_location_parser_state {
+ const char *str;
+} isc_http_location_parser_state_t;
+
+static bool
+rule_loc_path_absolute(isc_http_location_parser_state_t *);
+
+static bool
+rule_loc_segments(isc_http_location_parser_state_t *);
+
+static bool
+rule_loc_slash_segment(isc_http_location_parser_state_t *);
+
+static bool
+rule_loc_segment(isc_http_location_parser_state_t *);
+
+static bool
+rule_loc_segment_nz(isc_http_location_parser_state_t *);
+
+static bool
+rule_loc_pchar(isc_http_location_parser_state_t *);
+
+static bool
+rule_loc_unreserved(isc_http_location_parser_state_t *);
+
+static bool
+rule_loc_pct_encoded(isc_http_location_parser_state_t *);
+
+static bool
+rule_loc_sub_delims(isc_http_location_parser_state_t *);
+
+static bool
+rule_loc_path_absolute(isc_http_location_parser_state_t *st) {
+ if (MATCH('/')) {
+ ADVANCE();
+ } else {
+ return (false);
+ }
+
+ (void)rule_loc_segments(st);
+
+ if (MATCH('\0')) {
+ ADVANCE();
+ } else {
+ return (false);
+ }
+
+ return (true);
+}
+
+static bool
+rule_loc_segments(isc_http_location_parser_state_t *st) {
+ if (!rule_loc_segment_nz(st)) {
+ return (false);
+ }
+
+ while (rule_loc_slash_segment(st)) {
+ /* zero or more */;
+ }
+
+ return (true);
+}
+
+static bool
+rule_loc_slash_segment(isc_http_location_parser_state_t *st) {
+ if (MATCH('/')) {
+ ADVANCE();
+ } else {
+ return (false);
+ }
+
+ return (rule_loc_segment(st));
+}
+
+static bool
+rule_loc_segment(isc_http_location_parser_state_t *st) {
+ while (rule_loc_pchar(st)) {
+ /* zero or more */;
+ }
+
+ return (true);
+}
+
+static bool
+rule_loc_segment_nz(isc_http_location_parser_state_t *st) {
+ if (!rule_loc_pchar(st)) {
+ return (false);
+ }
+
+ while (rule_loc_pchar(st)) {
+ /* zero or more */;
+ }
+
+ return (true);
+}
+
+static bool
+rule_loc_pchar(isc_http_location_parser_state_t *st) {
+ if (rule_loc_unreserved(st)) {
+ return (true);
+ } else if (rule_loc_pct_encoded(st)) {
+ return (true);
+ } else if (rule_loc_sub_delims(st)) {
+ return (true);
+ } else if (MATCH(':') || MATCH('@')) {
+ ADVANCE();
+ return (true);
+ }
+
+ return (false);
+}
+
+static bool
+rule_loc_unreserved(isc_http_location_parser_state_t *st) {
+ if (MATCH_ALPHA() | MATCH_DIGIT() | MATCH('-') | MATCH('.') |
+ MATCH('_') | MATCH('~'))
+ {
+ ADVANCE();
+ return (true);
+ }
+ return (false);
+}
+
+static bool
+rule_loc_pct_encoded(isc_http_location_parser_state_t *st) {
+ if (!MATCH('%')) {
+ return (false);
+ }
+ ADVANCE();
+
+ if (!MATCH_XDIGIT()) {
+ return (false);
+ }
+ ADVANCE();
+
+ if (!MATCH_XDIGIT()) {
+ return (false);
+ }
+ ADVANCE();
+
+ return (true);
+}
+
+static bool
+rule_loc_sub_delims(isc_http_location_parser_state_t *st) {
+ if (MATCH('!') | MATCH('$') | MATCH('&') | MATCH('\'') | MATCH('(') |
+ MATCH(')') | MATCH('*') | MATCH('+') | MATCH(',') | MATCH(';') |
+ MATCH('='))
+ {
+ ADVANCE();
+ return (true);
+ }
+
+ return (false);
+}
+
+bool
+isc_nm_http_path_isvalid(const char *path) {
+ isc_http_location_parser_state_t state = { 0 };
+
+ REQUIRE(path != NULL);
+
+ state.str = path;
+
+ return (rule_loc_path_absolute(&state));
+}
diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h
new file mode 100644
index 0000000..364a933
--- /dev/null
+++ b/lib/isc/netmgr/netmgr-int.h
@@ -0,0 +1,2273 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <unistd.h>
+#include <uv.h>
+
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+
+#include <isc/astack.h>
+#include <isc/atomic.h>
+#include <isc/barrier.h>
+#include <isc/buffer.h>
+#include <isc/condition.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/netmgr.h>
+#include <isc/quota.h>
+#include <isc/random.h>
+#include <isc/refcount.h>
+#include <isc/region.h>
+#include <isc/result.h>
+#include <isc/sockaddr.h>
+#include <isc/stats.h>
+#include <isc/thread.h>
+#include <isc/tls.h>
+#include <isc/util.h>
+
+#include "uv-compat.h"
+
+#define ISC_NETMGR_TID_UNKNOWN -1
+
+/* Must be different from ISC_NETMGR_TID_UNKNOWN */
+#define ISC_NETMGR_NON_INTERLOCKED -2
+
+/*
+ * Receive buffers
+ */
+#if HAVE_DECL_UV_UDP_MMSG_CHUNK
+/*
+ * The value 20 here is UV__MMSG_MAXWIDTH taken from the current libuv source,
+ * libuv will not receive more that 20 datagrams in a single recvmmsg call.
+ */
+#define ISC_NETMGR_UDP_RECVBUF_SIZE (20 * UINT16_MAX)
+#else
+/*
+ * A single DNS message size
+ */
+#define ISC_NETMGR_UDP_RECVBUF_SIZE UINT16_MAX
+#endif
+
+/*
+ * The TCP receive buffer can fit one maximum sized DNS message plus its size,
+ * the receive buffer here affects TCP, DoT and DoH.
+ */
+#define ISC_NETMGR_TCP_RECVBUF_SIZE (sizeof(uint16_t) + UINT16_MAX)
+
+/* Pick the larger buffer */
+#define ISC_NETMGR_RECVBUF_SIZE \
+ (ISC_NETMGR_UDP_RECVBUF_SIZE >= ISC_NETMGR_TCP_RECVBUF_SIZE \
+ ? ISC_NETMGR_UDP_RECVBUF_SIZE \
+ : ISC_NETMGR_TCP_RECVBUF_SIZE)
+
+/*
+ * Send buffer
+ */
+#define ISC_NETMGR_SENDBUF_SIZE (sizeof(uint16_t) + UINT16_MAX)
+
+/*
+ * Make sure our RECVBUF size is large enough
+ */
+
+STATIC_ASSERT(ISC_NETMGR_UDP_RECVBUF_SIZE <= ISC_NETMGR_RECVBUF_SIZE,
+ "UDP receive buffer size must be smaller or equal than worker "
+ "receive buffer size");
+
+STATIC_ASSERT(ISC_NETMGR_TCP_RECVBUF_SIZE <= ISC_NETMGR_RECVBUF_SIZE,
+ "TCP receive buffer size must be smaller or equal than worker "
+ "receive buffer size");
+
+/*%
+ * Regular TCP buffer size.
+ */
+#define NM_REG_BUF 4096
+
+/*%
+ * Larger buffer for when the regular one isn't enough; this will
+ * hold two full DNS packets with lengths. netmgr receives 64k at
+ * most in TCPDNS or TLSDNS connections, so there's no risk of overrun
+ * when using a buffer this size.
+ */
+#define NM_BIG_BUF ISC_NETMGR_TCP_RECVBUF_SIZE * 2
+
+/*%
+ * Maximum segment size (MSS) of TCP socket on which the server responds to
+ * queries. Value lower than common MSS on Ethernet (1220, that is 1280 (IPv6
+ * minimum link MTU) - 40 (IPv6 fixed header) - 20 (TCP fixed header)) will
+ * address path MTU problem.
+ */
+#define NM_MAXSEG (1280 - 20 - 40)
+
+/*
+ * Define NETMGR_TRACE to activate tracing of handles and sockets.
+ * This will impair performance but enables us to quickly determine,
+ * if netmgr resources haven't been cleaned up on shutdown, which ones
+ * are still in use.
+ */
+#ifdef NETMGR_TRACE
+#define TRACE_SIZE 8
+
+void
+isc__nm_dump_active(isc_nm_t *nm);
+
+#if defined(__linux__)
+#include <syscall.h>
+#define gettid() (uint32_t) syscall(SYS_gettid)
+#else
+#define gettid() (uint32_t) pthread_self()
+#endif
+
+#ifdef NETMGR_TRACE_VERBOSE
+#define NETMGR_TRACE_LOG(format, ...) \
+ fprintf(stderr, "%" PRIu32 ":%d:%s:%u:%s:" format, gettid(), \
+ isc_nm_tid(), file, line, func, __VA_ARGS__)
+#else
+#define NETMGR_TRACE_LOG(format, ...) \
+ (void)file; \
+ (void)line; \
+ (void)func;
+#endif
+
+#define FLARG_PASS , file, line, func
+#define FLARG \
+ , const char *file __attribute__((unused)), \
+ unsigned int line __attribute__((unused)), \
+ const char *func __attribute__((unused))
+#define FLARG_IEVENT(ievent) \
+ const char *file = ievent->file; \
+ unsigned int line = ievent->line; \
+ const char *func = ievent->func;
+#define FLARG_IEVENT_PASS(ievent) \
+ ievent->file = file; \
+ ievent->line = line; \
+ ievent->func = func;
+#define isc__nm_uvreq_get(req, sock) \
+ isc___nm_uvreq_get(req, sock, __FILE__, __LINE__, __func__)
+#define isc__nm_uvreq_put(req, sock) \
+ isc___nm_uvreq_put(req, sock, __FILE__, __LINE__, __func__)
+#define isc__nmsocket_init(sock, mgr, type, iface) \
+ isc___nmsocket_init(sock, mgr, type, iface, __FILE__, __LINE__, \
+ __func__)
+#define isc__nmsocket_put(sockp) \
+ isc___nmsocket_put(sockp, __FILE__, __LINE__, __func__)
+#define isc__nmsocket_attach(sock, target) \
+ isc___nmsocket_attach(sock, target, __FILE__, __LINE__, __func__)
+#define isc__nmsocket_detach(socketp) \
+ isc___nmsocket_detach(socketp, __FILE__, __LINE__, __func__)
+#define isc__nmsocket_close(socketp) \
+ isc___nmsocket_close(socketp, __FILE__, __LINE__, __func__)
+#define isc__nmhandle_get(sock, peer, local) \
+ isc___nmhandle_get(sock, peer, local, __FILE__, __LINE__, __func__)
+#define isc__nmsocket_prep_destroy(sock) \
+ isc___nmsocket_prep_destroy(sock, __FILE__, __LINE__, __func__)
+#else
+#define NETMGR_TRACE_LOG(format, ...)
+
+#define FLARG_PASS
+#define FLARG
+#define FLARG_IEVENT(ievent)
+#define FLARG_IEVENT_PASS(ievent)
+#define isc__nm_uvreq_get(req, sock) isc___nm_uvreq_get(req, sock)
+#define isc__nm_uvreq_put(req, sock) isc___nm_uvreq_put(req, sock)
+#define isc__nmsocket_init(sock, mgr, type, iface) \
+ isc___nmsocket_init(sock, mgr, type, iface)
+#define isc__nmsocket_put(sockp) isc___nmsocket_put(sockp)
+#define isc__nmsocket_attach(sock, target) isc___nmsocket_attach(sock, target)
+#define isc__nmsocket_detach(socketp) isc___nmsocket_detach(socketp)
+#define isc__nmsocket_close(socketp) isc___nmsocket_close(socketp)
+#define isc__nmhandle_get(sock, peer, local) \
+ isc___nmhandle_get(sock, peer, local)
+#define isc__nmsocket_prep_destroy(sock) isc___nmsocket_prep_destroy(sock)
+#endif
+
+/*
+ * Queue types in the order of processing priority.
+ */
+typedef enum {
+ NETIEVENT_PRIORITY = 0,
+ NETIEVENT_PRIVILEGED = 1,
+ NETIEVENT_TASK = 2,
+ NETIEVENT_NORMAL = 3,
+ NETIEVENT_MAX = 4,
+} netievent_type_t;
+
+typedef struct isc__nm_uvreq isc__nm_uvreq_t;
+typedef struct isc__netievent isc__netievent_t;
+
+typedef ISC_LIST(isc__netievent_t) isc__netievent_list_t;
+
+typedef struct ievent {
+ isc_mutex_t lock;
+ isc_condition_t cond;
+ isc__netievent_list_t list;
+} ievent_t;
+
+/*
+ * Single network event loop worker.
+ */
+typedef struct isc__networker {
+ isc_nm_t *mgr;
+ int id; /* thread id */
+ uv_loop_t loop; /* libuv loop structure */
+ uv_async_t async; /* async channel to send
+ * data to this networker */
+ bool paused;
+ bool finished;
+ isc_thread_t thread;
+ ievent_t ievents[NETIEVENT_MAX];
+
+ isc_refcount_t references;
+ atomic_int_fast64_t pktcount;
+ char *recvbuf;
+ char *sendbuf;
+ bool recvbuf_inuse;
+} isc__networker_t;
+
+/*
+ * A general handle for a connection bound to a networker. For UDP
+ * connections we have peer address here, so both TCP and UDP can be
+ * handled with a simple send-like function
+ */
+#define NMHANDLE_MAGIC ISC_MAGIC('N', 'M', 'H', 'D')
+#define VALID_NMHANDLE(t) \
+ (ISC_MAGIC_VALID(t, NMHANDLE_MAGIC) && \
+ atomic_load(&(t)->references) > 0)
+
+typedef void (*isc__nm_closecb)(isc_nmhandle_t *);
+typedef struct isc_nm_http_session isc_nm_http_session_t;
+
+struct isc_nmhandle {
+ int magic;
+ isc_refcount_t references;
+
+ /*
+ * The socket is not 'attached' in the traditional
+ * reference-counting sense. Instead, we keep all handles in an
+ * array in the socket object. This way, we don't have circular
+ * dependencies and we can close all handles when we're destroying
+ * the socket.
+ */
+ isc_nmsocket_t *sock;
+
+ isc_nm_http_session_t *httpsession;
+
+ isc_sockaddr_t peer;
+ isc_sockaddr_t local;
+ isc_nm_opaquecb_t doreset; /* reset extra callback, external */
+ isc_nm_opaquecb_t dofree; /* free extra callback, external */
+#ifdef NETMGR_TRACE
+ void *backtrace[TRACE_SIZE];
+ int backtrace_size;
+ LINK(isc_nmhandle_t) active_link;
+#endif
+ void *opaque;
+ char extra[];
+};
+
+typedef enum isc__netievent_type {
+ netievent_udpconnect,
+ netievent_udpclose,
+ netievent_udpsend,
+ netievent_udpread,
+ netievent_udpcancel,
+
+ netievent_routeconnect,
+
+ netievent_tcpconnect,
+ netievent_tcpclose,
+ netievent_tcpsend,
+ netievent_tcpstartread,
+ netievent_tcppauseread,
+ netievent_tcpaccept,
+ netievent_tcpcancel,
+
+ netievent_tcpdnsaccept,
+ netievent_tcpdnsconnect,
+ netievent_tcpdnsclose,
+ netievent_tcpdnssend,
+ netievent_tcpdnsread,
+ netievent_tcpdnscancel,
+
+ netievent_tlsclose,
+ netievent_tlssend,
+ netievent_tlsstartread,
+ netievent_tlsconnect,
+ netievent_tlsdobio,
+ netievent_tlscancel,
+
+ netievent_tlsdnsaccept,
+ netievent_tlsdnsconnect,
+ netievent_tlsdnsclose,
+ netievent_tlsdnssend,
+ netievent_tlsdnsread,
+ netievent_tlsdnscancel,
+ netievent_tlsdnscycle,
+ netievent_tlsdnsshutdown,
+
+ netievent_httpclose,
+ netievent_httpsend,
+ netievent_httpendpoints,
+
+ netievent_shutdown,
+ netievent_stop,
+ netievent_pause,
+
+ netievent_connectcb,
+ netievent_readcb,
+ netievent_sendcb,
+
+ netievent_detach,
+ netievent_close,
+
+ netievent_task,
+ netievent_privilegedtask,
+
+ netievent_settlsctx,
+
+ /*
+ * event type values higher than this will be treated
+ * as high-priority events, which can be processed
+ * while the netmgr is pausing or paused.
+ */
+ netievent_prio = 0xff,
+
+ netievent_udplisten,
+ netievent_udpstop,
+ netievent_tcplisten,
+ netievent_tcpstop,
+ netievent_tcpdnslisten,
+ netievent_tcpdnsstop,
+ netievent_tlsdnslisten,
+ netievent_tlsdnsstop,
+ netievent_sockstop, /* for multilayer sockets */
+
+ netievent_resume,
+} isc__netievent_type;
+
+typedef union {
+ isc_nm_recv_cb_t recv;
+ isc_nm_cb_t send;
+ isc_nm_cb_t connect;
+ isc_nm_accept_cb_t accept;
+} isc__nm_cb_t;
+
+/*
+ * Wrapper around uv_req_t with 'our' fields in it. req->data should
+ * always point to its parent. Note that we always allocate more than
+ * sizeof(struct) because we make room for different req types;
+ */
+#define UVREQ_MAGIC ISC_MAGIC('N', 'M', 'U', 'R')
+#define VALID_UVREQ(t) ISC_MAGIC_VALID(t, UVREQ_MAGIC)
+
+typedef struct isc__nm_uvreq isc__nm_uvreq_t;
+struct isc__nm_uvreq {
+ int magic;
+ isc_nmsocket_t *sock;
+ isc_nmhandle_t *handle;
+ char tcplen[2]; /* The TCP DNS message length */
+ uv_buf_t uvbuf; /* translated isc_region_t, to be
+ * sent or received */
+ isc_sockaddr_t local; /* local address */
+ isc_sockaddr_t peer; /* peer address */
+ isc__nm_cb_t cb; /* callback */
+ void *cbarg; /* callback argument */
+ isc_nm_timer_t *timer; /* TCP write timer */
+ int connect_tries; /* connect retries */
+
+ union {
+ uv_handle_t handle;
+ uv_req_t req;
+ uv_getaddrinfo_t getaddrinfo;
+ uv_getnameinfo_t getnameinfo;
+ uv_shutdown_t shutdown;
+ uv_write_t write;
+ uv_connect_t connect;
+ uv_udp_send_t udp_send;
+ uv_fs_t fs;
+ uv_work_t work;
+ } uv_req;
+ ISC_LINK(isc__nm_uvreq_t) link;
+};
+
+void *
+isc__nm_get_netievent(isc_nm_t *mgr, isc__netievent_type type);
+/*%<
+ * Allocate an ievent and set the type.
+ */
+void
+isc__nm_put_netievent(isc_nm_t *mgr, void *ievent);
+
+/*
+ * The macros here are used to simulate the "inheritance" in C, there's the base
+ * netievent structure that contains just its own type and socket, and there are
+ * extended netievent types that also have handles or requests or other data.
+ *
+ * The macros here ensure that:
+ *
+ * 1. every netievent type has matching definition, declaration and
+ * implementation
+ *
+ * 2. we handle all the netievent types of same subclass the same, e.g. if the
+ * extended netievent contains handle, we always attach to the handle in
+ * the ctor and detach from the handle in dtor.
+ *
+ * There are three macros here for each netievent subclass:
+ *
+ * 1. NETIEVENT_*_TYPE(type) creates the typedef for each type; used below in
+ * this header
+ *
+ * 2. NETIEVENT_*_DECL(type) generates the declaration of the get and put
+ * functions (isc__nm_get_netievent_* and isc__nm_put_netievent_*); used
+ * below in this header
+ *
+ * 3. NETIEVENT_*_DEF(type) generates the definition of the functions; used
+ * either in netmgr.c or matching protocol file (e.g. udp.c, tcp.c, etc.)
+ */
+
+#define NETIEVENT__SOCKET \
+ isc__netievent_type type; \
+ ISC_LINK(isc__netievent_t) link; \
+ isc_nmsocket_t *sock; \
+ const char *file; \
+ unsigned int line; \
+ const char *func;
+
+typedef struct isc__netievent__socket {
+ NETIEVENT__SOCKET;
+} isc__netievent__socket_t;
+
+#define NETIEVENT_SOCKET_TYPE(type) \
+ typedef isc__netievent__socket_t isc__netievent_##type##_t;
+
+#define NETIEVENT_SOCKET_DECL(type) \
+ isc__netievent_##type##_t *isc__nm_get_netievent_##type( \
+ isc_nm_t *nm, isc_nmsocket_t *sock); \
+ void isc__nm_put_netievent_##type(isc_nm_t *nm, \
+ isc__netievent_##type##_t *ievent);
+
+#define NETIEVENT_SOCKET_DEF(type) \
+ isc__netievent_##type##_t *isc__nm_get_netievent_##type( \
+ isc_nm_t *nm, isc_nmsocket_t *sock) { \
+ isc__netievent_##type##_t *ievent = \
+ isc__nm_get_netievent(nm, netievent_##type); \
+ isc__nmsocket_attach(sock, &ievent->sock); \
+ \
+ return (ievent); \
+ } \
+ \
+ void isc__nm_put_netievent_##type(isc_nm_t *nm, \
+ isc__netievent_##type##_t *ievent) { \
+ isc__nmsocket_detach(&ievent->sock); \
+ isc__nm_put_netievent(nm, ievent); \
+ }
+
+typedef struct isc__netievent__socket_req {
+ NETIEVENT__SOCKET;
+ isc__nm_uvreq_t *req;
+} isc__netievent__socket_req_t;
+
+#define NETIEVENT_SOCKET_REQ_TYPE(type) \
+ typedef isc__netievent__socket_req_t isc__netievent_##type##_t;
+
+#define NETIEVENT_SOCKET_REQ_DECL(type) \
+ isc__netievent_##type##_t *isc__nm_get_netievent_##type( \
+ isc_nm_t *nm, isc_nmsocket_t *sock, isc__nm_uvreq_t *req); \
+ void isc__nm_put_netievent_##type(isc_nm_t *nm, \
+ isc__netievent_##type##_t *ievent);
+
+#define NETIEVENT_SOCKET_REQ_DEF(type) \
+ isc__netievent_##type##_t *isc__nm_get_netievent_##type( \
+ isc_nm_t *nm, isc_nmsocket_t *sock, isc__nm_uvreq_t *req) { \
+ isc__netievent_##type##_t *ievent = \
+ isc__nm_get_netievent(nm, netievent_##type); \
+ isc__nmsocket_attach(sock, &ievent->sock); \
+ ievent->req = req; \
+ \
+ return (ievent); \
+ } \
+ \
+ void isc__nm_put_netievent_##type(isc_nm_t *nm, \
+ isc__netievent_##type##_t *ievent) { \
+ isc__nmsocket_detach(&ievent->sock); \
+ isc__nm_put_netievent(nm, ievent); \
+ }
+
+typedef struct isc__netievent__socket_req_result {
+ NETIEVENT__SOCKET;
+ isc__nm_uvreq_t *req;
+ isc_result_t result;
+} isc__netievent__socket_req_result_t;
+
+#define NETIEVENT_SOCKET_REQ_RESULT_TYPE(type) \
+ typedef isc__netievent__socket_req_result_t isc__netievent_##type##_t;
+
+#define NETIEVENT_SOCKET_REQ_RESULT_DECL(type) \
+ isc__netievent_##type##_t *isc__nm_get_netievent_##type( \
+ isc_nm_t *nm, isc_nmsocket_t *sock, isc__nm_uvreq_t *req, \
+ isc_result_t result); \
+ void isc__nm_put_netievent_##type(isc_nm_t *nm, \
+ isc__netievent_##type##_t *ievent);
+
+#define NETIEVENT_SOCKET_REQ_RESULT_DEF(type) \
+ isc__netievent_##type##_t *isc__nm_get_netievent_##type( \
+ isc_nm_t *nm, isc_nmsocket_t *sock, isc__nm_uvreq_t *req, \
+ isc_result_t result) { \
+ isc__netievent_##type##_t *ievent = \
+ isc__nm_get_netievent(nm, netievent_##type); \
+ isc__nmsocket_attach(sock, &ievent->sock); \
+ ievent->req = req; \
+ ievent->result = result; \
+ \
+ return (ievent); \
+ } \
+ \
+ void isc__nm_put_netievent_##type(isc_nm_t *nm, \
+ isc__netievent_##type##_t *ievent) { \
+ isc__nmsocket_detach(&ievent->sock); \
+ isc__nm_put_netievent(nm, ievent); \
+ }
+
+typedef struct isc__netievent__socket_handle {
+ NETIEVENT__SOCKET;
+ isc_nmhandle_t *handle;
+} isc__netievent__socket_handle_t;
+
+#define NETIEVENT_SOCKET_HANDLE_TYPE(type) \
+ typedef isc__netievent__socket_handle_t isc__netievent_##type##_t;
+
+#define NETIEVENT_SOCKET_HANDLE_DECL(type) \
+ isc__netievent_##type##_t *isc__nm_get_netievent_##type( \
+ isc_nm_t *nm, isc_nmsocket_t *sock, isc_nmhandle_t *handle); \
+ void isc__nm_put_netievent_##type(isc_nm_t *nm, \
+ isc__netievent_##type##_t *ievent);
+
+#define NETIEVENT_SOCKET_HANDLE_DEF(type) \
+ isc__netievent_##type##_t *isc__nm_get_netievent_##type( \
+ isc_nm_t *nm, isc_nmsocket_t *sock, isc_nmhandle_t *handle) { \
+ isc__netievent_##type##_t *ievent = \
+ isc__nm_get_netievent(nm, netievent_##type); \
+ isc__nmsocket_attach(sock, &ievent->sock); \
+ isc_nmhandle_attach(handle, &ievent->handle); \
+ \
+ return (ievent); \
+ } \
+ \
+ void isc__nm_put_netievent_##type(isc_nm_t *nm, \
+ isc__netievent_##type##_t *ievent) { \
+ isc__nmsocket_detach(&ievent->sock); \
+ isc_nmhandle_detach(&ievent->handle); \
+ isc__nm_put_netievent(nm, ievent); \
+ }
+
+typedef struct isc__netievent__socket_quota {
+ NETIEVENT__SOCKET;
+ isc_quota_t *quota;
+} isc__netievent__socket_quota_t;
+
+#define NETIEVENT_SOCKET_QUOTA_TYPE(type) \
+ typedef isc__netievent__socket_quota_t isc__netievent_##type##_t;
+
+#define NETIEVENT_SOCKET_QUOTA_DECL(type) \
+ isc__netievent_##type##_t *isc__nm_get_netievent_##type( \
+ isc_nm_t *nm, isc_nmsocket_t *sock, isc_quota_t *quota); \
+ void isc__nm_put_netievent_##type(isc_nm_t *nm, \
+ isc__netievent_##type##_t *ievent);
+
+#define NETIEVENT_SOCKET_QUOTA_DEF(type) \
+ isc__netievent_##type##_t *isc__nm_get_netievent_##type( \
+ isc_nm_t *nm, isc_nmsocket_t *sock, isc_quota_t *quota) { \
+ isc__netievent_##type##_t *ievent = \
+ isc__nm_get_netievent(nm, netievent_##type); \
+ isc__nmsocket_attach(sock, &ievent->sock); \
+ ievent->quota = quota; \
+ \
+ return (ievent); \
+ } \
+ \
+ void isc__nm_put_netievent_##type(isc_nm_t *nm, \
+ isc__netievent_##type##_t *ievent) { \
+ isc__nmsocket_detach(&ievent->sock); \
+ isc__nm_put_netievent(nm, ievent); \
+ }
+
+typedef struct isc__netievent__task {
+ isc__netievent_type type;
+ ISC_LINK(isc__netievent_t) link;
+ isc_task_t *task;
+} isc__netievent__task_t;
+
+#define NETIEVENT_TASK_TYPE(type) \
+ typedef isc__netievent__task_t isc__netievent_##type##_t;
+
+#define NETIEVENT_TASK_DECL(type) \
+ isc__netievent_##type##_t *isc__nm_get_netievent_##type( \
+ isc_nm_t *nm, isc_task_t *task); \
+ void isc__nm_put_netievent_##type(isc_nm_t *nm, \
+ isc__netievent_##type##_t *ievent);
+
+#define NETIEVENT_TASK_DEF(type) \
+ isc__netievent_##type##_t *isc__nm_get_netievent_##type( \
+ isc_nm_t *nm, isc_task_t *task) { \
+ isc__netievent_##type##_t *ievent = \
+ isc__nm_get_netievent(nm, netievent_##type); \
+ ievent->task = task; \
+ \
+ return (ievent); \
+ } \
+ \
+ void isc__nm_put_netievent_##type(isc_nm_t *nm, \
+ isc__netievent_##type##_t *ievent) { \
+ ievent->task = NULL; \
+ isc__nm_put_netievent(nm, ievent); \
+ }
+
+typedef struct isc__netievent_udpsend {
+ NETIEVENT__SOCKET;
+ isc_sockaddr_t peer;
+ isc__nm_uvreq_t *req;
+} isc__netievent_udpsend_t;
+
+typedef struct isc__netievent_tlsconnect {
+ NETIEVENT__SOCKET;
+ SSL_CTX *ctx;
+ isc_sockaddr_t local; /* local address */
+ isc_sockaddr_t peer; /* peer address */
+} isc__netievent_tlsconnect_t;
+
+typedef struct isc__netievent {
+ isc__netievent_type type;
+ ISC_LINK(isc__netievent_t) link;
+} isc__netievent_t;
+
+#define NETIEVENT_TYPE(type) typedef isc__netievent_t isc__netievent_##type##_t;
+
+#define NETIEVENT_DECL(type) \
+ isc__netievent_##type##_t *isc__nm_get_netievent_##type(isc_nm_t *nm); \
+ void isc__nm_put_netievent_##type(isc_nm_t *nm, \
+ isc__netievent_##type##_t *ievent);
+
+#define NETIEVENT_DEF(type) \
+ isc__netievent_##type##_t *isc__nm_get_netievent_##type( \
+ isc_nm_t *nm) { \
+ isc__netievent_##type##_t *ievent = \
+ isc__nm_get_netievent(nm, netievent_##type); \
+ \
+ return (ievent); \
+ } \
+ \
+ void isc__nm_put_netievent_##type(isc_nm_t *nm, \
+ isc__netievent_##type##_t *ievent) { \
+ isc__nm_put_netievent(nm, ievent); \
+ }
+
+typedef struct isc__netievent__tlsctx {
+ NETIEVENT__SOCKET;
+ isc_tlsctx_t *tlsctx;
+} isc__netievent__tlsctx_t;
+
+#define NETIEVENT_SOCKET_TLSCTX_TYPE(type) \
+ typedef isc__netievent__tlsctx_t isc__netievent_##type##_t;
+
+#define NETIEVENT_SOCKET_TLSCTX_DECL(type) \
+ isc__netievent_##type##_t *isc__nm_get_netievent_##type( \
+ isc_nm_t *nm, isc_nmsocket_t *sock, isc_tlsctx_t *tlsctx); \
+ void isc__nm_put_netievent_##type(isc_nm_t *nm, \
+ isc__netievent_##type##_t *ievent);
+
+#define NETIEVENT_SOCKET_TLSCTX_DEF(type) \
+ isc__netievent_##type##_t *isc__nm_get_netievent_##type( \
+ isc_nm_t *nm, isc_nmsocket_t *sock, isc_tlsctx_t *tlsctx) { \
+ isc__netievent_##type##_t *ievent = \
+ isc__nm_get_netievent(nm, netievent_##type); \
+ isc__nmsocket_attach(sock, &ievent->sock); \
+ isc_tlsctx_attach(tlsctx, &ievent->tlsctx); \
+ \
+ return (ievent); \
+ } \
+ \
+ void isc__nm_put_netievent_##type(isc_nm_t *nm, \
+ isc__netievent_##type##_t *ievent) { \
+ isc_tlsctx_free(&ievent->tlsctx); \
+ isc__nmsocket_detach(&ievent->sock); \
+ isc__nm_put_netievent(nm, ievent); \
+ }
+
+#ifdef HAVE_LIBNGHTTP2
+typedef struct isc__netievent__http_eps {
+ NETIEVENT__SOCKET;
+ isc_nm_http_endpoints_t *endpoints;
+} isc__netievent__http_eps_t;
+
+#define NETIEVENT_SOCKET_HTTP_EPS_TYPE(type) \
+ typedef isc__netievent__http_eps_t isc__netievent_##type##_t;
+
+#define NETIEVENT_SOCKET_HTTP_EPS_DECL(type) \
+ isc__netievent_##type##_t *isc__nm_get_netievent_##type( \
+ isc_nm_t *nm, isc_nmsocket_t *sock, \
+ isc_nm_http_endpoints_t *endpoints); \
+ void isc__nm_put_netievent_##type(isc_nm_t *nm, \
+ isc__netievent_##type##_t *ievent);
+
+#define NETIEVENT_SOCKET_HTTP_EPS_DEF(type) \
+ isc__netievent_##type##_t *isc__nm_get_netievent_##type( \
+ isc_nm_t *nm, isc_nmsocket_t *sock, \
+ isc_nm_http_endpoints_t *endpoints) { \
+ isc__netievent_##type##_t *ievent = \
+ isc__nm_get_netievent(nm, netievent_##type); \
+ isc__nmsocket_attach(sock, &ievent->sock); \
+ isc_nm_http_endpoints_attach(endpoints, &ievent->endpoints); \
+ \
+ return (ievent); \
+ } \
+ \
+ void isc__nm_put_netievent_##type(isc_nm_t *nm, \
+ isc__netievent_##type##_t *ievent) { \
+ isc_nm_http_endpoints_detach(&ievent->endpoints); \
+ isc__nmsocket_detach(&ievent->sock); \
+ isc__nm_put_netievent(nm, ievent); \
+ }
+#endif /* HAVE_LIBNGHTTP2 */
+
+typedef union {
+ isc__netievent_t ni;
+ isc__netievent__socket_t nis;
+ isc__netievent__socket_req_t nisr;
+ isc__netievent_udpsend_t nius;
+ isc__netievent__socket_quota_t nisq;
+ isc__netievent_tlsconnect_t nitc;
+ isc__netievent__tlsctx_t nitls;
+#ifdef HAVE_LIBNGHTTP2
+ isc__netievent__http_eps_t nihttpeps;
+#endif /* HAVE_LIBNGHTTP2 */
+} isc__netievent_storage_t;
+
+/*
+ * Work item for a uv_work threadpool.
+ */
+typedef struct isc__nm_work {
+ isc_nm_t *netmgr;
+ uv_work_t req;
+ isc_nm_workcb_t cb;
+ isc_nm_after_workcb_t after_cb;
+ void *data;
+} isc__nm_work_t;
+
+/*
+ * Network manager
+ */
+#define NM_MAGIC ISC_MAGIC('N', 'E', 'T', 'M')
+#define VALID_NM(t) ISC_MAGIC_VALID(t, NM_MAGIC)
+
+struct isc_nm {
+ int magic;
+ isc_refcount_t references;
+ isc_mem_t *mctx;
+ int nworkers;
+ isc_mutex_t lock;
+ isc_condition_t wkstatecond;
+ isc_condition_t wkpausecond;
+ isc__networker_t *workers;
+
+ isc_stats_t *stats;
+
+ uint_fast32_t workers_running;
+ atomic_uint_fast32_t workers_paused;
+ atomic_uint_fast32_t maxudp;
+
+ bool load_balance_sockets;
+
+ atomic_bool paused;
+
+ /*
+ * Active connections are being closed and new connections are
+ * no longer allowed.
+ */
+ atomic_bool closing;
+
+ /*
+ * A worker is actively waiting for other workers, for example to
+ * stop listening; that means no other thread can do the same thing
+ * or pause, or we'll deadlock. We have to either re-enqueue our
+ * event or wait for the other one to finish if we want to pause.
+ */
+ atomic_int interlocked;
+
+ /*
+ * Timeout values for TCP connections, corresponding to
+ * tcp-intiial-timeout, tcp-idle-timeout, tcp-keepalive-timeout,
+ * and tcp-advertised-timeout. Note that these are stored in
+ * milliseconds so they can be used directly with the libuv timer,
+ * but they are configured in tenths of seconds.
+ */
+ atomic_uint_fast32_t init;
+ atomic_uint_fast32_t idle;
+ atomic_uint_fast32_t keepalive;
+ atomic_uint_fast32_t advertised;
+
+ isc_barrier_t pausing;
+ isc_barrier_t resuming;
+
+ /*
+ * Socket SO_RCVBUF and SO_SNDBUF values
+ */
+ atomic_int_fast32_t recv_udp_buffer_size;
+ atomic_int_fast32_t send_udp_buffer_size;
+ atomic_int_fast32_t recv_tcp_buffer_size;
+ atomic_int_fast32_t send_tcp_buffer_size;
+
+#ifdef NETMGR_TRACE
+ ISC_LIST(isc_nmsocket_t) active_sockets;
+#endif
+};
+
+/*%
+ * A universal structure for either a single socket or a group of
+ * dup'd/SO_REUSE_PORT-using sockets listening on the same interface.
+ */
+#define NMSOCK_MAGIC ISC_MAGIC('N', 'M', 'S', 'K')
+#define VALID_NMSOCK(t) ISC_MAGIC_VALID(t, NMSOCK_MAGIC)
+
+/*%
+ * Index into socket stat counter arrays.
+ */
+typedef enum {
+ STATID_OPEN = 0,
+ STATID_OPENFAIL = 1,
+ STATID_CLOSE = 2,
+ STATID_BINDFAIL = 3,
+ STATID_CONNECTFAIL = 4,
+ STATID_CONNECT = 5,
+ STATID_ACCEPTFAIL = 6,
+ STATID_ACCEPT = 7,
+ STATID_SENDFAIL = 8,
+ STATID_RECVFAIL = 9,
+ STATID_ACTIVE = 10,
+ STATID_MAX = 11,
+} isc__nm_statid_t;
+
+#if HAVE_LIBNGHTTP2
+typedef struct isc_nmsocket_tls_send_req {
+ isc_nmsocket_t *tlssock;
+ isc_region_t data;
+ isc_nm_cb_t cb;
+ void *cbarg;
+ isc_nmhandle_t *handle;
+ bool finish;
+ uint8_t smallbuf[512];
+} isc_nmsocket_tls_send_req_t;
+
+typedef enum isc_http_request_type {
+ ISC_HTTP_REQ_GET,
+ ISC_HTTP_REQ_POST,
+ ISC_HTTP_REQ_UNSUPPORTED
+} isc_http_request_type_t;
+
+typedef enum isc_http_scheme_type {
+ ISC_HTTP_SCHEME_HTTP,
+ ISC_HTTP_SCHEME_HTTP_SECURE,
+ ISC_HTTP_SCHEME_UNSUPPORTED
+} isc_http_scheme_type_t;
+
+typedef struct isc_nm_httpcbarg {
+ isc_nm_recv_cb_t cb;
+ void *cbarg;
+ LINK(struct isc_nm_httpcbarg) link;
+} isc_nm_httpcbarg_t;
+
+typedef struct isc_nm_httphandler {
+ char *path;
+ isc_nm_recv_cb_t cb;
+ void *cbarg;
+ size_t extrahandlesize;
+ LINK(struct isc_nm_httphandler) link;
+} isc_nm_httphandler_t;
+
+struct isc_nm_http_endpoints {
+ uint32_t magic;
+ isc_mem_t *mctx;
+
+ ISC_LIST(isc_nm_httphandler_t) handlers;
+ ISC_LIST(isc_nm_httpcbarg_t) handler_cbargs;
+
+ isc_refcount_t references;
+ atomic_bool in_use;
+};
+
+typedef struct isc_nmsocket_h2 {
+ isc_nmsocket_t *psock; /* owner of the structure */
+ char *request_path;
+ char *query_data;
+ size_t query_data_len;
+ bool query_too_large;
+ isc_nm_httphandler_t *handler;
+
+ isc_buffer_t rbuf;
+ isc_buffer_t wbuf;
+
+ int32_t stream_id;
+ isc_nm_http_session_t *session;
+
+ isc_nmsocket_t *httpserver;
+
+ /* maximum concurrent streams (server-side) */
+ atomic_uint_fast32_t max_concurrent_streams;
+
+ uint32_t min_ttl; /* used to set "max-age" in responses */
+
+ isc_http_request_type_t request_type;
+ isc_http_scheme_type_t request_scheme;
+
+ size_t content_length;
+ char clenbuf[128];
+
+ char cache_control_buf[128];
+
+ int headers_error_code;
+ size_t headers_data_processed;
+
+ isc_nm_recv_cb_t cb;
+ void *cbarg;
+ LINK(struct isc_nmsocket_h2) link;
+
+ isc_nm_http_endpoints_t **listener_endpoints;
+ size_t n_listener_endpoints;
+
+ bool response_submitted;
+ struct {
+ char *uri;
+ bool post;
+ isc_tlsctx_t *tlsctx;
+ isc_sockaddr_t local_interface;
+ void *cstream;
+ const char *tls_peer_verify_string;
+ } connect;
+} isc_nmsocket_h2_t;
+#endif /* HAVE_LIBNGHTTP2 */
+
+typedef void (*isc_nm_closehandlecb_t)(void *arg);
+/*%<
+ * Opaque callback function, used for isc_nmhandle 'reset' and 'free'
+ * callbacks.
+ */
+
+struct isc_nmsocket {
+ /*% Unlocked, RO */
+ int magic;
+ int tid;
+ isc_nmsocket_type type;
+ isc_nm_t *mgr;
+
+ /*% Parent socket for multithreaded listeners */
+ isc_nmsocket_t *parent;
+ /*% Listener socket this connection was accepted on */
+ isc_nmsocket_t *listener;
+ /*% Self socket */
+ isc_nmsocket_t *self;
+
+ isc_barrier_t startlistening;
+ isc_barrier_t stoplistening;
+
+ /*% TLS stuff */
+ struct tls {
+ isc_tls_t *tls;
+ isc_tlsctx_t *ctx;
+ isc_tlsctx_client_session_cache_t *client_sess_cache;
+ bool client_session_saved;
+ BIO *app_rbio;
+ BIO *app_wbio;
+ BIO *ssl_rbio;
+ BIO *ssl_wbio;
+ enum {
+ TLS_STATE_NONE,
+ TLS_STATE_HANDSHAKE,
+ TLS_STATE_IO,
+ TLS_STATE_ERROR,
+ TLS_STATE_CLOSING
+ } state;
+ isc_region_t senddata;
+ ISC_LIST(isc__nm_uvreq_t) sendreqs;
+ bool cycle;
+ isc_result_t pending_error;
+ /* List of active send requests. */
+ isc__nm_uvreq_t *pending_req;
+ bool alpn_negotiated;
+ const char *tls_verify_errmsg;
+ } tls;
+
+#if HAVE_LIBNGHTTP2
+ /*% TLS stuff */
+ struct tlsstream {
+ bool server;
+ BIO *bio_in;
+ BIO *bio_out;
+ isc_tls_t *tls;
+ isc_tlsctx_t *ctx;
+ isc_tlsctx_t **listener_tls_ctx; /*%< A context reference per
+ worker */
+ size_t n_listener_tls_ctx;
+ isc_tlsctx_client_session_cache_t *client_sess_cache;
+ bool client_session_saved;
+ isc_nmsocket_t *tlslistener;
+ isc_nmsocket_t *tlssocket;
+ atomic_bool result_updated;
+ enum {
+ TLS_INIT,
+ TLS_HANDSHAKE,
+ TLS_IO,
+ TLS_CLOSED
+ } state; /*%< The order of these is significant */
+ size_t nsending;
+ bool reading;
+ } tlsstream;
+
+ isc_nmsocket_h2_t h2;
+#endif /* HAVE_LIBNGHTTP2 */
+ /*%
+ * quota is the TCP client, attached when a TCP connection
+ * is established. pquota is a non-attached pointer to the
+ * TCP client quota, stored in listening sockets but only
+ * attached in connected sockets.
+ */
+ isc_quota_t *quota;
+ isc_quota_t *pquota;
+ isc_quota_cb_t quotacb;
+
+ /*%
+ * Socket statistics
+ */
+ const isc_statscounter_t *statsindex;
+
+ /*%
+ * TCP read/connect timeout timers.
+ */
+ uv_timer_t read_timer;
+ uint64_t read_timeout;
+ uint64_t connect_timeout;
+
+ /*%
+ * TCP write timeout timer.
+ */
+ uint64_t write_timeout;
+
+ /*% outer socket is for 'wrapped' sockets - e.g. tcpdns in tcp */
+ isc_nmsocket_t *outer;
+
+ /*% server socket for connections */
+ isc_nmsocket_t *server;
+
+ /*% Child sockets for multi-socket setups */
+ isc_nmsocket_t *children;
+ uint_fast32_t nchildren;
+ isc_sockaddr_t iface;
+ isc_nmhandle_t *statichandle;
+ isc_nmhandle_t *outerhandle;
+
+ /*% Extra data allocated at the end of each isc_nmhandle_t */
+ size_t extrahandlesize;
+
+ /*% TCP backlog */
+ int backlog;
+
+ /*% libuv data */
+ uv_os_sock_t fd;
+ union uv_any_handle uv_handle;
+
+ /*% Peer address */
+ isc_sockaddr_t peer;
+
+ /* Atomic */
+ /*% Number of running (e.g. listening) child sockets */
+ atomic_uint_fast32_t rchildren;
+
+ /*%
+ * Socket is active if it's listening, working, etc. If it's
+ * closing, then it doesn't make a sense, for example, to
+ * push handles or reqs for reuse.
+ */
+ atomic_bool active;
+ atomic_bool destroying;
+
+ bool route_sock;
+
+ /*%
+ * Socket is closed if it's not active and all the possible
+ * callbacks were fired, there are no active handles, etc.
+ * If active==false but closed==false, that means the socket
+ * is closing.
+ */
+ atomic_bool closing;
+ atomic_bool closed;
+ atomic_bool listening;
+ atomic_bool connecting;
+ atomic_bool connected;
+ atomic_bool accepting;
+ atomic_bool reading;
+ atomic_bool timedout;
+ isc_refcount_t references;
+
+ /*%
+ * Established an outgoing connection, as client not server.
+ */
+ atomic_bool client;
+
+ /*%
+ * TCPDNS socket has been set not to pipeline.
+ */
+ atomic_bool sequential;
+
+ /*%
+ * The socket is processing read callback, this is guard to not read
+ * data before the readcb is back.
+ */
+ bool processing;
+
+ /*%
+ * A TCP socket has had isc_nm_pauseread() called.
+ */
+ atomic_bool readpaused;
+
+ /*%
+ * A TCP or TCPDNS socket has been set to use the keepalive
+ * timeout instead of the default idle timeout.
+ */
+ atomic_bool keepalive;
+
+ /*%
+ * 'spare' handles for that can be reused to avoid allocations,
+ * for UDP.
+ */
+ isc_astack_t *inactivehandles;
+ isc_astack_t *inactivereqs;
+
+ /*%
+ * Used to wait for TCP listening events to complete, and
+ * for the number of running children to reach zero during
+ * shutdown.
+ *
+ * We use two condition variables to prevent the race where the netmgr
+ * threads would be able to finish and destroy the socket before it's
+ * unlocked by the isc_nm_listen<proto>() function. So, the flow is as
+ * follows:
+ *
+ * 1. parent thread creates all children sockets and passes then to
+ * netthreads, looks at the signaling variable and WAIT(cond) until
+ * the childrens are done initializing
+ *
+ * 2. the events get picked by netthreads, calls the libuv API (and
+ * either succeeds or fails) and WAIT(scond) until all other
+ * children sockets in netthreads are initialized and the listening
+ * socket lock is unlocked
+ *
+ * 3. the control is given back to the parent thread which now either
+ * returns success or shutdowns the listener if an error has
+ * occured in the children netthread
+ *
+ * NOTE: The other approach would be doing an extra attach to the parent
+ * listening socket, and then detach it in the parent thread, but that
+ * breaks the promise that once the libuv socket is initialized on the
+ * nmsocket, the nmsocket needs to be handled only by matching
+ * netthread, so in fact that would add a complexity in a way that
+ * isc__nmsocket_detach would have to be converted to use an
+ * asynchrounous netievent.
+ */
+ isc_mutex_t lock;
+ isc_condition_t cond;
+ isc_condition_t scond;
+
+ /*%
+ * Used to pass a result back from listen or connect events.
+ */
+ isc_result_t result;
+
+ /*%
+ * Current number of active handles.
+ */
+ atomic_int_fast32_t ah;
+
+ /*% Buffer for TCPDNS processing */
+ size_t buf_size;
+ size_t buf_len;
+ unsigned char *buf;
+
+ /*%
+ * This function will be called with handle->sock
+ * as the argument whenever a handle's references drop
+ * to zero, after its reset callback has been called.
+ */
+ isc_nm_closehandlecb_t closehandle_cb;
+
+ isc_nmhandle_t *recv_handle;
+ isc_nm_recv_cb_t recv_cb;
+ void *recv_cbarg;
+ bool recv_read;
+
+ isc_nm_cb_t connect_cb;
+ void *connect_cbarg;
+
+ isc_nm_accept_cb_t accept_cb;
+ void *accept_cbarg;
+
+ atomic_int_fast32_t active_child_connections;
+
+ isc_barrier_t barrier;
+ bool barrier_initialised;
+#ifdef NETMGR_TRACE
+ void *backtrace[TRACE_SIZE];
+ int backtrace_size;
+ LINK(isc_nmsocket_t) active_link;
+ ISC_LIST(isc_nmhandle_t) active_handles;
+#endif
+};
+
+bool
+isc__nm_in_netthread(void);
+/*%<
+ * Returns 'true' if we're in the network thread.
+ */
+
+void
+isc__nm_maybe_enqueue_ievent(isc__networker_t *worker, isc__netievent_t *event);
+/*%<
+ * If the caller is already in the matching nmthread, process the netievent
+ * directly, if not enqueue using isc__nm_enqueue_ievent().
+ */
+
+void
+isc__nm_enqueue_ievent(isc__networker_t *worker, isc__netievent_t *event);
+/*%<
+ * Enqueue an ievent onto a specific worker queue. (This the only safe
+ * way to use an isc__networker_t from another thread.)
+ */
+
+void
+isc__nm_free_uvbuf(isc_nmsocket_t *sock, const uv_buf_t *buf);
+/*%<
+ * Free a buffer allocated for a receive operation.
+ *
+ * Note that as currently implemented, this doesn't actually
+ * free anything, marks the isc__networker's UDP receive buffer
+ * as "not in use".
+ */
+
+isc_nmhandle_t *
+isc___nmhandle_get(isc_nmsocket_t *sock, isc_sockaddr_t *peer,
+ isc_sockaddr_t *local FLARG);
+/*%<
+ * Get a handle for the socket 'sock', allocating a new one
+ * if there isn't one available in 'sock->inactivehandles'.
+ *
+ * If 'peer' is not NULL, set the handle's peer address to 'peer',
+ * otherwise set it to 'sock->peer'.
+ *
+ * If 'local' is not NULL, set the handle's local address to 'local',
+ * otherwise set it to 'sock->iface->addr'.
+ *
+ * 'sock' will be attached to 'handle->sock'. The caller may need
+ * to detach the socket afterward.
+ */
+
+isc__nm_uvreq_t *
+isc___nm_uvreq_get(isc_nm_t *mgr, isc_nmsocket_t *sock FLARG);
+/*%<
+ * Get a UV request structure for the socket 'sock', allocating a
+ * new one if there isn't one available in 'sock->inactivereqs'.
+ */
+
+void
+isc___nm_uvreq_put(isc__nm_uvreq_t **req, isc_nmsocket_t *sock FLARG);
+/*%<
+ * Completes the use of a UV request structure, setting '*req' to NULL.
+ *
+ * The UV request is pushed onto the 'sock->inactivereqs' stack or,
+ * if that doesn't work, freed.
+ */
+
+void
+isc___nmsocket_init(isc_nmsocket_t *sock, isc_nm_t *mgr, isc_nmsocket_type type,
+ isc_sockaddr_t *iface FLARG);
+/*%<
+ * Initialize socket 'sock', attach it to 'mgr', and set it to type 'type'
+ * and its interface to 'iface'.
+ */
+
+void
+isc___nmsocket_attach(isc_nmsocket_t *sock, isc_nmsocket_t **target FLARG);
+/*%<
+ * Attach to a socket, increasing refcount
+ */
+
+void
+isc___nmsocket_detach(isc_nmsocket_t **socketp FLARG);
+/*%<
+ * Detach from socket, decreasing refcount and possibly destroying the
+ * socket if it's no longer referenced.
+ */
+
+void
+isc___nmsocket_prep_destroy(isc_nmsocket_t *sock FLARG);
+/*%<
+ * Market 'sock' as inactive, close it if necessary, and destroy it
+ * if there are no remaining references or active handles.
+ */
+
+void
+isc__nmsocket_shutdown(isc_nmsocket_t *sock);
+/*%<
+ * Initiate the socket shutdown which actively calls the active
+ * callbacks.
+ */
+
+void
+isc__nmsocket_reset(isc_nmsocket_t *sock);
+/*%<
+ * Reset and close the socket.
+ */
+
+bool
+isc__nmsocket_active(isc_nmsocket_t *sock);
+/*%<
+ * Determine whether 'sock' is active by checking 'sock->active'
+ * or, for child sockets, 'sock->parent->active'.
+ */
+
+bool
+isc__nmsocket_deactivate(isc_nmsocket_t *sock);
+/*%<
+ * @brief Deactivate active socket
+ *
+ * Atomically deactive the socket by setting @p sock->active or, for child
+ * sockets, @p sock->parent->active to @c false
+ *
+ * @param[in] sock - valid nmsocket
+ * @return @c false if the socket was already inactive, @c true otherwise
+ */
+
+void
+isc__nmsocket_clearcb(isc_nmsocket_t *sock);
+/*%<
+ * Clear the recv and accept callbacks in 'sock'.
+ */
+
+void
+isc__nmsocket_timer_stop(isc_nmsocket_t *sock);
+void
+isc__nmsocket_timer_start(isc_nmsocket_t *sock);
+void
+isc__nmsocket_timer_restart(isc_nmsocket_t *sock);
+bool
+isc__nmsocket_timer_running(isc_nmsocket_t *sock);
+/*%<
+ * Start/stop/restart/check the timeout on the socket
+ */
+
+void
+isc__nm_connectcb(isc_nmsocket_t *sock, isc__nm_uvreq_t *uvreq,
+ isc_result_t eresult, bool async);
+
+void
+isc__nm_async_connectcb(isc__networker_t *worker, isc__netievent_t *ev0);
+/*%<
+ * Issue a connect callback on the socket, used to call the callback
+ */
+
+void
+isc__nm_readcb(isc_nmsocket_t *sock, isc__nm_uvreq_t *uvreq,
+ isc_result_t eresult);
+void
+isc__nm_async_readcb(isc__networker_t *worker, isc__netievent_t *ev0);
+
+/*%<
+ * Issue a read callback on the socket, used to call the callback
+ * on failed conditions when the event can't be scheduled on the uv loop.
+ *
+ */
+
+void
+isc__nm_sendcb(isc_nmsocket_t *sock, isc__nm_uvreq_t *uvreq,
+ isc_result_t eresult, bool async);
+void
+isc__nm_async_sendcb(isc__networker_t *worker, isc__netievent_t *ev0);
+/*%<
+ * Issue a write callback on the socket, used to call the callback
+ * on failed conditions when the event can't be scheduled on the uv loop.
+ */
+
+void
+isc__nm_async_shutdown(isc__networker_t *worker, isc__netievent_t *ev0);
+/*%<
+ * Walk through all uv handles, get the underlying sockets and issue
+ * close on them.
+ */
+
+void
+isc__nm_udp_send(isc_nmhandle_t *handle, const isc_region_t *region,
+ isc_nm_cb_t cb, void *cbarg);
+/*%<
+ * Back-end implementation of isc_nm_send() for UDP handles.
+ */
+
+void
+isc__nm_udp_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg);
+/*
+ * Back-end implementation of isc_nm_read() for UDP handles.
+ */
+
+void
+isc__nm_udp_close(isc_nmsocket_t *sock);
+/*%<
+ * Close a UDP socket.
+ */
+
+void
+isc__nm_udp_cancelread(isc_nmhandle_t *handle);
+/*%<
+ * Stop reading on a connected UDP handle.
+ */
+
+void
+isc__nm_udp_shutdown(isc_nmsocket_t *sock);
+/*%<
+ * Called during the shutdown process to close and clean up connected
+ * sockets.
+ */
+
+void
+isc__nm_udp_stoplistening(isc_nmsocket_t *sock);
+/*%<
+ * Stop listening on 'sock'.
+ */
+
+void
+isc__nm_udp_settimeout(isc_nmhandle_t *handle, uint32_t timeout);
+/*%<
+ * Set or clear the recv timeout for the UDP socket associated with 'handle'.
+ */
+
+void
+isc__nm_async_udplisten(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_udpconnect(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_udpstop(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_udpsend(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_udpread(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_udpcancel(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_udpclose(isc__networker_t *worker, isc__netievent_t *ev0);
+/*%<
+ * Callback handlers for asynchronous UDP events (listen, stoplisten, send).
+ */
+
+void
+isc__nm_async_routeconnect(isc__networker_t *worker, isc__netievent_t *ev0);
+/*%<
+ * Callback handler for route socket events.
+ */
+
+void
+isc__nm_tcp_send(isc_nmhandle_t *handle, const isc_region_t *region,
+ isc_nm_cb_t cb, void *cbarg);
+/*%<
+ * Back-end implementation of isc_nm_send() for TCP handles.
+ */
+
+void
+isc__nm_tcp_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg);
+/*
+ * Back-end implementation of isc_nm_read() for TCP handles.
+ */
+
+void
+isc__nm_tcp_close(isc_nmsocket_t *sock);
+/*%<
+ * Close a TCP socket.
+ */
+void
+isc__nm_tcp_pauseread(isc_nmhandle_t *handle);
+/*%<
+ * Pause reading on this handle, while still remembering the callback.
+ */
+
+void
+isc__nm_tcp_resumeread(isc_nmhandle_t *handle);
+/*%<
+ * Resume reading from socket.
+ *
+ */
+
+void
+isc__nm_tcp_shutdown(isc_nmsocket_t *sock);
+/*%<
+ * Called during the shutdown process to close and clean up connected
+ * sockets.
+ */
+
+void
+isc__nm_tcp_cancelread(isc_nmhandle_t *handle);
+/*%<
+ * Stop reading on a connected TCP handle.
+ */
+
+void
+isc__nm_tcp_stoplistening(isc_nmsocket_t *sock);
+/*%<
+ * Stop listening on 'sock'.
+ */
+
+int_fast32_t
+isc__nm_tcp_listener_nactive(isc_nmsocket_t *sock);
+/*%<
+ * Returns the number of active connections for the TCP listener socket.
+ */
+
+void
+isc__nm_tcp_settimeout(isc_nmhandle_t *handle, uint32_t timeout);
+/*%<
+ * Set the read timeout for the TCP socket associated with 'handle'.
+ */
+
+void
+isc__nm_async_tcpconnect(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tcplisten(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tcpaccept(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tcpstop(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tcpsend(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_startread(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_pauseread(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tcpstartread(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tcppauseread(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tcpcancel(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tcpclose(isc__networker_t *worker, isc__netievent_t *ev0);
+/*%<
+ * Callback handlers for asynchronous TCP events (connect, listen,
+ * stoplisten, send, read, pause, close).
+ */
+
+void
+isc__nm_async_tlsclose(isc__networker_t *worker, isc__netievent_t *ev0);
+
+void
+isc__nm_async_tlssend(isc__networker_t *worker, isc__netievent_t *ev0);
+
+void
+isc__nm_async_tlsstartread(isc__networker_t *worker, isc__netievent_t *ev0);
+
+void
+isc__nm_async_tlsdobio(isc__networker_t *worker, isc__netievent_t *ev0);
+
+void
+isc__nm_async_tlscancel(isc__networker_t *worker, isc__netievent_t *ev0);
+/*%<
+ * Callback handlers for asynchronous TLS events.
+ */
+
+void
+isc__nm_tcpdns_send(isc_nmhandle_t *handle, isc_region_t *region,
+ isc_nm_cb_t cb, void *cbarg);
+/*%<
+ * Back-end implementation of isc_nm_send() for TCPDNS handles.
+ */
+
+void
+isc__nm_tcpdns_shutdown(isc_nmsocket_t *sock);
+
+void
+isc__nm_tcpdns_close(isc_nmsocket_t *sock);
+/*%<
+ * Close a TCPDNS socket.
+ */
+
+void
+isc__nm_tcpdns_stoplistening(isc_nmsocket_t *sock);
+/*%<
+ * Stop listening on 'sock'.
+ */
+
+void
+isc__nm_tcpdns_settimeout(isc_nmhandle_t *handle, uint32_t timeout);
+/*%<
+ * Set the read timeout and reset the timer for the TCPDNS socket
+ * associated with 'handle', and the TCP socket it wraps around.
+ */
+
+void
+isc__nm_async_tcpdnsaccept(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tcpdnsconnect(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tcpdnslisten(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tcpdnscancel(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tcpdnsclose(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tcpdnssend(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tcpdnsstop(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tcpdnsread(isc__networker_t *worker, isc__netievent_t *ev0);
+/*%<
+ * Callback handlers for asynchronous TCPDNS events.
+ */
+
+void
+isc__nm_tcpdns_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg);
+/*
+ * Back-end implementation of isc_nm_read() for TCPDNS handles.
+ */
+
+void
+isc__nm_tcpdns_cancelread(isc_nmhandle_t *handle);
+/*%<
+ * Stop reading on a connected TCPDNS handle.
+ */
+
+void
+isc__nm_tlsdns_send(isc_nmhandle_t *handle, isc_region_t *region,
+ isc_nm_cb_t cb, void *cbarg);
+
+void
+isc__nm_tlsdns_shutdown(isc_nmsocket_t *sock);
+
+void
+isc__nm_tlsdns_close(isc_nmsocket_t *sock);
+/*%<
+ * Close a TLSDNS socket.
+ */
+
+void
+isc__nm_tlsdns_stoplistening(isc_nmsocket_t *sock);
+/*%<
+ * Stop listening on 'sock'.
+ */
+
+void
+isc__nm_tlsdns_settimeout(isc_nmhandle_t *handle, uint32_t timeout);
+/*%<
+ * Set the read timeout and reset the timer for the TLSDNS socket
+ * associated with 'handle', and the TCP socket it wraps around.
+ */
+
+void
+isc__nm_tlsdns_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg);
+/*
+ * Back-end implementation of isc_nm_read() for TLSDNS handles.
+ */
+
+void
+isc__nm_tlsdns_cancelread(isc_nmhandle_t *handle);
+/*%<
+ * Stop reading on a connected TLSDNS handle.
+ */
+
+const char *
+isc__nm_tlsdns_verify_tls_peer_result_string(const isc_nmhandle_t *handle);
+
+void
+isc__nm_async_tlsdnscycle(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tlsdnsaccept(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tlsdnsconnect(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tlsdnslisten(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tlsdnscancel(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tlsdnsclose(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tlsdnssend(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tlsdnsstop(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tlsdnsshutdown(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tlsdnsread(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tlsdns_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx,
+ const int tid);
+/*%<
+ * Callback handlers for asynchronous TLSDNS events.
+ */
+
+isc_result_t
+isc__nm_tlsdns_xfr_checkperm(isc_nmsocket_t *sock);
+/*%<
+ * Check if it is permitted to do a zone transfer over the given TLSDNS
+ * socket.
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS Success, permission check passed successfully
+ * \li #ISC_R_DOTALPNERROR No permission because of ALPN tag mismatch
+ * \li any other result indicates failure (i.e. no permission)
+ *
+ * Requires:
+ * \li 'sock' is a valid TLSDNS socket.
+ */
+
+void
+isc__nm_tlsdns_cleanup_data(isc_nmsocket_t *sock);
+
+#if HAVE_LIBNGHTTP2
+void
+isc__nm_tls_send(isc_nmhandle_t *handle, const isc_region_t *region,
+ isc_nm_cb_t cb, void *cbarg);
+
+void
+isc__nm_tls_cancelread(isc_nmhandle_t *handle);
+
+/*%<
+ * Back-end implementation of isc_nm_send() for TLSDNS handles.
+ */
+
+void
+isc__nm_tls_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg);
+
+void
+isc__nm_tls_close(isc_nmsocket_t *sock);
+/*%<
+ * Close a TLS socket.
+ */
+
+void
+isc__nm_tls_pauseread(isc_nmhandle_t *handle);
+/*%<
+ * Pause reading on this handle, while still remembering the callback.
+ */
+
+void
+isc__nm_tls_resumeread(isc_nmhandle_t *handle);
+/*%<
+ * Resume reading from the handle.
+ *
+ */
+
+void
+isc__nm_tls_cleanup_data(isc_nmsocket_t *sock);
+
+void
+isc__nm_tls_stoplistening(isc_nmsocket_t *sock);
+
+void
+isc__nm_tls_settimeout(isc_nmhandle_t *handle, uint32_t timeout);
+void
+isc__nm_tls_cleartimeout(isc_nmhandle_t *handle);
+/*%<
+ * Set the read timeout and reset the timer for the socket
+ * associated with 'handle', and the TCP socket it wraps
+ * around.
+ */
+
+const char *
+isc__nm_tls_verify_tls_peer_result_string(const isc_nmhandle_t *handle);
+
+void
+isc__nmhandle_tls_keepalive(isc_nmhandle_t *handle, bool value);
+/*%<
+ * Set the keepalive value on the underlying TCP handle.
+ */
+
+void
+isc__nm_async_tls_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx,
+ const int tid);
+
+void
+isc__nmhandle_tls_setwritetimeout(isc_nmhandle_t *handle,
+ uint64_t write_timeout);
+
+void
+isc__nm_http_stoplistening(isc_nmsocket_t *sock);
+
+void
+isc__nm_http_settimeout(isc_nmhandle_t *handle, uint32_t timeout);
+void
+isc__nm_http_cleartimeout(isc_nmhandle_t *handle);
+/*%<
+ * Set the read timeout and reset the timer for the socket
+ * associated with 'handle', and the TLS/TCP socket it wraps
+ * around.
+ */
+
+void
+isc__nmhandle_http_keepalive(isc_nmhandle_t *handle, bool value);
+/*%<
+ * Set the keepalive value on the underlying session handle
+ */
+
+void
+isc__nm_http_initsocket(isc_nmsocket_t *sock);
+
+void
+isc__nm_http_cleanup_data(isc_nmsocket_t *sock);
+
+isc_result_t
+isc__nm_http_request(isc_nmhandle_t *handle, isc_region_t *region,
+ isc_nm_recv_cb_t reply_cb, void *cbarg);
+
+void
+isc__nm_http_send(isc_nmhandle_t *handle, const isc_region_t *region,
+ isc_nm_cb_t cb, void *cbarg);
+
+void
+isc__nm_http_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg);
+
+void
+isc__nm_http_close(isc_nmsocket_t *sock);
+
+void
+isc__nm_http_bad_request(isc_nmhandle_t *handle);
+/*%<
+ * Respond to the request with 400 "Bad Request" status.
+ *
+ * Requires:
+ * \li 'handle' is a valid HTTP netmgr handle object, referencing a server-side
+ * socket
+ */
+
+bool
+isc__nm_http_has_encryption(const isc_nmhandle_t *handle);
+
+void
+isc__nm_http_set_maxage(isc_nmhandle_t *handle, const uint32_t ttl);
+
+const char *
+isc__nm_http_verify_tls_peer_result_string(const isc_nmhandle_t *handle);
+
+void
+isc__nm_async_httpsend(isc__networker_t *worker, isc__netievent_t *ev0);
+
+void
+isc__nm_async_httpclose(isc__networker_t *worker, isc__netievent_t *ev0);
+
+void
+isc__nm_async_httpendpoints(isc__networker_t *worker, isc__netievent_t *ev0);
+
+bool
+isc__nm_parse_httpquery(const char *query_string, const char **start,
+ size_t *len);
+
+char *
+isc__nm_base64url_to_base64(isc_mem_t *mem, const char *base64url,
+ const size_t base64url_len, size_t *res_len);
+
+char *
+isc__nm_base64_to_base64url(isc_mem_t *mem, const char *base64,
+ const size_t base64_len, size_t *res_len);
+
+void
+isc__nm_httpsession_attach(isc_nm_http_session_t *source,
+ isc_nm_http_session_t **targetp);
+void
+isc__nm_httpsession_detach(isc_nm_http_session_t **sessionp);
+
+void
+isc__nm_http_set_tlsctx(isc_nmsocket_t *sock, isc_tlsctx_t *tlsctx);
+
+void
+isc__nm_http_set_max_streams(isc_nmsocket_t *listener,
+ const uint32_t max_concurrent_streams);
+
+#endif
+
+void
+isc__nm_async_settlsctx(isc__networker_t *worker, isc__netievent_t *ev0);
+
+#define isc__nm_uverr2result(x) \
+ isc___nm_uverr2result(x, true, __FILE__, __LINE__, __func__)
+isc_result_t
+isc___nm_uverr2result(int uverr, bool dolog, const char *file,
+ unsigned int line, const char *func);
+/*%<
+ * Convert a libuv error value into an isc_result_t. The
+ * list of supported error values is not complete; new users
+ * of this function should add any expected errors that are
+ * not already there.
+ */
+
+bool
+isc__nm_acquire_interlocked(isc_nm_t *mgr);
+/*%<
+ * Try to acquire interlocked state; return true if successful.
+ */
+
+void
+isc__nm_drop_interlocked(isc_nm_t *mgr);
+/*%<
+ * Drop interlocked state; signal waiters.
+ */
+
+void
+isc__nm_acquire_interlocked_force(isc_nm_t *mgr);
+/*%<
+ * Actively wait for interlocked state.
+ */
+
+void
+isc__nm_async_sockstop(isc__networker_t *worker, isc__netievent_t *ev0);
+
+void
+isc__nm_incstats(isc_nmsocket_t *sock, isc__nm_statid_t id);
+/*%<
+ * Increment socket-related statistics counters.
+ */
+
+void
+isc__nm_decstats(isc_nmsocket_t *sock, isc__nm_statid_t id);
+/*%<
+ * Decrement socket-related statistics counters.
+ */
+
+isc_result_t
+isc__nm_socket(int domain, int type, int protocol, uv_os_sock_t *sockp);
+/*%<
+ * Platform independent socket() version
+ */
+
+void
+isc__nm_closesocket(uv_os_sock_t sock);
+/*%<
+ * Platform independent closesocket() version
+ */
+
+isc_result_t
+isc__nm_socket_freebind(uv_os_sock_t fd, sa_family_t sa_family);
+/*%<
+ * Set the IP_FREEBIND (or equivalent) socket option on the uv_handle
+ */
+
+isc_result_t
+isc__nm_socket_reuse(uv_os_sock_t fd);
+/*%<
+ * Set the SO_REUSEADDR or SO_REUSEPORT (or equivalent) socket option on the fd
+ */
+
+isc_result_t
+isc__nm_socket_reuse_lb(uv_os_sock_t fd);
+/*%<
+ * Set the SO_REUSEPORT_LB (or equivalent) socket option on the fd
+ */
+
+isc_result_t
+isc__nm_socket_incoming_cpu(uv_os_sock_t fd);
+/*%<
+ * Set the SO_INCOMING_CPU socket option on the fd if available
+ */
+
+isc_result_t
+isc__nm_socket_disable_pmtud(uv_os_sock_t fd, sa_family_t sa_family);
+/*%<
+ * Disable the Path MTU Discovery, either by disabling IP(V6)_DONTFRAG socket
+ * option, or setting the IP(V6)_MTU_DISCOVER socket option to IP_PMTUDISC_OMIT
+ */
+
+isc_result_t
+isc__nm_socket_v6only(uv_os_sock_t fd, sa_family_t sa_family);
+/*%<
+ * Restrict the socket to sending and receiving IPv6 packets only
+ */
+
+isc_result_t
+isc__nm_socket_connectiontimeout(uv_os_sock_t fd, int timeout_ms);
+/*%<
+ * Set the connection timeout in milliseconds, on non-Linux platforms,
+ * the minimum value must be at least 1000 (1 second).
+ */
+
+isc_result_t
+isc__nm_socket_tcp_nodelay(uv_os_sock_t fd);
+/*%<
+ * Disables Nagle's algorithm on a TCP socket (sets TCP_NODELAY).
+ */
+
+isc_result_t
+isc__nm_socket_tcp_maxseg(uv_os_sock_t fd, int size);
+/*%<
+ * Set the TCP maximum segment size
+ */
+
+isc_result_t
+isc__nm_socket_min_mtu(uv_os_sock_t fd, sa_family_t sa_family);
+/*%<
+ * Use minimum MTU on IPv6 sockets
+ */
+
+void
+isc__nm_set_network_buffers(isc_nm_t *nm, uv_handle_t *handle);
+/*%>
+ * Sets the pre-configured network buffers size on the handle.
+ */
+
+void
+isc__nmsocket_barrier_init(isc_nmsocket_t *listener);
+/*%>
+ * Initialise the socket synchronisation barrier according to the
+ * number of children.
+ */
+
+void
+isc__nmsocket_stop(isc_nmsocket_t *listener);
+/*%>
+ * Broadcast "stop" event for a listener socket across all workers and
+ * wait its processing completion - then, stop and close the underlying
+ * transport listener socket.
+ *
+ * The primitive is used in multi-layer transport listener sockets to
+ * implement shutdown properly: after the broadcasted events has been
+ * processed it is safe to destroy the shared data within the listener
+ * socket (including shutting down the underlying transport listener
+ * socket).
+ */
+
+/*
+ * typedef all the netievent types
+ */
+
+NETIEVENT_SOCKET_TYPE(close);
+NETIEVENT_SOCKET_TYPE(tcpclose);
+NETIEVENT_SOCKET_TYPE(tcplisten);
+NETIEVENT_SOCKET_TYPE(tcppauseread);
+NETIEVENT_SOCKET_TYPE(tcpstop);
+NETIEVENT_SOCKET_TYPE(tlsclose);
+/* NETIEVENT_SOCKET_TYPE(tlsconnect); */ /* unique type, defined independently
+ */
+NETIEVENT_SOCKET_TYPE(tlsdobio);
+NETIEVENT_SOCKET_TYPE(tlsstartread);
+NETIEVENT_SOCKET_HANDLE_TYPE(tlscancel);
+NETIEVENT_SOCKET_TYPE(udpclose);
+NETIEVENT_SOCKET_TYPE(udplisten);
+NETIEVENT_SOCKET_TYPE(udpread);
+/* NETIEVENT_SOCKET_TYPE(udpsend); */ /* unique type, defined independently */
+NETIEVENT_SOCKET_TYPE(udpstop);
+
+NETIEVENT_SOCKET_TYPE(tcpdnsclose);
+NETIEVENT_SOCKET_TYPE(tcpdnsread);
+NETIEVENT_SOCKET_TYPE(tcpdnsstop);
+NETIEVENT_SOCKET_TYPE(tcpdnslisten);
+NETIEVENT_SOCKET_REQ_TYPE(tcpdnsconnect);
+NETIEVENT_SOCKET_REQ_TYPE(tcpdnssend);
+NETIEVENT_SOCKET_HANDLE_TYPE(tcpdnscancel);
+NETIEVENT_SOCKET_QUOTA_TYPE(tcpdnsaccept);
+
+NETIEVENT_SOCKET_TYPE(tlsdnsclose);
+NETIEVENT_SOCKET_TYPE(tlsdnsread);
+NETIEVENT_SOCKET_TYPE(tlsdnsstop);
+NETIEVENT_SOCKET_TYPE(tlsdnsshutdown);
+NETIEVENT_SOCKET_TYPE(tlsdnslisten);
+NETIEVENT_SOCKET_REQ_TYPE(tlsdnsconnect);
+NETIEVENT_SOCKET_REQ_TYPE(tlsdnssend);
+NETIEVENT_SOCKET_HANDLE_TYPE(tlsdnscancel);
+NETIEVENT_SOCKET_QUOTA_TYPE(tlsdnsaccept);
+NETIEVENT_SOCKET_TYPE(tlsdnscycle);
+
+#ifdef HAVE_LIBNGHTTP2
+NETIEVENT_SOCKET_REQ_TYPE(httpsend);
+NETIEVENT_SOCKET_TYPE(httpclose);
+NETIEVENT_SOCKET_HTTP_EPS_TYPE(httpendpoints);
+#endif /* HAVE_LIBNGHTTP2 */
+
+NETIEVENT_SOCKET_REQ_TYPE(tcpconnect);
+NETIEVENT_SOCKET_REQ_TYPE(tcpsend);
+NETIEVENT_SOCKET_TYPE(tcpstartread);
+NETIEVENT_SOCKET_REQ_TYPE(tlssend);
+NETIEVENT_SOCKET_REQ_TYPE(udpconnect);
+
+NETIEVENT_SOCKET_REQ_TYPE(routeconnect);
+
+NETIEVENT_SOCKET_REQ_RESULT_TYPE(connectcb);
+NETIEVENT_SOCKET_REQ_RESULT_TYPE(readcb);
+NETIEVENT_SOCKET_REQ_RESULT_TYPE(sendcb);
+
+NETIEVENT_SOCKET_HANDLE_TYPE(detach);
+NETIEVENT_SOCKET_HANDLE_TYPE(tcpcancel);
+NETIEVENT_SOCKET_HANDLE_TYPE(udpcancel);
+
+NETIEVENT_SOCKET_QUOTA_TYPE(tcpaccept);
+
+NETIEVENT_TYPE(pause);
+NETIEVENT_TYPE(resume);
+NETIEVENT_TYPE(shutdown);
+NETIEVENT_TYPE(stop);
+
+NETIEVENT_TASK_TYPE(task);
+NETIEVENT_TASK_TYPE(privilegedtask);
+
+NETIEVENT_SOCKET_TLSCTX_TYPE(settlsctx);
+NETIEVENT_SOCKET_TYPE(sockstop);
+
+/* Now declared the helper functions */
+
+NETIEVENT_SOCKET_DECL(close);
+NETIEVENT_SOCKET_DECL(tcpclose);
+NETIEVENT_SOCKET_DECL(tcplisten);
+NETIEVENT_SOCKET_DECL(tcppauseread);
+NETIEVENT_SOCKET_DECL(tcpstartread);
+NETIEVENT_SOCKET_DECL(tcpstop);
+NETIEVENT_SOCKET_DECL(tlsclose);
+NETIEVENT_SOCKET_DECL(tlsconnect);
+NETIEVENT_SOCKET_DECL(tlsdobio);
+NETIEVENT_SOCKET_DECL(tlsstartread);
+NETIEVENT_SOCKET_HANDLE_DECL(tlscancel);
+NETIEVENT_SOCKET_DECL(udpclose);
+NETIEVENT_SOCKET_DECL(udplisten);
+NETIEVENT_SOCKET_DECL(udpread);
+NETIEVENT_SOCKET_DECL(udpsend);
+NETIEVENT_SOCKET_DECL(udpstop);
+
+NETIEVENT_SOCKET_DECL(tcpdnsclose);
+NETIEVENT_SOCKET_DECL(tcpdnsread);
+NETIEVENT_SOCKET_DECL(tcpdnsstop);
+NETIEVENT_SOCKET_DECL(tcpdnslisten);
+NETIEVENT_SOCKET_REQ_DECL(tcpdnsconnect);
+NETIEVENT_SOCKET_REQ_DECL(tcpdnssend);
+NETIEVENT_SOCKET_HANDLE_DECL(tcpdnscancel);
+NETIEVENT_SOCKET_QUOTA_DECL(tcpdnsaccept);
+
+NETIEVENT_SOCKET_DECL(tlsdnsclose);
+NETIEVENT_SOCKET_DECL(tlsdnsread);
+NETIEVENT_SOCKET_DECL(tlsdnsstop);
+NETIEVENT_SOCKET_DECL(tlsdnsshutdown);
+NETIEVENT_SOCKET_DECL(tlsdnslisten);
+NETIEVENT_SOCKET_REQ_DECL(tlsdnsconnect);
+NETIEVENT_SOCKET_REQ_DECL(tlsdnssend);
+NETIEVENT_SOCKET_HANDLE_DECL(tlsdnscancel);
+NETIEVENT_SOCKET_QUOTA_DECL(tlsdnsaccept);
+NETIEVENT_SOCKET_DECL(tlsdnscycle);
+
+#ifdef HAVE_LIBNGHTTP2
+NETIEVENT_SOCKET_REQ_DECL(httpsend);
+NETIEVENT_SOCKET_DECL(httpclose);
+NETIEVENT_SOCKET_HTTP_EPS_DECL(httpendpoints);
+#endif /* HAVE_LIBNGHTTP2 */
+
+NETIEVENT_SOCKET_REQ_DECL(tcpconnect);
+NETIEVENT_SOCKET_REQ_DECL(tcpsend);
+NETIEVENT_SOCKET_REQ_DECL(tlssend);
+NETIEVENT_SOCKET_REQ_DECL(udpconnect);
+
+NETIEVENT_SOCKET_REQ_DECL(routeconnect);
+
+NETIEVENT_SOCKET_REQ_RESULT_DECL(connectcb);
+NETIEVENT_SOCKET_REQ_RESULT_DECL(readcb);
+NETIEVENT_SOCKET_REQ_RESULT_DECL(sendcb);
+
+NETIEVENT_SOCKET_HANDLE_DECL(udpcancel);
+NETIEVENT_SOCKET_HANDLE_DECL(tcpcancel);
+NETIEVENT_SOCKET_DECL(detach);
+
+NETIEVENT_SOCKET_QUOTA_DECL(tcpaccept);
+
+NETIEVENT_DECL(pause);
+NETIEVENT_DECL(resume);
+NETIEVENT_DECL(shutdown);
+NETIEVENT_DECL(stop);
+
+NETIEVENT_TASK_DECL(task);
+NETIEVENT_TASK_DECL(privilegedtask);
+
+NETIEVENT_SOCKET_TLSCTX_DECL(settlsctx);
+NETIEVENT_SOCKET_DECL(sockstop);
+
+void
+isc__nm_udp_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result);
+void
+isc__nm_tcp_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result);
+void
+isc__nm_tcpdns_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result);
+void
+isc__nm_tlsdns_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result,
+ bool async);
+
+isc_result_t
+isc__nm_tcpdns_processbuffer(isc_nmsocket_t *sock);
+isc_result_t
+isc__nm_tlsdns_processbuffer(isc_nmsocket_t *sock);
+
+isc__nm_uvreq_t *
+isc__nm_get_read_req(isc_nmsocket_t *sock, isc_sockaddr_t *sockaddr);
+
+void
+isc__nm_alloc_cb(uv_handle_t *handle, size_t size, uv_buf_t *buf);
+
+void
+isc__nm_udp_read_cb(uv_udp_t *handle, ssize_t nrecv, const uv_buf_t *buf,
+ const struct sockaddr *addr, unsigned flags);
+void
+isc__nm_tcp_read_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf);
+void
+isc__nm_tcpdns_read_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf);
+void
+isc__nm_tlsdns_read_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf);
+
+isc_result_t
+isc__nm_start_reading(isc_nmsocket_t *sock);
+void
+isc__nm_stop_reading(isc_nmsocket_t *sock);
+isc_result_t
+isc__nm_process_sock_buffer(isc_nmsocket_t *sock);
+void
+isc__nm_resume_processing(void *arg);
+bool
+isc__nmsocket_closing(isc_nmsocket_t *sock);
+bool
+isc__nm_closing(isc_nmsocket_t *sock);
+
+void
+isc__nm_alloc_dnsbuf(isc_nmsocket_t *sock, size_t len);
+
+void
+isc__nm_failed_send_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req,
+ isc_result_t eresult);
+void
+isc__nm_failed_accept_cb(isc_nmsocket_t *sock, isc_result_t eresult);
+void
+isc__nm_failed_connect_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req,
+ isc_result_t eresult, bool async);
+void
+isc__nm_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result, bool async);
+
+void
+isc__nm_accept_connection_log(isc_result_t result, bool can_log_quota);
+
+/*
+ * Timeout callbacks
+ */
+void
+isc__nmsocket_connecttimeout_cb(uv_timer_t *timer);
+void
+isc__nmsocket_readtimeout_cb(uv_timer_t *timer);
+void
+isc__nmsocket_writetimeout_cb(void *data, isc_result_t eresult);
+
+#define UV_RUNTIME_CHECK(func, ret) \
+ if (ret != 0) { \
+ FATAL_ERROR("%s failed: %s\n", #func, uv_strerror(ret)); \
+ }
+
+void
+isc__nmsocket_log_tls_session_reuse(isc_nmsocket_t *sock, isc_tls_t *tls);
diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c
new file mode 100644
index 0000000..b19d468
--- /dev/null
+++ b/lib/isc/netmgr/netmgr.c
@@ -0,0 +1,3991 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <inttypes.h>
+#include <unistd.h>
+#include <uv.h>
+
+#include <isc/atomic.h>
+#include <isc/backtrace.h>
+#include <isc/barrier.h>
+#include <isc/buffer.h>
+#include <isc/condition.h>
+#include <isc/errno.h>
+#include <isc/list.h>
+#include <isc/log.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/netmgr.h>
+#include <isc/print.h>
+#include <isc/quota.h>
+#include <isc/random.h>
+#include <isc/refcount.h>
+#include <isc/region.h>
+#include <isc/result.h>
+#include <isc/sockaddr.h>
+#include <isc/stats.h>
+#include <isc/task.h>
+#include <isc/thread.h>
+#include <isc/tls.h>
+#include <isc/util.h>
+
+#include "netmgr-int.h"
+#include "netmgr_p.h"
+#include "openssl_shim.h"
+#include "trampoline_p.h"
+#include "uv-compat.h"
+
+/*%
+ * How many isc_nmhandles and isc_nm_uvreqs will we be
+ * caching for reuse in a socket.
+ */
+#define ISC_NM_HANDLES_STACK_SIZE 600
+#define ISC_NM_REQS_STACK_SIZE 600
+
+/*%
+ * Shortcut index arrays to get access to statistics counters.
+ */
+
+static const isc_statscounter_t udp4statsindex[] = {
+ isc_sockstatscounter_udp4open,
+ isc_sockstatscounter_udp4openfail,
+ isc_sockstatscounter_udp4close,
+ isc_sockstatscounter_udp4bindfail,
+ isc_sockstatscounter_udp4connectfail,
+ isc_sockstatscounter_udp4connect,
+ -1,
+ -1,
+ isc_sockstatscounter_udp4sendfail,
+ isc_sockstatscounter_udp4recvfail,
+ isc_sockstatscounter_udp4active
+};
+
+static const isc_statscounter_t udp6statsindex[] = {
+ isc_sockstatscounter_udp6open,
+ isc_sockstatscounter_udp6openfail,
+ isc_sockstatscounter_udp6close,
+ isc_sockstatscounter_udp6bindfail,
+ isc_sockstatscounter_udp6connectfail,
+ isc_sockstatscounter_udp6connect,
+ -1,
+ -1,
+ isc_sockstatscounter_udp6sendfail,
+ isc_sockstatscounter_udp6recvfail,
+ isc_sockstatscounter_udp6active
+};
+
+static const isc_statscounter_t tcp4statsindex[] = {
+ isc_sockstatscounter_tcp4open, isc_sockstatscounter_tcp4openfail,
+ isc_sockstatscounter_tcp4close, isc_sockstatscounter_tcp4bindfail,
+ isc_sockstatscounter_tcp4connectfail, isc_sockstatscounter_tcp4connect,
+ isc_sockstatscounter_tcp4acceptfail, isc_sockstatscounter_tcp4accept,
+ isc_sockstatscounter_tcp4sendfail, isc_sockstatscounter_tcp4recvfail,
+ isc_sockstatscounter_tcp4active
+};
+
+static const isc_statscounter_t tcp6statsindex[] = {
+ isc_sockstatscounter_tcp6open, isc_sockstatscounter_tcp6openfail,
+ isc_sockstatscounter_tcp6close, isc_sockstatscounter_tcp6bindfail,
+ isc_sockstatscounter_tcp6connectfail, isc_sockstatscounter_tcp6connect,
+ isc_sockstatscounter_tcp6acceptfail, isc_sockstatscounter_tcp6accept,
+ isc_sockstatscounter_tcp6sendfail, isc_sockstatscounter_tcp6recvfail,
+ isc_sockstatscounter_tcp6active
+};
+
+#if 0
+/* XXX: not currently used */
+static const isc_statscounter_t unixstatsindex[] = {
+ isc_sockstatscounter_unixopen,
+ isc_sockstatscounter_unixopenfail,
+ isc_sockstatscounter_unixclose,
+ isc_sockstatscounter_unixbindfail,
+ isc_sockstatscounter_unixconnectfail,
+ isc_sockstatscounter_unixconnect,
+ isc_sockstatscounter_unixacceptfail,
+ isc_sockstatscounter_unixaccept,
+ isc_sockstatscounter_unixsendfail,
+ isc_sockstatscounter_unixrecvfail,
+ isc_sockstatscounter_unixactive
+};
+#endif /* if 0 */
+
+/*
+ * libuv is not thread safe, but has mechanisms to pass messages
+ * between threads. Each socket is owned by a thread. For UDP
+ * sockets we have a set of sockets for each interface and we can
+ * choose a sibling and send the message directly. For TCP, or if
+ * we're calling from a non-networking thread, we need to pass the
+ * request using async_cb.
+ */
+
+static thread_local int isc__nm_tid_v = ISC_NETMGR_TID_UNKNOWN;
+
+static void
+nmsocket_maybe_destroy(isc_nmsocket_t *sock FLARG);
+static void
+nmhandle_free(isc_nmsocket_t *sock, isc_nmhandle_t *handle);
+static isc_threadresult_t
+nm_thread(isc_threadarg_t worker0);
+static void
+async_cb(uv_async_t *handle);
+
+static bool
+process_netievent(isc__networker_t *worker, isc__netievent_t *ievent);
+static isc_result_t
+process_queue(isc__networker_t *worker, netievent_type_t type);
+static void
+wait_for_priority_queue(isc__networker_t *worker);
+static void
+drain_queue(isc__networker_t *worker, netievent_type_t type);
+
+static void
+isc__nm_async_stop(isc__networker_t *worker, isc__netievent_t *ev0);
+static void
+isc__nm_async_pause(isc__networker_t *worker, isc__netievent_t *ev0);
+static void
+isc__nm_async_resume(isc__networker_t *worker, isc__netievent_t *ev0);
+static void
+isc__nm_async_detach(isc__networker_t *worker, isc__netievent_t *ev0);
+static void
+isc__nm_async_close(isc__networker_t *worker, isc__netievent_t *ev0);
+
+static void
+isc__nm_threadpool_initialize(uint32_t workers);
+static void
+isc__nm_work_cb(uv_work_t *req);
+static void
+isc__nm_after_work_cb(uv_work_t *req, int status);
+
+/*%<
+ * Issue a 'handle closed' callback on the socket.
+ */
+
+static void
+nmhandle_detach_cb(isc_nmhandle_t **handlep FLARG);
+
+int
+isc_nm_tid(void) {
+ return (isc__nm_tid_v);
+}
+
+bool
+isc__nm_in_netthread(void) {
+ return (isc__nm_tid_v >= 0);
+}
+
+void
+isc__nm_force_tid(int tid) {
+ isc__nm_tid_v = tid;
+}
+
+static void
+isc__nm_threadpool_initialize(uint32_t workers) {
+ char buf[11];
+ int r = uv_os_getenv("UV_THREADPOOL_SIZE", buf,
+ &(size_t){ sizeof(buf) });
+ if (r == UV_ENOENT) {
+ snprintf(buf, sizeof(buf), "%" PRIu32, workers);
+ uv_os_setenv("UV_THREADPOOL_SIZE", buf);
+ }
+}
+
+#if HAVE_DECL_UV_UDP_LINUX_RECVERR
+#define MINIMAL_UV_VERSION UV_VERSION(1, 42, 0)
+#elif HAVE_DECL_UV_UDP_MMSG_FREE
+#define MINIMAL_UV_VERSION UV_VERSION(1, 40, 0)
+#elif HAVE_DECL_UV_UDP_RECVMMSG
+#define MAXIMAL_UV_VERSION UV_VERSION(1, 39, 99)
+#define MINIMAL_UV_VERSION UV_VERSION(1, 37, 0)
+#else
+#define MAXIMAL_UV_VERSION UV_VERSION(1, 34, 99)
+#define MINIMAL_UV_VERSION UV_VERSION(1, 0, 0)
+#endif
+
+void
+isc__netmgr_create(isc_mem_t *mctx, uint32_t workers, isc_nm_t **netmgrp) {
+ isc_nm_t *mgr = NULL;
+ char name[32];
+
+ REQUIRE(workers > 0);
+
+#ifdef MAXIMAL_UV_VERSION
+ if (uv_version() > MAXIMAL_UV_VERSION) {
+ FATAL_ERROR("libuv version too new: running with libuv %s "
+ "when compiled with libuv %s will lead to "
+ "libuv failures",
+ uv_version_string(), UV_VERSION_STRING);
+ }
+#endif /* MAXIMAL_UV_VERSION */
+
+ if (uv_version() < MINIMAL_UV_VERSION) {
+ FATAL_ERROR("libuv version too old: running with libuv %s "
+ "when compiled with libuv %s will lead to "
+ "libuv failures",
+ uv_version_string(), UV_VERSION_STRING);
+ }
+
+ isc__nm_threadpool_initialize(workers);
+
+ mgr = isc_mem_get(mctx, sizeof(*mgr));
+ *mgr = (isc_nm_t){ .nworkers = workers };
+
+ isc_mem_attach(mctx, &mgr->mctx);
+ isc_mutex_init(&mgr->lock);
+ isc_condition_init(&mgr->wkstatecond);
+ isc_condition_init(&mgr->wkpausecond);
+ isc_refcount_init(&mgr->references, 1);
+ atomic_init(&mgr->maxudp, 0);
+ atomic_init(&mgr->interlocked, ISC_NETMGR_NON_INTERLOCKED);
+ atomic_init(&mgr->workers_paused, 0);
+ atomic_init(&mgr->paused, false);
+ atomic_init(&mgr->closing, false);
+ atomic_init(&mgr->recv_tcp_buffer_size, 0);
+ atomic_init(&mgr->send_tcp_buffer_size, 0);
+ atomic_init(&mgr->recv_udp_buffer_size, 0);
+ atomic_init(&mgr->send_udp_buffer_size, 0);
+#if HAVE_SO_REUSEPORT_LB
+ mgr->load_balance_sockets = true;
+#else
+ mgr->load_balance_sockets = false;
+#endif
+
+#ifdef NETMGR_TRACE
+ ISC_LIST_INIT(mgr->active_sockets);
+#endif
+
+ /*
+ * Default TCP timeout values.
+ * May be updated by isc_nm_tcptimeouts().
+ */
+ atomic_init(&mgr->init, 30000);
+ atomic_init(&mgr->idle, 30000);
+ atomic_init(&mgr->keepalive, 30000);
+ atomic_init(&mgr->advertised, 30000);
+
+ isc_barrier_init(&mgr->pausing, workers);
+ isc_barrier_init(&mgr->resuming, workers);
+
+ mgr->workers = isc_mem_get(mctx, workers * sizeof(isc__networker_t));
+ for (size_t i = 0; i < workers; i++) {
+ isc__networker_t *worker = &mgr->workers[i];
+ int r;
+
+ *worker = (isc__networker_t){
+ .mgr = mgr,
+ .id = i,
+ };
+
+ r = uv_loop_init(&worker->loop);
+ UV_RUNTIME_CHECK(uv_loop_init, r);
+
+ worker->loop.data = &mgr->workers[i];
+
+ r = uv_async_init(&worker->loop, &worker->async, async_cb);
+ UV_RUNTIME_CHECK(uv_async_init, r);
+
+ for (size_t type = 0; type < NETIEVENT_MAX; type++) {
+ isc_mutex_init(&worker->ievents[type].lock);
+ isc_condition_init(&worker->ievents[type].cond);
+ ISC_LIST_INIT(worker->ievents[type].list);
+ }
+
+ worker->recvbuf = isc_mem_get(mctx, ISC_NETMGR_RECVBUF_SIZE);
+ worker->sendbuf = isc_mem_get(mctx, ISC_NETMGR_SENDBUF_SIZE);
+
+ /*
+ * We need to do this here and not in nm_thread to avoid a
+ * race - we could exit isc_nm_start, launch nm_destroy,
+ * and nm_thread would still not be up.
+ */
+ mgr->workers_running++;
+ isc_thread_create(nm_thread, &mgr->workers[i], &worker->thread);
+
+ snprintf(name, sizeof(name), "isc-net-%04zu", i);
+ isc_thread_setname(worker->thread, name);
+ }
+
+ mgr->magic = NM_MAGIC;
+ *netmgrp = mgr;
+}
+
+/*
+ * Free the resources of the network manager.
+ */
+static void
+nm_destroy(isc_nm_t **mgr0) {
+ REQUIRE(VALID_NM(*mgr0));
+ REQUIRE(!isc__nm_in_netthread());
+
+ isc_nm_t *mgr = *mgr0;
+ *mgr0 = NULL;
+
+ isc_refcount_destroy(&mgr->references);
+
+ mgr->magic = 0;
+
+ for (int i = 0; i < mgr->nworkers; i++) {
+ isc__networker_t *worker = &mgr->workers[i];
+ isc__netievent_t *event = isc__nm_get_netievent_stop(mgr);
+ isc__nm_enqueue_ievent(worker, event);
+ }
+
+ LOCK(&mgr->lock);
+ while (mgr->workers_running > 0) {
+ WAIT(&mgr->wkstatecond, &mgr->lock);
+ }
+ UNLOCK(&mgr->lock);
+
+ for (int i = 0; i < mgr->nworkers; i++) {
+ isc__networker_t *worker = &mgr->workers[i];
+ int r;
+
+ r = uv_loop_close(&worker->loop);
+ UV_RUNTIME_CHECK(uv_loop_close, r);
+
+ for (size_t type = 0; type < NETIEVENT_MAX; type++) {
+ INSIST(ISC_LIST_EMPTY(worker->ievents[type].list));
+ isc_condition_destroy(&worker->ievents[type].cond);
+ isc_mutex_destroy(&worker->ievents[type].lock);
+ }
+
+ isc_mem_put(mgr->mctx, worker->sendbuf,
+ ISC_NETMGR_SENDBUF_SIZE);
+ isc_mem_put(mgr->mctx, worker->recvbuf,
+ ISC_NETMGR_RECVBUF_SIZE);
+ isc_thread_join(worker->thread, NULL);
+ }
+
+ if (mgr->stats != NULL) {
+ isc_stats_detach(&mgr->stats);
+ }
+
+ isc_barrier_destroy(&mgr->resuming);
+ isc_barrier_destroy(&mgr->pausing);
+
+ isc_condition_destroy(&mgr->wkstatecond);
+ isc_condition_destroy(&mgr->wkpausecond);
+ isc_mutex_destroy(&mgr->lock);
+
+ isc_mem_put(mgr->mctx, mgr->workers,
+ mgr->nworkers * sizeof(isc__networker_t));
+ isc_mem_putanddetach(&mgr->mctx, mgr, sizeof(*mgr));
+}
+
+static void
+enqueue_pause(isc__networker_t *worker) {
+ isc__netievent_pause_t *event =
+ isc__nm_get_netievent_pause(worker->mgr);
+ isc__nm_enqueue_ievent(worker, (isc__netievent_t *)event);
+}
+
+static void
+isc__nm_async_pause(isc__networker_t *worker, isc__netievent_t *ev0) {
+ UNUSED(ev0);
+ REQUIRE(worker->paused == false);
+
+ worker->paused = true;
+ uv_stop(&worker->loop);
+}
+
+void
+isc_nm_pause(isc_nm_t *mgr) {
+ REQUIRE(VALID_NM(mgr));
+ REQUIRE(!atomic_load(&mgr->paused));
+
+ isc__nm_acquire_interlocked_force(mgr);
+
+ if (isc__nm_in_netthread()) {
+ REQUIRE(isc_nm_tid() == 0);
+ }
+
+ for (int i = 0; i < mgr->nworkers; i++) {
+ isc__networker_t *worker = &mgr->workers[i];
+ if (i == isc_nm_tid()) {
+ isc__nm_async_pause(worker, NULL);
+ } else {
+ enqueue_pause(worker);
+ }
+ }
+
+ if (isc__nm_in_netthread()) {
+ atomic_fetch_add(&mgr->workers_paused, 1);
+ isc_barrier_wait(&mgr->pausing);
+ }
+
+ LOCK(&mgr->lock);
+ while (atomic_load(&mgr->workers_paused) != mgr->workers_running) {
+ WAIT(&mgr->wkstatecond, &mgr->lock);
+ }
+ UNLOCK(&mgr->lock);
+
+ atomic_compare_exchange_enforced(&mgr->paused, &(bool){ false }, true);
+}
+
+static void
+enqueue_resume(isc__networker_t *worker) {
+ isc__netievent_resume_t *event =
+ isc__nm_get_netievent_resume(worker->mgr);
+ isc__nm_enqueue_ievent(worker, (isc__netievent_t *)event);
+}
+
+static void
+isc__nm_async_resume(isc__networker_t *worker, isc__netievent_t *ev0) {
+ UNUSED(ev0);
+ REQUIRE(worker->paused == true);
+
+ worker->paused = false;
+}
+
+void
+isc_nm_resume(isc_nm_t *mgr) {
+ REQUIRE(VALID_NM(mgr));
+ REQUIRE(atomic_load(&mgr->paused));
+
+ if (isc__nm_in_netthread()) {
+ REQUIRE(isc_nm_tid() == 0);
+ drain_queue(&mgr->workers[isc_nm_tid()], NETIEVENT_PRIORITY);
+ }
+
+ for (int i = 0; i < mgr->nworkers; i++) {
+ isc__networker_t *worker = &mgr->workers[i];
+ if (i == isc_nm_tid()) {
+ isc__nm_async_resume(worker, NULL);
+ } else {
+ enqueue_resume(worker);
+ }
+ }
+
+ if (isc__nm_in_netthread()) {
+ drain_queue(&mgr->workers[isc_nm_tid()], NETIEVENT_PRIVILEGED);
+
+ atomic_fetch_sub(&mgr->workers_paused, 1);
+ isc_barrier_wait(&mgr->resuming);
+ }
+
+ LOCK(&mgr->lock);
+ while (atomic_load(&mgr->workers_paused) != 0) {
+ WAIT(&mgr->wkstatecond, &mgr->lock);
+ }
+ UNLOCK(&mgr->lock);
+
+ atomic_compare_exchange_enforced(&mgr->paused, &(bool){ true }, false);
+
+ isc__nm_drop_interlocked(mgr);
+}
+
+void
+isc_nm_attach(isc_nm_t *mgr, isc_nm_t **dst) {
+ REQUIRE(VALID_NM(mgr));
+ REQUIRE(dst != NULL && *dst == NULL);
+
+ isc_refcount_increment(&mgr->references);
+
+ *dst = mgr;
+}
+
+void
+isc_nm_detach(isc_nm_t **mgr0) {
+ isc_nm_t *mgr = NULL;
+
+ REQUIRE(mgr0 != NULL);
+ REQUIRE(VALID_NM(*mgr0));
+
+ mgr = *mgr0;
+ *mgr0 = NULL;
+
+ if (isc_refcount_decrement(&mgr->references) == 1) {
+ nm_destroy(&mgr);
+ }
+}
+
+void
+isc__netmgr_shutdown(isc_nm_t *mgr) {
+ REQUIRE(VALID_NM(mgr));
+
+ atomic_store(&mgr->closing, true);
+ for (int i = 0; i < mgr->nworkers; i++) {
+ isc__netievent_t *event = NULL;
+ event = isc__nm_get_netievent_shutdown(mgr);
+ isc__nm_enqueue_ievent(&mgr->workers[i], event);
+ }
+}
+
+void
+isc__netmgr_destroy(isc_nm_t **netmgrp) {
+ isc_nm_t *mgr = NULL;
+ int counter = 0;
+
+ REQUIRE(VALID_NM(*netmgrp));
+
+ mgr = *netmgrp;
+
+ /*
+ * Close active connections.
+ */
+ isc__netmgr_shutdown(mgr);
+
+ /*
+ * Wait for the manager to be dereferenced elsewhere.
+ */
+ while (isc_refcount_current(&mgr->references) > 1 && counter++ < 1000) {
+ uv_sleep(10);
+ }
+
+#ifdef NETMGR_TRACE
+ if (isc_refcount_current(&mgr->references) > 1) {
+ isc__nm_dump_active(mgr);
+ UNREACHABLE();
+ }
+#endif
+
+ /*
+ * Now just patiently wait
+ */
+ while (isc_refcount_current(&mgr->references) > 1) {
+ uv_sleep(10);
+ }
+
+ /*
+ * Detach final reference.
+ */
+ isc_nm_detach(netmgrp);
+}
+
+void
+isc_nm_maxudp(isc_nm_t *mgr, uint32_t maxudp) {
+ REQUIRE(VALID_NM(mgr));
+
+ atomic_store(&mgr->maxudp, maxudp);
+}
+
+void
+isc_nmhandle_setwritetimeout(isc_nmhandle_t *handle, uint64_t write_timeout) {
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+ REQUIRE(handle->sock->tid == isc_nm_tid());
+
+ switch (handle->sock->type) {
+ case isc_nm_tcpsocket:
+ case isc_nm_udpsocket:
+ case isc_nm_tcpdnssocket:
+ case isc_nm_tlsdnssocket:
+ handle->sock->write_timeout = write_timeout;
+ break;
+#ifdef HAVE_LIBNGHTTP2
+ case isc_nm_tlssocket:
+ isc__nmhandle_tls_setwritetimeout(handle, write_timeout);
+ break;
+#endif /* HAVE_LIBNGHTTP2 */
+ default:
+ UNREACHABLE();
+ break;
+ }
+}
+
+void
+isc_nm_settimeouts(isc_nm_t *mgr, uint32_t init, uint32_t idle,
+ uint32_t keepalive, uint32_t advertised) {
+ REQUIRE(VALID_NM(mgr));
+
+ atomic_store(&mgr->init, init);
+ atomic_store(&mgr->idle, idle);
+ atomic_store(&mgr->keepalive, keepalive);
+ atomic_store(&mgr->advertised, advertised);
+}
+
+void
+isc_nm_setnetbuffers(isc_nm_t *mgr, int32_t recv_tcp, int32_t send_tcp,
+ int32_t recv_udp, int32_t send_udp) {
+ REQUIRE(VALID_NM(mgr));
+
+ atomic_store(&mgr->recv_tcp_buffer_size, recv_tcp);
+ atomic_store(&mgr->send_tcp_buffer_size, send_tcp);
+ atomic_store(&mgr->recv_udp_buffer_size, recv_udp);
+ atomic_store(&mgr->send_udp_buffer_size, send_udp);
+}
+
+bool
+isc_nm_getloadbalancesockets(isc_nm_t *mgr) {
+ REQUIRE(VALID_NM(mgr));
+
+ return (mgr->load_balance_sockets);
+}
+
+void
+isc_nm_setloadbalancesockets(isc_nm_t *mgr, bool enabled) {
+ REQUIRE(VALID_NM(mgr));
+
+#if HAVE_SO_REUSEPORT_LB
+ mgr->load_balance_sockets = enabled;
+#else
+ UNUSED(enabled);
+#endif
+}
+
+void
+isc_nm_gettimeouts(isc_nm_t *mgr, uint32_t *initial, uint32_t *idle,
+ uint32_t *keepalive, uint32_t *advertised) {
+ REQUIRE(VALID_NM(mgr));
+
+ if (initial != NULL) {
+ *initial = atomic_load(&mgr->init);
+ }
+
+ if (idle != NULL) {
+ *idle = atomic_load(&mgr->idle);
+ }
+
+ if (keepalive != NULL) {
+ *keepalive = atomic_load(&mgr->keepalive);
+ }
+
+ if (advertised != NULL) {
+ *advertised = atomic_load(&mgr->advertised);
+ }
+}
+
+/*
+ * nm_thread is a single worker thread, that runs uv_run event loop
+ * until asked to stop.
+ *
+ * There are four queues for asynchronous events:
+ *
+ * 1. priority queue - netievents on the priority queue are run even when
+ * the taskmgr enters exclusive mode and the netmgr is paused. This
+ * is needed to properly start listening on the interfaces, free
+ * resources on shutdown, or resume from a pause.
+ *
+ * 2. privileged task queue - only privileged tasks are queued here and
+ * this is the first queue that gets processed when network manager
+ * is unpaused using isc_nm_resume(). All netmgr workers need to
+ * clean the privileged task queue before they all proceed to normal
+ * operation. Both task queues are processed when the workers are
+ * shutting down.
+ *
+ * 3. task queue - only (traditional) tasks are scheduled here, and this
+ * queue and the privileged task queue are both processed when the
+ * netmgr workers are finishing. This is needed to process the task
+ * shutdown events.
+ *
+ * 4. normal queue - this is the queue with netmgr events, e.g. reading,
+ * sending, callbacks, etc.
+ */
+
+static isc_threadresult_t
+nm_thread(isc_threadarg_t worker0) {
+ isc__networker_t *worker = (isc__networker_t *)worker0;
+ isc_nm_t *mgr = worker->mgr;
+
+ isc__nm_tid_v = worker->id;
+
+ while (true) {
+ /*
+ * uv_run() runs async_cb() in a loop, which processes
+ * all four event queues until a "pause" or "stop" event
+ * is encountered. On pause, we process only priority and
+ * privileged events until resuming.
+ */
+ int r = uv_run(&worker->loop, UV_RUN_DEFAULT);
+ INSIST(r > 0 || worker->finished);
+
+ if (worker->paused) {
+ INSIST(atomic_load(&mgr->interlocked) != isc_nm_tid());
+
+ atomic_fetch_add(&mgr->workers_paused, 1);
+ if (isc_barrier_wait(&mgr->pausing) != 0) {
+ LOCK(&mgr->lock);
+ SIGNAL(&mgr->wkstatecond);
+ UNLOCK(&mgr->lock);
+ }
+
+ while (worker->paused) {
+ wait_for_priority_queue(worker);
+ }
+
+ /*
+ * All workers must drain the privileged event
+ * queue before we resume from pause.
+ */
+ drain_queue(worker, NETIEVENT_PRIVILEGED);
+
+ atomic_fetch_sub(&mgr->workers_paused, 1);
+ if (isc_barrier_wait(&mgr->resuming) != 0) {
+ LOCK(&mgr->lock);
+ SIGNAL(&mgr->wkstatecond);
+ UNLOCK(&mgr->lock);
+ }
+ }
+
+ if (r == 0) {
+ INSIST(worker->finished);
+ break;
+ }
+
+ INSIST(!worker->finished);
+ }
+
+ /*
+ * We are shutting down. Drain the queues.
+ */
+ drain_queue(worker, NETIEVENT_PRIVILEGED);
+ drain_queue(worker, NETIEVENT_TASK);
+
+ for (size_t type = 0; type < NETIEVENT_MAX; type++) {
+ LOCK(&worker->ievents[type].lock);
+ INSIST(ISC_LIST_EMPTY(worker->ievents[type].list));
+ UNLOCK(&worker->ievents[type].lock);
+ }
+
+ LOCK(&mgr->lock);
+ mgr->workers_running--;
+ SIGNAL(&mgr->wkstatecond);
+ UNLOCK(&mgr->lock);
+
+ return ((isc_threadresult_t)0);
+}
+
+static bool
+process_all_queues(isc__networker_t *worker) {
+ bool reschedule = false;
+ /*
+ * The queue processing functions will return false when the
+ * system is pausing or stopping and we don't want to process
+ * the other queues in such case, but we need the async event
+ * to be rescheduled in the next uv_run().
+ */
+ for (size_t type = 0; type < NETIEVENT_MAX; type++) {
+ isc_result_t result = process_queue(worker, type);
+ switch (result) {
+ case ISC_R_SUSPEND:
+ reschedule = true;
+ break;
+ case ISC_R_EMPTY:
+ /* empty queue */
+ break;
+ case ISC_R_SUCCESS:
+ reschedule = true;
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+
+ return (reschedule);
+}
+
+/*
+ * async_cb() is a universal callback for 'async' events sent to event loop.
+ * It's the only way to safely pass data to the libuv event loop. We use a
+ * single async event and a set of lockless queues of 'isc__netievent_t'
+ * structures passed from other threads.
+ */
+static void
+async_cb(uv_async_t *handle) {
+ isc__networker_t *worker = (isc__networker_t *)handle->loop->data;
+
+ if (process_all_queues(worker)) {
+ /*
+ * If we didn't process all the events, we need to enqueue
+ * async_cb to be run in the next iteration of the uv_loop
+ */
+ uv_async_send(handle);
+ }
+}
+
+static void
+isc__nm_async_stop(isc__networker_t *worker, isc__netievent_t *ev0) {
+ UNUSED(ev0);
+ worker->finished = true;
+ /* Close the async handler */
+ uv_close((uv_handle_t *)&worker->async, NULL);
+}
+
+void
+isc_nm_task_enqueue(isc_nm_t *nm, isc_task_t *task, int threadid) {
+ isc__netievent_t *event = NULL;
+ int tid;
+ isc__networker_t *worker = NULL;
+
+ if (threadid == -1) {
+ tid = (int)isc_random_uniform(nm->nworkers);
+ } else {
+ tid = threadid % nm->nworkers;
+ }
+
+ worker = &nm->workers[tid];
+
+ if (isc_task_privileged(task)) {
+ event = (isc__netievent_t *)
+ isc__nm_get_netievent_privilegedtask(nm, task);
+ } else {
+ event = (isc__netievent_t *)isc__nm_get_netievent_task(nm,
+ task);
+ }
+
+ isc__nm_enqueue_ievent(worker, event);
+}
+
+#define isc__nm_async_privilegedtask(worker, ev0) \
+ isc__nm_async_task(worker, ev0)
+
+static void
+isc__nm_async_task(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_task_t *ievent = (isc__netievent_task_t *)ev0;
+ isc_result_t result;
+
+ UNUSED(worker);
+
+ result = isc_task_run(ievent->task);
+
+ /*
+ * Tasks can block for a long time, especially when used by tools in
+ * interactive mode. Update the event loop's time to avoid unexpected
+ * errors when processing later events during the same callback.
+ * For example, newly started timers can fire too early, because the
+ * current time was stale. See the note about uv_update_time() in the
+ * https://docs.libuv.org/en/v1.x/timer.html#c.uv_timer_start page.
+ */
+ uv_update_time(&worker->loop);
+
+ switch (result) {
+ case ISC_R_QUOTA:
+ isc_task_ready(ievent->task);
+ return;
+ case ISC_R_SUCCESS:
+ return;
+ default:
+ UNREACHABLE();
+ }
+}
+
+static void
+wait_for_priority_queue(isc__networker_t *worker) {
+ isc_condition_t *cond = &worker->ievents[NETIEVENT_PRIORITY].cond;
+ isc_mutex_t *lock = &worker->ievents[NETIEVENT_PRIORITY].lock;
+ isc__netievent_list_t *list =
+ &(worker->ievents[NETIEVENT_PRIORITY].list);
+
+ LOCK(lock);
+ while (ISC_LIST_EMPTY(*list)) {
+ WAIT(cond, lock);
+ }
+ UNLOCK(lock);
+
+ drain_queue(worker, NETIEVENT_PRIORITY);
+}
+
+static void
+drain_queue(isc__networker_t *worker, netievent_type_t type) {
+ bool empty = false;
+ while (!empty) {
+ if (process_queue(worker, type) == ISC_R_EMPTY) {
+ LOCK(&worker->ievents[type].lock);
+ empty = ISC_LIST_EMPTY(worker->ievents[type].list);
+ UNLOCK(&worker->ievents[type].lock);
+ }
+ }
+}
+
+/*
+ * The two macros here generate the individual cases for the process_netievent()
+ * function. The NETIEVENT_CASE(type) macro is the common case, and
+ * NETIEVENT_CASE_NOMORE(type) is a macro that causes the loop in the
+ * process_queue() to stop, e.g. it's only used for the netievent that
+ * stops/pauses processing the enqueued netievents.
+ */
+#define NETIEVENT_CASE(type) \
+ case netievent_##type: { \
+ isc__nm_async_##type(worker, ievent); \
+ isc__nm_put_netievent_##type( \
+ worker->mgr, (isc__netievent_##type##_t *)ievent); \
+ return (true); \
+ }
+
+#define NETIEVENT_CASE_NOMORE(type) \
+ case netievent_##type: { \
+ isc__nm_async_##type(worker, ievent); \
+ isc__nm_put_netievent_##type(worker->mgr, ievent); \
+ return (false); \
+ }
+
+static bool
+process_netievent(isc__networker_t *worker, isc__netievent_t *ievent) {
+ REQUIRE(worker->id == isc_nm_tid());
+
+ switch (ievent->type) {
+ /* Don't process more ievents when we are stopping */
+ NETIEVENT_CASE_NOMORE(stop);
+
+ NETIEVENT_CASE(privilegedtask);
+ NETIEVENT_CASE(task);
+
+ NETIEVENT_CASE(udpconnect);
+ NETIEVENT_CASE(udplisten);
+ NETIEVENT_CASE(udpstop);
+ NETIEVENT_CASE(udpsend);
+ NETIEVENT_CASE(udpread);
+ NETIEVENT_CASE(udpcancel);
+ NETIEVENT_CASE(udpclose);
+
+ NETIEVENT_CASE(routeconnect);
+
+ NETIEVENT_CASE(tcpaccept);
+ NETIEVENT_CASE(tcpconnect);
+ NETIEVENT_CASE(tcplisten);
+ NETIEVENT_CASE(tcpstartread);
+ NETIEVENT_CASE(tcppauseread);
+ NETIEVENT_CASE(tcpsend);
+ NETIEVENT_CASE(tcpstop);
+ NETIEVENT_CASE(tcpcancel);
+ NETIEVENT_CASE(tcpclose);
+
+ NETIEVENT_CASE(tcpdnsaccept);
+ NETIEVENT_CASE(tcpdnslisten);
+ NETIEVENT_CASE(tcpdnsconnect);
+ NETIEVENT_CASE(tcpdnssend);
+ NETIEVENT_CASE(tcpdnscancel);
+ NETIEVENT_CASE(tcpdnsclose);
+ NETIEVENT_CASE(tcpdnsread);
+ NETIEVENT_CASE(tcpdnsstop);
+
+ NETIEVENT_CASE(tlsdnscycle);
+ NETIEVENT_CASE(tlsdnsaccept);
+ NETIEVENT_CASE(tlsdnslisten);
+ NETIEVENT_CASE(tlsdnsconnect);
+ NETIEVENT_CASE(tlsdnssend);
+ NETIEVENT_CASE(tlsdnscancel);
+ NETIEVENT_CASE(tlsdnsclose);
+ NETIEVENT_CASE(tlsdnsread);
+ NETIEVENT_CASE(tlsdnsstop);
+ NETIEVENT_CASE(tlsdnsshutdown);
+
+#if HAVE_LIBNGHTTP2
+ NETIEVENT_CASE(tlsstartread);
+ NETIEVENT_CASE(tlssend);
+ NETIEVENT_CASE(tlsclose);
+ NETIEVENT_CASE(tlsdobio);
+ NETIEVENT_CASE(tlscancel);
+
+ NETIEVENT_CASE(httpsend);
+ NETIEVENT_CASE(httpclose);
+ NETIEVENT_CASE(httpendpoints);
+#endif
+ NETIEVENT_CASE(settlsctx);
+ NETIEVENT_CASE(sockstop);
+
+ NETIEVENT_CASE(connectcb);
+ NETIEVENT_CASE(readcb);
+ NETIEVENT_CASE(sendcb);
+
+ NETIEVENT_CASE(close);
+ NETIEVENT_CASE(detach);
+
+ NETIEVENT_CASE(shutdown);
+ NETIEVENT_CASE(resume);
+ NETIEVENT_CASE_NOMORE(pause);
+ default:
+ UNREACHABLE();
+ }
+ return (true);
+}
+
+static isc_result_t
+process_queue(isc__networker_t *worker, netievent_type_t type) {
+ isc__netievent_t *ievent = NULL;
+ isc__netievent_list_t list;
+
+ ISC_LIST_INIT(list);
+
+ LOCK(&worker->ievents[type].lock);
+ ISC_LIST_MOVE(list, worker->ievents[type].list);
+ UNLOCK(&worker->ievents[type].lock);
+
+ ievent = ISC_LIST_HEAD(list);
+ if (ievent == NULL) {
+ /* There's nothing scheduled */
+ return (ISC_R_EMPTY);
+ }
+
+ while (ievent != NULL) {
+ isc__netievent_t *next = ISC_LIST_NEXT(ievent, link);
+ ISC_LIST_DEQUEUE(list, ievent, link);
+
+ if (!process_netievent(worker, ievent)) {
+ /* The netievent told us to stop */
+ if (!ISC_LIST_EMPTY(list)) {
+ /*
+ * Reschedule the rest of the unprocessed
+ * events.
+ */
+ LOCK(&worker->ievents[type].lock);
+ ISC_LIST_PREPENDLIST(worker->ievents[type].list,
+ list, link);
+ UNLOCK(&worker->ievents[type].lock);
+ }
+ return (ISC_R_SUSPEND);
+ }
+
+ ievent = next;
+ }
+
+ /* We processed at least one */
+ return (ISC_R_SUCCESS);
+}
+
+void *
+isc__nm_get_netievent(isc_nm_t *mgr, isc__netievent_type type) {
+ isc__netievent_storage_t *event = isc_mem_get(mgr->mctx,
+ sizeof(*event));
+
+ *event = (isc__netievent_storage_t){ .ni.type = type };
+ ISC_LINK_INIT(&(event->ni), link);
+ return (event);
+}
+
+void
+isc__nm_put_netievent(isc_nm_t *mgr, void *ievent) {
+ isc_mem_put(mgr->mctx, ievent, sizeof(isc__netievent_storage_t));
+}
+
+NETIEVENT_SOCKET_DEF(tcpclose);
+NETIEVENT_SOCKET_DEF(tcplisten);
+NETIEVENT_SOCKET_DEF(tcppauseread);
+NETIEVENT_SOCKET_DEF(tcpstartread);
+NETIEVENT_SOCKET_DEF(tcpstop);
+NETIEVENT_SOCKET_DEF(tlsclose);
+NETIEVENT_SOCKET_DEF(tlsconnect);
+NETIEVENT_SOCKET_DEF(tlsdobio);
+NETIEVENT_SOCKET_DEF(tlsstartread);
+NETIEVENT_SOCKET_HANDLE_DEF(tlscancel);
+NETIEVENT_SOCKET_DEF(udpclose);
+NETIEVENT_SOCKET_DEF(udplisten);
+NETIEVENT_SOCKET_DEF(udpread);
+NETIEVENT_SOCKET_DEF(udpsend);
+NETIEVENT_SOCKET_DEF(udpstop);
+
+NETIEVENT_SOCKET_DEF(tcpdnsclose);
+NETIEVENT_SOCKET_DEF(tcpdnsread);
+NETIEVENT_SOCKET_DEF(tcpdnsstop);
+NETIEVENT_SOCKET_DEF(tcpdnslisten);
+NETIEVENT_SOCKET_REQ_DEF(tcpdnsconnect);
+NETIEVENT_SOCKET_REQ_DEF(tcpdnssend);
+NETIEVENT_SOCKET_HANDLE_DEF(tcpdnscancel);
+NETIEVENT_SOCKET_QUOTA_DEF(tcpdnsaccept);
+
+NETIEVENT_SOCKET_DEF(tlsdnsclose);
+NETIEVENT_SOCKET_DEF(tlsdnsread);
+NETIEVENT_SOCKET_DEF(tlsdnsstop);
+NETIEVENT_SOCKET_DEF(tlsdnslisten);
+NETIEVENT_SOCKET_REQ_DEF(tlsdnsconnect);
+NETIEVENT_SOCKET_REQ_DEF(tlsdnssend);
+NETIEVENT_SOCKET_HANDLE_DEF(tlsdnscancel);
+NETIEVENT_SOCKET_QUOTA_DEF(tlsdnsaccept);
+NETIEVENT_SOCKET_DEF(tlsdnscycle);
+NETIEVENT_SOCKET_DEF(tlsdnsshutdown);
+
+#ifdef HAVE_LIBNGHTTP2
+NETIEVENT_SOCKET_REQ_DEF(httpsend);
+NETIEVENT_SOCKET_DEF(httpclose);
+NETIEVENT_SOCKET_HTTP_EPS_DEF(httpendpoints);
+#endif /* HAVE_LIBNGHTTP2 */
+
+NETIEVENT_SOCKET_REQ_DEF(tcpconnect);
+NETIEVENT_SOCKET_REQ_DEF(tcpsend);
+NETIEVENT_SOCKET_REQ_DEF(tlssend);
+NETIEVENT_SOCKET_REQ_DEF(udpconnect);
+NETIEVENT_SOCKET_REQ_DEF(routeconnect);
+NETIEVENT_SOCKET_REQ_RESULT_DEF(connectcb);
+NETIEVENT_SOCKET_REQ_RESULT_DEF(readcb);
+NETIEVENT_SOCKET_REQ_RESULT_DEF(sendcb);
+
+NETIEVENT_SOCKET_DEF(detach);
+NETIEVENT_SOCKET_HANDLE_DEF(tcpcancel);
+NETIEVENT_SOCKET_HANDLE_DEF(udpcancel);
+
+NETIEVENT_SOCKET_QUOTA_DEF(tcpaccept);
+
+NETIEVENT_SOCKET_DEF(close);
+NETIEVENT_DEF(pause);
+NETIEVENT_DEF(resume);
+NETIEVENT_DEF(shutdown);
+NETIEVENT_DEF(stop);
+
+NETIEVENT_TASK_DEF(task);
+NETIEVENT_TASK_DEF(privilegedtask);
+
+NETIEVENT_SOCKET_TLSCTX_DEF(settlsctx);
+NETIEVENT_SOCKET_DEF(sockstop);
+
+void
+isc__nm_maybe_enqueue_ievent(isc__networker_t *worker,
+ isc__netievent_t *event) {
+ /*
+ * If we are already in the matching nmthread, process the ievent
+ * directly.
+ */
+ if (worker->id == isc_nm_tid()) {
+ process_netievent(worker, event);
+ return;
+ }
+
+ isc__nm_enqueue_ievent(worker, event);
+}
+
+void
+isc__nm_enqueue_ievent(isc__networker_t *worker, isc__netievent_t *event) {
+ netievent_type_t type;
+
+ if (event->type > netievent_prio) {
+ type = NETIEVENT_PRIORITY;
+ } else {
+ switch (event->type) {
+ case netievent_prio:
+ UNREACHABLE();
+ break;
+ case netievent_privilegedtask:
+ type = NETIEVENT_PRIVILEGED;
+ break;
+ case netievent_task:
+ type = NETIEVENT_TASK;
+ break;
+ default:
+ type = NETIEVENT_NORMAL;
+ break;
+ }
+ }
+
+ /*
+ * We need to make sure this signal will be delivered and
+ * the queue will be processed.
+ */
+ LOCK(&worker->ievents[type].lock);
+ ISC_LIST_ENQUEUE(worker->ievents[type].list, event, link);
+ if (type == NETIEVENT_PRIORITY) {
+ SIGNAL(&worker->ievents[type].cond);
+ }
+ UNLOCK(&worker->ievents[type].lock);
+
+ uv_async_send(&worker->async);
+}
+
+bool
+isc__nmsocket_active(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+ if (sock->parent != NULL) {
+ return (atomic_load(&sock->parent->active));
+ }
+
+ return (atomic_load(&sock->active));
+}
+
+bool
+isc__nmsocket_deactivate(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+
+ if (sock->parent != NULL) {
+ return (atomic_compare_exchange_strong(&sock->parent->active,
+ &(bool){ true }, false));
+ }
+
+ return (atomic_compare_exchange_strong(&sock->active, &(bool){ true },
+ false));
+}
+
+void
+isc___nmsocket_attach(isc_nmsocket_t *sock, isc_nmsocket_t **target FLARG) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(target != NULL && *target == NULL);
+
+ isc_nmsocket_t *rsock = NULL;
+
+ if (sock->parent != NULL) {
+ rsock = sock->parent;
+ INSIST(rsock->parent == NULL); /* sanity check */
+ } else {
+ rsock = sock;
+ }
+
+ NETMGR_TRACE_LOG("isc__nmsocket_attach():%p->references = %" PRIuFAST32
+ "\n",
+ rsock, isc_refcount_current(&rsock->references) + 1);
+
+ isc_refcount_increment0(&rsock->references);
+
+ *target = sock;
+}
+
+/*
+ * Free all resources inside a socket (including its children if any).
+ */
+static void
+nmsocket_cleanup(isc_nmsocket_t *sock, bool dofree FLARG) {
+ isc_nmhandle_t *handle = NULL;
+ isc__nm_uvreq_t *uvreq = NULL;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(!isc__nmsocket_active(sock));
+
+ NETMGR_TRACE_LOG("nmsocket_cleanup():%p->references = %" PRIuFAST32
+ "\n",
+ sock, isc_refcount_current(&sock->references));
+
+ isc__nm_decstats(sock, STATID_ACTIVE);
+
+ atomic_store(&sock->destroying, true);
+
+ if (sock->parent == NULL && sock->children != NULL) {
+ /*
+ * We shouldn't be here unless there are no active handles,
+ * so we can clean up and free the children.
+ */
+ for (size_t i = 0; i < sock->nchildren; i++) {
+ if (!atomic_load(&sock->children[i].destroying)) {
+ nmsocket_cleanup(&sock->children[i],
+ false FLARG_PASS);
+ }
+ }
+
+ /*
+ * This was a parent socket: destroy the listening
+ * barriers that synchronized the children.
+ */
+ isc_barrier_destroy(&sock->startlistening);
+ isc_barrier_destroy(&sock->stoplistening);
+
+ /*
+ * Now free them.
+ */
+ isc_mem_put(sock->mgr->mctx, sock->children,
+ sock->nchildren * sizeof(*sock));
+ sock->children = NULL;
+ sock->nchildren = 0;
+ }
+
+ sock->statichandle = NULL;
+
+ if (sock->outerhandle != NULL) {
+ isc__nmhandle_detach(&sock->outerhandle FLARG_PASS);
+ }
+
+ if (sock->outer != NULL) {
+ isc___nmsocket_detach(&sock->outer FLARG_PASS);
+ }
+
+ while ((handle = isc_astack_pop(sock->inactivehandles)) != NULL) {
+ nmhandle_free(sock, handle);
+ }
+
+ if (sock->buf != NULL) {
+ isc_mem_put(sock->mgr->mctx, sock->buf, sock->buf_size);
+ }
+
+ if (sock->quota != NULL) {
+ isc_quota_detach(&sock->quota);
+ }
+
+ sock->pquota = NULL;
+
+ isc_astack_destroy(sock->inactivehandles);
+
+ while ((uvreq = isc_astack_pop(sock->inactivereqs)) != NULL) {
+ isc_mem_put(sock->mgr->mctx, uvreq, sizeof(*uvreq));
+ }
+
+ isc_astack_destroy(sock->inactivereqs);
+ sock->magic = 0;
+
+ isc_condition_destroy(&sock->scond);
+ isc_condition_destroy(&sock->cond);
+ isc_mutex_destroy(&sock->lock);
+ isc__nm_tlsdns_cleanup_data(sock);
+#if HAVE_LIBNGHTTP2
+ isc__nm_tls_cleanup_data(sock);
+ isc__nm_http_cleanup_data(sock);
+#endif
+
+ INSIST(ISC_LIST_EMPTY(sock->tls.sendreqs));
+
+ if (sock->barrier_initialised) {
+ isc_barrier_destroy(&sock->barrier);
+ }
+
+#ifdef NETMGR_TRACE
+ LOCK(&sock->mgr->lock);
+ ISC_LIST_UNLINK(sock->mgr->active_sockets, sock, active_link);
+ UNLOCK(&sock->mgr->lock);
+#endif
+ if (dofree) {
+ isc_nm_t *mgr = sock->mgr;
+ isc_mem_put(mgr->mctx, sock, sizeof(*sock));
+ isc_nm_detach(&mgr);
+ } else {
+ isc_nm_detach(&sock->mgr);
+ }
+}
+
+static void
+nmsocket_maybe_destroy(isc_nmsocket_t *sock FLARG) {
+ int active_handles;
+ bool destroy = false;
+
+ NETMGR_TRACE_LOG("%s():%p->references = %" PRIuFAST32 "\n", __func__,
+ sock, isc_refcount_current(&sock->references));
+
+ if (sock->parent != NULL) {
+ /*
+ * This is a child socket and cannot be destroyed except
+ * as a side effect of destroying the parent, so let's go
+ * see if the parent is ready to be destroyed.
+ */
+ nmsocket_maybe_destroy(sock->parent FLARG_PASS);
+ return;
+ }
+
+ /*
+ * This is a parent socket (or a standalone). See whether the
+ * children have active handles before deciding whether to
+ * accept destruction.
+ */
+ LOCK(&sock->lock);
+ if (atomic_load(&sock->active) || atomic_load(&sock->destroying) ||
+ !atomic_load(&sock->closed) || atomic_load(&sock->references) != 0)
+ {
+ UNLOCK(&sock->lock);
+ return;
+ }
+
+ active_handles = atomic_load(&sock->ah);
+ if (sock->children != NULL) {
+ for (size_t i = 0; i < sock->nchildren; i++) {
+ LOCK(&sock->children[i].lock);
+ active_handles += atomic_load(&sock->children[i].ah);
+ UNLOCK(&sock->children[i].lock);
+ }
+ }
+
+ if (active_handles == 0 || sock->statichandle != NULL) {
+ destroy = true;
+ }
+
+ NETMGR_TRACE_LOG("%s:%p->active_handles = %d, .statichandle = %p\n",
+ __func__, sock, active_handles, sock->statichandle);
+
+ if (destroy) {
+ atomic_store(&sock->destroying, true);
+ UNLOCK(&sock->lock);
+ nmsocket_cleanup(sock, true FLARG_PASS);
+ } else {
+ UNLOCK(&sock->lock);
+ }
+}
+
+void
+isc___nmsocket_prep_destroy(isc_nmsocket_t *sock FLARG) {
+ REQUIRE(sock->parent == NULL);
+
+ NETMGR_TRACE_LOG("isc___nmsocket_prep_destroy():%p->references = "
+ "%" PRIuFAST32 "\n",
+ sock, isc_refcount_current(&sock->references));
+
+ /*
+ * The final external reference to the socket is gone. We can try
+ * destroying the socket, but we have to wait for all the inflight
+ * handles to finish first.
+ */
+ atomic_store(&sock->active, false);
+
+ /*
+ * If the socket has children, they'll need to be marked inactive
+ * so they can be cleaned up too.
+ */
+ if (sock->children != NULL) {
+ for (size_t i = 0; i < sock->nchildren; i++) {
+ atomic_store(&sock->children[i].active, false);
+ }
+ }
+
+ /*
+ * If we're here then we already stopped listening; otherwise
+ * we'd have a hanging reference from the listening process.
+ *
+ * If it's a regular socket we may need to close it.
+ */
+ if (!atomic_load(&sock->closed)) {
+ switch (sock->type) {
+ case isc_nm_udpsocket:
+ isc__nm_udp_close(sock);
+ return;
+ case isc_nm_tcpsocket:
+ isc__nm_tcp_close(sock);
+ return;
+ case isc_nm_tcpdnssocket:
+ isc__nm_tcpdns_close(sock);
+ return;
+ case isc_nm_tlsdnssocket:
+ isc__nm_tlsdns_close(sock);
+ return;
+#if HAVE_LIBNGHTTP2
+ case isc_nm_tlssocket:
+ isc__nm_tls_close(sock);
+ break;
+ case isc_nm_httpsocket:
+ isc__nm_http_close(sock);
+ return;
+#endif
+ default:
+ break;
+ }
+ }
+
+ nmsocket_maybe_destroy(sock FLARG_PASS);
+}
+
+void
+isc___nmsocket_detach(isc_nmsocket_t **sockp FLARG) {
+ REQUIRE(sockp != NULL && *sockp != NULL);
+ REQUIRE(VALID_NMSOCK(*sockp));
+
+ isc_nmsocket_t *sock = *sockp, *rsock = NULL;
+ *sockp = NULL;
+
+ /*
+ * If the socket is a part of a set (a child socket) we are
+ * counting references for the whole set at the parent.
+ */
+ if (sock->parent != NULL) {
+ rsock = sock->parent;
+ INSIST(rsock->parent == NULL); /* Sanity check */
+ } else {
+ rsock = sock;
+ }
+
+ NETMGR_TRACE_LOG("isc__nmsocket_detach():%p->references = %" PRIuFAST32
+ "\n",
+ rsock, isc_refcount_current(&rsock->references) - 1);
+
+ if (isc_refcount_decrement(&rsock->references) == 1) {
+ isc___nmsocket_prep_destroy(rsock FLARG_PASS);
+ }
+}
+
+void
+isc_nmsocket_close(isc_nmsocket_t **sockp) {
+ REQUIRE(sockp != NULL);
+ REQUIRE(VALID_NMSOCK(*sockp));
+ REQUIRE((*sockp)->type == isc_nm_udplistener ||
+ (*sockp)->type == isc_nm_tcplistener ||
+ (*sockp)->type == isc_nm_tcpdnslistener ||
+ (*sockp)->type == isc_nm_tlsdnslistener ||
+ (*sockp)->type == isc_nm_tlslistener ||
+ (*sockp)->type == isc_nm_httplistener);
+
+ isc__nmsocket_detach(sockp);
+}
+
+void
+isc___nmsocket_init(isc_nmsocket_t *sock, isc_nm_t *mgr, isc_nmsocket_type type,
+ isc_sockaddr_t *iface FLARG) {
+ uint16_t family;
+
+ REQUIRE(sock != NULL);
+ REQUIRE(mgr != NULL);
+
+ *sock = (isc_nmsocket_t){ .type = type,
+ .fd = -1,
+ .inactivehandles = isc_astack_new(
+ mgr->mctx, ISC_NM_HANDLES_STACK_SIZE),
+ .inactivereqs = isc_astack_new(
+ mgr->mctx, ISC_NM_REQS_STACK_SIZE) };
+
+ ISC_LIST_INIT(sock->tls.sendreqs);
+
+ if (iface != NULL) {
+ family = iface->type.sa.sa_family;
+ sock->iface = *iface;
+ } else {
+ family = AF_UNSPEC;
+ }
+
+#if NETMGR_TRACE
+ sock->backtrace_size = isc_backtrace(sock->backtrace, TRACE_SIZE);
+ ISC_LINK_INIT(sock, active_link);
+ ISC_LIST_INIT(sock->active_handles);
+ LOCK(&mgr->lock);
+ ISC_LIST_APPEND(mgr->active_sockets, sock, active_link);
+ UNLOCK(&mgr->lock);
+#endif
+
+ isc_nm_attach(mgr, &sock->mgr);
+ sock->uv_handle.handle.data = sock;
+
+ ISC_LINK_INIT(&sock->quotacb, link);
+
+ switch (type) {
+ case isc_nm_udpsocket:
+ case isc_nm_udplistener:
+ switch (family) {
+ case AF_INET:
+ sock->statsindex = udp4statsindex;
+ break;
+ case AF_INET6:
+ sock->statsindex = udp6statsindex;
+ break;
+ case AF_UNSPEC:
+ /*
+ * Route sockets are AF_UNSPEC, and don't
+ * have stats counters.
+ */
+ break;
+ default:
+ UNREACHABLE();
+ }
+ break;
+ case isc_nm_tcpsocket:
+ case isc_nm_tcplistener:
+ case isc_nm_tcpdnssocket:
+ case isc_nm_tcpdnslistener:
+ case isc_nm_tlsdnssocket:
+ case isc_nm_tlsdnslistener:
+ case isc_nm_httpsocket:
+ case isc_nm_httplistener:
+ switch (family) {
+ case AF_INET:
+ sock->statsindex = tcp4statsindex;
+ break;
+ case AF_INET6:
+ sock->statsindex = tcp6statsindex;
+ break;
+ default:
+ UNREACHABLE();
+ }
+ break;
+ default:
+ break;
+ }
+
+ isc_mutex_init(&sock->lock);
+ isc_condition_init(&sock->cond);
+ isc_condition_init(&sock->scond);
+ isc_refcount_init(&sock->references, 1);
+
+#if HAVE_LIBNGHTTP2
+ memset(&sock->tlsstream, 0, sizeof(sock->tlsstream));
+#endif /* HAVE_LIBNGHTTP2 */
+
+ NETMGR_TRACE_LOG("isc__nmsocket_init():%p->references = %" PRIuFAST32
+ "\n",
+ sock, isc_refcount_current(&sock->references));
+
+ atomic_init(&sock->active, true);
+ atomic_init(&sock->sequential, false);
+ atomic_init(&sock->readpaused, false);
+ atomic_init(&sock->closing, false);
+ atomic_init(&sock->listening, 0);
+ atomic_init(&sock->closed, 0);
+ atomic_init(&sock->destroying, 0);
+ atomic_init(&sock->ah, 0);
+ atomic_init(&sock->client, 0);
+ atomic_init(&sock->connecting, false);
+ atomic_init(&sock->keepalive, false);
+ atomic_init(&sock->connected, false);
+ atomic_init(&sock->timedout, false);
+
+ atomic_init(&sock->active_child_connections, 0);
+
+#if HAVE_LIBNGHTTP2
+ isc__nm_http_initsocket(sock);
+#endif
+
+ sock->magic = NMSOCK_MAGIC;
+
+ isc__nm_incstats(sock, STATID_ACTIVE);
+}
+
+void
+isc__nmsocket_clearcb(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(!isc__nm_in_netthread() || sock->tid == isc_nm_tid());
+
+ sock->recv_cb = NULL;
+ sock->recv_cbarg = NULL;
+ sock->accept_cb = NULL;
+ sock->accept_cbarg = NULL;
+ sock->connect_cb = NULL;
+ sock->connect_cbarg = NULL;
+}
+
+void
+isc__nm_free_uvbuf(isc_nmsocket_t *sock, const uv_buf_t *buf) {
+ isc__networker_t *worker = NULL;
+
+ REQUIRE(VALID_NMSOCK(sock));
+
+ worker = &sock->mgr->workers[sock->tid];
+ REQUIRE(buf->base == worker->recvbuf);
+
+ worker->recvbuf_inuse = false;
+}
+
+static isc_nmhandle_t *
+alloc_handle(isc_nmsocket_t *sock) {
+ isc_nmhandle_t *handle =
+ isc_mem_get(sock->mgr->mctx,
+ sizeof(isc_nmhandle_t) + sock->extrahandlesize);
+
+ *handle = (isc_nmhandle_t){ .magic = NMHANDLE_MAGIC };
+#ifdef NETMGR_TRACE
+ ISC_LINK_INIT(handle, active_link);
+#endif
+ isc_refcount_init(&handle->references, 1);
+
+ return (handle);
+}
+
+isc_nmhandle_t *
+isc___nmhandle_get(isc_nmsocket_t *sock, isc_sockaddr_t *peer,
+ isc_sockaddr_t *local FLARG) {
+ isc_nmhandle_t *handle = NULL;
+
+ REQUIRE(VALID_NMSOCK(sock));
+
+ handle = isc_astack_pop(sock->inactivehandles);
+
+ if (handle == NULL) {
+ handle = alloc_handle(sock);
+ } else {
+ isc_refcount_init(&handle->references, 1);
+ INSIST(VALID_NMHANDLE(handle));
+ }
+
+ NETMGR_TRACE_LOG(
+ "isc__nmhandle_get():handle %p->references = %" PRIuFAST32 "\n",
+ handle, isc_refcount_current(&handle->references));
+
+ isc___nmsocket_attach(sock, &handle->sock FLARG_PASS);
+
+#if NETMGR_TRACE
+ handle->backtrace_size = isc_backtrace(handle->backtrace, TRACE_SIZE);
+#endif
+
+ if (peer != NULL) {
+ handle->peer = *peer;
+ } else {
+ handle->peer = sock->peer;
+ }
+
+ if (local != NULL) {
+ handle->local = *local;
+ } else {
+ handle->local = sock->iface;
+ }
+
+ (void)atomic_fetch_add(&sock->ah, 1);
+
+#ifdef NETMGR_TRACE
+ LOCK(&sock->lock);
+ ISC_LIST_APPEND(sock->active_handles, handle, active_link);
+ UNLOCK(&sock->lock);
+#endif
+
+ switch (sock->type) {
+ case isc_nm_udpsocket:
+ case isc_nm_tcpdnssocket:
+ case isc_nm_tlsdnssocket:
+ if (!atomic_load(&sock->client)) {
+ break;
+ }
+ FALLTHROUGH;
+ case isc_nm_tcpsocket:
+ case isc_nm_tlssocket:
+ INSIST(sock->statichandle == NULL);
+
+ /*
+ * statichandle must be assigned, not attached;
+ * otherwise, if a handle was detached elsewhere
+ * it could never reach 0 references, and the
+ * handle and socket would never be freed.
+ */
+ sock->statichandle = handle;
+ break;
+ default:
+ break;
+ }
+
+#if HAVE_LIBNGHTTP2
+ if (sock->type == isc_nm_httpsocket && sock->h2.session) {
+ isc__nm_httpsession_attach(sock->h2.session,
+ &handle->httpsession);
+ }
+#endif
+
+ return (handle);
+}
+
+void
+isc__nmhandle_attach(isc_nmhandle_t *handle, isc_nmhandle_t **handlep FLARG) {
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(handlep != NULL && *handlep == NULL);
+
+ NETMGR_TRACE_LOG("isc__nmhandle_attach():handle %p->references = "
+ "%" PRIuFAST32 "\n",
+ handle, isc_refcount_current(&handle->references) + 1);
+
+ isc_refcount_increment(&handle->references);
+ *handlep = handle;
+}
+
+bool
+isc_nmhandle_is_stream(isc_nmhandle_t *handle) {
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ return (handle->sock->type == isc_nm_tcpsocket ||
+ handle->sock->type == isc_nm_tcpdnssocket ||
+ handle->sock->type == isc_nm_tlssocket ||
+ handle->sock->type == isc_nm_tlsdnssocket ||
+ handle->sock->type == isc_nm_httpsocket);
+}
+
+static void
+nmhandle_free(isc_nmsocket_t *sock, isc_nmhandle_t *handle) {
+ size_t extra = sock->extrahandlesize;
+
+ isc_refcount_destroy(&handle->references);
+
+ if (handle->dofree != NULL) {
+ handle->dofree(handle->opaque);
+ }
+
+ *handle = (isc_nmhandle_t){ .magic = 0 };
+
+ isc_mem_put(sock->mgr->mctx, handle, sizeof(isc_nmhandle_t) + extra);
+}
+
+static void
+nmhandle_deactivate(isc_nmsocket_t *sock, isc_nmhandle_t *handle) {
+ bool reuse = false;
+ uint_fast32_t ah;
+
+ /*
+ * We do all of this under lock to avoid races with socket
+ * destruction. We have to do this now, because at this point the
+ * socket is either unused or still attached to event->sock.
+ */
+ LOCK(&sock->lock);
+
+#ifdef NETMGR_TRACE
+ ISC_LIST_UNLINK(sock->active_handles, handle, active_link);
+#endif
+
+ ah = atomic_fetch_sub(&sock->ah, 1);
+ INSIST(ah > 0);
+
+#if !__SANITIZE_ADDRESS__ && !__SANITIZE_THREAD__
+ if (atomic_load(&sock->active)) {
+ reuse = isc_astack_trypush(sock->inactivehandles, handle);
+ }
+#endif /* !__SANITIZE_ADDRESS__ && !__SANITIZE_THREAD__ */
+ if (!reuse) {
+ nmhandle_free(sock, handle);
+ }
+ UNLOCK(&sock->lock);
+}
+
+void
+isc__nmhandle_detach(isc_nmhandle_t **handlep FLARG) {
+ isc_nmsocket_t *sock = NULL;
+ isc_nmhandle_t *handle = NULL;
+
+ REQUIRE(handlep != NULL);
+ REQUIRE(VALID_NMHANDLE(*handlep));
+
+ handle = *handlep;
+ *handlep = NULL;
+
+ /*
+ * If the closehandle_cb is set, it needs to run asynchronously to
+ * ensure correct ordering of the isc__nm_process_sock_buffer().
+ */
+ sock = handle->sock;
+ if (sock->tid == isc_nm_tid() && sock->closehandle_cb == NULL) {
+ nmhandle_detach_cb(&handle FLARG_PASS);
+ } else {
+ isc__netievent_detach_t *event =
+ isc__nm_get_netievent_detach(sock->mgr, sock);
+ /*
+ * we are using implicit "attach" as the last reference
+ * need to be destroyed explicitly in the async callback
+ */
+ event->handle = handle;
+ FLARG_IEVENT_PASS(event);
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)event);
+ }
+}
+
+static void
+nmhandle_detach_cb(isc_nmhandle_t **handlep FLARG) {
+ isc_nmsocket_t *sock = NULL;
+ isc_nmhandle_t *handle = NULL;
+
+ REQUIRE(handlep != NULL);
+ REQUIRE(VALID_NMHANDLE(*handlep));
+
+ handle = *handlep;
+ *handlep = NULL;
+
+ NETMGR_TRACE_LOG("isc__nmhandle_detach():%p->references = %" PRIuFAST32
+ "\n",
+ handle, isc_refcount_current(&handle->references) - 1);
+
+ if (isc_refcount_decrement(&handle->references) > 1) {
+ return;
+ }
+
+ /* We need an acquire memory barrier here */
+ (void)isc_refcount_current(&handle->references);
+
+ sock = handle->sock;
+ handle->sock = NULL;
+
+ if (handle->doreset != NULL) {
+ handle->doreset(handle->opaque);
+ }
+
+#if HAVE_LIBNGHTTP2
+ if (sock->type == isc_nm_httpsocket && handle->httpsession != NULL) {
+ isc__nm_httpsession_detach(&handle->httpsession);
+ }
+#endif
+
+ nmhandle_deactivate(sock, handle);
+
+ /*
+ * The handle is gone now. If the socket has a callback configured
+ * for that (e.g., to perform cleanup after request processing),
+ * call it now, or schedule it to run asynchronously.
+ */
+ if (sock->closehandle_cb != NULL) {
+ if (sock->tid == isc_nm_tid()) {
+ sock->closehandle_cb(sock);
+ } else {
+ isc__netievent_close_t *event =
+ isc__nm_get_netievent_close(sock->mgr, sock);
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)event);
+ }
+ }
+
+ if (handle == sock->statichandle) {
+ /* statichandle is assigned, not attached. */
+ sock->statichandle = NULL;
+ }
+
+ isc___nmsocket_detach(&sock FLARG_PASS);
+}
+
+void *
+isc_nmhandle_getdata(isc_nmhandle_t *handle) {
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ return (handle->opaque);
+}
+
+void
+isc_nmhandle_setdata(isc_nmhandle_t *handle, void *arg,
+ isc_nm_opaquecb_t doreset, isc_nm_opaquecb_t dofree) {
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ handle->opaque = arg;
+ handle->doreset = doreset;
+ handle->dofree = dofree;
+}
+
+void
+isc__nm_alloc_dnsbuf(isc_nmsocket_t *sock, size_t len) {
+ REQUIRE(len <= NM_BIG_BUF);
+
+ if (sock->buf == NULL) {
+ /* We don't have the buffer at all */
+ size_t alloc_len = len < NM_REG_BUF ? NM_REG_BUF : NM_BIG_BUF;
+ sock->buf = isc_mem_get(sock->mgr->mctx, alloc_len);
+ sock->buf_size = alloc_len;
+ } else {
+ /* We have the buffer but it's too small */
+ sock->buf = isc_mem_reget(sock->mgr->mctx, sock->buf,
+ sock->buf_size, NM_BIG_BUF);
+ sock->buf_size = NM_BIG_BUF;
+ }
+}
+
+void
+isc__nm_failed_send_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req,
+ isc_result_t eresult) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(VALID_UVREQ(req));
+
+ if (req->cb.send != NULL) {
+ isc__nm_sendcb(sock, req, eresult, true);
+ } else {
+ isc__nm_uvreq_put(&req, sock);
+ }
+}
+
+void
+isc__nm_failed_accept_cb(isc_nmsocket_t *sock, isc_result_t eresult) {
+ REQUIRE(atomic_load(&sock->accepting));
+ REQUIRE(sock->server);
+
+ /*
+ * Detach the quota early to make room for other connections;
+ * otherwise it'd be detached later asynchronously, and clog
+ * the quota unnecessarily.
+ */
+ if (sock->quota != NULL) {
+ isc_quota_detach(&sock->quota);
+ }
+
+ isc__nmsocket_detach(&sock->server);
+
+ atomic_store(&sock->accepting, false);
+
+ switch (eresult) {
+ case ISC_R_NOTCONNECTED:
+ /* IGNORE: The client disconnected before we could accept */
+ break;
+ default:
+ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_NETMGR, ISC_LOG_ERROR,
+ "Accepting TCP connection failed: %s",
+ isc_result_totext(eresult));
+ }
+}
+
+void
+isc__nm_failed_connect_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req,
+ isc_result_t eresult, bool async) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(VALID_UVREQ(req));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(req->cb.connect != NULL);
+
+ isc__nm_incstats(sock, STATID_CONNECTFAIL);
+
+ isc__nmsocket_timer_stop(sock);
+ uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock);
+
+ atomic_compare_exchange_enforced(&sock->connecting, &(bool){ true },
+ false);
+
+ isc__nmsocket_clearcb(sock);
+ isc__nm_connectcb(sock, req, eresult, async);
+
+ isc__nmsocket_prep_destroy(sock);
+}
+
+void
+isc__nm_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result, bool async) {
+ REQUIRE(VALID_NMSOCK(sock));
+ switch (sock->type) {
+ case isc_nm_udpsocket:
+ isc__nm_udp_failed_read_cb(sock, result);
+ return;
+ case isc_nm_tcpsocket:
+ isc__nm_tcp_failed_read_cb(sock, result);
+ return;
+ case isc_nm_tcpdnssocket:
+ isc__nm_tcpdns_failed_read_cb(sock, result);
+ return;
+ case isc_nm_tlsdnssocket:
+ isc__nm_tlsdns_failed_read_cb(sock, result, async);
+ return;
+ default:
+ UNREACHABLE();
+ }
+}
+
+void
+isc__nmsocket_connecttimeout_cb(uv_timer_t *timer) {
+ uv_connect_t *uvreq = uv_handle_get_data((uv_handle_t *)timer);
+ isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)uvreq->handle);
+ isc__nm_uvreq_t *req = uv_handle_get_data((uv_handle_t *)uvreq);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(atomic_load(&sock->connecting));
+ REQUIRE(VALID_UVREQ(req));
+ REQUIRE(VALID_NMHANDLE(req->handle));
+
+ isc__nmsocket_timer_stop(sock);
+
+ /*
+ * Mark the connection as timed out and shutdown the socket.
+ */
+ atomic_compare_exchange_enforced(&sock->timedout, &(bool){ false },
+ true);
+ isc__nmsocket_clearcb(sock);
+ isc__nmsocket_shutdown(sock);
+}
+
+void
+isc__nm_accept_connection_log(isc_result_t result, bool can_log_quota) {
+ int level;
+
+ switch (result) {
+ case ISC_R_SUCCESS:
+ case ISC_R_NOCONN:
+ return;
+ case ISC_R_QUOTA:
+ case ISC_R_SOFTQUOTA:
+ if (!can_log_quota) {
+ return;
+ }
+ level = ISC_LOG_INFO;
+ break;
+ case ISC_R_NOTCONNECTED:
+ level = ISC_LOG_INFO;
+ break;
+ default:
+ level = ISC_LOG_ERROR;
+ }
+
+ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_NETMGR,
+ level, "Accepting TCP connection failed: %s",
+ isc_result_totext(result));
+}
+
+void
+isc__nmsocket_writetimeout_cb(void *data, isc_result_t eresult) {
+ isc__nm_uvreq_t *req = data;
+ isc_nmsocket_t *sock = NULL;
+
+ REQUIRE(eresult == ISC_R_TIMEDOUT);
+ REQUIRE(VALID_UVREQ(req));
+ REQUIRE(VALID_NMSOCK(req->sock));
+
+ sock = req->sock;
+
+ isc__nmsocket_reset(sock);
+}
+
+void
+isc__nmsocket_readtimeout_cb(uv_timer_t *timer) {
+ isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)timer);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(atomic_load(&sock->reading));
+
+ if (atomic_load(&sock->client)) {
+ uv_timer_stop(timer);
+
+ sock->recv_read = false;
+
+ if (sock->recv_cb != NULL) {
+ isc__nm_uvreq_t *req = isc__nm_get_read_req(sock, NULL);
+ isc__nm_readcb(sock, req, ISC_R_TIMEDOUT);
+ }
+
+ if (!isc__nmsocket_timer_running(sock)) {
+ isc__nmsocket_clearcb(sock);
+ isc__nm_failed_read_cb(sock, ISC_R_CANCELED, false);
+ }
+ } else {
+ isc__nm_failed_read_cb(sock, ISC_R_TIMEDOUT, false);
+ }
+}
+
+void
+isc__nmsocket_timer_restart(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+
+ if (uv_is_closing((uv_handle_t *)&sock->read_timer)) {
+ return;
+ }
+
+ if (atomic_load(&sock->connecting)) {
+ int r;
+
+ if (sock->connect_timeout == 0) {
+ return;
+ }
+
+ r = uv_timer_start(&sock->read_timer,
+ isc__nmsocket_connecttimeout_cb,
+ sock->connect_timeout + 10, 0);
+ UV_RUNTIME_CHECK(uv_timer_start, r);
+
+ } else {
+ int r;
+
+ if (sock->read_timeout == 0) {
+ return;
+ }
+
+ r = uv_timer_start(&sock->read_timer,
+ isc__nmsocket_readtimeout_cb,
+ sock->read_timeout, 0);
+ UV_RUNTIME_CHECK(uv_timer_start, r);
+ }
+}
+
+bool
+isc__nmsocket_timer_running(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+
+ return (uv_is_active((uv_handle_t *)&sock->read_timer));
+}
+
+void
+isc__nmsocket_timer_start(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+
+ if (isc__nmsocket_timer_running(sock)) {
+ return;
+ }
+
+ isc__nmsocket_timer_restart(sock);
+}
+
+void
+isc__nmsocket_timer_stop(isc_nmsocket_t *sock) {
+ int r;
+
+ REQUIRE(VALID_NMSOCK(sock));
+
+ /* uv_timer_stop() is idempotent, no need to check if running */
+
+ r = uv_timer_stop(&sock->read_timer);
+ UV_RUNTIME_CHECK(uv_timer_stop, r);
+}
+
+isc__nm_uvreq_t *
+isc__nm_get_read_req(isc_nmsocket_t *sock, isc_sockaddr_t *sockaddr) {
+ isc__nm_uvreq_t *req = NULL;
+
+ req = isc__nm_uvreq_get(sock->mgr, sock);
+ req->cb.recv = sock->recv_cb;
+ req->cbarg = sock->recv_cbarg;
+
+ switch (sock->type) {
+ case isc_nm_tcpsocket:
+ case isc_nm_tlssocket:
+ isc_nmhandle_attach(sock->statichandle, &req->handle);
+ break;
+ default:
+ if (atomic_load(&sock->client) && sock->statichandle != NULL) {
+ isc_nmhandle_attach(sock->statichandle, &req->handle);
+ } else {
+ req->handle = isc__nmhandle_get(sock, sockaddr, NULL);
+ }
+ break;
+ }
+
+ return (req);
+}
+
+/*%<
+ * Allocator callback for read operations.
+ *
+ * Note this doesn't actually allocate anything, it just assigns the
+ * worker's receive buffer to a socket, and marks it as "in use".
+ */
+void
+isc__nm_alloc_cb(uv_handle_t *handle, size_t size, uv_buf_t *buf) {
+ isc_nmsocket_t *sock = uv_handle_get_data(handle);
+ isc__networker_t *worker = NULL;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(isc__nm_in_netthread());
+ /*
+ * The size provided by libuv is only suggested size, and it always
+ * defaults to 64 * 1024 in the current versions of libuv (see
+ * src/unix/udp.c and src/unix/stream.c).
+ */
+ UNUSED(size);
+
+ worker = &sock->mgr->workers[sock->tid];
+ INSIST(!worker->recvbuf_inuse);
+ INSIST(worker->recvbuf != NULL);
+
+ switch (sock->type) {
+ case isc_nm_udpsocket:
+ buf->len = ISC_NETMGR_UDP_RECVBUF_SIZE;
+ break;
+ case isc_nm_tcpsocket:
+ case isc_nm_tcpdnssocket:
+ case isc_nm_tlsdnssocket:
+ buf->len = ISC_NETMGR_TCP_RECVBUF_SIZE;
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ REQUIRE(buf->len <= ISC_NETMGR_RECVBUF_SIZE);
+ buf->base = worker->recvbuf;
+
+ worker->recvbuf_inuse = true;
+}
+
+isc_result_t
+isc__nm_start_reading(isc_nmsocket_t *sock) {
+ isc_result_t result = ISC_R_SUCCESS;
+ int r;
+
+ if (atomic_load(&sock->reading)) {
+ return (ISC_R_SUCCESS);
+ }
+
+ switch (sock->type) {
+ case isc_nm_udpsocket:
+ r = uv_udp_recv_start(&sock->uv_handle.udp, isc__nm_alloc_cb,
+ isc__nm_udp_read_cb);
+ break;
+ case isc_nm_tcpsocket:
+ r = uv_read_start(&sock->uv_handle.stream, isc__nm_alloc_cb,
+ isc__nm_tcp_read_cb);
+ break;
+ case isc_nm_tcpdnssocket:
+ r = uv_read_start(&sock->uv_handle.stream, isc__nm_alloc_cb,
+ isc__nm_tcpdns_read_cb);
+ break;
+ case isc_nm_tlsdnssocket:
+ r = uv_read_start(&sock->uv_handle.stream, isc__nm_alloc_cb,
+ isc__nm_tlsdns_read_cb);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ if (r != 0) {
+ result = isc__nm_uverr2result(r);
+ } else {
+ atomic_store(&sock->reading, true);
+ }
+
+ return (result);
+}
+
+void
+isc__nm_stop_reading(isc_nmsocket_t *sock) {
+ int r;
+
+ if (!atomic_load(&sock->reading)) {
+ return;
+ }
+
+ switch (sock->type) {
+ case isc_nm_udpsocket:
+ r = uv_udp_recv_stop(&sock->uv_handle.udp);
+ UV_RUNTIME_CHECK(uv_udp_recv_stop, r);
+ break;
+ case isc_nm_tcpsocket:
+ case isc_nm_tcpdnssocket:
+ case isc_nm_tlsdnssocket:
+ r = uv_read_stop(&sock->uv_handle.stream);
+ UV_RUNTIME_CHECK(uv_read_stop, r);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ atomic_store(&sock->reading, false);
+}
+
+bool
+isc__nm_closing(isc_nmsocket_t *sock) {
+ return (atomic_load(&sock->mgr->closing));
+}
+
+bool
+isc__nmsocket_closing(isc_nmsocket_t *sock) {
+ return (!isc__nmsocket_active(sock) || atomic_load(&sock->closing) ||
+ isc__nm_closing(sock) ||
+ (sock->server != NULL && !isc__nmsocket_active(sock->server)));
+}
+
+static isc_result_t
+processbuffer(isc_nmsocket_t *sock) {
+ switch (sock->type) {
+ case isc_nm_tcpdnssocket:
+ return (isc__nm_tcpdns_processbuffer(sock));
+ case isc_nm_tlsdnssocket:
+ return (isc__nm_tlsdns_processbuffer(sock));
+ default:
+ UNREACHABLE();
+ }
+}
+
+/*
+ * Process a DNS message.
+ *
+ * If we only have an incomplete DNS message, we don't touch any
+ * timers. If we do have a full message, reset the timer.
+ *
+ * Stop reading if this is a client socket, or if the server socket
+ * has been set to sequential mode. In this case we'll be called again
+ * later by isc__nm_resume_processing().
+ */
+isc_result_t
+isc__nm_process_sock_buffer(isc_nmsocket_t *sock) {
+ for (;;) {
+ int_fast32_t ah = atomic_load(&sock->ah);
+ isc_result_t result = processbuffer(sock);
+ switch (result) {
+ case ISC_R_NOMORE:
+ /*
+ * Don't reset the timer until we have a
+ * full DNS message.
+ */
+ result = isc__nm_start_reading(sock);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ /*
+ * Start the timer only if there are no externally used
+ * active handles, there's always one active handle
+ * attached internally to sock->recv_handle in
+ * accept_connection()
+ */
+ if (ah == 1) {
+ isc__nmsocket_timer_start(sock);
+ }
+ goto done;
+ case ISC_R_CANCELED:
+ isc__nmsocket_timer_stop(sock);
+ isc__nm_stop_reading(sock);
+ goto done;
+ case ISC_R_SUCCESS:
+ /*
+ * Stop the timer on the successful message read, this
+ * also allows to restart the timer when we have no more
+ * data.
+ */
+ isc__nmsocket_timer_stop(sock);
+
+ if (atomic_load(&sock->client) ||
+ atomic_load(&sock->sequential))
+ {
+ isc__nm_stop_reading(sock);
+ goto done;
+ }
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+done:
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc__nm_resume_processing(void *arg) {
+ isc_nmsocket_t *sock = (isc_nmsocket_t *)arg;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(!atomic_load(&sock->client));
+
+ if (isc__nmsocket_closing(sock)) {
+ return;
+ }
+
+ isc__nm_process_sock_buffer(sock);
+}
+
+void
+isc_nmhandle_cleartimeout(isc_nmhandle_t *handle) {
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ switch (handle->sock->type) {
+#if HAVE_LIBNGHTTP2
+ case isc_nm_httpsocket:
+ isc__nm_http_cleartimeout(handle);
+ return;
+ case isc_nm_tlssocket:
+ isc__nm_tls_cleartimeout(handle);
+ return;
+#endif
+ default:
+ handle->sock->read_timeout = 0;
+
+ if (uv_is_active((uv_handle_t *)&handle->sock->read_timer)) {
+ isc__nmsocket_timer_stop(handle->sock);
+ }
+ }
+}
+
+void
+isc_nmhandle_settimeout(isc_nmhandle_t *handle, uint32_t timeout) {
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ switch (handle->sock->type) {
+#if HAVE_LIBNGHTTP2
+ case isc_nm_httpsocket:
+ isc__nm_http_settimeout(handle, timeout);
+ return;
+ case isc_nm_tlssocket:
+ isc__nm_tls_settimeout(handle, timeout);
+ return;
+#endif
+ default:
+ handle->sock->read_timeout = timeout;
+ isc__nmsocket_timer_restart(handle->sock);
+ }
+}
+
+void
+isc_nmhandle_keepalive(isc_nmhandle_t *handle, bool value) {
+ isc_nmsocket_t *sock = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ sock = handle->sock;
+
+ switch (sock->type) {
+ case isc_nm_tcpsocket:
+ case isc_nm_tcpdnssocket:
+ case isc_nm_tlsdnssocket:
+ atomic_store(&sock->keepalive, value);
+ sock->read_timeout = value ? atomic_load(&sock->mgr->keepalive)
+ : atomic_load(&sock->mgr->idle);
+ sock->write_timeout = value ? atomic_load(&sock->mgr->keepalive)
+ : atomic_load(&sock->mgr->idle);
+ break;
+#if HAVE_LIBNGHTTP2
+ case isc_nm_tlssocket:
+ isc__nmhandle_tls_keepalive(handle, value);
+ break;
+ case isc_nm_httpsocket:
+ isc__nmhandle_http_keepalive(handle, value);
+ break;
+#endif /* HAVE_LIBNGHTTP2 */
+ default:
+ /*
+ * For any other protocol, this is a no-op.
+ */
+ return;
+ }
+}
+
+bool
+isc_nmhandle_timer_running(isc_nmhandle_t *handle) {
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ return (isc__nmsocket_timer_running(handle->sock));
+}
+
+void *
+isc_nmhandle_getextra(isc_nmhandle_t *handle) {
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ return (handle->extra);
+}
+
+isc_sockaddr_t
+isc_nmhandle_peeraddr(isc_nmhandle_t *handle) {
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ return (handle->peer);
+}
+
+isc_sockaddr_t
+isc_nmhandle_localaddr(isc_nmhandle_t *handle) {
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ return (handle->local);
+}
+
+isc_nm_t *
+isc_nmhandle_netmgr(isc_nmhandle_t *handle) {
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ return (handle->sock->mgr);
+}
+
+isc__nm_uvreq_t *
+isc___nm_uvreq_get(isc_nm_t *mgr, isc_nmsocket_t *sock FLARG) {
+ isc__nm_uvreq_t *req = NULL;
+
+ REQUIRE(VALID_NM(mgr));
+ REQUIRE(VALID_NMSOCK(sock));
+
+ if (sock != NULL && isc__nmsocket_active(sock)) {
+ /* Try to reuse one */
+ req = isc_astack_pop(sock->inactivereqs);
+ }
+
+ if (req == NULL) {
+ req = isc_mem_get(mgr->mctx, sizeof(*req));
+ }
+
+ *req = (isc__nm_uvreq_t){
+ .magic = 0,
+ .connect_tries = 3,
+ };
+ ISC_LINK_INIT(req, link);
+ req->uv_req.req.data = req;
+ isc___nmsocket_attach(sock, &req->sock FLARG_PASS);
+ req->magic = UVREQ_MAGIC;
+
+ return (req);
+}
+
+void
+isc___nm_uvreq_put(isc__nm_uvreq_t **req0, isc_nmsocket_t *sock FLARG) {
+ isc__nm_uvreq_t *req = NULL;
+ isc_nmhandle_t *handle = NULL;
+
+ REQUIRE(req0 != NULL);
+ REQUIRE(VALID_UVREQ(*req0));
+
+ req = *req0;
+ *req0 = NULL;
+
+ INSIST(sock == req->sock);
+
+ req->magic = 0;
+
+ /*
+ * We need to save this first to make sure that handle,
+ * sock, and the netmgr won't all disappear.
+ */
+ handle = req->handle;
+ req->handle = NULL;
+
+#if !__SANITIZE_ADDRESS__ && !__SANITIZE_THREAD__
+ if (!isc__nmsocket_active(sock) ||
+ !isc_astack_trypush(sock->inactivereqs, req))
+ {
+ isc_mem_put(sock->mgr->mctx, req, sizeof(*req));
+ }
+#else /* !__SANITIZE_ADDRESS__ && !__SANITIZE_THREAD__ */
+ isc_mem_put(sock->mgr->mctx, req, sizeof(*req));
+#endif /* !__SANITIZE_ADDRESS__ && !__SANITIZE_THREAD__ */
+
+ if (handle != NULL) {
+ isc__nmhandle_detach(&handle FLARG_PASS);
+ }
+
+ isc___nmsocket_detach(&sock FLARG_PASS);
+}
+
+void
+isc_nm_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
+ void *cbarg) {
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ switch (handle->sock->type) {
+ case isc_nm_udpsocket:
+ case isc_nm_udplistener:
+ isc__nm_udp_send(handle, region, cb, cbarg);
+ break;
+ case isc_nm_tcpsocket:
+ isc__nm_tcp_send(handle, region, cb, cbarg);
+ break;
+ case isc_nm_tcpdnssocket:
+ isc__nm_tcpdns_send(handle, region, cb, cbarg);
+ break;
+ case isc_nm_tlsdnssocket:
+ isc__nm_tlsdns_send(handle, region, cb, cbarg);
+ break;
+#if HAVE_LIBNGHTTP2
+ case isc_nm_tlssocket:
+ isc__nm_tls_send(handle, region, cb, cbarg);
+ break;
+ case isc_nm_httpsocket:
+ isc__nm_http_send(handle, region, cb, cbarg);
+ break;
+#endif
+ default:
+ UNREACHABLE();
+ }
+}
+
+void
+isc_nm_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ switch (handle->sock->type) {
+ case isc_nm_udpsocket:
+ isc__nm_udp_read(handle, cb, cbarg);
+ break;
+ case isc_nm_tcpsocket:
+ isc__nm_tcp_read(handle, cb, cbarg);
+ break;
+ case isc_nm_tcpdnssocket:
+ isc__nm_tcpdns_read(handle, cb, cbarg);
+ break;
+ case isc_nm_tlsdnssocket:
+ isc__nm_tlsdns_read(handle, cb, cbarg);
+ break;
+#if HAVE_LIBNGHTTP2
+ case isc_nm_tlssocket:
+ isc__nm_tls_read(handle, cb, cbarg);
+ break;
+ case isc_nm_httpsocket:
+ isc__nm_http_read(handle, cb, cbarg);
+ break;
+#endif
+ default:
+ UNREACHABLE();
+ }
+}
+
+void
+isc_nm_cancelread(isc_nmhandle_t *handle) {
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ switch (handle->sock->type) {
+ case isc_nm_udpsocket:
+ isc__nm_udp_cancelread(handle);
+ break;
+ case isc_nm_tcpsocket:
+ isc__nm_tcp_cancelread(handle);
+ break;
+ case isc_nm_tcpdnssocket:
+ isc__nm_tcpdns_cancelread(handle);
+ break;
+ case isc_nm_tlsdnssocket:
+ isc__nm_tlsdns_cancelread(handle);
+ break;
+#if HAVE_LIBNGHTTP2
+ case isc_nm_tlssocket:
+ isc__nm_tls_cancelread(handle);
+ break;
+#endif
+ default:
+ UNREACHABLE();
+ }
+}
+
+void
+isc_nm_pauseread(isc_nmhandle_t *handle) {
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ isc_nmsocket_t *sock = handle->sock;
+
+ switch (sock->type) {
+ case isc_nm_tcpsocket:
+ isc__nm_tcp_pauseread(handle);
+ break;
+#if HAVE_LIBNGHTTP2
+ case isc_nm_tlssocket:
+ isc__nm_tls_pauseread(handle);
+ break;
+#endif
+ default:
+ UNREACHABLE();
+ }
+}
+
+void
+isc_nm_resumeread(isc_nmhandle_t *handle) {
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ isc_nmsocket_t *sock = handle->sock;
+
+ switch (sock->type) {
+ case isc_nm_tcpsocket:
+ isc__nm_tcp_resumeread(handle);
+ break;
+#if HAVE_LIBNGHTTP2
+ case isc_nm_tlssocket:
+ isc__nm_tls_resumeread(handle);
+ break;
+#endif
+ default:
+ UNREACHABLE();
+ }
+}
+
+void
+isc_nm_stoplistening(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+
+ switch (sock->type) {
+ case isc_nm_udplistener:
+ isc__nm_udp_stoplistening(sock);
+ break;
+ case isc_nm_tcpdnslistener:
+ isc__nm_tcpdns_stoplistening(sock);
+ break;
+ case isc_nm_tcplistener:
+ isc__nm_tcp_stoplistening(sock);
+ break;
+ case isc_nm_tlsdnslistener:
+ isc__nm_tlsdns_stoplistening(sock);
+ break;
+#if HAVE_LIBNGHTTP2
+ case isc_nm_tlslistener:
+ isc__nm_tls_stoplistening(sock);
+ break;
+ case isc_nm_httplistener:
+ isc__nm_http_stoplistening(sock);
+ break;
+#endif
+ default:
+ UNREACHABLE();
+ }
+}
+
+void
+isc__nmsocket_stop(isc_nmsocket_t *listener) {
+ isc__netievent_sockstop_t ievent = { .sock = listener };
+
+ REQUIRE(VALID_NMSOCK(listener));
+
+ if (!atomic_compare_exchange_strong(&listener->closing,
+ &(bool){ false }, true))
+ {
+ UNREACHABLE();
+ }
+
+ for (size_t i = 0; i < listener->nchildren; i++) {
+ isc__networker_t *worker = &listener->mgr->workers[i];
+ isc__netievent_sockstop_t *ev;
+
+ if (isc__nm_in_netthread() && i == (size_t)isc_nm_tid()) {
+ continue;
+ }
+
+ ev = isc__nm_get_netievent_sockstop(listener->mgr, listener);
+ isc__nm_enqueue_ievent(worker, (isc__netievent_t *)ev);
+ }
+
+ if (isc__nm_in_netthread()) {
+ isc__nm_async_sockstop(&listener->mgr->workers[isc_nm_tid()],
+ (isc__netievent_t *)&ievent);
+ }
+}
+
+void
+isc__nmsocket_barrier_init(isc_nmsocket_t *listener) {
+ REQUIRE(listener->nchildren > 0);
+ isc_barrier_init(&listener->barrier, listener->nchildren);
+ listener->barrier_initialised = true;
+}
+
+void
+isc__nm_async_sockstop(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_sockstop_t *ievent = (isc__netievent_sockstop_t *)ev0;
+ isc_nmsocket_t *listener = ievent->sock;
+ UNUSED(worker);
+
+ (void)atomic_fetch_sub(&listener->rchildren, 1);
+ isc_barrier_wait(&listener->barrier);
+
+ if (listener->tid != isc_nm_tid()) {
+ return;
+ }
+
+ if (!atomic_compare_exchange_strong(&listener->listening,
+ &(bool){ true }, false))
+ {
+ UNREACHABLE();
+ }
+
+ INSIST(atomic_load(&listener->rchildren) == 0);
+
+ listener->accept_cb = NULL;
+ listener->accept_cbarg = NULL;
+ listener->recv_cb = NULL;
+ listener->recv_cbarg = NULL;
+
+ if (listener->outer != NULL) {
+ isc_nm_stoplistening(listener->outer);
+ isc__nmsocket_detach(&listener->outer);
+ }
+
+ atomic_store(&listener->closed, true);
+}
+
+void
+isc__nm_connectcb(isc_nmsocket_t *sock, isc__nm_uvreq_t *uvreq,
+ isc_result_t eresult, bool async) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(VALID_UVREQ(uvreq));
+ REQUIRE(VALID_NMHANDLE(uvreq->handle));
+
+ if (!async) {
+ isc__netievent_connectcb_t ievent = { .sock = sock,
+ .req = uvreq,
+ .result = eresult };
+ isc__nm_async_connectcb(NULL, (isc__netievent_t *)&ievent);
+ } else {
+ isc__netievent_connectcb_t *ievent =
+ isc__nm_get_netievent_connectcb(sock->mgr, sock, uvreq,
+ eresult);
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+ }
+}
+
+void
+isc__nm_async_connectcb(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_connectcb_t *ievent = (isc__netievent_connectcb_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+ isc__nm_uvreq_t *uvreq = ievent->req;
+ isc_result_t eresult = ievent->result;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(VALID_UVREQ(uvreq));
+ REQUIRE(VALID_NMHANDLE(uvreq->handle));
+ REQUIRE(ievent->sock->tid == isc_nm_tid());
+ REQUIRE(uvreq->cb.connect != NULL);
+
+ uvreq->cb.connect(uvreq->handle, eresult, uvreq->cbarg);
+
+ isc__nm_uvreq_put(&uvreq, sock);
+}
+
+void
+isc__nm_readcb(isc_nmsocket_t *sock, isc__nm_uvreq_t *uvreq,
+ isc_result_t eresult) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(VALID_UVREQ(uvreq));
+ REQUIRE(VALID_NMHANDLE(uvreq->handle));
+
+ if (eresult == ISC_R_SUCCESS || eresult == ISC_R_TIMEDOUT) {
+ isc__netievent_readcb_t ievent = { .sock = sock,
+ .req = uvreq,
+ .result = eresult };
+
+ isc__nm_async_readcb(NULL, (isc__netievent_t *)&ievent);
+ } else {
+ isc__netievent_readcb_t *ievent = isc__nm_get_netievent_readcb(
+ sock->mgr, sock, uvreq, eresult);
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+ }
+}
+
+void
+isc__nm_async_readcb(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_readcb_t *ievent = (isc__netievent_readcb_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+ isc__nm_uvreq_t *uvreq = ievent->req;
+ isc_result_t eresult = ievent->result;
+ isc_region_t region;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(VALID_UVREQ(uvreq));
+ REQUIRE(VALID_NMHANDLE(uvreq->handle));
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ region.base = (unsigned char *)uvreq->uvbuf.base;
+ region.length = uvreq->uvbuf.len;
+
+ uvreq->cb.recv(uvreq->handle, eresult, &region, uvreq->cbarg);
+
+ isc__nm_uvreq_put(&uvreq, sock);
+}
+
+void
+isc__nm_sendcb(isc_nmsocket_t *sock, isc__nm_uvreq_t *uvreq,
+ isc_result_t eresult, bool async) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(VALID_UVREQ(uvreq));
+ REQUIRE(VALID_NMHANDLE(uvreq->handle));
+
+ if (!async) {
+ isc__netievent_sendcb_t ievent = { .sock = sock,
+ .req = uvreq,
+ .result = eresult };
+ isc__nm_async_sendcb(NULL, (isc__netievent_t *)&ievent);
+ return;
+ }
+
+ isc__netievent_sendcb_t *ievent =
+ isc__nm_get_netievent_sendcb(sock->mgr, sock, uvreq, eresult);
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+}
+
+void
+isc__nm_async_sendcb(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_sendcb_t *ievent = (isc__netievent_sendcb_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+ isc__nm_uvreq_t *uvreq = ievent->req;
+ isc_result_t eresult = ievent->result;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(VALID_UVREQ(uvreq));
+ REQUIRE(VALID_NMHANDLE(uvreq->handle));
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ uvreq->cb.send(uvreq->handle, eresult, uvreq->cbarg);
+
+ isc__nm_uvreq_put(&uvreq, sock);
+}
+
+static void
+isc__nm_async_close(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_close_t *ievent = (isc__netievent_close_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+
+ REQUIRE(VALID_NMSOCK(ievent->sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(sock->closehandle_cb != NULL);
+
+ UNUSED(worker);
+
+ ievent->sock->closehandle_cb(sock);
+}
+
+void
+isc__nm_async_detach(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_detach_t *ievent = (isc__netievent_detach_t *)ev0;
+ FLARG_IEVENT(ievent);
+
+ REQUIRE(VALID_NMSOCK(ievent->sock));
+ REQUIRE(VALID_NMHANDLE(ievent->handle));
+ REQUIRE(ievent->sock->tid == isc_nm_tid());
+
+ UNUSED(worker);
+
+ nmhandle_detach_cb(&ievent->handle FLARG_PASS);
+}
+
+static void
+reset_shutdown(uv_handle_t *handle) {
+ isc_nmsocket_t *sock = uv_handle_get_data(handle);
+
+ isc__nmsocket_shutdown(sock);
+ isc__nmsocket_detach(&sock);
+}
+
+void
+isc__nmsocket_reset(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+
+ switch (sock->type) {
+ case isc_nm_tcpsocket:
+ case isc_nm_tcpdnssocket:
+ case isc_nm_tlsdnssocket:
+ /*
+ * This can be called from the TCP write timeout, or
+ * from the TCPDNS or TLSDNS branches of isc_nm_bad_request().
+ */
+ REQUIRE(sock->parent == NULL);
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+
+ if (!uv_is_closing(&sock->uv_handle.handle) &&
+ uv_is_active(&sock->uv_handle.handle))
+ {
+ /*
+ * The real shutdown will be handled in the respective
+ * close functions.
+ */
+ isc__nmsocket_attach(sock, &(isc_nmsocket_t *){ NULL });
+ int r = uv_tcp_close_reset(&sock->uv_handle.tcp,
+ reset_shutdown);
+ UV_RUNTIME_CHECK(uv_tcp_close_reset, r);
+ } else {
+ isc__nmsocket_shutdown(sock);
+ }
+}
+
+void
+isc__nmsocket_shutdown(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+ switch (sock->type) {
+ case isc_nm_udpsocket:
+ isc__nm_udp_shutdown(sock);
+ break;
+ case isc_nm_tcpsocket:
+ isc__nm_tcp_shutdown(sock);
+ break;
+ case isc_nm_tcpdnssocket:
+ isc__nm_tcpdns_shutdown(sock);
+ break;
+ case isc_nm_tlsdnssocket:
+ isc__nm_tlsdns_shutdown(sock);
+ break;
+ case isc_nm_udplistener:
+ case isc_nm_tcplistener:
+ case isc_nm_tcpdnslistener:
+ case isc_nm_tlsdnslistener:
+ return;
+ default:
+ UNREACHABLE();
+ }
+}
+
+static void
+shutdown_walk_cb(uv_handle_t *handle, void *arg) {
+ isc_nmsocket_t *sock = uv_handle_get_data(handle);
+ UNUSED(arg);
+
+ if (uv_is_closing(handle)) {
+ return;
+ }
+
+ switch (handle->type) {
+ case UV_UDP:
+ isc__nmsocket_shutdown(sock);
+ return;
+ case UV_TCP:
+ switch (sock->type) {
+ case isc_nm_tcpsocket:
+ case isc_nm_tcpdnssocket:
+ case isc_nm_tlsdnssocket:
+ if (sock->parent == NULL) {
+ /* Reset the TCP connections on shutdown */
+ isc__nmsocket_reset(sock);
+ return;
+ }
+ FALLTHROUGH;
+ default:
+ isc__nmsocket_shutdown(sock);
+ }
+
+ return;
+ default:
+ return;
+ }
+}
+
+void
+isc__nm_async_shutdown(isc__networker_t *worker, isc__netievent_t *ev0) {
+ UNUSED(ev0);
+
+ uv_walk(&worker->loop, shutdown_walk_cb, NULL);
+}
+
+bool
+isc__nm_acquire_interlocked(isc_nm_t *mgr) {
+ if (!isc__nm_in_netthread()) {
+ return (false);
+ }
+
+ LOCK(&mgr->lock);
+ bool success = atomic_compare_exchange_strong(
+ &mgr->interlocked, &(int){ ISC_NETMGR_NON_INTERLOCKED },
+ isc_nm_tid());
+
+ UNLOCK(&mgr->lock);
+ return (success);
+}
+
+void
+isc__nm_drop_interlocked(isc_nm_t *mgr) {
+ if (!isc__nm_in_netthread()) {
+ return;
+ }
+
+ LOCK(&mgr->lock);
+ int tid = atomic_exchange(&mgr->interlocked,
+ ISC_NETMGR_NON_INTERLOCKED);
+ INSIST(tid != ISC_NETMGR_NON_INTERLOCKED);
+ BROADCAST(&mgr->wkstatecond);
+ UNLOCK(&mgr->lock);
+}
+
+void
+isc__nm_acquire_interlocked_force(isc_nm_t *mgr) {
+ if (!isc__nm_in_netthread()) {
+ return;
+ }
+
+ LOCK(&mgr->lock);
+ while (!atomic_compare_exchange_strong(
+ &mgr->interlocked, &(int){ ISC_NETMGR_NON_INTERLOCKED },
+ isc_nm_tid()))
+ {
+ WAIT(&mgr->wkstatecond, &mgr->lock);
+ }
+ UNLOCK(&mgr->lock);
+}
+
+void
+isc_nm_setstats(isc_nm_t *mgr, isc_stats_t *stats) {
+ REQUIRE(VALID_NM(mgr));
+ REQUIRE(mgr->stats == NULL);
+ REQUIRE(isc_stats_ncounters(stats) == isc_sockstatscounter_max);
+
+ isc_stats_attach(stats, &mgr->stats);
+}
+
+void
+isc__nm_incstats(isc_nmsocket_t *sock, isc__nm_statid_t id) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(id < STATID_MAX);
+
+ if (sock->statsindex != NULL && sock->mgr->stats != NULL) {
+ isc_stats_increment(sock->mgr->stats, sock->statsindex[id]);
+ }
+}
+
+void
+isc__nm_decstats(isc_nmsocket_t *sock, isc__nm_statid_t id) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(id < STATID_MAX);
+
+ if (sock->statsindex != NULL && sock->mgr->stats != NULL) {
+ isc_stats_decrement(sock->mgr->stats, sock->statsindex[id]);
+ }
+}
+
+isc_result_t
+isc__nm_socket(int domain, int type, int protocol, uv_os_sock_t *sockp) {
+ int sock = socket(domain, type, protocol);
+ if (sock < 0) {
+ return (isc_errno_toresult(errno));
+ }
+
+ *sockp = (uv_os_sock_t)sock;
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc__nm_closesocket(uv_os_sock_t sock) {
+ close(sock);
+}
+
+#define setsockopt_on(socket, level, name) \
+ setsockopt(socket, level, name, &(int){ 1 }, sizeof(int))
+
+#define setsockopt_off(socket, level, name) \
+ setsockopt(socket, level, name, &(int){ 0 }, sizeof(int))
+
+isc_result_t
+isc__nm_socket_freebind(uv_os_sock_t fd, sa_family_t sa_family) {
+ /*
+ * Set the IP_FREEBIND (or equivalent option) on the uv_handle.
+ */
+#ifdef IP_FREEBIND
+ UNUSED(sa_family);
+ if (setsockopt_on(fd, IPPROTO_IP, IP_FREEBIND) == -1) {
+ return (ISC_R_FAILURE);
+ }
+ return (ISC_R_SUCCESS);
+#elif defined(IP_BINDANY) || defined(IPV6_BINDANY)
+ if (sa_family == AF_INET) {
+#if defined(IP_BINDANY)
+ if (setsockopt_on(fd, IPPROTO_IP, IP_BINDANY) == -1) {
+ return (ISC_R_FAILURE);
+ }
+ return (ISC_R_SUCCESS);
+#endif
+ } else if (sa_family == AF_INET6) {
+#if defined(IPV6_BINDANY)
+ if (setsockopt_on(fd, IPPROTO_IPV6, IPV6_BINDANY) == -1) {
+ return (ISC_R_FAILURE);
+ }
+ return (ISC_R_SUCCESS);
+#endif
+ }
+ return (ISC_R_NOTIMPLEMENTED);
+#elif defined(SO_BINDANY)
+ UNUSED(sa_family);
+ if (setsockopt_on(fd, SOL_SOCKET, SO_BINDANY) == -1) {
+ return (ISC_R_FAILURE);
+ }
+ return (ISC_R_SUCCESS);
+#else
+ UNUSED(fd);
+ UNUSED(sa_family);
+ return (ISC_R_NOTIMPLEMENTED);
+#endif
+}
+
+isc_result_t
+isc__nm_socket_reuse(uv_os_sock_t fd) {
+ /*
+ * Generally, the SO_REUSEADDR socket option allows reuse of
+ * local addresses.
+ *
+ * On the BSDs, SO_REUSEPORT implies SO_REUSEADDR but with some
+ * additional refinements for programs that use multicast.
+ *
+ * On Linux, SO_REUSEPORT has different semantics: it _shares_ the port
+ * rather than steal it from the current listener, so we don't use it
+ * here, but rather in isc__nm_socket_reuse_lb().
+ *
+ * On Windows, it also allows a socket to forcibly bind to a port in use
+ * by another socket.
+ */
+
+#if defined(SO_REUSEPORT) && !defined(__linux__)
+ if (setsockopt_on(fd, SOL_SOCKET, SO_REUSEPORT) == -1) {
+ return (ISC_R_FAILURE);
+ }
+ return (ISC_R_SUCCESS);
+#elif defined(SO_REUSEADDR)
+ if (setsockopt_on(fd, SOL_SOCKET, SO_REUSEADDR) == -1) {
+ return (ISC_R_FAILURE);
+ }
+ return (ISC_R_SUCCESS);
+#else
+ UNUSED(fd);
+ return (ISC_R_NOTIMPLEMENTED);
+#endif
+}
+
+isc_result_t
+isc__nm_socket_reuse_lb(uv_os_sock_t fd) {
+ /*
+ * On FreeBSD 12+, SO_REUSEPORT_LB socket option allows sockets to be
+ * bound to an identical socket address. For UDP sockets, the use of
+ * this option can provide better distribution of incoming datagrams to
+ * multiple processes (or threads) as compared to the traditional
+ * technique of having multiple processes compete to receive datagrams
+ * on the same socket.
+ *
+ * On Linux, the same thing is achieved simply with SO_REUSEPORT.
+ */
+#if defined(SO_REUSEPORT_LB)
+ if (setsockopt_on(fd, SOL_SOCKET, SO_REUSEPORT_LB) == -1) {
+ return (ISC_R_FAILURE);
+ } else {
+ return (ISC_R_SUCCESS);
+ }
+#elif defined(SO_REUSEPORT) && defined(__linux__)
+ if (setsockopt_on(fd, SOL_SOCKET, SO_REUSEPORT) == -1) {
+ return (ISC_R_FAILURE);
+ } else {
+ return (ISC_R_SUCCESS);
+ }
+#else
+ UNUSED(fd);
+ return (ISC_R_NOTIMPLEMENTED);
+#endif
+}
+
+isc_result_t
+isc__nm_socket_incoming_cpu(uv_os_sock_t fd) {
+#ifdef SO_INCOMING_CPU
+ if (setsockopt_on(fd, SOL_SOCKET, SO_INCOMING_CPU) == -1) {
+ return (ISC_R_FAILURE);
+ } else {
+ return (ISC_R_SUCCESS);
+ }
+#else
+ UNUSED(fd);
+#endif
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+isc_result_t
+isc__nm_socket_disable_pmtud(uv_os_sock_t fd, sa_family_t sa_family) {
+ /*
+ * Disable the Path MTU Discovery on IP packets
+ */
+ if (sa_family == AF_INET6) {
+#if defined(IPV6_DONTFRAG)
+ if (setsockopt_off(fd, IPPROTO_IPV6, IPV6_DONTFRAG) == -1) {
+ return (ISC_R_FAILURE);
+ } else {
+ return (ISC_R_SUCCESS);
+ }
+#elif defined(IPV6_MTU_DISCOVER) && defined(IP_PMTUDISC_OMIT)
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_MTU_DISCOVER,
+ &(int){ IP_PMTUDISC_OMIT }, sizeof(int)) == -1)
+ {
+ return (ISC_R_FAILURE);
+ } else {
+ return (ISC_R_SUCCESS);
+ }
+#else
+ UNUSED(fd);
+#endif
+ } else if (sa_family == AF_INET) {
+#if defined(IP_DONTFRAG)
+ if (setsockopt_off(fd, IPPROTO_IP, IP_DONTFRAG) == -1) {
+ return (ISC_R_FAILURE);
+ } else {
+ return (ISC_R_SUCCESS);
+ }
+#elif defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_OMIT)
+ if (setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER,
+ &(int){ IP_PMTUDISC_OMIT }, sizeof(int)) == -1)
+ {
+ return (ISC_R_FAILURE);
+ } else {
+ return (ISC_R_SUCCESS);
+ }
+#else
+ UNUSED(fd);
+#endif
+ } else {
+ return (ISC_R_FAMILYNOSUPPORT);
+ }
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+isc_result_t
+isc__nm_socket_v6only(uv_os_sock_t fd, sa_family_t sa_family) {
+ /*
+ * Enable the IPv6-only option on IPv6 sockets
+ */
+ if (sa_family == AF_INET6) {
+#if defined(IPV6_V6ONLY)
+ if (setsockopt_on(fd, IPPROTO_IPV6, IPV6_V6ONLY) == -1) {
+ return (ISC_R_FAILURE);
+ } else {
+ return (ISC_R_SUCCESS);
+ }
+#else
+ UNUSED(fd);
+#endif
+ }
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+isc_result_t
+isc_nm_checkaddr(const isc_sockaddr_t *addr, isc_socktype_t type) {
+ int proto, pf, addrlen, fd, r;
+
+ REQUIRE(addr != NULL);
+
+ switch (type) {
+ case isc_socktype_tcp:
+ proto = SOCK_STREAM;
+ break;
+ case isc_socktype_udp:
+ proto = SOCK_DGRAM;
+ break;
+ default:
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+
+ pf = isc_sockaddr_pf(addr);
+ if (pf == AF_INET) {
+ addrlen = sizeof(struct sockaddr_in);
+ } else {
+ addrlen = sizeof(struct sockaddr_in6);
+ }
+
+ fd = socket(pf, proto, 0);
+ if (fd < 0) {
+ return (isc_errno_toresult(errno));
+ }
+
+ r = bind(fd, (const struct sockaddr *)&addr->type.sa, addrlen);
+ if (r < 0) {
+ close(fd);
+ return (isc_errno_toresult(errno));
+ }
+
+ close(fd);
+ return (ISC_R_SUCCESS);
+}
+
+#if defined(TCP_CONNECTIONTIMEOUT)
+#define TIMEOUT_TYPE int
+#define TIMEOUT_DIV 1000
+#define TIMEOUT_OPTNAME TCP_CONNECTIONTIMEOUT
+#elif defined(TCP_RXT_CONNDROPTIME)
+#define TIMEOUT_TYPE int
+#define TIMEOUT_DIV 1000
+#define TIMEOUT_OPTNAME TCP_RXT_CONNDROPTIME
+#elif defined(TCP_USER_TIMEOUT)
+#define TIMEOUT_TYPE unsigned int
+#define TIMEOUT_DIV 1
+#define TIMEOUT_OPTNAME TCP_USER_TIMEOUT
+#elif defined(TCP_KEEPINIT)
+#define TIMEOUT_TYPE int
+#define TIMEOUT_DIV 1000
+#define TIMEOUT_OPTNAME TCP_KEEPINIT
+#endif
+
+isc_result_t
+isc__nm_socket_connectiontimeout(uv_os_sock_t fd, int timeout_ms) {
+#if defined(TIMEOUT_OPTNAME)
+ TIMEOUT_TYPE timeout = timeout_ms / TIMEOUT_DIV;
+
+ if (timeout == 0) {
+ timeout = 1;
+ }
+
+ if (setsockopt(fd, IPPROTO_TCP, TIMEOUT_OPTNAME, &timeout,
+ sizeof(timeout)) == -1)
+ {
+ return (ISC_R_FAILURE);
+ }
+
+ return (ISC_R_SUCCESS);
+#else
+ UNUSED(fd);
+ UNUSED(timeout_ms);
+
+ return (ISC_R_SUCCESS);
+#endif
+}
+
+isc_result_t
+isc__nm_socket_tcp_nodelay(uv_os_sock_t fd) {
+#ifdef TCP_NODELAY
+ if (setsockopt_on(fd, IPPROTO_TCP, TCP_NODELAY) == -1) {
+ return (ISC_R_FAILURE);
+ } else {
+ return (ISC_R_SUCCESS);
+ }
+#else
+ UNUSED(fd);
+ return (ISC_R_SUCCESS);
+#endif
+}
+
+isc_result_t
+isc__nm_socket_tcp_maxseg(uv_os_sock_t fd, int size) {
+#ifdef TCP_MAXSEG
+ if (setsockopt(fd, IPPROTO_TCP, TCP_MAXSEG, (void *)&size,
+ sizeof(size)))
+ {
+ return (ISC_R_FAILURE);
+ } else {
+ return (ISC_R_SUCCESS);
+ }
+#else
+ UNUSED(fd);
+ UNUSED(size);
+ return (ISC_R_SUCCESS);
+#endif
+}
+
+isc_result_t
+isc__nm_socket_min_mtu(uv_os_sock_t fd, sa_family_t sa_family) {
+ if (sa_family != AF_INET6) {
+ return (ISC_R_SUCCESS);
+ }
+#ifdef IPV6_USE_MIN_MTU
+ if (setsockopt_on(fd, IPPROTO_IPV6, IPV6_USE_MIN_MTU) == -1) {
+ return (ISC_R_FAILURE);
+ }
+#elif defined(IPV6_MTU)
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_MTU, &(int){ 1280 },
+ sizeof(int)) == -1)
+ {
+ return (ISC_R_FAILURE);
+ }
+#else
+ UNUSED(fd);
+#endif
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc__nm_set_network_buffers(isc_nm_t *nm, uv_handle_t *handle) {
+ int32_t recv_buffer_size = 0;
+ int32_t send_buffer_size = 0;
+
+ switch (handle->type) {
+ case UV_TCP:
+ recv_buffer_size =
+ atomic_load_relaxed(&nm->recv_tcp_buffer_size);
+ send_buffer_size =
+ atomic_load_relaxed(&nm->send_tcp_buffer_size);
+ break;
+ case UV_UDP:
+ recv_buffer_size =
+ atomic_load_relaxed(&nm->recv_udp_buffer_size);
+ send_buffer_size =
+ atomic_load_relaxed(&nm->send_udp_buffer_size);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ if (recv_buffer_size > 0) {
+ int r = uv_recv_buffer_size(handle, &recv_buffer_size);
+ UV_RUNTIME_CHECK(uv_recv_buffer_size, r);
+ }
+
+ if (send_buffer_size > 0) {
+ int r = uv_send_buffer_size(handle, &send_buffer_size);
+ UV_RUNTIME_CHECK(uv_send_buffer_size, r);
+ }
+}
+
+static isc_threadresult_t
+isc__nm_work_run(isc_threadarg_t arg) {
+ isc__nm_work_t *work = (isc__nm_work_t *)arg;
+
+ work->cb(work->data);
+
+ return ((isc_threadresult_t)0);
+}
+
+static void
+isc__nm_work_cb(uv_work_t *req) {
+ isc__nm_work_t *work = uv_req_get_data((uv_req_t *)req);
+
+ if (isc_tid_v == SIZE_MAX) {
+ isc__trampoline_t *trampoline_arg =
+ isc__trampoline_get(isc__nm_work_run, work);
+ (void)isc__trampoline_run(trampoline_arg);
+ } else {
+ (void)isc__nm_work_run((isc_threadarg_t)work);
+ }
+}
+
+static void
+isc__nm_after_work_cb(uv_work_t *req, int status) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc__nm_work_t *work = uv_req_get_data((uv_req_t *)req);
+ isc_nm_t *netmgr = work->netmgr;
+
+ if (status != 0) {
+ result = isc__nm_uverr2result(status);
+ }
+
+ work->after_cb(work->data, result);
+
+ isc_mem_put(netmgr->mctx, work, sizeof(*work));
+
+ isc_nm_detach(&netmgr);
+}
+
+void
+isc_nm_work_offload(isc_nm_t *netmgr, isc_nm_workcb_t work_cb,
+ isc_nm_after_workcb_t after_work_cb, void *data) {
+ isc__networker_t *worker = NULL;
+ isc__nm_work_t *work = NULL;
+ int r;
+
+ REQUIRE(isc__nm_in_netthread());
+ REQUIRE(VALID_NM(netmgr));
+
+ worker = &netmgr->workers[isc_nm_tid()];
+
+ work = isc_mem_get(netmgr->mctx, sizeof(*work));
+ *work = (isc__nm_work_t){
+ .cb = work_cb,
+ .after_cb = after_work_cb,
+ .data = data,
+ };
+
+ isc_nm_attach(netmgr, &work->netmgr);
+
+ uv_req_set_data((uv_req_t *)&work->req, work);
+
+ r = uv_queue_work(&worker->loop, &work->req, isc__nm_work_cb,
+ isc__nm_after_work_cb);
+ UV_RUNTIME_CHECK(uv_queue_work, r);
+}
+
+void
+isc_nm_sequential(isc_nmhandle_t *handle) {
+ isc_nmsocket_t *sock = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ sock = handle->sock;
+
+ switch (sock->type) {
+ case isc_nm_tcpdnssocket:
+ case isc_nm_tlsdnssocket:
+ break;
+ case isc_nm_httpsocket:
+ return;
+ default:
+ UNREACHABLE();
+ }
+
+ /*
+ * We don't want pipelining on this connection. That means
+ * that we need to pause after reading each request, and
+ * resume only after the request has been processed. This
+ * is done in isc__nm_resume_processing(), which is the
+ * socket's closehandle_cb callback, called whenever a handle
+ * is released.
+ */
+ isc__nmsocket_timer_stop(sock);
+ isc__nm_stop_reading(sock);
+ atomic_store(&sock->sequential, true);
+}
+
+void
+isc_nm_bad_request(isc_nmhandle_t *handle) {
+ isc_nmsocket_t *sock = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ sock = handle->sock;
+
+ switch (sock->type) {
+ case isc_nm_udpsocket:
+ return;
+ case isc_nm_tcpdnssocket:
+ case isc_nm_tlsdnssocket:
+ REQUIRE(sock->parent == NULL);
+ isc__nmsocket_reset(sock);
+ return;
+#if HAVE_LIBNGHTTP2
+ case isc_nm_httpsocket:
+ isc__nm_http_bad_request(handle);
+ return;
+#endif /* HAVE_LIBNGHTTP2 */
+ case isc_nm_tcpsocket:
+#if HAVE_LIBNGHTTP2
+ case isc_nm_tlssocket:
+#endif /* HAVE_LIBNGHTTP2 */
+ default:
+ UNREACHABLE();
+ break;
+ }
+}
+
+isc_result_t
+isc_nm_xfr_checkperm(isc_nmhandle_t *handle) {
+ isc_nmsocket_t *sock = NULL;
+ isc_result_t result = ISC_R_NOPERM;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ sock = handle->sock;
+
+ switch (sock->type) {
+ case isc_nm_tcpdnssocket:
+ result = ISC_R_SUCCESS;
+ break;
+ case isc_nm_tlsdnssocket:
+ result = isc__nm_tlsdns_xfr_checkperm(sock);
+ break;
+ default:
+ break;
+ }
+
+ return (result);
+}
+
+bool
+isc_nm_is_http_handle(isc_nmhandle_t *handle) {
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ return (handle->sock->type == isc_nm_httpsocket);
+}
+
+void
+isc_nm_set_maxage(isc_nmhandle_t *handle, const uint32_t ttl) {
+ isc_nmsocket_t *sock = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+ REQUIRE(!atomic_load(&handle->sock->client));
+
+#if !HAVE_LIBNGHTTP2
+ UNUSED(ttl);
+#endif
+
+ sock = handle->sock;
+ switch (sock->type) {
+#if HAVE_LIBNGHTTP2
+ case isc_nm_httpsocket:
+ isc__nm_http_set_maxage(handle, ttl);
+ break;
+#endif /* HAVE_LIBNGHTTP2 */
+ case isc_nm_udpsocket:
+ case isc_nm_tcpdnssocket:
+ case isc_nm_tlsdnssocket:
+ return;
+ break;
+
+ case isc_nm_tcpsocket:
+#if HAVE_LIBNGHTTP2
+ case isc_nm_tlssocket:
+#endif /* HAVE_LIBNGHTTP2 */
+ default:
+ UNREACHABLE();
+ break;
+ }
+}
+
+isc_nmsocket_type
+isc_nm_socket_type(const isc_nmhandle_t *handle) {
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ return (handle->sock->type);
+}
+
+bool
+isc_nm_has_encryption(const isc_nmhandle_t *handle) {
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ switch (handle->sock->type) {
+ case isc_nm_tlsdnssocket:
+#if HAVE_LIBNGHTTP2
+ case isc_nm_tlssocket:
+#endif /* HAVE_LIBNGHTTP2 */
+ return (true);
+#if HAVE_LIBNGHTTP2
+ case isc_nm_httpsocket:
+ return (isc__nm_http_has_encryption(handle));
+#endif /* HAVE_LIBNGHTTP2 */
+ default:
+ return (false);
+ };
+
+ return (false);
+}
+
+void
+isc__nm_async_settlsctx(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent__tlsctx_t *ev_tlsctx = (isc__netievent__tlsctx_t *)ev0;
+ const int tid = isc_nm_tid();
+ isc_nmsocket_t *listener = ev_tlsctx->sock;
+ isc_tlsctx_t *tlsctx = ev_tlsctx->tlsctx;
+
+ UNUSED(worker);
+
+ switch (listener->type) {
+ case isc_nm_tlsdnslistener:
+ isc__nm_async_tlsdns_set_tlsctx(listener, tlsctx, tid);
+ break;
+#if HAVE_LIBNGHTTP2
+ case isc_nm_tlslistener:
+ isc__nm_async_tls_set_tlsctx(listener, tlsctx, tid);
+ break;
+#endif /* HAVE_LIBNGHTTP2 */
+ default:
+ UNREACHABLE();
+ break;
+ };
+}
+
+static void
+set_tlsctx_workers(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx) {
+ /* Update the TLS context reference for every worker thread. */
+ for (size_t i = 0; i < (size_t)listener->mgr->nworkers; i++) {
+ isc__netievent__tlsctx_t *ievent =
+ isc__nm_get_netievent_settlsctx(listener->mgr, listener,
+ tlsctx);
+ isc__nm_enqueue_ievent(&listener->mgr->workers[i],
+ (isc__netievent_t *)ievent);
+ }
+}
+
+void
+isc_nmsocket_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx) {
+ REQUIRE(VALID_NMSOCK(listener));
+ REQUIRE(tlsctx != NULL);
+
+ switch (listener->type) {
+#if HAVE_LIBNGHTTP2
+ case isc_nm_httplistener:
+ /*
+ * We handle HTTP listener sockets differently, as they rely
+ * on underlying TLS sockets for networking. The TLS context
+ * will get passed to these underlying sockets via the call to
+ * isc__nm_http_set_tlsctx().
+ */
+ isc__nm_http_set_tlsctx(listener, tlsctx);
+ break;
+ case isc_nm_tlslistener:
+ set_tlsctx_workers(listener, tlsctx);
+ break;
+#endif /* HAVE_LIBNGHTTP2 */
+ case isc_nm_tlsdnslistener:
+ set_tlsctx_workers(listener, tlsctx);
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ };
+}
+
+const char *
+isc_nm_verify_tls_peer_result_string(const isc_nmhandle_t *handle) {
+ isc_nmsocket_t *sock;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ sock = handle->sock;
+ switch (sock->type) {
+ case isc_nm_tlsdnssocket:
+ return (isc__nm_tlsdns_verify_tls_peer_result_string(handle));
+ break;
+#if HAVE_LIBNGHTTP2
+ case isc_nm_tlssocket:
+ return (isc__nm_tls_verify_tls_peer_result_string(handle));
+ break;
+ case isc_nm_httpsocket:
+ return (isc__nm_http_verify_tls_peer_result_string(handle));
+ break;
+#endif /* HAVE_LIBNGHTTP2 */
+ default:
+ break;
+ }
+
+ return (NULL);
+}
+
+void
+isc_nmsocket_set_max_streams(isc_nmsocket_t *listener,
+ const uint32_t max_streams) {
+ REQUIRE(VALID_NMSOCK(listener));
+ switch (listener->type) {
+#if HAVE_LIBNGHTTP2
+ case isc_nm_httplistener:
+ isc__nm_http_set_max_streams(listener, max_streams);
+ break;
+#endif /* HAVE_LIBNGHTTP2 */
+ default:
+ UNUSED(max_streams);
+ break;
+ };
+ return;
+}
+
+void
+isc__nmsocket_log_tls_session_reuse(isc_nmsocket_t *sock, isc_tls_t *tls) {
+ const int log_level = ISC_LOG_DEBUG(1);
+ char client_sabuf[ISC_SOCKADDR_FORMATSIZE];
+ char local_sabuf[ISC_SOCKADDR_FORMATSIZE];
+
+ REQUIRE(tls != NULL);
+
+ if (!isc_log_wouldlog(isc_lctx, log_level)) {
+ return;
+ };
+
+ isc_sockaddr_format(&sock->peer, client_sabuf, sizeof(client_sabuf));
+ isc_sockaddr_format(&sock->iface, local_sabuf, sizeof(local_sabuf));
+ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_NETMGR,
+ log_level, "TLS %s session %s for %s on %s",
+ SSL_is_server(tls) ? "server" : "client",
+ SSL_session_reused(tls) ? "resumed" : "created",
+ client_sabuf, local_sabuf);
+}
+
+#ifdef NETMGR_TRACE
+/*
+ * Dump all active sockets in netmgr. We output to stderr
+ * as the logger might be already shut down.
+ */
+
+static const char *
+nmsocket_type_totext(isc_nmsocket_type type) {
+ switch (type) {
+ case isc_nm_udpsocket:
+ return ("isc_nm_udpsocket");
+ case isc_nm_udplistener:
+ return ("isc_nm_udplistener");
+ case isc_nm_tcpsocket:
+ return ("isc_nm_tcpsocket");
+ case isc_nm_tcplistener:
+ return ("isc_nm_tcplistener");
+ case isc_nm_tcpdnslistener:
+ return ("isc_nm_tcpdnslistener");
+ case isc_nm_tcpdnssocket:
+ return ("isc_nm_tcpdnssocket");
+ case isc_nm_tlssocket:
+ return ("isc_nm_tlssocket");
+ case isc_nm_tlslistener:
+ return ("isc_nm_tlslistener");
+ case isc_nm_tlsdnslistener:
+ return ("isc_nm_tlsdnslistener");
+ case isc_nm_tlsdnssocket:
+ return ("isc_nm_tlsdnssocket");
+ case isc_nm_httplistener:
+ return ("isc_nm_httplistener");
+ case isc_nm_httpsocket:
+ return ("isc_nm_httpsocket");
+ default:
+ UNREACHABLE();
+ }
+}
+
+static void
+nmhandle_dump(isc_nmhandle_t *handle) {
+ fprintf(stderr, "Active handle %p, refs %" PRIuFAST32 "\n", handle,
+ isc_refcount_current(&handle->references));
+ fprintf(stderr, "Created by:\n");
+ isc_backtrace_symbols_fd(handle->backtrace, handle->backtrace_size,
+ STDERR_FILENO);
+ fprintf(stderr, "\n\n");
+}
+
+static void
+nmsocket_dump(isc_nmsocket_t *sock) {
+ isc_nmhandle_t *handle = NULL;
+
+ LOCK(&sock->lock);
+ fprintf(stderr, "\n=================\n");
+ fprintf(stderr, "Active %s socket %p, type %s, refs %" PRIuFAST32 "\n",
+ atomic_load(&sock->client) ? "client" : "server", sock,
+ nmsocket_type_totext(sock->type),
+ isc_refcount_current(&sock->references));
+ fprintf(stderr,
+ "Parent %p, listener %p, server %p, statichandle = "
+ "%p\n",
+ sock->parent, sock->listener, sock->server, sock->statichandle);
+ fprintf(stderr, "Flags:%s%s%s%s%s\n",
+ atomic_load(&sock->active) ? " active" : "",
+ atomic_load(&sock->closing) ? " closing" : "",
+ atomic_load(&sock->destroying) ? " destroying" : "",
+ atomic_load(&sock->connecting) ? " connecting" : "",
+ atomic_load(&sock->accepting) ? " accepting" : "");
+ fprintf(stderr, "Created by:\n");
+ isc_backtrace_symbols_fd(sock->backtrace, sock->backtrace_size,
+ STDERR_FILENO);
+ fprintf(stderr, "\n");
+
+ for (handle = ISC_LIST_HEAD(sock->active_handles); handle != NULL;
+ handle = ISC_LIST_NEXT(handle, active_link))
+ {
+ static bool first = true;
+ if (first) {
+ fprintf(stderr, "Active handles:\n");
+ first = false;
+ }
+ nmhandle_dump(handle);
+ }
+
+ fprintf(stderr, "\n");
+ UNLOCK(&sock->lock);
+}
+
+void
+isc__nm_dump_active(isc_nm_t *nm) {
+ isc_nmsocket_t *sock = NULL;
+
+ REQUIRE(VALID_NM(nm));
+
+ LOCK(&nm->lock);
+ for (sock = ISC_LIST_HEAD(nm->active_sockets); sock != NULL;
+ sock = ISC_LIST_NEXT(sock, active_link))
+ {
+ static bool first = true;
+ if (first) {
+ fprintf(stderr, "Outstanding sockets\n");
+ first = false;
+ }
+ nmsocket_dump(sock);
+ }
+ UNLOCK(&nm->lock);
+}
+#endif
diff --git a/lib/isc/netmgr/tcp.c b/lib/isc/netmgr/tcp.c
new file mode 100644
index 0000000..2a644fe
--- /dev/null
+++ b/lib/isc/netmgr/tcp.c
@@ -0,0 +1,1456 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <libgen.h>
+#include <unistd.h>
+#include <uv.h>
+
+#include <isc/atomic.h>
+#include <isc/barrier.h>
+#include <isc/buffer.h>
+#include <isc/condition.h>
+#include <isc/errno.h>
+#include <isc/log.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/netmgr.h>
+#include <isc/quota.h>
+#include <isc/random.h>
+#include <isc/refcount.h>
+#include <isc/region.h>
+#include <isc/result.h>
+#include <isc/sockaddr.h>
+#include <isc/stdtime.h>
+#include <isc/thread.h>
+#include <isc/util.h>
+
+#include "netmgr-int.h"
+#include "uv-compat.h"
+
+static atomic_uint_fast32_t last_tcpquota_log = 0;
+
+static bool
+can_log_tcp_quota(void) {
+ isc_stdtime_t now, last;
+
+ isc_stdtime_get(&now);
+ last = atomic_exchange_relaxed(&last_tcpquota_log, now);
+ if (now != last) {
+ return (true);
+ }
+
+ return (false);
+}
+
+static isc_result_t
+tcp_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req);
+
+static void
+tcp_close_direct(isc_nmsocket_t *sock);
+
+static isc_result_t
+tcp_send_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req);
+static void
+tcp_connect_cb(uv_connect_t *uvreq, int status);
+
+static void
+tcp_connection_cb(uv_stream_t *server, int status);
+
+static void
+tcp_close_cb(uv_handle_t *uvhandle);
+
+static isc_result_t
+accept_connection(isc_nmsocket_t *ssock, isc_quota_t *quota);
+
+static void
+quota_accept_cb(isc_quota_t *quota, void *sock0);
+
+static void
+failed_accept_cb(isc_nmsocket_t *sock, isc_result_t eresult);
+
+static void
+stop_tcp_parent(isc_nmsocket_t *sock);
+static void
+stop_tcp_child(isc_nmsocket_t *sock);
+
+static void
+failed_accept_cb(isc_nmsocket_t *sock, isc_result_t eresult) {
+ REQUIRE(atomic_load(&sock->accepting));
+ REQUIRE(sock->server);
+
+ /*
+ * Detach the quota early to make room for other connections;
+ * otherwise it'd be detached later asynchronously, and clog
+ * the quota unnecessarily.
+ */
+ if (sock->quota != NULL) {
+ isc_quota_detach(&sock->quota);
+ }
+
+ isc__nmsocket_detach(&sock->server);
+
+ atomic_store(&sock->accepting, false);
+
+ switch (eresult) {
+ case ISC_R_NOTCONNECTED:
+ /* IGNORE: The client disconnected before we could accept */
+ break;
+ default:
+ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_NETMGR, ISC_LOG_ERROR,
+ "Accepting TCP connection failed: %s",
+ isc_result_totext(eresult));
+ }
+}
+
+static isc_result_t
+tcp_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) {
+ isc__networker_t *worker = NULL;
+ isc_result_t result = ISC_R_UNSET;
+ int r;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(VALID_UVREQ(req));
+
+ REQUIRE(isc__nm_in_netthread());
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ worker = &sock->mgr->workers[sock->tid];
+
+ atomic_store(&sock->connecting, true);
+
+ /* 2 minute timeout */
+ result = isc__nm_socket_connectiontimeout(sock->fd, 120 * 1000);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ r = uv_tcp_init(&worker->loop, &sock->uv_handle.tcp);
+ UV_RUNTIME_CHECK(uv_tcp_init, r);
+ uv_handle_set_data(&sock->uv_handle.handle, sock);
+
+ r = uv_timer_init(&worker->loop, &sock->read_timer);
+ UV_RUNTIME_CHECK(uv_timer_init, r);
+ uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock);
+
+ r = uv_tcp_open(&sock->uv_handle.tcp, sock->fd);
+ if (r != 0) {
+ isc__nm_closesocket(sock->fd);
+ isc__nm_incstats(sock, STATID_OPENFAIL);
+ goto done;
+ }
+ isc__nm_incstats(sock, STATID_OPEN);
+
+ if (req->local.length != 0) {
+ r = uv_tcp_bind(&sock->uv_handle.tcp, &req->local.type.sa, 0);
+ if (r != 0) {
+ isc__nm_incstats(sock, STATID_BINDFAIL);
+ goto done;
+ }
+ }
+
+ isc__nm_set_network_buffers(sock->mgr, &sock->uv_handle.handle);
+
+ uv_handle_set_data(&req->uv_req.handle, req);
+ r = uv_tcp_connect(&req->uv_req.connect, &sock->uv_handle.tcp,
+ &req->peer.type.sa, tcp_connect_cb);
+ if (r != 0) {
+ isc__nm_incstats(sock, STATID_CONNECTFAIL);
+ goto done;
+ }
+
+ uv_handle_set_data((uv_handle_t *)&sock->read_timer,
+ &req->uv_req.connect);
+ isc__nmsocket_timer_start(sock);
+
+ atomic_store(&sock->connected, true);
+
+done:
+ result = isc__nm_uverr2result(r);
+ LOCK(&sock->lock);
+ sock->result = result;
+ SIGNAL(&sock->cond);
+ if (!atomic_load(&sock->active)) {
+ WAIT(&sock->scond, &sock->lock);
+ }
+ INSIST(atomic_load(&sock->active));
+ UNLOCK(&sock->lock);
+
+ return (result);
+}
+
+void
+isc__nm_async_tcpconnect(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tcpconnect_t *ievent =
+ (isc__netievent_tcpconnect_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+ isc__nm_uvreq_t *req = ievent->req;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_tcpsocket);
+ REQUIRE(sock->parent == NULL);
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ result = tcp_connect_direct(sock, req);
+ if (result != ISC_R_SUCCESS) {
+ atomic_store(&sock->active, false);
+ if (sock->fd != (uv_os_sock_t)(-1)) {
+ isc__nm_tcp_close(sock);
+ }
+ isc__nm_connectcb(sock, req, result, true);
+ }
+
+ /*
+ * The sock is now attached to the handle.
+ */
+ isc__nmsocket_detach(&sock);
+}
+
+static void
+tcp_connect_cb(uv_connect_t *uvreq, int status) {
+ isc_result_t result = ISC_R_UNSET;
+ isc__nm_uvreq_t *req = NULL;
+ isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)uvreq->handle);
+ struct sockaddr_storage ss;
+ int r;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ req = uv_handle_get_data((uv_handle_t *)uvreq);
+
+ REQUIRE(VALID_UVREQ(req));
+ REQUIRE(VALID_NMHANDLE(req->handle));
+
+ if (atomic_load(&sock->timedout)) {
+ result = ISC_R_TIMEDOUT;
+ goto error;
+ } else if (!atomic_load(&sock->connecting)) {
+ /*
+ * The connect was cancelled from timeout; just clean up
+ * the req.
+ */
+ isc__nm_uvreq_put(&req, sock);
+ return;
+ } else if (isc__nm_closing(sock)) {
+ /* Network manager shutting down */
+ result = ISC_R_SHUTTINGDOWN;
+ goto error;
+ } else if (isc__nmsocket_closing(sock)) {
+ /* Connection canceled */
+ result = ISC_R_CANCELED;
+ goto error;
+ } else if (status == UV_ETIMEDOUT) {
+ /* Timeout status code here indicates hard error */
+ result = ISC_R_TIMEDOUT;
+ goto error;
+ } else if (status == UV_EADDRINUSE) {
+ /*
+ * On FreeBSD the TCP connect() call sometimes results in a
+ * spurious transient EADDRINUSE. Try a few more times before
+ * giving up.
+ */
+ if (--req->connect_tries > 0) {
+ r = uv_tcp_connect(&req->uv_req.connect,
+ &sock->uv_handle.tcp,
+ &req->peer.type.sa, tcp_connect_cb);
+ if (r != 0) {
+ result = isc__nm_uverr2result(r);
+ goto error;
+ }
+ return;
+ }
+ result = isc__nm_uverr2result(status);
+ goto error;
+ } else if (status != 0) {
+ result = isc__nm_uverr2result(status);
+ goto error;
+ }
+
+ isc__nmsocket_timer_stop(sock);
+ uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock);
+
+ isc__nm_incstats(sock, STATID_CONNECT);
+ r = uv_tcp_getpeername(&sock->uv_handle.tcp, (struct sockaddr *)&ss,
+ &(int){ sizeof(ss) });
+ if (r != 0) {
+ result = isc__nm_uverr2result(r);
+ goto error;
+ }
+
+ atomic_store(&sock->connecting, false);
+
+ result = isc_sockaddr_fromsockaddr(&sock->peer, (struct sockaddr *)&ss);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ isc__nm_connectcb(sock, req, ISC_R_SUCCESS, false);
+
+ return;
+error:
+ isc__nm_failed_connect_cb(sock, req, result, false);
+}
+
+void
+isc_nm_tcpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
+ isc_nm_cb_t cb, void *cbarg, unsigned int timeout,
+ size_t extrahandlesize) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_nmsocket_t *sock = NULL;
+ isc__netievent_tcpconnect_t *ievent = NULL;
+ isc__nm_uvreq_t *req = NULL;
+ sa_family_t sa_family;
+
+ REQUIRE(VALID_NM(mgr));
+ REQUIRE(local != NULL);
+ REQUIRE(peer != NULL);
+
+ sa_family = peer->type.sa.sa_family;
+
+ sock = isc_mem_get(mgr->mctx, sizeof(*sock));
+ isc__nmsocket_init(sock, mgr, isc_nm_tcpsocket, local);
+
+ sock->extrahandlesize = extrahandlesize;
+ sock->connect_timeout = timeout;
+ sock->result = ISC_R_UNSET;
+ sock->fd = (uv_os_sock_t)-1;
+ atomic_init(&sock->client, true);
+
+ req = isc__nm_uvreq_get(mgr, sock);
+ req->cb.connect = cb;
+ req->cbarg = cbarg;
+ req->peer = *peer;
+ req->local = *local;
+ req->handle = isc__nmhandle_get(sock, &req->peer, &sock->iface);
+
+ result = isc__nm_socket(sa_family, SOCK_STREAM, 0, &sock->fd);
+ if (result != ISC_R_SUCCESS) {
+ if (isc__nm_in_netthread()) {
+ sock->tid = isc_nm_tid();
+ isc__nmsocket_clearcb(sock);
+ isc__nm_connectcb(sock, req, result, false);
+ } else {
+ isc__nmsocket_clearcb(sock);
+ sock->tid = isc_random_uniform(mgr->nworkers);
+ isc__nm_connectcb(sock, req, result, true);
+ }
+ atomic_store(&sock->closed, true);
+ isc__nmsocket_detach(&sock);
+ return;
+ }
+
+ (void)isc__nm_socket_min_mtu(sock->fd, sa_family);
+ (void)isc__nm_socket_tcp_maxseg(sock->fd, NM_MAXSEG);
+
+ ievent = isc__nm_get_netievent_tcpconnect(mgr, sock, req);
+
+ if (isc__nm_in_netthread()) {
+ atomic_store(&sock->active, true);
+ sock->tid = isc_nm_tid();
+ isc__nm_async_tcpconnect(&mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+ isc__nm_put_netievent_tcpconnect(mgr, ievent);
+ } else {
+ atomic_init(&sock->active, false);
+ sock->tid = isc_random_uniform(mgr->nworkers);
+ isc__nm_enqueue_ievent(&mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+ }
+ LOCK(&sock->lock);
+ while (sock->result == ISC_R_UNSET) {
+ WAIT(&sock->cond, &sock->lock);
+ }
+ atomic_store(&sock->active, true);
+ BROADCAST(&sock->scond);
+ UNLOCK(&sock->lock);
+}
+
+static uv_os_sock_t
+isc__nm_tcp_lb_socket(isc_nm_t *mgr, sa_family_t sa_family) {
+ isc_result_t result;
+ uv_os_sock_t sock;
+
+ result = isc__nm_socket(sa_family, SOCK_STREAM, 0, &sock);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ (void)isc__nm_socket_incoming_cpu(sock);
+ (void)isc__nm_socket_v6only(sock, sa_family);
+
+ /* FIXME: set mss */
+
+ result = isc__nm_socket_reuse(sock);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ if (mgr->load_balance_sockets) {
+ result = isc__nm_socket_reuse_lb(sock);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ }
+
+ return (sock);
+}
+
+static void
+start_tcp_child(isc_nm_t *mgr, isc_sockaddr_t *iface, isc_nmsocket_t *sock,
+ uv_os_sock_t fd, int tid) {
+ isc__netievent_tcplisten_t *ievent = NULL;
+ isc_nmsocket_t *csock = &sock->children[tid];
+
+ isc__nmsocket_init(csock, mgr, isc_nm_tcpsocket, iface);
+ csock->parent = sock;
+ csock->accept_cb = sock->accept_cb;
+ csock->accept_cbarg = sock->accept_cbarg;
+ csock->extrahandlesize = sock->extrahandlesize;
+ csock->backlog = sock->backlog;
+ csock->tid = tid;
+ /*
+ * We don't attach to quota, just assign - to avoid
+ * increasing quota unnecessarily.
+ */
+ csock->pquota = sock->pquota;
+ isc_quota_cb_init(&csock->quotacb, quota_accept_cb, csock);
+
+ if (mgr->load_balance_sockets) {
+ UNUSED(fd);
+ csock->fd = isc__nm_tcp_lb_socket(mgr,
+ iface->type.sa.sa_family);
+ } else {
+ csock->fd = dup(fd);
+ }
+ REQUIRE(csock->fd >= 0);
+
+ ievent = isc__nm_get_netievent_tcplisten(mgr, csock);
+ isc__nm_maybe_enqueue_ievent(&mgr->workers[tid],
+ (isc__netievent_t *)ievent);
+}
+
+static void
+enqueue_stoplistening(isc_nmsocket_t *sock) {
+ isc__netievent_tcpstop_t *ievent =
+ isc__nm_get_netievent_tcpstop(sock->mgr, sock);
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+}
+
+isc_result_t
+isc_nm_listentcp(isc_nm_t *mgr, isc_sockaddr_t *iface,
+ isc_nm_accept_cb_t accept_cb, void *accept_cbarg,
+ size_t extrahandlesize, int backlog, isc_quota_t *quota,
+ isc_nmsocket_t **sockp) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_nmsocket_t *sock = NULL;
+ size_t children_size = 0;
+ uv_os_sock_t fd = -1;
+
+ REQUIRE(VALID_NM(mgr));
+
+ sock = isc_mem_get(mgr->mctx, sizeof(*sock));
+ isc__nmsocket_init(sock, mgr, isc_nm_tcplistener, iface);
+
+ atomic_init(&sock->rchildren, 0);
+ sock->nchildren = mgr->nworkers;
+ children_size = sock->nchildren * sizeof(sock->children[0]);
+ sock->children = isc_mem_get(mgr->mctx, children_size);
+ memset(sock->children, 0, children_size);
+
+ sock->result = ISC_R_UNSET;
+
+ sock->accept_cb = accept_cb;
+ sock->accept_cbarg = accept_cbarg;
+ sock->extrahandlesize = extrahandlesize;
+ sock->backlog = backlog;
+ sock->pquota = quota;
+
+ sock->tid = 0;
+ sock->fd = -1;
+
+ if (!mgr->load_balance_sockets) {
+ fd = isc__nm_tcp_lb_socket(mgr, iface->type.sa.sa_family);
+ }
+
+ isc_barrier_init(&sock->startlistening, sock->nchildren);
+
+ for (size_t i = 0; i < sock->nchildren; i++) {
+ if ((int)i == isc_nm_tid()) {
+ continue;
+ }
+ start_tcp_child(mgr, iface, sock, fd, i);
+ }
+
+ if (isc__nm_in_netthread()) {
+ start_tcp_child(mgr, iface, sock, fd, isc_nm_tid());
+ }
+
+ if (!mgr->load_balance_sockets) {
+ isc__nm_closesocket(fd);
+ }
+
+ LOCK(&sock->lock);
+ while (atomic_load(&sock->rchildren) != sock->nchildren) {
+ WAIT(&sock->cond, &sock->lock);
+ }
+ result = sock->result;
+ atomic_store(&sock->active, true);
+ UNLOCK(&sock->lock);
+
+ INSIST(result != ISC_R_UNSET);
+
+ if (result == ISC_R_SUCCESS) {
+ REQUIRE(atomic_load(&sock->rchildren) == sock->nchildren);
+ *sockp = sock;
+ } else {
+ atomic_store(&sock->active, false);
+ enqueue_stoplistening(sock);
+ isc_nmsocket_close(&sock);
+ }
+
+ return (result);
+}
+
+void
+isc__nm_async_tcplisten(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tcplisten_t *ievent = (isc__netievent_tcplisten_t *)ev0;
+ sa_family_t sa_family;
+ int r;
+ int flags = 0;
+ isc_nmsocket_t *sock = NULL;
+ isc_result_t result;
+ isc_nm_t *mgr;
+
+ REQUIRE(VALID_NMSOCK(ievent->sock));
+ REQUIRE(ievent->sock->tid == isc_nm_tid());
+ REQUIRE(VALID_NMSOCK(ievent->sock->parent));
+
+ sock = ievent->sock;
+ sa_family = sock->iface.type.sa.sa_family;
+ mgr = sock->mgr;
+
+ REQUIRE(sock->type == isc_nm_tcpsocket);
+ REQUIRE(sock->parent != NULL);
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ (void)isc__nm_socket_min_mtu(sock->fd, sa_family);
+ (void)isc__nm_socket_tcp_maxseg(sock->fd, NM_MAXSEG);
+
+ r = uv_tcp_init(&worker->loop, &sock->uv_handle.tcp);
+ UV_RUNTIME_CHECK(uv_tcp_init, r);
+
+ uv_handle_set_data(&sock->uv_handle.handle, sock);
+ /* This keeps the socket alive after everything else is gone */
+ isc__nmsocket_attach(sock, &(isc_nmsocket_t *){ NULL });
+
+ r = uv_timer_init(&worker->loop, &sock->read_timer);
+ UV_RUNTIME_CHECK(uv_timer_init, r);
+ uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock);
+
+ LOCK(&sock->parent->lock);
+
+ r = uv_tcp_open(&sock->uv_handle.tcp, sock->fd);
+ if (r < 0) {
+ isc__nm_closesocket(sock->fd);
+ isc__nm_incstats(sock, STATID_OPENFAIL);
+ goto done;
+ }
+ isc__nm_incstats(sock, STATID_OPEN);
+
+ if (sa_family == AF_INET6) {
+ flags = UV_TCP_IPV6ONLY;
+ }
+
+ if (mgr->load_balance_sockets) {
+ r = isc_uv_tcp_freebind(&sock->uv_handle.tcp,
+ &sock->iface.type.sa, flags);
+ if (r < 0) {
+ isc__nm_incstats(sock, STATID_BINDFAIL);
+ goto done;
+ }
+ } else {
+ if (sock->parent->fd == -1) {
+ r = isc_uv_tcp_freebind(&sock->uv_handle.tcp,
+ &sock->iface.type.sa, flags);
+ if (r < 0) {
+ isc__nm_incstats(sock, STATID_BINDFAIL);
+ goto done;
+ }
+ sock->parent->uv_handle.tcp.flags =
+ sock->uv_handle.tcp.flags;
+ sock->parent->fd = sock->fd;
+ } else {
+ /* The socket is already bound, just copy the flags */
+ sock->uv_handle.tcp.flags =
+ sock->parent->uv_handle.tcp.flags;
+ }
+ }
+
+ isc__nm_set_network_buffers(sock->mgr, &sock->uv_handle.handle);
+
+ /*
+ * The callback will run in the same thread uv_listen() was called
+ * from, so a race with tcp_connection_cb() isn't possible.
+ */
+ r = uv_listen((uv_stream_t *)&sock->uv_handle.tcp, sock->backlog,
+ tcp_connection_cb);
+ if (r != 0) {
+ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_NETMGR, ISC_LOG_ERROR,
+ "uv_listen failed: %s",
+ isc_result_totext(isc__nm_uverr2result(r)));
+ isc__nm_incstats(sock, STATID_BINDFAIL);
+ goto done;
+ }
+
+ atomic_store(&sock->listening, true);
+
+done:
+ result = isc__nm_uverr2result(r);
+ if (result != ISC_R_SUCCESS) {
+ sock->pquota = NULL;
+ }
+
+ atomic_fetch_add(&sock->parent->rchildren, 1);
+ if (sock->parent->result == ISC_R_UNSET) {
+ sock->parent->result = result;
+ }
+ SIGNAL(&sock->parent->cond);
+ UNLOCK(&sock->parent->lock);
+
+ isc_barrier_wait(&sock->parent->startlistening);
+}
+
+static void
+tcp_connection_cb(uv_stream_t *server, int status) {
+ isc_nmsocket_t *ssock = uv_handle_get_data((uv_handle_t *)server);
+ isc_result_t result;
+ isc_quota_t *quota = NULL;
+
+ if (status != 0) {
+ result = isc__nm_uverr2result(status);
+ goto done;
+ }
+
+ REQUIRE(VALID_NMSOCK(ssock));
+ REQUIRE(ssock->tid == isc_nm_tid());
+
+ if (isc__nmsocket_closing(ssock)) {
+ result = ISC_R_CANCELED;
+ goto done;
+ }
+
+ if (ssock->pquota != NULL) {
+ result = isc_quota_attach_cb(ssock->pquota, &quota,
+ &ssock->quotacb);
+ if (result == ISC_R_QUOTA) {
+ isc__nm_incstats(ssock, STATID_ACCEPTFAIL);
+ goto done;
+ }
+ }
+
+ result = accept_connection(ssock, quota);
+done:
+ isc__nm_accept_connection_log(result, can_log_tcp_quota());
+}
+
+void
+isc__nm_tcp_stoplistening(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_tcplistener);
+
+ if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false },
+ true))
+ {
+ UNREACHABLE();
+ }
+
+ if (!isc__nm_in_netthread()) {
+ enqueue_stoplistening(sock);
+ } else {
+ stop_tcp_parent(sock);
+ }
+}
+
+void
+isc__nm_async_tcpstop(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tcpstop_t *ievent = (isc__netievent_tcpstop_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ if (sock->parent != NULL) {
+ stop_tcp_child(sock);
+ return;
+ }
+
+ stop_tcp_parent(sock);
+}
+
+void
+isc__nm_tcp_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(result != ISC_R_SUCCESS);
+
+ isc__nmsocket_timer_stop(sock);
+ isc__nm_stop_reading(sock);
+
+ if (!sock->recv_read) {
+ goto destroy;
+ }
+ sock->recv_read = false;
+
+ if (sock->recv_cb != NULL) {
+ isc__nm_uvreq_t *req = isc__nm_get_read_req(sock, NULL);
+ isc__nmsocket_clearcb(sock);
+ isc__nm_readcb(sock, req, result);
+ }
+
+destroy:
+ isc__nmsocket_prep_destroy(sock);
+
+ /*
+ * We need to detach from quota after the read callback function had a
+ * chance to be executed.
+ */
+ if (sock->quota != NULL) {
+ isc_quota_detach(&sock->quota);
+ }
+}
+
+void
+isc__nm_tcp_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ isc_nmsocket_t *sock = handle->sock;
+ isc__netievent_tcpstartread_t *ievent = NULL;
+
+ REQUIRE(sock->type == isc_nm_tcpsocket);
+ REQUIRE(sock->statichandle == handle);
+
+ sock->recv_cb = cb;
+ sock->recv_cbarg = cbarg;
+ sock->recv_read = true;
+ if (sock->read_timeout == 0) {
+ sock->read_timeout =
+ (atomic_load(&sock->keepalive)
+ ? atomic_load(&sock->mgr->keepalive)
+ : atomic_load(&sock->mgr->idle));
+ }
+
+ ievent = isc__nm_get_netievent_tcpstartread(sock->mgr, sock);
+
+ /*
+ * This MUST be done asynchronously, no matter which thread we're
+ * in. The callback function for isc_nm_read() often calls
+ * isc_nm_read() again; if we tried to do that synchronously
+ * we'd clash in processbuffer() and grow the stack indefinitely.
+ */
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+
+ return;
+}
+
+void
+isc__nm_async_tcpstartread(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tcpstartread_t *ievent =
+ (isc__netievent_tcpstartread_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+ isc_result_t result;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ UNUSED(worker);
+
+ if (isc__nmsocket_closing(sock)) {
+ result = ISC_R_CANCELED;
+ } else {
+ result = isc__nm_start_reading(sock);
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ atomic_store(&sock->reading, true);
+ isc__nm_tcp_failed_read_cb(sock, result);
+ return;
+ }
+
+ isc__nmsocket_timer_start(sock);
+}
+
+void
+isc__nm_tcp_pauseread(isc_nmhandle_t *handle) {
+ isc__netievent_tcppauseread_t *ievent = NULL;
+ isc_nmsocket_t *sock = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ sock = handle->sock;
+
+ REQUIRE(VALID_NMSOCK(sock));
+
+ if (!atomic_compare_exchange_strong(&sock->readpaused, &(bool){ false },
+ true))
+ {
+ return;
+ }
+
+ ievent = isc__nm_get_netievent_tcppauseread(sock->mgr, sock);
+
+ isc__nm_maybe_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+
+ return;
+}
+
+void
+isc__nm_async_tcppauseread(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tcppauseread_t *ievent =
+ (isc__netievent_tcppauseread_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ UNUSED(worker);
+
+ isc__nmsocket_timer_stop(sock);
+ isc__nm_stop_reading(sock);
+}
+
+void
+isc__nm_tcp_resumeread(isc_nmhandle_t *handle) {
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ isc__netievent_tcpstartread_t *ievent = NULL;
+ isc_nmsocket_t *sock = handle->sock;
+
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ if (sock->recv_cb == NULL) {
+ /* We are no longer reading */
+ return;
+ }
+
+ if (!isc__nmsocket_active(sock)) {
+ atomic_store(&sock->reading, true);
+ isc__nm_tcp_failed_read_cb(sock, ISC_R_CANCELED);
+ return;
+ }
+
+ if (!atomic_compare_exchange_strong(&sock->readpaused, &(bool){ true },
+ false))
+ {
+ return;
+ }
+
+ ievent = isc__nm_get_netievent_tcpstartread(sock->mgr, sock);
+
+ isc__nm_maybe_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+}
+
+void
+isc__nm_tcp_read_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) {
+ isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)stream);
+ isc__nm_uvreq_t *req = NULL;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(atomic_load(&sock->reading));
+ REQUIRE(buf != NULL);
+
+ if (isc__nmsocket_closing(sock)) {
+ isc__nm_tcp_failed_read_cb(sock, ISC_R_CANCELED);
+ goto free;
+ }
+
+ if (nread < 0) {
+ if (nread != UV_EOF) {
+ isc__nm_incstats(sock, STATID_RECVFAIL);
+ }
+
+ isc__nm_tcp_failed_read_cb(sock, isc__nm_uverr2result(nread));
+
+ goto free;
+ }
+
+ req = isc__nm_get_read_req(sock, NULL);
+
+ /*
+ * The callback will be called synchronously because the
+ * result is ISC_R_SUCCESS, so we don't need to retain
+ * the buffer
+ */
+ req->uvbuf.base = buf->base;
+ req->uvbuf.len = nread;
+
+ if (!atomic_load(&sock->client)) {
+ sock->read_timeout =
+ (atomic_load(&sock->keepalive)
+ ? atomic_load(&sock->mgr->keepalive)
+ : atomic_load(&sock->mgr->idle));
+ }
+
+ isc__nm_readcb(sock, req, ISC_R_SUCCESS);
+
+ /* The readcb could have paused the reading */
+ if (atomic_load(&sock->reading)) {
+ /* The timer will be updated */
+ isc__nmsocket_timer_restart(sock);
+ }
+
+free:
+ if (nread < 0) {
+ /*
+ * The buffer may be a null buffer on error.
+ */
+ if (buf->base == NULL && buf->len == 0) {
+ return;
+ }
+ }
+
+ isc__nm_free_uvbuf(sock, buf);
+}
+
+static void
+quota_accept_cb(isc_quota_t *quota, void *sock0) {
+ isc_nmsocket_t *sock = (isc_nmsocket_t *)sock0;
+ isc__netievent_tcpaccept_t *ievent = NULL;
+
+ REQUIRE(VALID_NMSOCK(sock));
+
+ /*
+ * Create a tcpaccept event and pass it using the async channel.
+ */
+ ievent = isc__nm_get_netievent_tcpaccept(sock->mgr, sock, quota);
+ isc__nm_maybe_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+}
+
+/*
+ * This is called after we get a quota_accept_cb() callback.
+ */
+void
+isc__nm_async_tcpaccept(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tcpaccept_t *ievent = (isc__netievent_tcpaccept_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+ isc_result_t result;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ result = accept_connection(sock, ievent->quota);
+ isc__nm_accept_connection_log(result, can_log_tcp_quota());
+}
+
+static isc_result_t
+accept_connection(isc_nmsocket_t *ssock, isc_quota_t *quota) {
+ isc_nmsocket_t *csock = NULL;
+ isc__networker_t *worker = NULL;
+ int r;
+ isc_result_t result;
+ struct sockaddr_storage ss;
+ isc_sockaddr_t local;
+ isc_nmhandle_t *handle = NULL;
+
+ REQUIRE(VALID_NMSOCK(ssock));
+ REQUIRE(ssock->tid == isc_nm_tid());
+
+ if (isc__nmsocket_closing(ssock)) {
+ if (quota != NULL) {
+ isc_quota_detach(&quota);
+ }
+ return (ISC_R_CANCELED);
+ }
+
+ csock = isc_mem_get(ssock->mgr->mctx, sizeof(isc_nmsocket_t));
+ isc__nmsocket_init(csock, ssock->mgr, isc_nm_tcpsocket, &ssock->iface);
+ csock->tid = ssock->tid;
+ csock->extrahandlesize = ssock->extrahandlesize;
+ isc__nmsocket_attach(ssock, &csock->server);
+ csock->recv_cb = ssock->recv_cb;
+ csock->recv_cbarg = ssock->recv_cbarg;
+ csock->quota = quota;
+ atomic_init(&csock->accepting, true);
+
+ worker = &csock->mgr->workers[isc_nm_tid()];
+
+ r = uv_tcp_init(&worker->loop, &csock->uv_handle.tcp);
+ UV_RUNTIME_CHECK(uv_tcp_init, r);
+ uv_handle_set_data(&csock->uv_handle.handle, csock);
+
+ r = uv_timer_init(&worker->loop, &csock->read_timer);
+ UV_RUNTIME_CHECK(uv_timer_init, r);
+ uv_handle_set_data((uv_handle_t *)&csock->read_timer, csock);
+
+ r = uv_accept(&ssock->uv_handle.stream, &csock->uv_handle.stream);
+ if (r != 0) {
+ result = isc__nm_uverr2result(r);
+ goto failure;
+ }
+
+ r = uv_tcp_getpeername(&csock->uv_handle.tcp, (struct sockaddr *)&ss,
+ &(int){ sizeof(ss) });
+ if (r != 0) {
+ result = isc__nm_uverr2result(r);
+ goto failure;
+ }
+
+ result = isc_sockaddr_fromsockaddr(&csock->peer,
+ (struct sockaddr *)&ss);
+ if (result != ISC_R_SUCCESS) {
+ goto failure;
+ }
+
+ r = uv_tcp_getsockname(&csock->uv_handle.tcp, (struct sockaddr *)&ss,
+ &(int){ sizeof(ss) });
+ if (r != 0) {
+ result = isc__nm_uverr2result(r);
+ goto failure;
+ }
+
+ result = isc_sockaddr_fromsockaddr(&local, (struct sockaddr *)&ss);
+ if (result != ISC_R_SUCCESS) {
+ goto failure;
+ }
+
+ handle = isc__nmhandle_get(csock, NULL, &local);
+
+ result = ssock->accept_cb(handle, ISC_R_SUCCESS, ssock->accept_cbarg);
+ if (result != ISC_R_SUCCESS) {
+ isc_nmhandle_detach(&handle);
+ goto failure;
+ }
+
+ atomic_store(&csock->accepting, false);
+
+ isc__nm_incstats(csock, STATID_ACCEPT);
+
+ csock->read_timeout = atomic_load(&csock->mgr->init);
+
+ atomic_fetch_add(&ssock->parent->active_child_connections, 1);
+
+ /*
+ * The acceptcb needs to attach to the handle if it wants to keep the
+ * connection alive
+ */
+ isc_nmhandle_detach(&handle);
+
+ /*
+ * sock is now attached to the handle.
+ */
+ isc__nmsocket_detach(&csock);
+
+ return (ISC_R_SUCCESS);
+
+failure:
+ atomic_store(&csock->active, false);
+
+ failed_accept_cb(csock, result);
+
+ isc__nmsocket_prep_destroy(csock);
+
+ isc__nmsocket_detach(&csock);
+
+ return (result);
+}
+
+void
+isc__nm_tcp_send(isc_nmhandle_t *handle, const isc_region_t *region,
+ isc_nm_cb_t cb, void *cbarg) {
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ isc_nmsocket_t *sock = handle->sock;
+ isc__netievent_tcpsend_t *ievent = NULL;
+ isc__nm_uvreq_t *uvreq = NULL;
+
+ REQUIRE(sock->type == isc_nm_tcpsocket);
+
+ uvreq = isc__nm_uvreq_get(sock->mgr, sock);
+ uvreq->uvbuf.base = (char *)region->base;
+ uvreq->uvbuf.len = region->length;
+
+ isc_nmhandle_attach(handle, &uvreq->handle);
+
+ uvreq->cb.send = cb;
+ uvreq->cbarg = cbarg;
+
+ ievent = isc__nm_get_netievent_tcpsend(sock->mgr, sock, uvreq);
+ isc__nm_maybe_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+
+ return;
+}
+
+static void
+tcp_send_cb(uv_write_t *req, int status) {
+ isc__nm_uvreq_t *uvreq = (isc__nm_uvreq_t *)req->data;
+ isc_nmsocket_t *sock = NULL;
+
+ REQUIRE(VALID_UVREQ(uvreq));
+ REQUIRE(VALID_NMSOCK(uvreq->sock));
+
+ sock = uvreq->sock;
+
+ isc_nm_timer_stop(uvreq->timer);
+ isc_nm_timer_detach(&uvreq->timer);
+
+ if (status < 0) {
+ isc__nm_incstats(sock, STATID_SENDFAIL);
+ isc__nm_failed_send_cb(sock, uvreq,
+ isc__nm_uverr2result(status));
+ return;
+ }
+
+ isc__nm_sendcb(sock, uvreq, ISC_R_SUCCESS, false);
+}
+
+/*
+ * Handle 'tcpsend' async event - send a packet on the socket
+ */
+void
+isc__nm_async_tcpsend(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc_result_t result;
+ isc__netievent_tcpsend_t *ievent = (isc__netievent_tcpsend_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+ isc__nm_uvreq_t *uvreq = ievent->req;
+
+ REQUIRE(sock->type == isc_nm_tcpsocket);
+ REQUIRE(sock->tid == isc_nm_tid());
+ UNUSED(worker);
+
+ if (sock->write_timeout == 0) {
+ sock->write_timeout =
+ (atomic_load(&sock->keepalive)
+ ? atomic_load(&sock->mgr->keepalive)
+ : atomic_load(&sock->mgr->idle));
+ }
+
+ result = tcp_send_direct(sock, uvreq);
+ if (result != ISC_R_SUCCESS) {
+ isc__nm_incstats(sock, STATID_SENDFAIL);
+ isc__nm_failed_send_cb(sock, uvreq, result);
+ }
+}
+
+static isc_result_t
+tcp_send_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(VALID_UVREQ(req));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(sock->type == isc_nm_tcpsocket);
+
+ int r;
+
+ if (isc__nmsocket_closing(sock)) {
+ return (ISC_R_CANCELED);
+ }
+
+ r = uv_write(&req->uv_req.write, &sock->uv_handle.stream, &req->uvbuf,
+ 1, tcp_send_cb);
+ if (r < 0) {
+ return (isc__nm_uverr2result(r));
+ }
+
+ isc_nm_timer_create(req->handle, isc__nmsocket_writetimeout_cb, req,
+ &req->timer);
+ if (sock->write_timeout > 0) {
+ isc_nm_timer_start(req->timer, sock->write_timeout);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+tcp_stop_cb(uv_handle_t *handle) {
+ isc_nmsocket_t *sock = uv_handle_get_data(handle);
+ uv_handle_set_data(handle, NULL);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(atomic_load(&sock->closing));
+
+ if (!atomic_compare_exchange_strong(&sock->closed, &(bool){ false },
+ true))
+ {
+ UNREACHABLE();
+ }
+
+ isc__nm_incstats(sock, STATID_CLOSE);
+
+ atomic_store(&sock->listening, false);
+
+ isc__nmsocket_detach(&sock);
+}
+
+static void
+tcp_close_sock(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(atomic_load(&sock->closing));
+
+ if (!atomic_compare_exchange_strong(&sock->closed, &(bool){ false },
+ true))
+ {
+ UNREACHABLE();
+ }
+
+ isc__nm_incstats(sock, STATID_CLOSE);
+
+ if (sock->server != NULL) {
+ isc__nmsocket_detach(&sock->server);
+ }
+
+ atomic_store(&sock->connected, false);
+
+ isc__nmsocket_prep_destroy(sock);
+}
+
+static void
+tcp_close_cb(uv_handle_t *handle) {
+ isc_nmsocket_t *sock = uv_handle_get_data(handle);
+ uv_handle_set_data(handle, NULL);
+
+ tcp_close_sock(sock);
+}
+
+static void
+read_timer_close_cb(uv_handle_t *handle) {
+ isc_nmsocket_t *sock = uv_handle_get_data(handle);
+ uv_handle_set_data(handle, NULL);
+
+ if (sock->parent) {
+ uv_close(&sock->uv_handle.handle, tcp_stop_cb);
+ } else if (uv_is_closing(&sock->uv_handle.handle)) {
+ tcp_close_sock(sock);
+ } else {
+ uv_close(&sock->uv_handle.handle, tcp_close_cb);
+ }
+}
+
+static void
+stop_tcp_child(isc_nmsocket_t *sock) {
+ REQUIRE(sock->type == isc_nm_tcpsocket);
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false },
+ true))
+ {
+ return;
+ }
+
+ tcp_close_direct(sock);
+
+ atomic_fetch_sub(&sock->parent->rchildren, 1);
+
+ isc_barrier_wait(&sock->parent->stoplistening);
+}
+
+static void
+stop_tcp_parent(isc_nmsocket_t *sock) {
+ isc_nmsocket_t *csock = NULL;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(sock->type == isc_nm_tcplistener);
+
+ isc_barrier_init(&sock->stoplistening, sock->nchildren);
+
+ for (size_t i = 0; i < sock->nchildren; i++) {
+ csock = &sock->children[i];
+ REQUIRE(VALID_NMSOCK(csock));
+
+ if ((int)i == isc_nm_tid()) {
+ /*
+ * We need to schedule closing the other sockets first
+ */
+ continue;
+ }
+
+ atomic_store(&csock->active, false);
+ enqueue_stoplistening(csock);
+ }
+
+ csock = &sock->children[isc_nm_tid()];
+ atomic_store(&csock->active, false);
+ stop_tcp_child(csock);
+
+ atomic_store(&sock->closed, true);
+ isc__nmsocket_prep_destroy(sock);
+}
+
+static void
+tcp_close_direct(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(atomic_load(&sock->closing));
+
+ if (sock->server != NULL) {
+ REQUIRE(VALID_NMSOCK(sock->server));
+ REQUIRE(VALID_NMSOCK(sock->server->parent));
+ if (sock->server->parent != NULL) {
+ atomic_fetch_sub(
+ &sock->server->parent->active_child_connections,
+ 1);
+ }
+ }
+
+ if (sock->quota != NULL) {
+ isc_quota_detach(&sock->quota);
+ }
+
+ isc__nmsocket_timer_stop(sock);
+ isc__nm_stop_reading(sock);
+
+ uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock);
+ uv_close((uv_handle_t *)&sock->read_timer, read_timer_close_cb);
+}
+
+void
+isc__nm_tcp_close(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_tcpsocket);
+ REQUIRE(!isc__nmsocket_active(sock));
+
+ if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false },
+ true))
+ {
+ return;
+ }
+
+ if (sock->tid == isc_nm_tid()) {
+ tcp_close_direct(sock);
+ } else {
+ /*
+ * We need to create an event and pass it using async channel
+ */
+ isc__netievent_tcpclose_t *ievent =
+ isc__nm_get_netievent_tcpclose(sock->mgr, sock);
+
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+ }
+}
+
+void
+isc__nm_async_tcpclose(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tcpclose_t *ievent = (isc__netievent_tcpclose_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ UNUSED(worker);
+
+ tcp_close_direct(sock);
+}
+
+static void
+tcp_close_connect_cb(uv_handle_t *handle) {
+ isc_nmsocket_t *sock = uv_handle_get_data(handle);
+
+ REQUIRE(VALID_NMSOCK(sock));
+
+ REQUIRE(isc__nm_in_netthread());
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ isc__nmsocket_prep_destroy(sock);
+ isc__nmsocket_detach(&sock);
+}
+
+void
+isc__nm_tcp_shutdown(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(sock->type == isc_nm_tcpsocket);
+
+ /*
+ * If the socket is active, mark it inactive and
+ * continue. If it isn't active, stop now.
+ */
+ if (!isc__nmsocket_deactivate(sock)) {
+ return;
+ }
+
+ if (atomic_load(&sock->accepting)) {
+ return;
+ }
+
+ if (atomic_load(&sock->connecting)) {
+ isc_nmsocket_t *tsock = NULL;
+ isc__nmsocket_attach(sock, &tsock);
+ uv_close(&sock->uv_handle.handle, tcp_close_connect_cb);
+ return;
+ }
+
+ if (sock->statichandle != NULL) {
+ if (isc__nm_closing(sock)) {
+ isc__nm_failed_read_cb(sock, ISC_R_SHUTTINGDOWN, false);
+ } else {
+ isc__nm_failed_read_cb(sock, ISC_R_CANCELED, false);
+ }
+ return;
+ }
+
+ /*
+ * Otherwise, we just send the socket to abyss...
+ */
+ if (sock->parent == NULL) {
+ isc__nmsocket_prep_destroy(sock);
+ }
+}
+
+void
+isc__nm_tcp_cancelread(isc_nmhandle_t *handle) {
+ isc_nmsocket_t *sock = NULL;
+ isc__netievent_tcpcancel_t *ievent = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ sock = handle->sock;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_tcpsocket);
+
+ ievent = isc__nm_get_netievent_tcpcancel(sock->mgr, sock, handle);
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+}
+
+void
+isc__nm_async_tcpcancel(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tcpcancel_t *ievent = (isc__netievent_tcpcancel_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ UNUSED(worker);
+
+ uv_timer_stop(&sock->read_timer);
+
+ isc__nm_tcp_failed_read_cb(sock, ISC_R_EOF);
+}
+
+int_fast32_t
+isc__nm_tcp_listener_nactive(isc_nmsocket_t *listener) {
+ int_fast32_t nactive;
+
+ REQUIRE(VALID_NMSOCK(listener));
+
+ nactive = atomic_load(&listener->active_child_connections);
+ INSIST(nactive >= 0);
+ return (nactive);
+}
diff --git a/lib/isc/netmgr/tcpdns.c b/lib/isc/netmgr/tcpdns.c
new file mode 100644
index 0000000..eda6aa6
--- /dev/null
+++ b/lib/isc/netmgr/tcpdns.c
@@ -0,0 +1,1500 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <libgen.h>
+#include <unistd.h>
+#include <uv.h>
+
+#include <isc/atomic.h>
+#include <isc/barrier.h>
+#include <isc/buffer.h>
+#include <isc/condition.h>
+#include <isc/errno.h>
+#include <isc/log.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/netmgr.h>
+#include <isc/quota.h>
+#include <isc/random.h>
+#include <isc/refcount.h>
+#include <isc/region.h>
+#include <isc/result.h>
+#include <isc/sockaddr.h>
+#include <isc/stdtime.h>
+#include <isc/thread.h>
+#include <isc/util.h>
+
+#include "netmgr-int.h"
+#include "uv-compat.h"
+
+static atomic_uint_fast32_t last_tcpdnsquota_log = 0;
+
+static bool
+can_log_tcpdns_quota(void) {
+ isc_stdtime_t now, last;
+
+ isc_stdtime_get(&now);
+ last = atomic_exchange_relaxed(&last_tcpdnsquota_log, now);
+ if (now != last) {
+ return (true);
+ }
+
+ return (false);
+}
+
+static isc_result_t
+tcpdns_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req);
+
+static void
+tcpdns_close_direct(isc_nmsocket_t *sock);
+
+static void
+tcpdns_connect_cb(uv_connect_t *uvreq, int status);
+
+static void
+tcpdns_connection_cb(uv_stream_t *server, int status);
+
+static void
+tcpdns_close_cb(uv_handle_t *uvhandle);
+
+static isc_result_t
+accept_connection(isc_nmsocket_t *ssock, isc_quota_t *quota);
+
+static void
+quota_accept_cb(isc_quota_t *quota, void *sock0);
+
+static void
+stop_tcpdns_parent(isc_nmsocket_t *sock);
+static void
+stop_tcpdns_child(isc_nmsocket_t *sock);
+
+static isc_result_t
+tcpdns_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) {
+ isc__networker_t *worker = NULL;
+ isc_result_t result = ISC_R_UNSET;
+ int r;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(VALID_UVREQ(req));
+
+ REQUIRE(isc__nm_in_netthread());
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ worker = &sock->mgr->workers[sock->tid];
+
+ atomic_store(&sock->connecting, true);
+
+ r = uv_tcp_init(&worker->loop, &sock->uv_handle.tcp);
+ UV_RUNTIME_CHECK(uv_tcp_init, r);
+ uv_handle_set_data(&sock->uv_handle.handle, sock);
+
+ r = uv_timer_init(&worker->loop, &sock->read_timer);
+ UV_RUNTIME_CHECK(uv_timer_init, r);
+ uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock);
+
+ if (isc__nm_closing(sock)) {
+ result = ISC_R_SHUTTINGDOWN;
+ goto error;
+ }
+
+ r = uv_tcp_open(&sock->uv_handle.tcp, sock->fd);
+ if (r != 0) {
+ isc__nm_closesocket(sock->fd);
+ isc__nm_incstats(sock, STATID_OPENFAIL);
+ goto done;
+ }
+ isc__nm_incstats(sock, STATID_OPEN);
+
+ if (req->local.length != 0) {
+ r = uv_tcp_bind(&sock->uv_handle.tcp, &req->local.type.sa, 0);
+ /*
+ * In case of shared socket UV_EINVAL will be returned and needs
+ * to be ignored
+ */
+ if (r != 0 && r != UV_EINVAL) {
+ isc__nm_incstats(sock, STATID_BINDFAIL);
+ goto done;
+ }
+ }
+
+ isc__nm_set_network_buffers(sock->mgr, &sock->uv_handle.handle);
+
+ uv_handle_set_data(&req->uv_req.handle, req);
+ r = uv_tcp_connect(&req->uv_req.connect, &sock->uv_handle.tcp,
+ &req->peer.type.sa, tcpdns_connect_cb);
+ if (r != 0) {
+ isc__nm_incstats(sock, STATID_CONNECTFAIL);
+ goto done;
+ }
+
+ uv_handle_set_data((uv_handle_t *)&sock->read_timer,
+ &req->uv_req.connect);
+ isc__nmsocket_timer_start(sock);
+
+ atomic_store(&sock->connected, true);
+
+done:
+ result = isc__nm_uverr2result(r);
+error:
+ LOCK(&sock->lock);
+ sock->result = result;
+ SIGNAL(&sock->cond);
+ if (!atomic_load(&sock->active)) {
+ WAIT(&sock->scond, &sock->lock);
+ }
+ INSIST(atomic_load(&sock->active));
+ UNLOCK(&sock->lock);
+
+ return (result);
+}
+
+void
+isc__nm_async_tcpdnsconnect(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tcpdnsconnect_t *ievent =
+ (isc__netievent_tcpdnsconnect_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+ isc__nm_uvreq_t *req = ievent->req;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_tcpdnssocket);
+ REQUIRE(sock->parent == NULL);
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ result = tcpdns_connect_direct(sock, req);
+ if (result != ISC_R_SUCCESS) {
+ isc__nmsocket_clearcb(sock);
+ isc__nm_connectcb(sock, req, result, true);
+ atomic_store(&sock->active, false);
+ isc__nm_tcpdns_close(sock);
+ }
+
+ /*
+ * The sock is now attached to the handle.
+ */
+ isc__nmsocket_detach(&sock);
+}
+
+static void
+tcpdns_connect_cb(uv_connect_t *uvreq, int status) {
+ isc_result_t result = ISC_R_UNSET;
+ isc__nm_uvreq_t *req = NULL;
+ isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)uvreq->handle);
+ struct sockaddr_storage ss;
+ int r;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ req = uv_handle_get_data((uv_handle_t *)uvreq);
+
+ REQUIRE(VALID_UVREQ(req));
+ REQUIRE(VALID_NMHANDLE(req->handle));
+
+ if (atomic_load(&sock->timedout)) {
+ result = ISC_R_TIMEDOUT;
+ goto error;
+ } else if (isc__nm_closing(sock)) {
+ /* Network manager shutting down */
+ result = ISC_R_SHUTTINGDOWN;
+ goto error;
+ } else if (isc__nmsocket_closing(sock)) {
+ /* Connection canceled */
+ result = ISC_R_CANCELED;
+ goto error;
+ } else if (status == UV_ETIMEDOUT) {
+ /* Timeout status code here indicates hard error */
+ result = ISC_R_TIMEDOUT;
+ goto error;
+ } else if (status == UV_EADDRINUSE) {
+ /*
+ * On FreeBSD the TCP connect() call sometimes results in a
+ * spurious transient EADDRINUSE. Try a few more times before
+ * giving up.
+ */
+ if (--req->connect_tries > 0) {
+ r = uv_tcp_connect(
+ &req->uv_req.connect, &sock->uv_handle.tcp,
+ &req->peer.type.sa, tcpdns_connect_cb);
+ if (r != 0) {
+ result = isc__nm_uverr2result(r);
+ goto error;
+ }
+ return;
+ }
+ result = isc__nm_uverr2result(status);
+ goto error;
+ } else if (status != 0) {
+ result = isc__nm_uverr2result(status);
+ goto error;
+ }
+
+ isc__nmsocket_timer_stop(sock);
+ uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock);
+
+ isc__nm_incstats(sock, STATID_CONNECT);
+ r = uv_tcp_getpeername(&sock->uv_handle.tcp, (struct sockaddr *)&ss,
+ &(int){ sizeof(ss) });
+ if (r != 0) {
+ result = isc__nm_uverr2result(r);
+ goto error;
+ }
+
+ atomic_store(&sock->connecting, false);
+
+ result = isc_sockaddr_fromsockaddr(&sock->peer, (struct sockaddr *)&ss);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ isc__nm_connectcb(sock, req, ISC_R_SUCCESS, false);
+
+ return;
+error:
+ isc__nm_failed_connect_cb(sock, req, result, false);
+}
+
+void
+isc_nm_tcpdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
+ isc_nm_cb_t cb, void *cbarg, unsigned int timeout,
+ size_t extrahandlesize) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_nmsocket_t *sock = NULL;
+ isc__netievent_tcpdnsconnect_t *ievent = NULL;
+ isc__nm_uvreq_t *req = NULL;
+ sa_family_t sa_family;
+
+ REQUIRE(VALID_NM(mgr));
+ REQUIRE(local != NULL);
+ REQUIRE(peer != NULL);
+
+ sa_family = peer->type.sa.sa_family;
+
+ sock = isc_mem_get(mgr->mctx, sizeof(*sock));
+ isc__nmsocket_init(sock, mgr, isc_nm_tcpdnssocket, local);
+
+ sock->extrahandlesize = extrahandlesize;
+ sock->connect_timeout = timeout;
+ sock->result = ISC_R_UNSET;
+ atomic_init(&sock->client, true);
+
+ req = isc__nm_uvreq_get(mgr, sock);
+ req->cb.connect = cb;
+ req->cbarg = cbarg;
+ req->peer = *peer;
+ req->local = *local;
+ req->handle = isc__nmhandle_get(sock, &req->peer, &sock->iface);
+
+ result = isc__nm_socket(sa_family, SOCK_STREAM, 0, &sock->fd);
+ if (result != ISC_R_SUCCESS) {
+ if (isc__nm_in_netthread()) {
+ sock->tid = isc_nm_tid();
+ }
+ isc__nmsocket_clearcb(sock);
+ isc__nm_connectcb(sock, req, result, true);
+ atomic_store(&sock->closed, true);
+ isc__nmsocket_detach(&sock);
+ return;
+ }
+
+ (void)isc__nm_socket_min_mtu(sock->fd, sa_family);
+ (void)isc__nm_socket_tcp_maxseg(sock->fd, NM_MAXSEG);
+
+ /* 2 minute timeout */
+ result = isc__nm_socket_connectiontimeout(sock->fd, 120 * 1000);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ ievent = isc__nm_get_netievent_tcpdnsconnect(mgr, sock, req);
+
+ if (isc__nm_in_netthread()) {
+ atomic_store(&sock->active, true);
+ sock->tid = isc_nm_tid();
+ isc__nm_async_tcpdnsconnect(&mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+ isc__nm_put_netievent_tcpdnsconnect(mgr, ievent);
+ } else {
+ atomic_init(&sock->active, false);
+ sock->tid = isc_random_uniform(mgr->nworkers);
+ isc__nm_enqueue_ievent(&mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+ }
+
+ LOCK(&sock->lock);
+ while (sock->result == ISC_R_UNSET) {
+ WAIT(&sock->cond, &sock->lock);
+ }
+ atomic_store(&sock->active, true);
+ BROADCAST(&sock->scond);
+ UNLOCK(&sock->lock);
+}
+
+static uv_os_sock_t
+isc__nm_tcpdns_lb_socket(isc_nm_t *mgr, sa_family_t sa_family) {
+ isc_result_t result;
+ uv_os_sock_t sock;
+
+ result = isc__nm_socket(sa_family, SOCK_STREAM, 0, &sock);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ (void)isc__nm_socket_incoming_cpu(sock);
+ (void)isc__nm_socket_v6only(sock, sa_family);
+
+ /* FIXME: set mss */
+
+ result = isc__nm_socket_reuse(sock);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ if (mgr->load_balance_sockets) {
+ result = isc__nm_socket_reuse_lb(sock);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ }
+
+ return (sock);
+}
+
+static void
+enqueue_stoplistening(isc_nmsocket_t *sock) {
+ isc__netievent_tcpdnsstop_t *ievent =
+ isc__nm_get_netievent_tcpdnsstop(sock->mgr, sock);
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+}
+
+static void
+start_tcpdns_child(isc_nm_t *mgr, isc_sockaddr_t *iface, isc_nmsocket_t *sock,
+ uv_os_sock_t fd, int tid) {
+ isc__netievent_tcpdnslisten_t *ievent = NULL;
+ isc_nmsocket_t *csock = &sock->children[tid];
+
+ isc__nmsocket_init(csock, mgr, isc_nm_tcpdnssocket, iface);
+ csock->parent = sock;
+ csock->accept_cb = sock->accept_cb;
+ csock->accept_cbarg = sock->accept_cbarg;
+ csock->recv_cb = sock->recv_cb;
+ csock->recv_cbarg = sock->recv_cbarg;
+ csock->extrahandlesize = sock->extrahandlesize;
+ csock->backlog = sock->backlog;
+ csock->tid = tid;
+ /*
+ * We don't attach to quota, just assign - to avoid
+ * increasing quota unnecessarily.
+ */
+ csock->pquota = sock->pquota;
+ isc_quota_cb_init(&csock->quotacb, quota_accept_cb, csock);
+
+ if (mgr->load_balance_sockets) {
+ UNUSED(fd);
+ csock->fd = isc__nm_tcpdns_lb_socket(mgr,
+ iface->type.sa.sa_family);
+ } else {
+ csock->fd = dup(fd);
+ }
+ REQUIRE(csock->fd >= 0);
+
+ ievent = isc__nm_get_netievent_tcpdnslisten(mgr, csock);
+ isc__nm_maybe_enqueue_ievent(&mgr->workers[tid],
+ (isc__netievent_t *)ievent);
+}
+isc_result_t
+isc_nm_listentcpdns(isc_nm_t *mgr, isc_sockaddr_t *iface,
+ isc_nm_recv_cb_t recv_cb, void *recv_cbarg,
+ isc_nm_accept_cb_t accept_cb, void *accept_cbarg,
+ size_t extrahandlesize, int backlog, isc_quota_t *quota,
+ isc_nmsocket_t **sockp) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_nmsocket_t *sock = NULL;
+ size_t children_size = 0;
+ uv_os_sock_t fd = -1;
+
+ REQUIRE(VALID_NM(mgr));
+
+ sock = isc_mem_get(mgr->mctx, sizeof(*sock));
+ isc__nmsocket_init(sock, mgr, isc_nm_tcpdnslistener, iface);
+
+ atomic_init(&sock->rchildren, 0);
+ sock->nchildren = mgr->nworkers;
+ children_size = sock->nchildren * sizeof(sock->children[0]);
+ sock->children = isc_mem_get(mgr->mctx, children_size);
+ memset(sock->children, 0, children_size);
+
+ sock->result = ISC_R_UNSET;
+ sock->accept_cb = accept_cb;
+ sock->accept_cbarg = accept_cbarg;
+ sock->recv_cb = recv_cb;
+ sock->recv_cbarg = recv_cbarg;
+ sock->extrahandlesize = extrahandlesize;
+ sock->backlog = backlog;
+ sock->pquota = quota;
+
+ sock->tid = 0;
+ sock->fd = -1;
+
+ if (!mgr->load_balance_sockets) {
+ fd = isc__nm_tcpdns_lb_socket(mgr, iface->type.sa.sa_family);
+ }
+
+ isc_barrier_init(&sock->startlistening, sock->nchildren);
+
+ for (size_t i = 0; i < sock->nchildren; i++) {
+ if ((int)i == isc_nm_tid()) {
+ continue;
+ }
+ start_tcpdns_child(mgr, iface, sock, fd, i);
+ }
+
+ if (isc__nm_in_netthread()) {
+ start_tcpdns_child(mgr, iface, sock, fd, isc_nm_tid());
+ }
+
+ if (!mgr->load_balance_sockets) {
+ isc__nm_closesocket(fd);
+ }
+
+ LOCK(&sock->lock);
+ while (atomic_load(&sock->rchildren) != sock->nchildren) {
+ WAIT(&sock->cond, &sock->lock);
+ }
+ result = sock->result;
+ atomic_store(&sock->active, true);
+ UNLOCK(&sock->lock);
+
+ INSIST(result != ISC_R_UNSET);
+
+ if (result == ISC_R_SUCCESS) {
+ REQUIRE(atomic_load(&sock->rchildren) == sock->nchildren);
+ *sockp = sock;
+ } else {
+ atomic_store(&sock->active, false);
+ enqueue_stoplistening(sock);
+ isc_nmsocket_close(&sock);
+ }
+
+ return (result);
+}
+
+void
+isc__nm_async_tcpdnslisten(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tcpdnslisten_t *ievent =
+ (isc__netievent_tcpdnslisten_t *)ev0;
+ sa_family_t sa_family;
+ int r;
+ int flags = 0;
+ isc_nmsocket_t *sock = NULL;
+ isc_result_t result = ISC_R_UNSET;
+ isc_nm_t *mgr = NULL;
+
+ REQUIRE(VALID_NMSOCK(ievent->sock));
+ REQUIRE(ievent->sock->tid == isc_nm_tid());
+ REQUIRE(VALID_NMSOCK(ievent->sock->parent));
+
+ sock = ievent->sock;
+ sa_family = sock->iface.type.sa.sa_family;
+ mgr = sock->mgr;
+
+ REQUIRE(sock->type == isc_nm_tcpdnssocket);
+ REQUIRE(sock->parent != NULL);
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ (void)isc__nm_socket_min_mtu(sock->fd, sa_family);
+ (void)isc__nm_socket_tcp_maxseg(sock->fd, NM_MAXSEG);
+
+ r = uv_tcp_init(&worker->loop, &sock->uv_handle.tcp);
+ UV_RUNTIME_CHECK(uv_tcp_init, r);
+ uv_handle_set_data(&sock->uv_handle.handle, sock);
+ /* This keeps the socket alive after everything else is gone */
+ isc__nmsocket_attach(sock, &(isc_nmsocket_t *){ NULL });
+
+ r = uv_timer_init(&worker->loop, &sock->read_timer);
+ UV_RUNTIME_CHECK(uv_timer_init, r);
+ uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock);
+
+ LOCK(&sock->parent->lock);
+
+ r = uv_tcp_open(&sock->uv_handle.tcp, sock->fd);
+ if (r < 0) {
+ isc__nm_closesocket(sock->fd);
+ isc__nm_incstats(sock, STATID_OPENFAIL);
+ goto done;
+ }
+ isc__nm_incstats(sock, STATID_OPEN);
+
+ if (sa_family == AF_INET6) {
+ flags = UV_TCP_IPV6ONLY;
+ }
+
+ if (mgr->load_balance_sockets) {
+ r = isc_uv_tcp_freebind(&sock->uv_handle.tcp,
+ &sock->iface.type.sa, flags);
+ if (r < 0) {
+ isc__nm_incstats(sock, STATID_BINDFAIL);
+ goto done;
+ }
+ } else {
+ if (sock->parent->fd == -1) {
+ r = isc_uv_tcp_freebind(&sock->uv_handle.tcp,
+ &sock->iface.type.sa, flags);
+ if (r < 0) {
+ isc__nm_incstats(sock, STATID_BINDFAIL);
+ goto done;
+ }
+ sock->parent->uv_handle.tcp.flags =
+ sock->uv_handle.tcp.flags;
+ sock->parent->fd = sock->fd;
+ } else {
+ /* The socket is already bound, just copy the flags */
+ sock->uv_handle.tcp.flags =
+ sock->parent->uv_handle.tcp.flags;
+ }
+ }
+
+ isc__nm_set_network_buffers(sock->mgr, &sock->uv_handle.handle);
+
+ /*
+ * The callback will run in the same thread uv_listen() was called
+ * from, so a race with tcpdns_connection_cb() isn't possible.
+ */
+ r = uv_listen((uv_stream_t *)&sock->uv_handle.tcp, sock->backlog,
+ tcpdns_connection_cb);
+ if (r != 0) {
+ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_NETMGR, ISC_LOG_ERROR,
+ "uv_listen failed: %s",
+ isc_result_totext(isc__nm_uverr2result(r)));
+ isc__nm_incstats(sock, STATID_BINDFAIL);
+ goto done;
+ }
+
+ atomic_store(&sock->listening, true);
+
+done:
+ result = isc__nm_uverr2result(r);
+ if (result != ISC_R_SUCCESS) {
+ sock->pquota = NULL;
+ }
+
+ atomic_fetch_add(&sock->parent->rchildren, 1);
+ if (sock->parent->result == ISC_R_UNSET) {
+ sock->parent->result = result;
+ }
+ SIGNAL(&sock->parent->cond);
+ UNLOCK(&sock->parent->lock);
+
+ isc_barrier_wait(&sock->parent->startlistening);
+}
+
+static void
+tcpdns_connection_cb(uv_stream_t *server, int status) {
+ isc_nmsocket_t *ssock = uv_handle_get_data((uv_handle_t *)server);
+ isc_result_t result;
+ isc_quota_t *quota = NULL;
+
+ if (status != 0) {
+ result = isc__nm_uverr2result(status);
+ goto done;
+ }
+
+ REQUIRE(VALID_NMSOCK(ssock));
+ REQUIRE(ssock->tid == isc_nm_tid());
+
+ if (isc__nmsocket_closing(ssock)) {
+ result = ISC_R_CANCELED;
+ goto done;
+ }
+
+ if (ssock->pquota != NULL) {
+ result = isc_quota_attach_cb(ssock->pquota, &quota,
+ &ssock->quotacb);
+ if (result == ISC_R_QUOTA) {
+ isc__nm_incstats(ssock, STATID_ACCEPTFAIL);
+ goto done;
+ }
+ }
+
+ result = accept_connection(ssock, quota);
+done:
+ isc__nm_accept_connection_log(result, can_log_tcpdns_quota());
+}
+
+void
+isc__nm_tcpdns_stoplistening(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_tcpdnslistener);
+
+ if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false },
+ true))
+ {
+ UNREACHABLE();
+ }
+
+ if (!isc__nm_in_netthread()) {
+ enqueue_stoplistening(sock);
+ } else {
+ stop_tcpdns_parent(sock);
+ }
+}
+
+void
+isc__nm_async_tcpdnsstop(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tcpdnsstop_t *ievent =
+ (isc__netievent_tcpdnsstop_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ if (sock->parent != NULL) {
+ stop_tcpdns_child(sock);
+ return;
+ }
+
+ stop_tcpdns_parent(sock);
+}
+
+void
+isc__nm_tcpdns_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(result != ISC_R_SUCCESS);
+
+ isc__nmsocket_timer_stop(sock);
+ isc__nm_stop_reading(sock);
+
+ if (!sock->recv_read) {
+ goto destroy;
+ }
+ sock->recv_read = false;
+
+ if (sock->recv_cb != NULL) {
+ isc__nm_uvreq_t *req = isc__nm_get_read_req(sock, NULL);
+ isc__nmsocket_clearcb(sock);
+ isc__nm_readcb(sock, req, result);
+ }
+
+destroy:
+ isc__nmsocket_prep_destroy(sock);
+
+ /*
+ * We need to detach from quota after the read callback function had a
+ * chance to be executed.
+ */
+ if (sock->quota != NULL) {
+ isc_quota_detach(&sock->quota);
+ }
+}
+
+void
+isc__nm_tcpdns_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ isc_nmsocket_t *sock = handle->sock;
+ isc__netievent_tcpdnsread_t *ievent = NULL;
+
+ REQUIRE(sock->type == isc_nm_tcpdnssocket);
+ REQUIRE(sock->statichandle == handle);
+
+ sock->recv_cb = cb;
+ sock->recv_cbarg = cbarg;
+ sock->recv_read = true;
+ if (sock->read_timeout == 0) {
+ sock->read_timeout =
+ (atomic_load(&sock->keepalive)
+ ? atomic_load(&sock->mgr->keepalive)
+ : atomic_load(&sock->mgr->idle));
+ }
+
+ ievent = isc__nm_get_netievent_tcpdnsread(sock->mgr, sock);
+
+ /*
+ * This MUST be done asynchronously, no matter which thread we're
+ * in. The callback function for isc_nm_read() often calls
+ * isc_nm_read() again; if we tried to do that synchronously
+ * we'd clash in processbuffer() and grow the stack indefinitely.
+ */
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+
+ return;
+}
+
+void
+isc__nm_async_tcpdnsread(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tcpdnsread_t *ievent =
+ (isc__netievent_tcpdnsread_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+ isc_result_t result;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ if (isc__nmsocket_closing(sock)) {
+ result = ISC_R_CANCELED;
+ } else {
+ result = isc__nm_process_sock_buffer(sock);
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ atomic_store(&sock->reading, true);
+ isc__nm_failed_read_cb(sock, result, false);
+ }
+}
+
+/*
+ * Process a single packet from the incoming buffer.
+ *
+ * Return ISC_R_SUCCESS and attach 'handlep' to a handle if something
+ * was processed; return ISC_R_NOMORE if there isn't a full message
+ * to be processed.
+ *
+ * The caller will need to unreference the handle.
+ */
+isc_result_t
+isc__nm_tcpdns_processbuffer(isc_nmsocket_t *sock) {
+ size_t len;
+ isc__nm_uvreq_t *req = NULL;
+ isc_nmhandle_t *handle = NULL;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ if (isc__nmsocket_closing(sock)) {
+ return (ISC_R_CANCELED);
+ }
+
+ /*
+ * If we don't even have the length yet, we can't do
+ * anything.
+ */
+ if (sock->buf_len < 2) {
+ return (ISC_R_NOMORE);
+ }
+
+ /*
+ * Process the first packet from the buffer, leaving
+ * the rest (if any) for later.
+ */
+ len = ntohs(*(uint16_t *)sock->buf);
+ if (len > sock->buf_len - 2) {
+ return (ISC_R_NOMORE);
+ }
+
+ if (sock->recv_cb == NULL) {
+ /*
+ * recv_cb has been cleared - there is
+ * nothing to do
+ */
+ return (ISC_R_CANCELED);
+ } else if (sock->statichandle == NULL &&
+ atomic_load(&sock->connected) &&
+ !atomic_load(&sock->connecting))
+ {
+ /*
+ * It seems that some unexpected data (a DNS message) has
+ * arrived while we are wrapping up.
+ */
+ return (ISC_R_CANCELED);
+ }
+
+ req = isc__nm_get_read_req(sock, NULL);
+ REQUIRE(VALID_UVREQ(req));
+
+ /*
+ * We need to launch isc__nm_resume_processing() after the buffer
+ * has been consumed, thus we must delay detaching the handle.
+ */
+ isc_nmhandle_attach(req->handle, &handle);
+
+ /*
+ * The callback will be called synchronously because the
+ * result is ISC_R_SUCCESS, so we don't need to have
+ * the buffer on the heap
+ */
+ req->uvbuf.base = (char *)sock->buf + 2;
+ req->uvbuf.len = len;
+
+ /*
+ * If isc__nm_tcpdns_read() was called, it will be satisfied by single
+ * DNS message in the next call.
+ */
+ sock->recv_read = false;
+
+ /*
+ * An assertion failure here means that there's an erroneous
+ * extra nmhandle detach happening in the callback and
+ * isc__nm_resume_processing() is called while we're
+ * processing the buffer.
+ */
+ REQUIRE(sock->processing == false);
+ sock->processing = true;
+ isc__nm_readcb(sock, req, ISC_R_SUCCESS);
+ sock->processing = false;
+
+ len += 2;
+ sock->buf_len -= len;
+ if (sock->buf_len > 0) {
+ memmove(sock->buf, sock->buf + len, sock->buf_len);
+ }
+
+ isc_nmhandle_detach(&handle);
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc__nm_tcpdns_read_cb(uv_stream_t *stream, ssize_t nread,
+ const uv_buf_t *buf) {
+ isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)stream);
+ uint8_t *base = NULL;
+ size_t len;
+ isc_result_t result;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(atomic_load(&sock->reading));
+ REQUIRE(buf != NULL);
+
+ if (isc__nmsocket_closing(sock)) {
+ isc__nm_failed_read_cb(sock, ISC_R_CANCELED, true);
+ goto free;
+ }
+
+ if (nread < 0) {
+ if (nread != UV_EOF) {
+ isc__nm_incstats(sock, STATID_RECVFAIL);
+ }
+
+ isc__nm_failed_read_cb(sock, isc__nm_uverr2result(nread), true);
+ goto free;
+ }
+
+ base = (uint8_t *)buf->base;
+ len = nread;
+
+ /*
+ * FIXME: We can avoid the memmove here if we know we have received full
+ * packet; e.g. we should be smarter, a.s. there are just few situations
+ *
+ * The tcp_alloc_buf should be smarter and point the uv_read_start to
+ * the position where previous read has ended in the sock->buf, that way
+ * the data could be read directly into sock->buf.
+ */
+
+ if (sock->buf_len + len > sock->buf_size) {
+ isc__nm_alloc_dnsbuf(sock, sock->buf_len + len);
+ }
+ memmove(sock->buf + sock->buf_len, base, len);
+ sock->buf_len += len;
+
+ if (!atomic_load(&sock->client)) {
+ sock->read_timeout = atomic_load(&sock->mgr->idle);
+ }
+
+ result = isc__nm_process_sock_buffer(sock);
+ if (result != ISC_R_SUCCESS) {
+ isc__nm_failed_read_cb(sock, result, true);
+ }
+free:
+ if (nread < 0) {
+ /*
+ * The buffer may be a null buffer on error.
+ */
+ if (buf->base == NULL && buf->len == 0) {
+ return;
+ }
+ }
+
+ isc__nm_free_uvbuf(sock, buf);
+}
+
+static void
+quota_accept_cb(isc_quota_t *quota, void *sock0) {
+ isc_nmsocket_t *sock = (isc_nmsocket_t *)sock0;
+
+ REQUIRE(VALID_NMSOCK(sock));
+
+ /*
+ * Create a tcpdnsaccept event and pass it using the async channel.
+ */
+
+ isc__netievent_tcpdnsaccept_t *ievent =
+ isc__nm_get_netievent_tcpdnsaccept(sock->mgr, sock, quota);
+ isc__nm_maybe_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+}
+
+/*
+ * This is called after we get a quota_accept_cb() callback.
+ */
+void
+isc__nm_async_tcpdnsaccept(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tcpdnsaccept_t *ievent =
+ (isc__netievent_tcpdnsaccept_t *)ev0;
+ isc_result_t result;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_NMSOCK(ievent->sock));
+ REQUIRE(ievent->sock->tid == isc_nm_tid());
+
+ result = accept_connection(ievent->sock, ievent->quota);
+ isc__nm_accept_connection_log(result, can_log_tcpdns_quota());
+}
+
+static isc_result_t
+accept_connection(isc_nmsocket_t *ssock, isc_quota_t *quota) {
+ isc_nmsocket_t *csock = NULL;
+ isc__networker_t *worker = NULL;
+ int r;
+ isc_result_t result;
+ struct sockaddr_storage peer_ss;
+ struct sockaddr_storage local_ss;
+ isc_sockaddr_t local;
+ isc_nmhandle_t *handle = NULL;
+
+ REQUIRE(VALID_NMSOCK(ssock));
+ REQUIRE(ssock->tid == isc_nm_tid());
+
+ if (isc__nmsocket_closing(ssock)) {
+ if (quota != NULL) {
+ isc_quota_detach(&quota);
+ }
+ return (ISC_R_CANCELED);
+ }
+
+ REQUIRE(ssock->accept_cb != NULL);
+
+ csock = isc_mem_get(ssock->mgr->mctx, sizeof(isc_nmsocket_t));
+ isc__nmsocket_init(csock, ssock->mgr, isc_nm_tcpdnssocket,
+ &ssock->iface);
+ csock->tid = ssock->tid;
+ csock->extrahandlesize = ssock->extrahandlesize;
+ isc__nmsocket_attach(ssock, &csock->server);
+ csock->recv_cb = ssock->recv_cb;
+ csock->recv_cbarg = ssock->recv_cbarg;
+ csock->quota = quota;
+ atomic_init(&csock->accepting, true);
+
+ worker = &csock->mgr->workers[csock->tid];
+
+ r = uv_tcp_init(&worker->loop, &csock->uv_handle.tcp);
+ UV_RUNTIME_CHECK(uv_tcp_init, r);
+ uv_handle_set_data(&csock->uv_handle.handle, csock);
+
+ r = uv_timer_init(&worker->loop, &csock->read_timer);
+ UV_RUNTIME_CHECK(uv_timer_init, r);
+ uv_handle_set_data((uv_handle_t *)&csock->read_timer, csock);
+
+ r = uv_accept(&ssock->uv_handle.stream, &csock->uv_handle.stream);
+ if (r != 0) {
+ result = isc__nm_uverr2result(r);
+ goto failure;
+ }
+
+ r = uv_tcp_getpeername(&csock->uv_handle.tcp,
+ (struct sockaddr *)&peer_ss,
+ &(int){ sizeof(peer_ss) });
+ if (r != 0) {
+ result = isc__nm_uverr2result(r);
+ goto failure;
+ }
+
+ result = isc_sockaddr_fromsockaddr(&csock->peer,
+ (struct sockaddr *)&peer_ss);
+ if (result != ISC_R_SUCCESS) {
+ goto failure;
+ }
+
+ r = uv_tcp_getsockname(&csock->uv_handle.tcp,
+ (struct sockaddr *)&local_ss,
+ &(int){ sizeof(local_ss) });
+ if (r != 0) {
+ result = isc__nm_uverr2result(r);
+ goto failure;
+ }
+
+ result = isc_sockaddr_fromsockaddr(&local,
+ (struct sockaddr *)&local_ss);
+ if (result != ISC_R_SUCCESS) {
+ goto failure;
+ }
+
+ /*
+ * The handle will be either detached on acceptcb failure or in the
+ * readcb.
+ */
+ handle = isc__nmhandle_get(csock, NULL, &local);
+
+ result = ssock->accept_cb(handle, ISC_R_SUCCESS, ssock->accept_cbarg);
+ if (result != ISC_R_SUCCESS) {
+ isc_nmhandle_detach(&handle);
+ goto failure;
+ }
+
+ atomic_store(&csock->accepting, false);
+
+ isc__nm_incstats(csock, STATID_ACCEPT);
+
+ csock->read_timeout = atomic_load(&csock->mgr->init);
+
+ csock->closehandle_cb = isc__nm_resume_processing;
+
+ /*
+ * We need to keep the handle alive until we fail to read or connection
+ * is closed by the other side, it will be detached via
+ * prep_destroy()->tcpdns_close_direct().
+ */
+ isc_nmhandle_attach(handle, &csock->recv_handle);
+ result = isc__nm_process_sock_buffer(csock);
+ if (result != ISC_R_SUCCESS) {
+ isc_nmhandle_detach(&csock->recv_handle);
+ isc_nmhandle_detach(&handle);
+ goto failure;
+ }
+
+ /*
+ * The initial timer has been set, update the read timeout for the next
+ * reads.
+ */
+ csock->read_timeout = (atomic_load(&csock->keepalive)
+ ? atomic_load(&csock->mgr->keepalive)
+ : atomic_load(&csock->mgr->idle));
+
+ isc_nmhandle_detach(&handle);
+
+ /*
+ * sock is now attached to the handle.
+ */
+ isc__nmsocket_detach(&csock);
+
+ return (ISC_R_SUCCESS);
+
+failure:
+
+ atomic_store(&csock->active, false);
+
+ isc__nm_failed_accept_cb(csock, result);
+
+ isc__nmsocket_prep_destroy(csock);
+
+ isc__nmsocket_detach(&csock);
+
+ return (result);
+}
+
+void
+isc__nm_tcpdns_send(isc_nmhandle_t *handle, isc_region_t *region,
+ isc_nm_cb_t cb, void *cbarg) {
+ isc__netievent_tcpdnssend_t *ievent = NULL;
+ isc__nm_uvreq_t *uvreq = NULL;
+ isc_nmsocket_t *sock = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ sock = handle->sock;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_tcpdnssocket);
+
+ uvreq = isc__nm_uvreq_get(sock->mgr, sock);
+ *(uint16_t *)uvreq->tcplen = htons(region->length);
+ uvreq->uvbuf.base = (char *)region->base;
+ uvreq->uvbuf.len = region->length;
+
+ isc_nmhandle_attach(handle, &uvreq->handle);
+
+ uvreq->cb.send = cb;
+ uvreq->cbarg = cbarg;
+
+ ievent = isc__nm_get_netievent_tcpdnssend(sock->mgr, sock, uvreq);
+ isc__nm_maybe_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+
+ return;
+}
+
+static void
+tcpdns_send_cb(uv_write_t *req, int status) {
+ isc__nm_uvreq_t *uvreq = (isc__nm_uvreq_t *)req->data;
+ isc_nmsocket_t *sock = NULL;
+
+ REQUIRE(VALID_UVREQ(uvreq));
+ REQUIRE(VALID_NMSOCK(uvreq->sock));
+
+ sock = uvreq->sock;
+
+ isc_nm_timer_stop(uvreq->timer);
+ isc_nm_timer_detach(&uvreq->timer);
+
+ if (status < 0) {
+ isc__nm_incstats(sock, STATID_SENDFAIL);
+ isc__nm_failed_send_cb(sock, uvreq,
+ isc__nm_uverr2result(status));
+ return;
+ }
+
+ isc__nm_sendcb(sock, uvreq, ISC_R_SUCCESS, false);
+}
+
+/*
+ * Handle 'tcpsend' async event - send a packet on the socket
+ */
+void
+isc__nm_async_tcpdnssend(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc_result_t result;
+ isc__netievent_tcpdnssend_t *ievent =
+ (isc__netievent_tcpdnssend_t *)ev0;
+ isc_nmsocket_t *sock = NULL;
+ isc__nm_uvreq_t *uvreq = NULL;
+ int r, nbufs = 2;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_UVREQ(ievent->req));
+ REQUIRE(VALID_NMSOCK(ievent->sock));
+ REQUIRE(ievent->sock->type == isc_nm_tcpdnssocket);
+ REQUIRE(ievent->sock->tid == isc_nm_tid());
+
+ sock = ievent->sock;
+ uvreq = ievent->req;
+
+ if (sock->write_timeout == 0) {
+ sock->write_timeout =
+ (atomic_load(&sock->keepalive)
+ ? atomic_load(&sock->mgr->keepalive)
+ : atomic_load(&sock->mgr->idle));
+ }
+
+ uv_buf_t bufs[2] = { { .base = uvreq->tcplen, .len = 2 },
+ { .base = uvreq->uvbuf.base,
+ .len = uvreq->uvbuf.len } };
+
+ if (isc__nmsocket_closing(sock)) {
+ result = ISC_R_CANCELED;
+ goto fail;
+ }
+
+ r = uv_try_write(&sock->uv_handle.stream, bufs, nbufs);
+
+ if (r == (int)(bufs[0].len + bufs[1].len)) {
+ /* Wrote everything */
+ isc__nm_sendcb(sock, uvreq, ISC_R_SUCCESS, true);
+ return;
+ }
+
+ if (r == 1) {
+ /* Partial write of DNSMSG length */
+ bufs[0].base = uvreq->tcplen + 1;
+ bufs[0].len = 1;
+ } else if (r > 0) {
+ /* Partial write of DNSMSG */
+ nbufs = 1;
+ bufs[0].base = uvreq->uvbuf.base + (r - 2);
+ bufs[0].len = uvreq->uvbuf.len - (r - 2);
+ } else if (r == UV_ENOSYS || r == UV_EAGAIN) {
+ /* uv_try_write not supported, send asynchronously */
+ } else {
+ /* error sending data */
+ result = isc__nm_uverr2result(r);
+ goto fail;
+ }
+
+ r = uv_write(&uvreq->uv_req.write, &sock->uv_handle.stream, bufs, nbufs,
+ tcpdns_send_cb);
+ if (r < 0) {
+ result = isc__nm_uverr2result(r);
+ goto fail;
+ }
+
+ isc_nm_timer_create(uvreq->handle, isc__nmsocket_writetimeout_cb, uvreq,
+ &uvreq->timer);
+ if (sock->write_timeout > 0) {
+ isc_nm_timer_start(uvreq->timer, sock->write_timeout);
+ }
+
+ return;
+fail:
+ isc__nm_incstats(sock, STATID_SENDFAIL);
+ isc__nm_failed_send_cb(sock, uvreq, result);
+}
+
+static void
+tcpdns_stop_cb(uv_handle_t *handle) {
+ isc_nmsocket_t *sock = uv_handle_get_data(handle);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(atomic_load(&sock->closing));
+
+ uv_handle_set_data(handle, NULL);
+
+ if (!atomic_compare_exchange_strong(&sock->closed, &(bool){ false },
+ true))
+ {
+ UNREACHABLE();
+ }
+
+ isc__nm_incstats(sock, STATID_CLOSE);
+
+ atomic_store(&sock->listening, false);
+
+ isc__nmsocket_detach(&sock);
+}
+
+static void
+tcpdns_close_sock(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(atomic_load(&sock->closing));
+
+ if (!atomic_compare_exchange_strong(&sock->closed, &(bool){ false },
+ true))
+ {
+ UNREACHABLE();
+ }
+
+ isc__nm_incstats(sock, STATID_CLOSE);
+
+ if (sock->server != NULL) {
+ isc__nmsocket_detach(&sock->server);
+ }
+
+ atomic_store(&sock->connected, false);
+
+ isc__nmsocket_prep_destroy(sock);
+}
+
+static void
+tcpdns_close_cb(uv_handle_t *handle) {
+ isc_nmsocket_t *sock = uv_handle_get_data(handle);
+
+ uv_handle_set_data(handle, NULL);
+
+ tcpdns_close_sock(sock);
+}
+
+static void
+read_timer_close_cb(uv_handle_t *timer) {
+ isc_nmsocket_t *sock = uv_handle_get_data(timer);
+ uv_handle_set_data(timer, NULL);
+
+ REQUIRE(VALID_NMSOCK(sock));
+
+ if (sock->parent) {
+ uv_close(&sock->uv_handle.handle, tcpdns_stop_cb);
+ } else if (uv_is_closing(&sock->uv_handle.handle)) {
+ tcpdns_close_sock(sock);
+ } else {
+ uv_close(&sock->uv_handle.handle, tcpdns_close_cb);
+ }
+}
+
+static void
+stop_tcpdns_child(isc_nmsocket_t *sock) {
+ REQUIRE(sock->type == isc_nm_tcpdnssocket);
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false },
+ true))
+ {
+ return;
+ }
+
+ tcpdns_close_direct(sock);
+
+ atomic_fetch_sub(&sock->parent->rchildren, 1);
+
+ isc_barrier_wait(&sock->parent->stoplistening);
+}
+
+static void
+stop_tcpdns_parent(isc_nmsocket_t *sock) {
+ isc_nmsocket_t *csock = NULL;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(sock->type == isc_nm_tcpdnslistener);
+
+ isc_barrier_init(&sock->stoplistening, sock->nchildren);
+
+ for (size_t i = 0; i < sock->nchildren; i++) {
+ csock = &sock->children[i];
+ REQUIRE(VALID_NMSOCK(csock));
+
+ if ((int)i == isc_nm_tid()) {
+ /*
+ * We need to schedule closing the other sockets first
+ */
+ continue;
+ }
+
+ atomic_store(&csock->active, false);
+ enqueue_stoplistening(csock);
+ }
+
+ csock = &sock->children[isc_nm_tid()];
+ atomic_store(&csock->active, false);
+ stop_tcpdns_child(csock);
+
+ atomic_store(&sock->closed, true);
+ isc__nmsocket_prep_destroy(sock);
+}
+
+static void
+tcpdns_close_direct(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(atomic_load(&sock->closing));
+
+ if (sock->quota != NULL) {
+ isc_quota_detach(&sock->quota);
+ }
+
+ if (sock->recv_handle != NULL) {
+ isc_nmhandle_detach(&sock->recv_handle);
+ }
+
+ isc__nmsocket_timer_stop(sock);
+ isc__nm_stop_reading(sock);
+
+ uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock);
+ uv_close((uv_handle_t *)&sock->read_timer, read_timer_close_cb);
+}
+
+void
+isc__nm_tcpdns_close(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_tcpdnssocket);
+ REQUIRE(!isc__nmsocket_active(sock));
+
+ if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false },
+ true))
+ {
+ return;
+ }
+
+ if (sock->tid == isc_nm_tid()) {
+ tcpdns_close_direct(sock);
+ } else {
+ /*
+ * We need to create an event and pass it using async channel
+ */
+ isc__netievent_tcpdnsclose_t *ievent =
+ isc__nm_get_netievent_tcpdnsclose(sock->mgr, sock);
+
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+ }
+}
+
+void
+isc__nm_async_tcpdnsclose(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tcpdnsclose_t *ievent =
+ (isc__netievent_tcpdnsclose_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ tcpdns_close_direct(sock);
+}
+
+static void
+tcpdns_close_connect_cb(uv_handle_t *handle) {
+ isc_nmsocket_t *sock = uv_handle_get_data(handle);
+
+ REQUIRE(VALID_NMSOCK(sock));
+
+ REQUIRE(isc__nm_in_netthread());
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ isc__nmsocket_prep_destroy(sock);
+ isc__nmsocket_detach(&sock);
+}
+
+void
+isc__nm_tcpdns_shutdown(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(sock->type == isc_nm_tcpdnssocket);
+
+ /*
+ * If the socket is active, mark it inactive and
+ * continue. If it isn't active, stop now.
+ */
+ if (!isc__nmsocket_deactivate(sock)) {
+ return;
+ }
+
+ if (atomic_load(&sock->accepting)) {
+ return;
+ }
+
+ if (atomic_load(&sock->connecting)) {
+ isc_nmsocket_t *tsock = NULL;
+ isc__nmsocket_attach(sock, &tsock);
+ uv_close(&sock->uv_handle.handle, tcpdns_close_connect_cb);
+ return;
+ }
+
+ if (sock->statichandle != NULL) {
+ if (isc__nm_closing(sock)) {
+ isc__nm_failed_read_cb(sock, ISC_R_SHUTTINGDOWN, false);
+ } else {
+ isc__nm_failed_read_cb(sock, ISC_R_CANCELED, false);
+ }
+ return;
+ }
+
+ /*
+ * Otherwise, we just send the socket to abyss...
+ */
+ if (sock->parent == NULL) {
+ isc__nmsocket_prep_destroy(sock);
+ }
+}
+
+void
+isc__nm_tcpdns_cancelread(isc_nmhandle_t *handle) {
+ isc_nmsocket_t *sock = NULL;
+ isc__netievent_tcpdnscancel_t *ievent = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ sock = handle->sock;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_tcpdnssocket);
+
+ ievent = isc__nm_get_netievent_tcpdnscancel(sock->mgr, sock, handle);
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+}
+
+void
+isc__nm_async_tcpdnscancel(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tcpdnscancel_t *ievent =
+ (isc__netievent_tcpdnscancel_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ isc__nm_failed_read_cb(sock, ISC_R_EOF, false);
+}
diff --git a/lib/isc/netmgr/timer.c b/lib/isc/netmgr/timer.c
new file mode 100644
index 0000000..8328775
--- /dev/null
+++ b/lib/isc/netmgr/timer.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <uv.h>
+
+#include <isc/netmgr.h>
+#include <isc/util.h>
+
+#include "netmgr-int.h"
+
+struct isc_nm_timer {
+ isc_refcount_t references;
+ uv_timer_t timer;
+ isc_nmhandle_t *handle;
+ isc_nm_timer_cb cb;
+ void *cbarg;
+};
+
+void
+isc_nm_timer_create(isc_nmhandle_t *handle, isc_nm_timer_cb cb, void *cbarg,
+ isc_nm_timer_t **timerp) {
+ isc__networker_t *worker = NULL;
+ isc_nmsocket_t *sock = NULL;
+ isc_nm_timer_t *timer = NULL;
+ int r;
+
+ REQUIRE(isc__nm_in_netthread());
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ sock = handle->sock;
+ worker = &sock->mgr->workers[isc_nm_tid()];
+
+ /* TODO: per-loop object cache */
+ timer = isc_mem_get(sock->mgr->mctx, sizeof(*timer));
+ *timer = (isc_nm_timer_t){ .cb = cb, .cbarg = cbarg };
+ isc_refcount_init(&timer->references, 1);
+ isc_nmhandle_attach(handle, &timer->handle);
+
+ r = uv_timer_init(&worker->loop, &timer->timer);
+ UV_RUNTIME_CHECK(uv_timer_init, r);
+
+ uv_handle_set_data((uv_handle_t *)&timer->timer, timer);
+
+ *timerp = timer;
+}
+
+void
+isc_nm_timer_attach(isc_nm_timer_t *timer, isc_nm_timer_t **timerp) {
+ REQUIRE(timer != NULL);
+ REQUIRE(timerp != NULL && *timerp == NULL);
+
+ isc_refcount_increment(&timer->references);
+ *timerp = timer;
+}
+
+static void
+timer_destroy(uv_handle_t *uvhandle) {
+ isc_nm_timer_t *timer = uv_handle_get_data(uvhandle);
+ isc_nmhandle_t *handle = timer->handle;
+ isc_mem_t *mctx = timer->handle->sock->mgr->mctx;
+
+ isc_mem_put(mctx, timer, sizeof(*timer));
+
+ isc_nmhandle_detach(&handle);
+}
+
+void
+isc_nm_timer_detach(isc_nm_timer_t **timerp) {
+ isc_nm_timer_t *timer = NULL;
+ isc_nmhandle_t *handle = NULL;
+
+ REQUIRE(timerp != NULL && *timerp != NULL);
+
+ timer = *timerp;
+ *timerp = NULL;
+
+ handle = timer->handle;
+
+ REQUIRE(isc__nm_in_netthread());
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ if (isc_refcount_decrement(&timer->references) == 1) {
+ int r = uv_timer_stop(&timer->timer);
+ UV_RUNTIME_CHECK(uv_timer_stop, r);
+ uv_close((uv_handle_t *)&timer->timer, timer_destroy);
+ }
+}
+
+static void
+timer_cb(uv_timer_t *uvtimer) {
+ isc_nm_timer_t *timer = uv_handle_get_data((uv_handle_t *)uvtimer);
+
+ REQUIRE(timer->cb != NULL);
+
+ timer->cb(timer->cbarg, ISC_R_TIMEDOUT);
+}
+
+void
+isc_nm_timer_start(isc_nm_timer_t *timer, uint64_t timeout) {
+ int r = uv_timer_start(&timer->timer, timer_cb, timeout, 0);
+ UV_RUNTIME_CHECK(uv_timer_start, r);
+}
+
+void
+isc_nm_timer_stop(isc_nm_timer_t *timer) {
+ int r = uv_timer_stop(&timer->timer);
+ UV_RUNTIME_CHECK(uv_timer_stop, r);
+}
diff --git a/lib/isc/netmgr/tlsdns.c b/lib/isc/netmgr/tlsdns.c
new file mode 100644
index 0000000..d30e33f
--- /dev/null
+++ b/lib/isc/netmgr/tlsdns.c
@@ -0,0 +1,2363 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <libgen.h>
+#include <unistd.h>
+#include <uv.h>
+
+#include <isc/atomic.h>
+#include <isc/barrier.h>
+#include <isc/buffer.h>
+#include <isc/condition.h>
+#include <isc/errno.h>
+#include <isc/log.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/netmgr.h>
+#include <isc/quota.h>
+#include <isc/random.h>
+#include <isc/refcount.h>
+#include <isc/region.h>
+#include <isc/result.h>
+#include <isc/sockaddr.h>
+#include <isc/stdtime.h>
+#include <isc/thread.h>
+#include <isc/util.h>
+
+#include "netmgr-int.h"
+#include "openssl_shim.h"
+#include "uv-compat.h"
+
+static atomic_uint_fast32_t last_tlsdnsquota_log = 0;
+
+static void
+tls_error(isc_nmsocket_t *sock, isc_result_t result);
+
+static isc_result_t
+tlsdns_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req);
+
+static void
+tlsdns_close_direct(isc_nmsocket_t *sock);
+
+static isc_result_t
+tlsdns_send_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req);
+static void
+tlsdns_connect_cb(uv_connect_t *uvreq, int status);
+
+static void
+tlsdns_connection_cb(uv_stream_t *server, int status);
+
+static void
+tlsdns_close_cb(uv_handle_t *uvhandle);
+
+static isc_result_t
+accept_connection(isc_nmsocket_t *ssock, isc_quota_t *quota);
+
+static void
+quota_accept_cb(isc_quota_t *quota, void *sock0);
+
+static void
+stop_tlsdns_parent(isc_nmsocket_t *sock);
+static void
+stop_tlsdns_child(isc_nmsocket_t *sock);
+
+static void
+async_tlsdns_cycle(isc_nmsocket_t *sock) __attribute__((unused));
+
+static isc_result_t
+tls_cycle(isc_nmsocket_t *sock);
+
+static void
+call_pending_send_callbacks(isc_nmsocket_t *sock, const isc_result_t result);
+
+static void
+tlsdns_keep_client_tls_session(isc_nmsocket_t *sock);
+
+static void
+tlsdns_set_tls_shutdown(isc_tls_t *tls) {
+ (void)SSL_set_shutdown(tls, SSL_SENT_SHUTDOWN);
+}
+
+static bool
+peer_verification_has_failed(isc_nmsocket_t *sock) {
+ if (sock->tls.tls != NULL && sock->tls.state == TLS_STATE_HANDSHAKE &&
+ SSL_get_verify_result(sock->tls.tls) != X509_V_OK)
+ {
+ return (true);
+ }
+
+ return (false);
+}
+
+static bool
+can_log_tlsdns_quota(void) {
+ isc_stdtime_t now, last;
+
+ isc_stdtime_get(&now);
+ last = atomic_exchange_relaxed(&last_tlsdnsquota_log, now);
+ if (now != last) {
+ return (true);
+ }
+
+ return (false);
+}
+
+static isc_result_t
+tlsdns_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) {
+ isc__networker_t *worker = NULL;
+ isc_result_t result = ISC_R_UNSET;
+ int r;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(VALID_UVREQ(req));
+
+ REQUIRE(isc__nm_in_netthread());
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ worker = &sock->mgr->workers[sock->tid];
+
+ atomic_store(&sock->connecting, true);
+
+ /* 2 minute timeout */
+ result = isc__nm_socket_connectiontimeout(sock->fd, 120 * 1000);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ r = uv_tcp_init(&worker->loop, &sock->uv_handle.tcp);
+ UV_RUNTIME_CHECK(uv_tcp_init, r);
+ uv_handle_set_data(&sock->uv_handle.handle, sock);
+
+ r = uv_timer_init(&worker->loop, &sock->read_timer);
+ UV_RUNTIME_CHECK(uv_timer_init, r);
+ uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock);
+
+ if (isc__nm_closing(sock)) {
+ result = ISC_R_SHUTTINGDOWN;
+ goto error;
+ }
+
+ r = uv_tcp_open(&sock->uv_handle.tcp, sock->fd);
+ if (r != 0) {
+ isc__nm_closesocket(sock->fd);
+ isc__nm_incstats(sock, STATID_OPENFAIL);
+ goto done;
+ }
+ isc__nm_incstats(sock, STATID_OPEN);
+
+ if (req->local.length != 0) {
+ r = uv_tcp_bind(&sock->uv_handle.tcp, &req->local.type.sa, 0);
+ /*
+ * In case of shared socket UV_EINVAL will be returned and needs
+ * to be ignored
+ */
+ if (r != 0 && r != UV_EINVAL) {
+ isc__nm_incstats(sock, STATID_BINDFAIL);
+ goto done;
+ }
+ }
+
+ isc__nm_set_network_buffers(sock->mgr, &sock->uv_handle.handle);
+
+ uv_handle_set_data(&req->uv_req.handle, req);
+ r = uv_tcp_connect(&req->uv_req.connect, &sock->uv_handle.tcp,
+ &req->peer.type.sa, tlsdns_connect_cb);
+ if (r != 0) {
+ isc__nm_incstats(sock, STATID_CONNECTFAIL);
+ goto done;
+ }
+
+ uv_handle_set_data((uv_handle_t *)&sock->read_timer,
+ &req->uv_req.connect);
+ isc__nmsocket_timer_start(sock);
+
+ atomic_store(&sock->connected, true);
+
+done:
+ result = isc__nm_uverr2result(r);
+error:
+ LOCK(&sock->lock);
+ sock->result = result;
+ SIGNAL(&sock->cond);
+ if (!atomic_load(&sock->active)) {
+ WAIT(&sock->scond, &sock->lock);
+ }
+ INSIST(atomic_load(&sock->active));
+ UNLOCK(&sock->lock);
+
+ return (result);
+}
+
+void
+isc__nm_async_tlsdnsconnect(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tlsdnsconnect_t *ievent =
+ (isc__netievent_tlsdnsconnect_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+ isc__nm_uvreq_t *req = ievent->req;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_tlsdnssocket);
+ REQUIRE(sock->parent == NULL);
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ result = tlsdns_connect_direct(sock, req);
+ if (result != ISC_R_SUCCESS) {
+ atomic_compare_exchange_enforced(&sock->connecting,
+ &(bool){ true }, false);
+ isc__nmsocket_clearcb(sock);
+ isc__nm_connectcb(sock, req, result, true);
+ atomic_store(&sock->active, false);
+ isc__nm_tlsdns_close(sock);
+ }
+
+ /*
+ * The sock is now attached to the handle.
+ */
+ isc__nmsocket_detach(&sock);
+}
+
+static void
+tlsdns_connect_cb(uv_connect_t *uvreq, int status) {
+ isc_result_t result = ISC_R_UNSET;
+ isc__nm_uvreq_t *req = NULL;
+ isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)uvreq->handle);
+ struct sockaddr_storage ss;
+ int r;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ req = uv_handle_get_data((uv_handle_t *)uvreq);
+
+ REQUIRE(VALID_UVREQ(req));
+ REQUIRE(VALID_NMHANDLE(req->handle));
+
+ if (atomic_load(&sock->timedout)) {
+ result = ISC_R_TIMEDOUT;
+ goto error;
+ } else if (isc__nm_closing(sock)) {
+ /* Network manager shutting down */
+ result = ISC_R_SHUTTINGDOWN;
+ goto error;
+ } else if (isc__nmsocket_closing(sock)) {
+ /* Connection canceled */
+ result = ISC_R_CANCELED;
+ goto error;
+ } else if (status == UV_ETIMEDOUT) {
+ /* Timeout status code here indicates hard error */
+ result = ISC_R_TIMEDOUT;
+ goto error;
+ } else if (status == UV_EADDRINUSE) {
+ /*
+ * On FreeBSD the TCP connect() call sometimes results in a
+ * spurious transient EADDRINUSE. Try a few more times before
+ * giving up.
+ */
+ if (--req->connect_tries > 0) {
+ r = uv_tcp_connect(
+ &req->uv_req.connect, &sock->uv_handle.tcp,
+ &req->peer.type.sa, tlsdns_connect_cb);
+ if (r != 0) {
+ result = isc__nm_uverr2result(r);
+ goto error;
+ }
+ return;
+ }
+ result = isc__nm_uverr2result(status);
+ goto error;
+ } else if (status != 0) {
+ result = isc__nm_uverr2result(status);
+ goto error;
+ }
+
+ isc__nm_incstats(sock, STATID_CONNECT);
+ r = uv_tcp_getpeername(&sock->uv_handle.tcp, (struct sockaddr *)&ss,
+ &(int){ sizeof(ss) });
+ if (r != 0) {
+ result = isc__nm_uverr2result(r);
+ goto error;
+ }
+
+ sock->tls.state = TLS_STATE_NONE;
+ sock->tls.tls = isc_tls_create(sock->tls.ctx);
+ RUNTIME_CHECK(sock->tls.tls != NULL);
+
+ /*
+ *
+ */
+ r = BIO_new_bio_pair(&sock->tls.ssl_wbio, ISC_NETMGR_TCP_RECVBUF_SIZE,
+ &sock->tls.app_rbio, ISC_NETMGR_TCP_RECVBUF_SIZE);
+ RUNTIME_CHECK(r == 1);
+
+ r = BIO_new_bio_pair(&sock->tls.ssl_rbio, ISC_NETMGR_TCP_RECVBUF_SIZE,
+ &sock->tls.app_wbio, ISC_NETMGR_TCP_RECVBUF_SIZE);
+ RUNTIME_CHECK(r == 1);
+
+#if HAVE_SSL_SET0_RBIO && HAVE_SSL_SET0_WBIO
+ /*
+ * Note that if the rbio and wbio are the same then
+ * SSL_set0_rbio() and SSL_set0_wbio() each take ownership of
+ * one reference. Therefore it may be necessary to increment the
+ * number of references available using BIO_up_ref(3) before
+ * calling the set0 functions.
+ */
+ SSL_set0_rbio(sock->tls.tls, sock->tls.ssl_rbio);
+ SSL_set0_wbio(sock->tls.tls, sock->tls.ssl_wbio);
+#else
+ SSL_set_bio(sock->tls.tls, sock->tls.ssl_rbio, sock->tls.ssl_wbio);
+#endif
+
+ result = isc_sockaddr_fromsockaddr(&sock->peer, (struct sockaddr *)&ss);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ if (sock->tls.client_sess_cache != NULL) {
+ isc_tlsctx_client_session_cache_reuse_sockaddr(
+ sock->tls.client_sess_cache, &sock->peer,
+ sock->tls.tls);
+ }
+
+ SSL_set_connect_state(sock->tls.tls);
+
+ /* Setting pending req */
+ sock->tls.pending_req = req;
+
+ result = isc__nm_process_sock_buffer(sock);
+ if (result != ISC_R_SUCCESS) {
+ sock->tls.pending_req = NULL;
+ goto error;
+ }
+
+ result = tls_cycle(sock);
+ if (result != ISC_R_SUCCESS) {
+ sock->tls.pending_req = NULL;
+ goto error;
+ }
+
+ return;
+
+error:
+ isc__nm_failed_connect_cb(sock, req, result, false);
+}
+
+void
+isc_nm_tlsdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
+ isc_nm_cb_t cb, void *cbarg, unsigned int timeout,
+ size_t extrahandlesize, isc_tlsctx_t *sslctx,
+ isc_tlsctx_client_session_cache_t *client_sess_cache) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_nmsocket_t *sock = NULL;
+ isc__netievent_tlsdnsconnect_t *ievent = NULL;
+ isc__nm_uvreq_t *req = NULL;
+ sa_family_t sa_family;
+
+ REQUIRE(VALID_NM(mgr));
+ REQUIRE(local != NULL);
+ REQUIRE(peer != NULL);
+ REQUIRE(sslctx != NULL);
+
+ sa_family = peer->type.sa.sa_family;
+
+ sock = isc_mem_get(mgr->mctx, sizeof(*sock));
+ isc__nmsocket_init(sock, mgr, isc_nm_tlsdnssocket, local);
+
+ sock->extrahandlesize = extrahandlesize;
+ sock->connect_timeout = timeout;
+ sock->result = ISC_R_UNSET;
+ isc_tlsctx_attach(sslctx, &sock->tls.ctx);
+ atomic_init(&sock->client, true);
+ atomic_init(&sock->connecting, true);
+
+ req = isc__nm_uvreq_get(mgr, sock);
+ req->cb.connect = cb;
+ req->cbarg = cbarg;
+ req->peer = *peer;
+ req->local = *local;
+ req->handle = isc__nmhandle_get(sock, &req->peer, &sock->iface);
+
+ if (client_sess_cache != NULL) {
+ INSIST(isc_tlsctx_client_session_cache_getctx(
+ client_sess_cache) == sslctx);
+ isc_tlsctx_client_session_cache_attach(
+ client_sess_cache, &sock->tls.client_sess_cache);
+ }
+
+ result = isc__nm_socket(sa_family, SOCK_STREAM, 0, &sock->fd);
+ if (result != ISC_R_SUCCESS) {
+ goto failure;
+ }
+
+ if (isc__nm_closing(sock)) {
+ result = ISC_R_SHUTTINGDOWN;
+ goto failure;
+ }
+
+ (void)isc__nm_socket_min_mtu(sock->fd, sa_family);
+ (void)isc__nm_socket_tcp_maxseg(sock->fd, NM_MAXSEG);
+
+ /* 2 minute timeout */
+ result = isc__nm_socket_connectiontimeout(sock->fd, 120 * 1000);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ ievent = isc__nm_get_netievent_tlsdnsconnect(mgr, sock, req);
+
+ if (isc__nm_in_netthread()) {
+ atomic_store(&sock->active, true);
+ sock->tid = isc_nm_tid();
+ isc__nm_async_tlsdnsconnect(&mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+ isc__nm_put_netievent_tlsdnsconnect(mgr, ievent);
+ } else {
+ atomic_init(&sock->active, false);
+ sock->tid = isc_random_uniform(mgr->nworkers);
+ isc__nm_enqueue_ievent(&mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+ }
+ LOCK(&sock->lock);
+ while (sock->result == ISC_R_UNSET) {
+ WAIT(&sock->cond, &sock->lock);
+ }
+ atomic_store(&sock->active, true);
+ BROADCAST(&sock->scond);
+ UNLOCK(&sock->lock);
+ return;
+
+failure:
+ if (isc__nm_in_netthread()) {
+ sock->tid = isc_nm_tid();
+ }
+
+ atomic_compare_exchange_enforced(&sock->connecting, &(bool){ true },
+ false);
+ isc__nmsocket_clearcb(sock);
+ isc__nm_connectcb(sock, req, result, true);
+ atomic_store(&sock->closed, true);
+ isc__nmsocket_detach(&sock);
+}
+
+static uv_os_sock_t
+isc__nm_tlsdns_lb_socket(isc_nm_t *mgr, sa_family_t sa_family) {
+ isc_result_t result;
+ uv_os_sock_t sock;
+
+ result = isc__nm_socket(sa_family, SOCK_STREAM, 0, &sock);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ (void)isc__nm_socket_incoming_cpu(sock);
+ (void)isc__nm_socket_v6only(sock, sa_family);
+
+ /* FIXME: set mss */
+
+ result = isc__nm_socket_reuse(sock);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ if (mgr->load_balance_sockets) {
+ result = isc__nm_socket_reuse_lb(sock);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ }
+
+ return (sock);
+}
+
+static void
+start_tlsdns_child(isc_nm_t *mgr, isc_sockaddr_t *iface, isc_nmsocket_t *sock,
+ uv_os_sock_t fd, int tid) {
+ isc__netievent_tlsdnslisten_t *ievent = NULL;
+ isc_nmsocket_t *csock = &sock->children[tid];
+
+ isc__nmsocket_init(csock, mgr, isc_nm_tlsdnssocket, iface);
+ csock->parent = sock;
+ csock->accept_cb = sock->accept_cb;
+ csock->accept_cbarg = sock->accept_cbarg;
+ csock->recv_cb = sock->recv_cb;
+ csock->recv_cbarg = sock->recv_cbarg;
+ csock->extrahandlesize = sock->extrahandlesize;
+ csock->backlog = sock->backlog;
+ csock->tid = tid;
+ isc_tlsctx_attach(sock->tls.ctx, &csock->tls.ctx);
+
+ /*
+ * We don't attach to quota, just assign - to avoid
+ * increasing quota unnecessarily.
+ */
+ csock->pquota = sock->pquota;
+ isc_quota_cb_init(&csock->quotacb, quota_accept_cb, csock);
+
+ if (mgr->load_balance_sockets) {
+ UNUSED(fd);
+ csock->fd = isc__nm_tlsdns_lb_socket(mgr,
+ iface->type.sa.sa_family);
+ } else {
+ csock->fd = dup(fd);
+ }
+ REQUIRE(csock->fd >= 0);
+
+ ievent = isc__nm_get_netievent_tlsdnslisten(mgr, csock);
+ isc__nm_maybe_enqueue_ievent(&mgr->workers[tid],
+ (isc__netievent_t *)ievent);
+}
+
+static void
+enqueue_stoplistening(isc_nmsocket_t *sock) {
+ isc__netievent_tlsdnsstop_t *ievent =
+ isc__nm_get_netievent_tlsdnsstop(sock->mgr, sock);
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+}
+
+isc_result_t
+isc_nm_listentlsdns(isc_nm_t *mgr, isc_sockaddr_t *iface,
+ isc_nm_recv_cb_t recv_cb, void *recv_cbarg,
+ isc_nm_accept_cb_t accept_cb, void *accept_cbarg,
+ size_t extrahandlesize, int backlog, isc_quota_t *quota,
+ isc_tlsctx_t *sslctx, isc_nmsocket_t **sockp) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_nmsocket_t *sock = NULL;
+ size_t children_size = 0;
+ uv_os_sock_t fd = -1;
+
+ REQUIRE(VALID_NM(mgr));
+
+ sock = isc_mem_get(mgr->mctx, sizeof(*sock));
+ isc__nmsocket_init(sock, mgr, isc_nm_tlsdnslistener, iface);
+
+ atomic_init(&sock->rchildren, 0);
+ sock->nchildren = mgr->nworkers;
+ children_size = sock->nchildren * sizeof(sock->children[0]);
+ sock->children = isc_mem_get(mgr->mctx, children_size);
+ memset(sock->children, 0, children_size);
+
+ sock->result = ISC_R_UNSET;
+ sock->accept_cb = accept_cb;
+ sock->accept_cbarg = accept_cbarg;
+ sock->recv_cb = recv_cb;
+ sock->recv_cbarg = recv_cbarg;
+ sock->extrahandlesize = extrahandlesize;
+ sock->backlog = backlog;
+ sock->pquota = quota;
+
+ isc_tlsctx_attach(sslctx, &sock->tls.ctx);
+
+ sock->tid = 0;
+ sock->fd = -1;
+
+ if (!mgr->load_balance_sockets) {
+ fd = isc__nm_tlsdns_lb_socket(mgr, iface->type.sa.sa_family);
+ }
+
+ isc_barrier_init(&sock->startlistening, sock->nchildren);
+
+ for (size_t i = 0; i < sock->nchildren; i++) {
+ if ((int)i == isc_nm_tid()) {
+ continue;
+ }
+ start_tlsdns_child(mgr, iface, sock, fd, i);
+ }
+
+ if (isc__nm_in_netthread()) {
+ start_tlsdns_child(mgr, iface, sock, fd, isc_nm_tid());
+ }
+
+ if (!mgr->load_balance_sockets) {
+ isc__nm_closesocket(fd);
+ }
+
+ LOCK(&sock->lock);
+ while (atomic_load(&sock->rchildren) != sock->nchildren) {
+ WAIT(&sock->cond, &sock->lock);
+ }
+ result = sock->result;
+ atomic_store(&sock->active, true);
+ UNLOCK(&sock->lock);
+
+ INSIST(result != ISC_R_UNSET);
+
+ if (result == ISC_R_SUCCESS) {
+ REQUIRE(atomic_load(&sock->rchildren) == sock->nchildren);
+ *sockp = sock;
+ } else {
+ atomic_store(&sock->active, false);
+ enqueue_stoplistening(sock);
+ isc_nmsocket_close(&sock);
+ }
+
+ return (result);
+}
+
+void
+isc__nm_async_tlsdnslisten(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tlsdnslisten_t *ievent =
+ (isc__netievent_tlsdnslisten_t *)ev0;
+ sa_family_t sa_family;
+ int r;
+ int flags = 0;
+ isc_nmsocket_t *sock = NULL;
+ isc_result_t result = ISC_R_UNSET;
+ isc_nm_t *mgr;
+
+ REQUIRE(VALID_NMSOCK(ievent->sock));
+ REQUIRE(ievent->sock->tid == isc_nm_tid());
+ REQUIRE(VALID_NMSOCK(ievent->sock->parent));
+
+ sock = ievent->sock;
+ sa_family = sock->iface.type.sa.sa_family;
+ mgr = sock->mgr;
+
+ REQUIRE(sock->type == isc_nm_tlsdnssocket);
+ REQUIRE(sock->parent != NULL);
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ (void)isc__nm_socket_min_mtu(sock->fd, sa_family);
+ (void)isc__nm_socket_tcp_maxseg(sock->fd, NM_MAXSEG);
+
+ r = uv_tcp_init(&worker->loop, &sock->uv_handle.tcp);
+ UV_RUNTIME_CHECK(uv_tcp_init, r);
+ uv_handle_set_data(&sock->uv_handle.handle, sock);
+ /* This keeps the socket alive after everything else is gone */
+ isc__nmsocket_attach(sock, &(isc_nmsocket_t *){ NULL });
+
+ r = uv_timer_init(&worker->loop, &sock->read_timer);
+ UV_RUNTIME_CHECK(uv_timer_init, r);
+ uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock);
+
+ LOCK(&sock->parent->lock);
+
+ r = uv_tcp_open(&sock->uv_handle.tcp, sock->fd);
+ if (r < 0) {
+ isc__nm_closesocket(sock->fd);
+ isc__nm_incstats(sock, STATID_OPENFAIL);
+ goto done;
+ }
+ isc__nm_incstats(sock, STATID_OPEN);
+
+ if (sa_family == AF_INET6) {
+ flags = UV_TCP_IPV6ONLY;
+ }
+
+ if (mgr->load_balance_sockets) {
+ r = isc_uv_tcp_freebind(&sock->uv_handle.tcp,
+ &sock->iface.type.sa, flags);
+ if (r < 0) {
+ isc__nm_incstats(sock, STATID_BINDFAIL);
+ goto done;
+ }
+ } else {
+ if (sock->parent->fd == -1) {
+ r = isc_uv_tcp_freebind(&sock->uv_handle.tcp,
+ &sock->iface.type.sa, flags);
+ if (r < 0) {
+ isc__nm_incstats(sock, STATID_BINDFAIL);
+ goto done;
+ }
+ sock->parent->uv_handle.tcp.flags =
+ sock->uv_handle.tcp.flags;
+ sock->parent->fd = sock->fd;
+ } else {
+ /* The socket is already bound, just copy the flags */
+ sock->uv_handle.tcp.flags =
+ sock->parent->uv_handle.tcp.flags;
+ }
+ }
+
+ isc__nm_set_network_buffers(sock->mgr, &sock->uv_handle.handle);
+
+ /*
+ * The callback will run in the same thread uv_listen() was
+ * called from, so a race with tlsdns_connection_cb() isn't
+ * possible.
+ */
+ r = uv_listen((uv_stream_t *)&sock->uv_handle.tcp, sock->backlog,
+ tlsdns_connection_cb);
+ if (r != 0) {
+ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_NETMGR, ISC_LOG_ERROR,
+ "uv_listen failed: %s",
+ isc_result_totext(isc__nm_uverr2result(r)));
+ isc__nm_incstats(sock, STATID_BINDFAIL);
+ goto done;
+ }
+
+ atomic_store(&sock->listening, true);
+
+done:
+ result = isc__nm_uverr2result(r);
+ if (result != ISC_R_SUCCESS) {
+ sock->pquota = NULL;
+ }
+
+ atomic_fetch_add(&sock->parent->rchildren, 1);
+ if (sock->parent->result == ISC_R_UNSET) {
+ sock->parent->result = result;
+ }
+ SIGNAL(&sock->parent->cond);
+ UNLOCK(&sock->parent->lock);
+
+ isc_barrier_wait(&sock->parent->startlistening);
+}
+
+static void
+tlsdns_connection_cb(uv_stream_t *server, int status) {
+ isc_nmsocket_t *ssock = uv_handle_get_data((uv_handle_t *)server);
+ isc_result_t result;
+ isc_quota_t *quota = NULL;
+
+ if (status != 0) {
+ result = isc__nm_uverr2result(status);
+ goto done;
+ }
+
+ REQUIRE(VALID_NMSOCK(ssock));
+ REQUIRE(ssock->tid == isc_nm_tid());
+
+ if (isc__nmsocket_closing(ssock)) {
+ result = ISC_R_CANCELED;
+ goto done;
+ }
+
+ if (ssock->pquota != NULL) {
+ result = isc_quota_attach_cb(ssock->pquota, &quota,
+ &ssock->quotacb);
+ if (result == ISC_R_QUOTA) {
+ isc__nm_incstats(ssock, STATID_ACCEPTFAIL);
+ goto done;
+ }
+ }
+
+ result = accept_connection(ssock, quota);
+done:
+ isc__nm_accept_connection_log(result, can_log_tlsdns_quota());
+}
+
+void
+isc__nm_tlsdns_stoplistening(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_tlsdnslistener);
+
+ if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false },
+ true))
+ {
+ UNREACHABLE();
+ }
+
+ if (!isc__nm_in_netthread()) {
+ enqueue_stoplistening(sock);
+ } else {
+ stop_tlsdns_parent(sock);
+ }
+}
+
+static void
+tls_shutdown(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+
+ isc__netievent_tlsdnsshutdown_t *ievent =
+ isc__nm_get_netievent_tlsdnsshutdown(sock->mgr, sock);
+ isc__nm_maybe_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+}
+
+void
+isc__nm_async_tlsdnsshutdown(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tlsdnsshutdown_t *ievent =
+ (isc__netievent_tlsdnsshutdown_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+ int rv;
+ int err;
+ isc_result_t result;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_NMSOCK(ievent->sock));
+
+ if (sock->tls.state != TLS_STATE_IO) {
+ /* Nothing to do */
+ return;
+ }
+
+ rv = SSL_shutdown(sock->tls.tls);
+
+ if (rv == 1) {
+ sock->tls.state = TLS_STATE_NONE;
+ /* FIXME: continue closing the socket */
+ return;
+ }
+
+ if (rv == 0) {
+ result = tls_cycle(sock);
+ if (result != ISC_R_SUCCESS) {
+ tls_error(sock, result);
+ return;
+ }
+
+ /* Reschedule closing the socket */
+ tls_shutdown(sock);
+ return;
+ }
+
+ err = SSL_get_error(sock->tls.tls, rv);
+
+ switch (err) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ result = tls_cycle(sock);
+ if (result != ISC_R_SUCCESS) {
+ tls_error(sock, result);
+ return;
+ }
+
+ /* Reschedule closing the socket */
+ tls_shutdown(sock);
+ return;
+ case 0:
+ UNREACHABLE();
+ case SSL_ERROR_ZERO_RETURN:
+ tls_error(sock, ISC_R_EOF);
+ break;
+ default:
+ tls_error(sock, ISC_R_TLSERROR);
+ }
+ return;
+}
+
+void
+isc__nm_async_tlsdnsstop(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tlsdnsstop_t *ievent =
+ (isc__netievent_tlsdnsstop_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ if (sock->parent != NULL) {
+ stop_tlsdns_child(sock);
+ return;
+ }
+
+ stop_tlsdns_parent(sock);
+}
+
+void
+isc__nm_tlsdns_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result,
+ bool async) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(result != ISC_R_SUCCESS);
+
+ isc__nmsocket_timer_stop(sock);
+ isc__nm_stop_reading(sock);
+
+ if (sock->tls.pending_req != NULL) {
+ isc_result_t failure_result = ISC_R_CANCELED;
+ isc__nm_uvreq_t *req = sock->tls.pending_req;
+ sock->tls.pending_req = NULL;
+
+ if (peer_verification_has_failed(sock)) {
+ /*
+ * Save error message as 'sock->tls' will get detached.
+ */
+ sock->tls.tls_verify_errmsg =
+ isc_tls_verify_peer_result_string(
+ sock->tls.tls);
+ failure_result = ISC_R_TLSBADPEERCERT;
+ }
+ isc__nm_failed_connect_cb(sock, req, failure_result, async);
+ }
+
+ if (!sock->recv_read) {
+ goto destroy;
+ }
+ sock->recv_read = false;
+
+ if (sock->recv_cb != NULL) {
+ isc__nm_uvreq_t *req = isc__nm_get_read_req(sock, NULL);
+ isc__nmsocket_clearcb(sock);
+ isc__nm_readcb(sock, req, result);
+ }
+
+destroy:
+ call_pending_send_callbacks(sock, result);
+ isc__nmsocket_prep_destroy(sock);
+
+ /*
+ * We need to detach from quota after the read callback function
+ * had a chance to be executed.
+ */
+ if (sock->quota != NULL) {
+ isc_quota_detach(&sock->quota);
+ }
+}
+
+void
+isc__nm_tlsdns_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ isc_nmsocket_t *sock = handle->sock;
+ isc__netievent_tlsdnsread_t *ievent = NULL;
+
+ REQUIRE(sock->type == isc_nm_tlsdnssocket);
+ REQUIRE(sock->statichandle == handle);
+
+ sock->recv_cb = cb;
+ sock->recv_cbarg = cbarg;
+ sock->recv_read = true;
+ if (sock->read_timeout == 0) {
+ sock->read_timeout =
+ (atomic_load(&sock->keepalive)
+ ? atomic_load(&sock->mgr->keepalive)
+ : atomic_load(&sock->mgr->idle));
+ }
+
+ ievent = isc__nm_get_netievent_tlsdnsread(sock->mgr, sock);
+
+ /*
+ * This MUST be done asynchronously, no matter which thread
+ * we're in. The callback function for isc_nm_read() often calls
+ * isc_nm_read() again; if we tried to do that synchronously
+ * we'd clash in processbuffer() and grow the stack
+ * indefinitely.
+ */
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+
+ return;
+}
+
+void
+isc__nm_async_tlsdnsread(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tlsdnsread_t *ievent =
+ (isc__netievent_tlsdnsread_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ if (isc__nmsocket_closing(sock)) {
+ atomic_store(&sock->reading, true);
+ isc__nm_failed_read_cb(sock, ISC_R_CANCELED, false);
+ return;
+ }
+
+ result = tls_cycle(sock);
+ if (result != ISC_R_SUCCESS) {
+ isc__nm_failed_read_cb(sock, result, false);
+ }
+}
+
+/*
+ * Process a single packet from the incoming buffer.
+ *
+ * Return ISC_R_SUCCESS and attach 'handlep' to a handle if something
+ * was processed; return ISC_R_NOMORE if there isn't a full message
+ * to be processed.
+ *
+ * The caller will need to unreference the handle.
+ */
+isc_result_t
+isc__nm_tlsdns_processbuffer(isc_nmsocket_t *sock) {
+ size_t len;
+ isc__nm_uvreq_t *req = NULL;
+ isc_nmhandle_t *handle = NULL;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ if (isc__nmsocket_closing(sock)) {
+ return (ISC_R_CANCELED);
+ }
+
+ /*
+ * If we don't even have the length yet, we can't do
+ * anything.
+ */
+ if (sock->buf_len < 2) {
+ return (ISC_R_NOMORE);
+ }
+
+ /*
+ * Process the first packet from the buffer, leaving
+ * the rest (if any) for later.
+ */
+ len = ntohs(*(uint16_t *)sock->buf);
+ if (len > sock->buf_len - 2) {
+ return (ISC_R_NOMORE);
+ }
+
+ if (sock->recv_cb == NULL) {
+ /*
+ * recv_cb has been cleared - there is
+ * nothing to do
+ */
+ return (ISC_R_CANCELED);
+ } else if (sock->statichandle == NULL &&
+ sock->tls.state == TLS_STATE_IO &&
+ atomic_load(&sock->connected) &&
+ !atomic_load(&sock->connecting))
+ {
+ /*
+ * It seems that some unexpected data (a DNS message) has
+ * arrived while we are wrapping up.
+ */
+ return (ISC_R_CANCELED);
+ }
+
+ req = isc__nm_get_read_req(sock, NULL);
+ REQUIRE(VALID_UVREQ(req));
+
+ /*
+ * We need to launch isc__nm_resume_processing() after the buffer
+ * has been consumed, thus we must delay detaching the handle.
+ */
+ isc_nmhandle_attach(req->handle, &handle);
+
+ /*
+ * The callback will be called synchronously because the
+ * result is ISC_R_SUCCESS, so we don't need to have
+ * the buffer on the heap
+ */
+ req->uvbuf.base = (char *)sock->buf + 2;
+ req->uvbuf.len = len;
+
+ /*
+ * If isc__nm_tlsdns_read() was called, it will be satisfied by
+ * single DNS message in the next call.
+ */
+ sock->recv_read = false;
+
+ /*
+ * An assertion failure here means that there's an erroneous
+ * extra nmhandle detach happening in the callback and
+ * isc__nm_resume_processing() is called while we're
+ * processing the buffer.
+ */
+ REQUIRE(sock->processing == false);
+ sock->processing = true;
+ isc__nm_readcb(sock, req, ISC_R_SUCCESS);
+ sock->processing = false;
+
+ len += 2;
+ sock->buf_len -= len;
+ if (sock->buf_len > 0) {
+ memmove(sock->buf, sock->buf + len, sock->buf_len);
+ }
+
+ isc_nmhandle_detach(&handle);
+
+ if (isc__nmsocket_closing(sock)) {
+ tlsdns_set_tls_shutdown(sock->tls.tls);
+ tlsdns_keep_client_tls_session(sock);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+tls_cycle_input(isc_nmsocket_t *sock) {
+ isc_result_t result = ISC_R_SUCCESS;
+ int err = 0;
+ int rv = 1;
+
+ if (sock->tls.state == TLS_STATE_IO) {
+ size_t len;
+
+ for (;;) {
+ (void)SSL_peek(sock->tls.tls, &(char){ '\0' }, 0);
+
+ int pending = SSL_pending(sock->tls.tls);
+ if (pending > (int)ISC_NETMGR_TCP_RECVBUF_SIZE) {
+ pending = (int)ISC_NETMGR_TCP_RECVBUF_SIZE;
+ }
+
+ if (pending != 0) {
+ if ((sock->buf_len + pending) > sock->buf_size)
+ {
+ isc__nm_alloc_dnsbuf(
+ sock, sock->buf_len + pending);
+ }
+
+ len = 0;
+ rv = SSL_read_ex(sock->tls.tls,
+ sock->buf + sock->buf_len,
+ sock->buf_size - sock->buf_len,
+ &len);
+ if (rv != 1) {
+ /*
+ * Process what's in the buffer so far
+ */
+ result = isc__nm_process_sock_buffer(
+ sock);
+ if (result != ISC_R_SUCCESS) {
+ goto failure;
+ }
+ /*
+ * FIXME: Should we call
+ * isc__nm_failed_read_cb()?
+ */
+ break;
+ }
+
+ INSIST((size_t)pending == len);
+
+ sock->buf_len += len;
+ }
+ result = isc__nm_process_sock_buffer(sock);
+ if (result != ISC_R_SUCCESS) {
+ goto failure;
+ }
+
+ if (pending == 0) {
+ break;
+ }
+ }
+ } else if (!SSL_is_init_finished(sock->tls.tls)) {
+ if (SSL_is_server(sock->tls.tls)) {
+ rv = SSL_accept(sock->tls.tls);
+ } else {
+ rv = SSL_connect(sock->tls.tls);
+ }
+
+ } else {
+ rv = 1;
+ }
+
+ if (rv <= 0) {
+ err = SSL_get_error(sock->tls.tls, rv);
+ }
+
+ switch (err) {
+ case SSL_ERROR_WANT_READ:
+ if (sock->tls.state == TLS_STATE_NONE &&
+ !SSL_is_init_finished(sock->tls.tls))
+ {
+ sock->tls.state = TLS_STATE_HANDSHAKE;
+ result = isc__nm_process_sock_buffer(sock);
+ if (result != ISC_R_SUCCESS) {
+ goto failure;
+ }
+ }
+ /* else continue reading */
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ async_tlsdns_cycle(sock);
+ break;
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ /* Continue reading/writing */
+ break;
+ case 0:
+ /* Everything is ok, continue */
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ return (ISC_R_EOF);
+ default:
+ return (ISC_R_TLSERROR);
+ }
+
+ /* Stop state after handshake */
+ if (sock->tls.state == TLS_STATE_HANDSHAKE &&
+ SSL_is_init_finished(sock->tls.tls))
+ {
+ const unsigned char *alpn = NULL;
+ unsigned int alpnlen = 0;
+
+ isc__nmsocket_log_tls_session_reuse(sock, sock->tls.tls);
+
+ isc_tls_get_selected_alpn(sock->tls.tls, &alpn, &alpnlen);
+ if (alpn != NULL && alpnlen == ISC_TLS_DOT_PROTO_ALPN_ID_LEN &&
+ memcmp(ISC_TLS_DOT_PROTO_ALPN_ID, alpn,
+ ISC_TLS_DOT_PROTO_ALPN_ID_LEN) == 0)
+ {
+ sock->tls.alpn_negotiated = true;
+ }
+
+ sock->tls.state = TLS_STATE_IO;
+
+ if (SSL_is_server(sock->tls.tls)) {
+ REQUIRE(sock->recv_handle != NULL);
+ result = sock->accept_cb(sock->recv_handle,
+ ISC_R_SUCCESS,
+ sock->accept_cbarg);
+
+ if (result != ISC_R_SUCCESS) {
+ isc_nmhandle_detach(&sock->recv_handle);
+ goto failure;
+ }
+ } else {
+ isc__nm_uvreq_t *req = sock->tls.pending_req;
+ sock->tls.pending_req = NULL;
+
+ isc__nmsocket_timer_stop(sock);
+ uv_handle_set_data((uv_handle_t *)&sock->read_timer,
+ sock);
+
+ atomic_compare_exchange_enforced(
+ &sock->connecting, &(bool){ true }, false);
+ isc__nm_connectcb(sock, req, ISC_R_SUCCESS, true);
+ }
+ async_tlsdns_cycle(sock);
+ }
+failure:
+ return (result);
+}
+
+static void
+tls_error(isc_nmsocket_t *sock, isc_result_t result) {
+ switch (sock->tls.state) {
+ case TLS_STATE_HANDSHAKE:
+ case TLS_STATE_IO:
+ if (atomic_load(&sock->connecting)) {
+ isc__nm_uvreq_t *req = sock->tls.pending_req;
+ sock->tls.pending_req = NULL;
+
+ isc__nm_failed_connect_cb(sock, req, result, false);
+ } else {
+ isc__nm_tlsdns_failed_read_cb(sock, result, false);
+ }
+ break;
+ case TLS_STATE_ERROR:
+ return;
+ default:
+ break;
+ }
+
+ sock->tls.state = TLS_STATE_ERROR;
+ sock->tls.pending_error = result;
+
+ isc__nmsocket_shutdown(sock);
+}
+
+static void
+call_pending_send_callbacks(isc_nmsocket_t *sock, const isc_result_t result) {
+ isc__nm_uvreq_t *cbreq = ISC_LIST_HEAD(sock->tls.sendreqs);
+ while (cbreq != NULL) {
+ isc__nm_uvreq_t *next = ISC_LIST_NEXT(cbreq, link);
+ ISC_LIST_UNLINK(sock->tls.sendreqs, cbreq, link);
+ INSIST(sock == cbreq->handle->sock);
+ isc__nm_sendcb(sock, cbreq, result, false);
+ cbreq = next;
+ }
+}
+
+static void
+free_senddata(isc_nmsocket_t *sock, const isc_result_t result) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tls.senddata.base != NULL);
+ REQUIRE(sock->tls.senddata.length > 0);
+
+ isc_mem_put(sock->mgr->mctx, sock->tls.senddata.base,
+ sock->tls.senddata.length);
+ sock->tls.senddata.base = NULL;
+ sock->tls.senddata.length = 0;
+
+ call_pending_send_callbacks(sock, result);
+}
+
+static void
+tls_write_cb(uv_write_t *req, int status) {
+ isc_result_t result = status != 0 ? isc__nm_uverr2result(status)
+ : ISC_R_SUCCESS;
+ isc__nm_uvreq_t *uvreq = (isc__nm_uvreq_t *)req->data;
+ isc_nmsocket_t *sock = uvreq->sock;
+
+ isc_nm_timer_stop(uvreq->timer);
+ isc_nm_timer_detach(&uvreq->timer);
+
+ free_senddata(sock, result);
+
+ isc__nm_uvreq_put(&uvreq, sock);
+
+ if (status != 0) {
+ tls_error(sock, result);
+ return;
+ }
+
+ result = tls_cycle(sock);
+ if (result != ISC_R_SUCCESS) {
+ tls_error(sock, result);
+ return;
+ }
+}
+
+static isc_result_t
+tls_cycle_output(isc_nmsocket_t *sock) {
+ isc_result_t result = ISC_R_SUCCESS;
+ int pending;
+
+ while ((pending = BIO_pending(sock->tls.app_rbio)) > 0) {
+ isc__nm_uvreq_t *req = NULL;
+ size_t bytes;
+ int rv;
+ int r;
+
+ if (sock->tls.senddata.base != NULL ||
+ sock->tls.senddata.length > 0)
+ {
+ break;
+ }
+
+ if (pending > (int)ISC_NETMGR_TCP_RECVBUF_SIZE) {
+ pending = (int)ISC_NETMGR_TCP_RECVBUF_SIZE;
+ }
+
+ sock->tls.senddata.base = isc_mem_get(sock->mgr->mctx, pending);
+ sock->tls.senddata.length = pending;
+
+ /* It's a bit misnomer here, but it does the right thing */
+ req = isc__nm_get_read_req(sock, NULL);
+ req->uvbuf.base = (char *)sock->tls.senddata.base;
+ req->uvbuf.len = sock->tls.senddata.length;
+
+ rv = BIO_read_ex(sock->tls.app_rbio, req->uvbuf.base,
+ req->uvbuf.len, &bytes);
+
+ RUNTIME_CHECK(rv == 1);
+ INSIST((size_t)pending == bytes);
+
+ r = uv_try_write(&sock->uv_handle.stream, &req->uvbuf, 1);
+
+ if (r == pending) {
+ /* Wrote everything, restart */
+ isc__nm_uvreq_put(&req, sock);
+ free_senddata(sock, ISC_R_SUCCESS);
+ continue;
+ }
+
+ if (r > 0) {
+ /* Partial write, send rest asynchronously */
+ memmove(req->uvbuf.base, req->uvbuf.base + r,
+ req->uvbuf.len - r);
+ req->uvbuf.len = req->uvbuf.len - r;
+ } else if (r == UV_ENOSYS || r == UV_EAGAIN) {
+ /* uv_try_write is not supported, send
+ * asynchronously */
+ } else {
+ result = isc__nm_uverr2result(r);
+ isc__nm_uvreq_put(&req, sock);
+ free_senddata(sock, result);
+ break;
+ }
+
+ r = uv_write(&req->uv_req.write, &sock->uv_handle.stream,
+ &req->uvbuf, 1, tls_write_cb);
+ if (r < 0) {
+ result = isc__nm_uverr2result(r);
+ isc__nm_uvreq_put(&req, sock);
+ free_senddata(sock, result);
+ break;
+ }
+
+ isc_nm_timer_create(req->handle, isc__nmsocket_writetimeout_cb,
+ req, &req->timer);
+ if (sock->write_timeout > 0) {
+ isc_nm_timer_start(req->timer, sock->write_timeout);
+ }
+
+ break;
+ }
+
+ return (result);
+}
+
+static isc_result_t
+tls_pop_error(isc_nmsocket_t *sock) {
+ isc_result_t result;
+
+ if (sock->tls.state != TLS_STATE_ERROR) {
+ return (ISC_R_SUCCESS);
+ }
+
+ if (sock->tls.pending_error == ISC_R_SUCCESS) {
+ return (ISC_R_TLSERROR);
+ }
+
+ result = sock->tls.pending_error;
+ sock->tls.pending_error = ISC_R_SUCCESS;
+
+ return (result);
+}
+
+static isc_result_t
+tls_cycle(isc_nmsocket_t *sock) {
+ isc_result_t result;
+
+ /*
+ * Clear the TLS error queue so that SSL_get_error() and SSL I/O
+ * routine calls will not get affected by prior error statuses.
+ *
+ * See here:
+ * https://www.openssl.org/docs/man3.0/man3/SSL_get_error.html
+ *
+ * In particular, it mentions the following:
+ *
+ * The current thread's error queue must be empty before the
+ * TLS/SSL I/O operation is attempted, or SSL_get_error() will not
+ * work reliably.
+ *
+ * As we use the result of SSL_get_error() to decide on I/O
+ * operations, we need to ensure that it works reliably by
+ * cleaning the error queue.
+ *
+ * The sum of details: https://stackoverflow.com/a/37980911
+ */
+ ERR_clear_error();
+
+ if (isc__nmsocket_closing(sock)) {
+ return (ISC_R_CANCELED);
+ }
+
+ result = tls_pop_error(sock);
+ if (result != ISC_R_SUCCESS) {
+ goto done;
+ }
+
+ if (sock->tls.cycle) {
+ return (ISC_R_SUCCESS);
+ }
+
+ sock->tls.cycle = true;
+ result = tls_cycle_input(sock);
+ if (result != ISC_R_SUCCESS) {
+ goto done;
+ }
+
+ result = tls_cycle_output(sock);
+ if (result != ISC_R_SUCCESS) {
+ goto done;
+ }
+done:
+ sock->tls.cycle = false;
+
+ return (result);
+}
+
+static void
+async_tlsdns_cycle(isc_nmsocket_t *sock) {
+ isc__netievent_tlsdnscycle_t *ievent = NULL;
+
+ REQUIRE(VALID_NMSOCK(sock));
+
+ /* Socket was closed midflight by isc__nm_tlsdns_shutdown() */
+ if (isc__nmsocket_closing(sock)) {
+ return;
+ }
+
+ ievent = isc__nm_get_netievent_tlsdnscycle(sock->mgr, sock);
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+}
+
+void
+isc__nm_async_tlsdnscycle(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tlsdnscycle_t *ievent =
+ (isc__netievent_tlsdnscycle_t *)ev0;
+ isc_result_t result;
+ isc_nmsocket_t *sock;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_NMSOCK(ievent->sock));
+ REQUIRE(ievent->sock->tid == isc_nm_tid());
+
+ sock = ievent->sock;
+
+ result = tls_cycle(sock);
+ if (result != ISC_R_SUCCESS) {
+ tls_error(sock, result);
+ }
+}
+
+void
+isc__nm_tlsdns_read_cb(uv_stream_t *stream, ssize_t nread,
+ const uv_buf_t *buf) {
+ isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)stream);
+ size_t len;
+ isc_result_t result;
+ int rv;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(atomic_load(&sock->reading));
+ REQUIRE(buf != NULL);
+
+ if (isc__nmsocket_closing(sock)) {
+ isc__nm_failed_read_cb(sock, ISC_R_CANCELED, true);
+ goto free;
+ }
+
+ if (nread < 0) {
+ if (nread != UV_EOF) {
+ isc__nm_incstats(sock, STATID_RECVFAIL);
+ }
+
+ isc__nm_failed_read_cb(sock, isc__nm_uverr2result(nread), true);
+
+ goto free;
+ }
+
+ if (!atomic_load(&sock->client)) {
+ sock->read_timeout = atomic_load(&sock->mgr->idle);
+ }
+
+ /*
+ * The input has to be fed into BIO
+ */
+ rv = BIO_write_ex(sock->tls.app_wbio, buf->base, (size_t)nread, &len);
+
+ if (rv <= 0 || (size_t)nread != len) {
+ isc__nm_failed_read_cb(sock, ISC_R_TLSERROR, true);
+ goto free;
+ }
+
+ result = tls_cycle(sock);
+ if (result != ISC_R_SUCCESS) {
+ isc__nm_failed_read_cb(sock, result, true);
+ }
+free:
+ async_tlsdns_cycle(sock);
+
+ if (nread < 0) {
+ /*
+ * The buffer may be a null buffer on error.
+ */
+ if (buf->base == NULL && buf->len == 0) {
+ return;
+ }
+ }
+
+ isc__nm_free_uvbuf(sock, buf);
+}
+
+static void
+quota_accept_cb(isc_quota_t *quota, void *sock0) {
+ isc_nmsocket_t *sock = (isc_nmsocket_t *)sock0;
+
+ REQUIRE(VALID_NMSOCK(sock));
+
+ /*
+ * Create a tlsdnsaccept event and pass it using the async
+ * channel.
+ */
+
+ isc__netievent_tlsdnsaccept_t *ievent =
+ isc__nm_get_netievent_tlsdnsaccept(sock->mgr, sock, quota);
+ isc__nm_maybe_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+}
+
+/*
+ * This is called after we get a quota_accept_cb() callback.
+ */
+void
+isc__nm_async_tlsdnsaccept(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tlsdnsaccept_t *ievent =
+ (isc__netievent_tlsdnsaccept_t *)ev0;
+ isc_result_t result;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_NMSOCK(ievent->sock));
+ REQUIRE(ievent->sock->tid == isc_nm_tid());
+
+ result = accept_connection(ievent->sock, ievent->quota);
+ isc__nm_accept_connection_log(result, can_log_tlsdns_quota());
+}
+
+static isc_result_t
+accept_connection(isc_nmsocket_t *ssock, isc_quota_t *quota) {
+ isc_nmsocket_t *csock = NULL;
+ isc__networker_t *worker = NULL;
+ int r;
+ isc_result_t result;
+ struct sockaddr_storage peer_ss;
+ struct sockaddr_storage local_ss;
+ isc_sockaddr_t local;
+
+ REQUIRE(VALID_NMSOCK(ssock));
+ REQUIRE(ssock->tid == isc_nm_tid());
+
+ if (isc__nmsocket_closing(ssock)) {
+ if (quota != NULL) {
+ isc_quota_detach(&quota);
+ }
+ return (ISC_R_CANCELED);
+ }
+
+ REQUIRE(ssock->accept_cb != NULL);
+
+ csock = isc_mem_get(ssock->mgr->mctx, sizeof(isc_nmsocket_t));
+ isc__nmsocket_init(csock, ssock->mgr, isc_nm_tlsdnssocket,
+ &ssock->iface);
+ csock->tid = ssock->tid;
+ csock->extrahandlesize = ssock->extrahandlesize;
+ isc__nmsocket_attach(ssock, &csock->server);
+ csock->accept_cb = ssock->accept_cb;
+ csock->accept_cbarg = ssock->accept_cbarg;
+ csock->recv_cb = ssock->recv_cb;
+ csock->recv_cbarg = ssock->recv_cbarg;
+ csock->quota = quota;
+ atomic_init(&csock->accepting, true);
+
+ worker = &csock->mgr->workers[csock->tid];
+
+ r = uv_tcp_init(&worker->loop, &csock->uv_handle.tcp);
+ UV_RUNTIME_CHECK(uv_tcp_init, r);
+ uv_handle_set_data(&csock->uv_handle.handle, csock);
+
+ r = uv_timer_init(&worker->loop, &csock->read_timer);
+ UV_RUNTIME_CHECK(uv_timer_init, r);
+ uv_handle_set_data((uv_handle_t *)&csock->read_timer, csock);
+
+ r = uv_accept(&ssock->uv_handle.stream, &csock->uv_handle.stream);
+ if (r != 0) {
+ result = isc__nm_uverr2result(r);
+ goto failure;
+ }
+
+ r = uv_tcp_getpeername(&csock->uv_handle.tcp,
+ (struct sockaddr *)&peer_ss,
+ &(int){ sizeof(peer_ss) });
+ if (r != 0) {
+ result = isc__nm_uverr2result(r);
+ goto failure;
+ }
+
+ result = isc_sockaddr_fromsockaddr(&csock->peer,
+ (struct sockaddr *)&peer_ss);
+ if (result != ISC_R_SUCCESS) {
+ goto failure;
+ }
+
+ r = uv_tcp_getsockname(&csock->uv_handle.tcp,
+ (struct sockaddr *)&local_ss,
+ &(int){ sizeof(local_ss) });
+ if (r != 0) {
+ result = isc__nm_uverr2result(r);
+ goto failure;
+ }
+
+ result = isc_sockaddr_fromsockaddr(&local,
+ (struct sockaddr *)&local_ss);
+ if (result != ISC_R_SUCCESS) {
+ goto failure;
+ }
+
+ csock->tls.state = TLS_STATE_NONE;
+
+ csock->tls.tls = isc_tls_create(ssock->tls.ctx);
+ RUNTIME_CHECK(csock->tls.tls != NULL);
+
+ r = BIO_new_bio_pair(&csock->tls.ssl_wbio, ISC_NETMGR_TCP_RECVBUF_SIZE,
+ &csock->tls.app_rbio, ISC_NETMGR_TCP_RECVBUF_SIZE);
+ RUNTIME_CHECK(r == 1);
+
+ r = BIO_new_bio_pair(&csock->tls.ssl_rbio, ISC_NETMGR_TCP_RECVBUF_SIZE,
+ &csock->tls.app_wbio, ISC_NETMGR_TCP_RECVBUF_SIZE);
+ RUNTIME_CHECK(r == 1);
+
+#if HAVE_SSL_SET0_RBIO && HAVE_SSL_SET0_WBIO
+ /*
+ * Note that if the rbio and wbio are the same then
+ * SSL_set0_rbio() and SSL_set0_wbio() each take ownership of
+ * one reference. Therefore it may be necessary to increment the
+ * number of references available using BIO_up_ref(3) before
+ * calling the set0 functions.
+ */
+ SSL_set0_rbio(csock->tls.tls, csock->tls.ssl_rbio);
+ SSL_set0_wbio(csock->tls.tls, csock->tls.ssl_wbio);
+#else
+ SSL_set_bio(csock->tls.tls, csock->tls.ssl_rbio, csock->tls.ssl_wbio);
+#endif
+
+ SSL_set_accept_state(csock->tls.tls);
+
+ /* FIXME: Set SSL_MODE_RELEASE_BUFFERS */
+
+ atomic_store(&csock->accepting, false);
+
+ isc__nm_incstats(csock, STATID_ACCEPT);
+
+ csock->read_timeout = atomic_load(&csock->mgr->init);
+
+ csock->closehandle_cb = isc__nm_resume_processing;
+
+ /*
+ * We need to keep the handle alive until we fail to read or
+ * connection is closed by the other side, it will be detached
+ * via prep_destroy()->tlsdns_close_direct().
+ *
+ * The handle will be either detached on acceptcb failure or in
+ * the readcb.
+ */
+ csock->recv_handle = isc__nmhandle_get(csock, NULL, &local);
+
+ /*
+ * The initial timer has been set, update the read timeout for
+ * the next reads.
+ */
+ csock->read_timeout = (atomic_load(&csock->keepalive)
+ ? atomic_load(&csock->mgr->keepalive)
+ : atomic_load(&csock->mgr->idle));
+
+ result = isc__nm_process_sock_buffer(csock);
+ if (result != ISC_R_SUCCESS) {
+ goto failure;
+ }
+
+ /*
+ * sock is now attached to the handle.
+ */
+ isc__nmsocket_detach(&csock);
+
+ return (ISC_R_SUCCESS);
+
+failure:
+ atomic_store(&csock->active, false);
+
+ isc__nm_failed_accept_cb(csock, result);
+
+ isc__nmsocket_prep_destroy(csock);
+
+ isc__nmsocket_detach(&csock);
+
+ return (result);
+}
+
+void
+isc__nm_tlsdns_send(isc_nmhandle_t *handle, isc_region_t *region,
+ isc_nm_cb_t cb, void *cbarg) {
+ isc__netievent_tlsdnssend_t *ievent = NULL;
+ isc__nm_uvreq_t *uvreq = NULL;
+ isc_nmsocket_t *sock = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ sock = handle->sock;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_tlsdnssocket);
+
+ uvreq = isc__nm_uvreq_get(sock->mgr, sock);
+ *(uint16_t *)uvreq->tcplen = htons(region->length);
+ uvreq->uvbuf.base = (char *)region->base;
+ uvreq->uvbuf.len = region->length;
+
+ isc_nmhandle_attach(handle, &uvreq->handle);
+
+ uvreq->cb.send = cb;
+ uvreq->cbarg = cbarg;
+
+ ievent = isc__nm_get_netievent_tlsdnssend(sock->mgr, sock, uvreq);
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+ return;
+}
+
+/*
+ * Handle 'tcpsend' async event - send a packet on the socket
+ */
+void
+isc__nm_async_tlsdnssend(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc_result_t result;
+ isc__netievent_tlsdnssend_t *ievent =
+ (isc__netievent_tlsdnssend_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+ isc__nm_uvreq_t *uvreq = ievent->req;
+
+ UNUSED(worker);
+
+ REQUIRE(sock->type == isc_nm_tlsdnssocket);
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ if (sock->write_timeout == 0) {
+ sock->write_timeout =
+ (atomic_load(&sock->keepalive)
+ ? atomic_load(&sock->mgr->keepalive)
+ : atomic_load(&sock->mgr->idle));
+ }
+
+ result = tlsdns_send_direct(sock, uvreq);
+ if (result != ISC_R_SUCCESS) {
+ isc__nm_incstats(sock, STATID_SENDFAIL);
+ isc__nm_failed_send_cb(sock, uvreq, result);
+ }
+}
+
+static void
+tlsdns_send_enqueue(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) {
+ isc__netievent_tlsdnssend_t *ievent =
+ isc__nm_get_netievent_tlsdnssend(sock->mgr, sock, req);
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+}
+
+static isc_result_t
+tlsdns_send_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) {
+ isc_result_t result;
+ int err = 0;
+ int rv;
+ size_t bytes = 0;
+ size_t sendlen;
+ isc__networker_t *worker = NULL;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(VALID_UVREQ(req));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(sock->type == isc_nm_tlsdnssocket);
+
+ result = tls_pop_error(sock);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ if (isc__nmsocket_closing(sock)) {
+ return (ISC_R_CANCELED);
+ }
+
+ /* Writes won't succeed until handshake end */
+ if (!SSL_is_init_finished(sock->tls.tls)) {
+ goto requeue;
+ }
+
+ /*
+ * Try to send any pending data before trying to call SSL_write_ex().
+ * Otherwise, it could fail with SSL_ERROR_WANT_WRITE error.
+ *
+ * It is important to stress that we want to avoid this happening
+ * due to how SSL_write_ex() works - mainly to avoid partial
+ * writes.
+ *
+ * Although the documentation for these functions is vague, it is
+ * not stated that partial writes are not possible. On the
+ * contrary, one can deduce that it is possible and recovering
+ * from this situation is complicated and unreasonably hard to
+ * implement to the point when it is better to avoid this
+ * situation altogether.
+ *
+ * In particular, the following can be found in the documentation:
+ *
+ * "The write functions will only return with success when the
+ * complete contents of buf of length num has been written. This
+ * default behaviour can be changed with the
+ * SSL_MODE_ENABLE_PARTIAL_WRITE option of
+ * SSL_CTX_set_mode(3). When this flag is set, the write functions
+ * will also return with success when a partial write has been
+ * successfully completed. In this case, the write function
+ * operation is considered completed. The bytes are sent, and a
+ * new write call with a new buffer (with the already sent bytes
+ * removed) must be started. A partial write is performed with the
+ * size of a message block, which is 16kB."
+
+ * That is, it is said that success is returned only when the
+ * complete chunk of data is written (encrypted), but it does not
+ * mention that partial writes are not possible (the behaviour can
+ * be changed using SSL_MODE_ENABLE_PARTIAL_WRITE). Another
+ * important aspect of this passage is that a partial write of up
+ * to 16 kilobytes can happen, and the call still can
+ * fail. Needless to say, this amount of data may include more
+ * than one DNS message.
+ *
+ * One could expect that SSL_write_ex() should return the number
+ * of bytes written, but no, that is not guaranteed (emphasis is
+ * mine): "*On success* SSL_write_ex() will store the number of
+ * bytes written in *written."
+ *
+ * Moreover, we can find the following guidance on how to handle
+ * the SSL_ERROR_WANT_WRITE error in the "Warnings" section of the
+ * documentation:
+
+ * "When a write function call has to be repeated because
+ * SSL_get_error(3) returned SSL_ERROR_WANT_READ or
+ * SSL_ERROR_WANT_WRITE, it must be repeated with the same
+ * arguments. The data that was passed might have been partially
+ * processed. When SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER was set
+ * using SSL_CTX_set_mode(3) the pointer can be different, but the
+ * data and length should still be the same."
+ *
+ * That is, when a call to SSL_write_ex() fails with
+ * SSL_ERROR_WANT_WRITE, we must attempt to make the next call to
+ * the function exactly with the same arguments. Of course, the
+ * code is structured in such a way that we cannot guarantee that
+ * (and keeping track of that would be unreasonably complicated to
+ * implement). The best we can do to avoid this error is to get
+ * (and send) the outgoing data from the SSL buffer ASAP before
+ * processing the subsequent write request. We can achieve that by
+ * calling tls_cycle() and rescheduling the write request for
+ * being processed later.
+ */
+ if (BIO_pending(sock->tls.app_rbio) > 0) {
+ /* Handle any pending data and requeue the write request. */
+ goto cycle;
+ }
+
+ /*
+ * There's no SSL_writev(), so we need to use a local buffer to
+ * assemble the whole message
+ */
+ worker = &sock->mgr->workers[sock->tid];
+ sendlen = req->uvbuf.len + sizeof(uint16_t);
+ memmove(worker->sendbuf, req->tcplen, sizeof(uint16_t));
+ memmove(worker->sendbuf + sizeof(uint16_t), req->uvbuf.base,
+ req->uvbuf.len);
+
+ rv = SSL_write_ex(sock->tls.tls, worker->sendbuf, sendlen, &bytes);
+ if (rv > 0) {
+ INSIST(sendlen == bytes);
+
+ ISC_LIST_APPEND(sock->tls.sendreqs, req, link);
+ async_tlsdns_cycle(sock);
+ return (ISC_R_SUCCESS);
+ }
+
+ /* Nothing was written, maybe enqueue? */
+ err = SSL_get_error(sock->tls.tls, rv);
+
+ switch (err) {
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_READ:
+ break;
+ case 0:
+ UNREACHABLE();
+ default:
+ return (ISC_R_TLSERROR);
+ }
+
+cycle:
+ result = tls_cycle(sock);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+requeue:
+ tlsdns_send_enqueue(sock, req);
+
+ return (result);
+}
+
+static void
+tlsdns_stop_cb(uv_handle_t *handle) {
+ isc_nmsocket_t *sock = uv_handle_get_data(handle);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(atomic_load(&sock->closing));
+
+ uv_handle_set_data(handle, NULL);
+
+ if (!atomic_compare_exchange_strong(&sock->closed, &(bool){ false },
+ true))
+ {
+ UNREACHABLE();
+ }
+
+ isc__nm_incstats(sock, STATID_CLOSE);
+
+ atomic_store(&sock->listening, false);
+
+ BIO_free_all(sock->tls.app_rbio);
+ BIO_free_all(sock->tls.app_wbio);
+
+ if (sock->tls.ctx != NULL) {
+ isc_tlsctx_free(&sock->tls.ctx);
+ }
+
+ isc__nmsocket_detach(&sock);
+}
+
+static void
+tlsdns_close_sock(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(atomic_load(&sock->closing));
+
+ if (!atomic_compare_exchange_strong(&sock->closed, &(bool){ false },
+ true))
+ {
+ UNREACHABLE();
+ }
+
+ isc__nm_incstats(sock, STATID_CLOSE);
+
+ if (sock->server != NULL) {
+ isc__nmsocket_detach(&sock->server);
+ }
+
+ atomic_store(&sock->connected, false);
+
+ if (sock->tls.tls != NULL) {
+ /*
+ * Let's shutdown the TLS session properly so that the session
+ * will remain resumable, if required.
+ */
+ tlsdns_set_tls_shutdown(sock->tls.tls);
+ tlsdns_keep_client_tls_session(sock);
+ isc_tls_free(&sock->tls.tls);
+ }
+
+ BIO_free_all(sock->tls.app_rbio);
+ BIO_free_all(sock->tls.app_wbio);
+
+ if (sock->tls.ctx != NULL) {
+ isc_tlsctx_free(&sock->tls.ctx);
+ }
+
+ isc__nmsocket_prep_destroy(sock);
+}
+
+static void
+tlsdns_close_cb(uv_handle_t *handle) {
+ isc_nmsocket_t *sock = uv_handle_get_data(handle);
+ uv_handle_set_data(handle, NULL);
+
+ tlsdns_close_sock(sock);
+}
+
+static void
+read_timer_close_cb(uv_handle_t *handle) {
+ isc_nmsocket_t *sock = uv_handle_get_data(handle);
+ uv_handle_set_data(handle, NULL);
+
+ REQUIRE(VALID_NMSOCK(sock));
+
+ if (sock->parent) {
+ uv_close(&sock->uv_handle.handle, tlsdns_stop_cb);
+ } else if (uv_is_closing(&sock->uv_handle.handle)) {
+ tlsdns_close_sock(sock);
+ } else {
+ uv_close(&sock->uv_handle.handle, tlsdns_close_cb);
+ }
+}
+
+static void
+stop_tlsdns_child(isc_nmsocket_t *sock) {
+ REQUIRE(sock->type == isc_nm_tlsdnssocket);
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false },
+ true))
+ {
+ return;
+ }
+
+ tlsdns_close_direct(sock);
+
+ atomic_fetch_sub(&sock->parent->rchildren, 1);
+
+ isc_barrier_wait(&sock->parent->stoplistening);
+}
+
+static void
+stop_tlsdns_parent(isc_nmsocket_t *sock) {
+ isc_nmsocket_t *csock = NULL;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(sock->type == isc_nm_tlsdnslistener);
+
+ isc_barrier_init(&sock->stoplistening, sock->nchildren);
+
+ for (size_t i = 0; i < sock->nchildren; i++) {
+ csock = &sock->children[i];
+
+ REQUIRE(VALID_NMSOCK(csock));
+
+ if ((int)i == isc_nm_tid()) {
+ /*
+ * We need to schedule closing the other sockets first
+ */
+ continue;
+ }
+
+ atomic_store(&csock->active, false);
+ enqueue_stoplistening(csock);
+ }
+
+ csock = &sock->children[isc_nm_tid()];
+ atomic_store(&csock->active, false);
+ stop_tlsdns_child(csock);
+
+ atomic_store(&sock->closed, true);
+ isc__nmsocket_prep_destroy(sock);
+}
+
+static void
+tlsdns_close_direct(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(atomic_load(&sock->closing));
+
+ REQUIRE(sock->tls.pending_req == NULL);
+
+ if (sock->quota != NULL) {
+ isc_quota_detach(&sock->quota);
+ }
+
+ if (sock->recv_handle != NULL) {
+ isc_nmhandle_detach(&sock->recv_handle);
+ }
+
+ isc__nmsocket_timer_stop(sock);
+ isc__nm_stop_reading(sock);
+
+ uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock);
+ uv_close((uv_handle_t *)&sock->read_timer, read_timer_close_cb);
+}
+
+void
+isc__nm_tlsdns_close(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_tlsdnssocket);
+ REQUIRE(!isc__nmsocket_active(sock));
+
+ if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false },
+ true))
+ {
+ return;
+ }
+
+ if (sock->tid == isc_nm_tid()) {
+ tlsdns_close_direct(sock);
+ } else {
+ /*
+ * We need to create an event and pass it using async
+ * channel
+ */
+ isc__netievent_tlsdnsclose_t *ievent =
+ isc__nm_get_netievent_tlsdnsclose(sock->mgr, sock);
+
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+ }
+}
+
+void
+isc__nm_async_tlsdnsclose(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tlsdnsclose_t *ievent =
+ (isc__netievent_tlsdnsclose_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ tlsdns_close_direct(sock);
+}
+
+static void
+tlsdns_close_connect_cb(uv_handle_t *handle) {
+ isc_nmsocket_t *sock = uv_handle_get_data(handle);
+
+ REQUIRE(VALID_NMSOCK(sock));
+
+ REQUIRE(isc__nm_in_netthread());
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ isc__nmsocket_prep_destroy(sock);
+ isc__nmsocket_detach(&sock);
+}
+
+void
+isc__nm_tlsdns_shutdown(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(sock->type == isc_nm_tlsdnssocket);
+
+ /*
+ * If the socket is active, mark it inactive and
+ * continue. If it isn't active, stop now.
+ */
+ if (!isc__nmsocket_deactivate(sock)) {
+ return;
+ }
+
+ if (sock->tls.tls) {
+ /* Shutdown any active TLS connections */
+ tlsdns_set_tls_shutdown(sock->tls.tls);
+ }
+
+ if (atomic_load(&sock->accepting)) {
+ return;
+ }
+
+ /* TLS handshake hasn't been completed yet */
+ if (atomic_load(&sock->connecting)) {
+ isc_nmsocket_t *tsock = NULL;
+
+ /*
+ * TCP connection has been established, now waiting on
+ * TLS handshake to complete
+ */
+ if (sock->tls.pending_req != NULL) {
+ isc_result_t result = ISC_R_CANCELED;
+ isc__nm_uvreq_t *req = sock->tls.pending_req;
+ sock->tls.pending_req = NULL;
+
+ if (peer_verification_has_failed(sock)) {
+ /*
+ * Save error message as 'sock->tls' will get
+ * detached.
+ */
+ sock->tls.tls_verify_errmsg =
+ isc_tls_verify_peer_result_string(
+ sock->tls.tls);
+ result = ISC_R_TLSBADPEERCERT;
+ }
+ isc__nm_failed_connect_cb(sock, req, result, false);
+ return;
+ }
+
+ /* The TCP connection hasn't been established yet */
+ isc__nmsocket_attach(sock, &tsock);
+ uv_close(&sock->uv_handle.handle, tlsdns_close_connect_cb);
+ return;
+ }
+
+ if (sock->statichandle != NULL) {
+ if (isc__nm_closing(sock)) {
+ isc__nm_failed_read_cb(sock, ISC_R_SHUTTINGDOWN, false);
+ } else {
+ isc__nm_failed_read_cb(sock, ISC_R_CANCELED, false);
+ }
+ return;
+ }
+
+ /*
+ * Otherwise, we just send the socket to abyss...
+ */
+ if (sock->parent == NULL) {
+ isc__nmsocket_prep_destroy(sock);
+ }
+}
+
+void
+isc__nm_tlsdns_cancelread(isc_nmhandle_t *handle) {
+ isc_nmsocket_t *sock = NULL;
+ isc__netievent_tlsdnscancel_t *ievent = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ sock = handle->sock;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_tlsdnssocket);
+
+ ievent = isc__nm_get_netievent_tlsdnscancel(sock->mgr, sock, handle);
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+}
+
+void
+isc__nm_async_tlsdnscancel(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tlsdnscancel_t *ievent =
+ (isc__netievent_tlsdnscancel_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ isc__nm_failed_read_cb(sock, ISC_R_EOF, false);
+}
+
+/* Zone transfers/updates over TLS are allowed only when "dot" ALPN
+ * was negotiated.
+ *
+ * Per the XoT spec, we must also check that the TLS version is >=
+ * 1.3. The check could be added here. However, we still need to
+ * support platforms where no cryptographic library with TLSv1.3
+ * support is available. As a result of this we cannot be too strict
+ * regarding the minimal TLS protocol version in order to make it
+ * possible to do encrypted zone transfers over TLSv1.2, as it would
+ * not be right to leave users on these platforms without means for
+ * encrypted zone transfers using BIND only.
+ *
+ * The ones requiring strict compatibility with the specification
+ * could disable TLSv1.2 in the configuration file.
+ */
+isc_result_t
+isc__nm_tlsdns_xfr_checkperm(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_tlsdnssocket);
+
+ if (!sock->tls.alpn_negotiated) {
+ return (ISC_R_DOTALPNERROR);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+const char *
+isc__nm_tlsdns_verify_tls_peer_result_string(const isc_nmhandle_t *handle) {
+ isc_nmsocket_t *sock = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+ REQUIRE(handle->sock->type == isc_nm_tlsdnssocket);
+
+ sock = handle->sock;
+ if (sock->tls.tls == NULL) {
+ return (sock->tls.tls_verify_errmsg);
+ }
+
+ return (isc_tls_verify_peer_result_string(sock->tls.tls));
+}
+
+void
+isc__nm_async_tlsdns_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx,
+ const int tid) {
+ REQUIRE(tid >= 0);
+
+ isc_tlsctx_free(&listener->children[tid].tls.ctx);
+ isc_tlsctx_attach(tlsctx, &listener->children[tid].tls.ctx);
+}
+
+void
+isc__nm_tlsdns_cleanup_data(isc_nmsocket_t *sock) {
+ if (sock->type == isc_nm_tlsdnslistener ||
+ sock->type == isc_nm_tlsdnssocket)
+ {
+ if (sock->tls.client_sess_cache != NULL) {
+ INSIST(atomic_load(&sock->client));
+ INSIST(sock->type == isc_nm_tlsdnssocket);
+ isc_tlsctx_client_session_cache_detach(
+ &sock->tls.client_sess_cache);
+ }
+ if (sock->tls.ctx != NULL) {
+ INSIST(ISC_LIST_EMPTY(sock->tls.sendreqs));
+ isc_tlsctx_free(&sock->tls.ctx);
+ }
+ }
+}
+
+static void
+tlsdns_keep_client_tls_session(isc_nmsocket_t *sock) {
+ /*
+ * Ensure that the isc_tls_t is being accessed from
+ * within the worker thread the socket is bound to.
+ */
+ REQUIRE(sock->tid == isc_nm_tid());
+ if (sock->tls.client_sess_cache != NULL &&
+ sock->tls.client_session_saved == false)
+ {
+ INSIST(atomic_load(&sock->client));
+ isc_tlsctx_client_session_cache_keep_sockaddr(
+ sock->tls.client_sess_cache, &sock->peer,
+ sock->tls.tls);
+ sock->tls.client_session_saved = true;
+ }
+}
diff --git a/lib/isc/netmgr/tlsstream.c b/lib/isc/netmgr/tlsstream.c
new file mode 100644
index 0000000..7b49071
--- /dev/null
+++ b/lib/isc/netmgr/tlsstream.c
@@ -0,0 +1,1348 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <errno.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <uv.h>
+
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+
+#include <isc/atomic.h>
+#include <isc/buffer.h>
+#include <isc/condition.h>
+#include <isc/log.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/netmgr.h>
+#include <isc/once.h>
+#include <isc/quota.h>
+#include <isc/random.h>
+#include <isc/refcount.h>
+#include <isc/region.h>
+#include <isc/result.h>
+#include <isc/sockaddr.h>
+#include <isc/stdtime.h>
+#include <isc/thread.h>
+#include <isc/util.h>
+
+#include "../openssl_shim.h"
+#include "netmgr-int.h"
+#include "uv-compat.h"
+
+#define TLS_BUF_SIZE (UINT16_MAX)
+
+static isc_result_t
+tls_error_to_result(const int tls_err, const int tls_state, isc_tls_t *tls) {
+ switch (tls_err) {
+ case SSL_ERROR_ZERO_RETURN:
+ return (ISC_R_EOF);
+ case SSL_ERROR_SSL:
+ if (tls != NULL && tls_state < TLS_IO &&
+ SSL_get_verify_result(tls) != X509_V_OK)
+ {
+ return (ISC_R_TLSBADPEERCERT);
+ }
+ return (ISC_R_TLSERROR);
+ default:
+ return (ISC_R_UNEXPECTED);
+ }
+}
+
+static void
+tls_failed_read_cb(isc_nmsocket_t *sock, const isc_result_t result);
+
+static void
+tls_do_bio(isc_nmsocket_t *sock, isc_region_t *received_data,
+ isc__nm_uvreq_t *send_data, bool finish);
+
+static void
+tls_readcb(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *region,
+ void *cbarg);
+
+static void
+tls_close_direct(isc_nmsocket_t *sock);
+
+static void
+async_tls_do_bio(isc_nmsocket_t *sock);
+
+static void
+tls_init_listener_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *ctx);
+
+static void
+tls_cleanup_listener_tlsctx(isc_nmsocket_t *listener);
+
+static isc_tlsctx_t *
+tls_get_listener_tlsctx(isc_nmsocket_t *listener, const int tid);
+
+static void
+tls_keep_client_tls_session(isc_nmsocket_t *sock);
+
+static void
+tls_try_shutdown(isc_tls_t *tls, const bool quite);
+
+/*
+ * The socket is closing, outerhandle has been detached, listener is
+ * inactive, or the netmgr is closing: any operation on it should abort
+ * with ISC_R_CANCELED.
+ */
+static bool
+inactive(isc_nmsocket_t *sock) {
+ return (!isc__nmsocket_active(sock) || atomic_load(&sock->closing) ||
+ sock->outerhandle == NULL ||
+ !isc__nmsocket_active(sock->outerhandle->sock) ||
+ atomic_load(&sock->outerhandle->sock->closing) ||
+ (sock->listener != NULL &&
+ !isc__nmsocket_active(sock->listener)) ||
+ isc__nm_closing(sock));
+}
+
+static void
+tls_call_connect_cb(isc_nmsocket_t *sock, isc_nmhandle_t *handle,
+ const isc_result_t result) {
+ if (sock->connect_cb == NULL) {
+ return;
+ }
+ sock->connect_cb(handle, result, sock->connect_cbarg);
+ if (result != ISC_R_SUCCESS) {
+ isc__nmsocket_clearcb(handle->sock);
+ }
+}
+
+static void
+tls_senddone(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
+ isc_nmsocket_tls_send_req_t *send_req =
+ (isc_nmsocket_tls_send_req_t *)cbarg;
+ isc_nmsocket_t *tlssock = NULL;
+ bool finish = send_req->finish;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+ REQUIRE(VALID_NMSOCK(send_req->tlssock));
+
+ tlssock = send_req->tlssock;
+ send_req->tlssock = NULL;
+
+ if (finish) {
+ tls_try_shutdown(tlssock->tlsstream.tls, true);
+ }
+
+ if (send_req->cb != NULL) {
+ INSIST(VALID_NMHANDLE(tlssock->statichandle));
+ send_req->cb(send_req->handle, eresult, send_req->cbarg);
+ isc_nmhandle_detach(&send_req->handle);
+ /* The last handle has been just detached: close the underlying
+ * socket. */
+ if (tlssock->statichandle == NULL) {
+ finish = true;
+ }
+ }
+
+ /* We are tying to avoid a memory allocation for small write
+ * requests. See the mirroring code in the tls_send_outgoing()
+ * function. */
+ if (send_req->data.length > sizeof(send_req->smallbuf)) {
+ isc_mem_put(handle->sock->mgr->mctx, send_req->data.base,
+ send_req->data.length);
+ } else {
+ INSIST(&send_req->smallbuf[0] == send_req->data.base);
+ }
+ isc_mem_put(handle->sock->mgr->mctx, send_req, sizeof(*send_req));
+ tlssock->tlsstream.nsending--;
+
+ if (finish && eresult == ISC_R_SUCCESS) {
+ tlssock->tlsstream.reading = false;
+ isc_nm_cancelread(handle);
+ } else if (eresult == ISC_R_SUCCESS) {
+ tls_do_bio(tlssock, NULL, NULL, false);
+ } else if (eresult != ISC_R_SUCCESS &&
+ tlssock->tlsstream.state <= TLS_HANDSHAKE &&
+ !tlssock->tlsstream.server)
+ {
+ /*
+ * We are still waiting for the handshake to complete, but
+ * it isn't going to happen. Call the connect callback,
+ * passing the error code there.
+ *
+ * (Note: tls_failed_read_cb() calls the connect
+ * rather than the read callback in this case.
+ * XXX: clarify?)
+ */
+ tls_failed_read_cb(tlssock, eresult);
+ }
+
+ isc__nmsocket_detach(&tlssock);
+}
+
+static void
+tls_failed_read_cb(isc_nmsocket_t *sock, const isc_result_t result) {
+ bool destroy = true;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(result != ISC_R_SUCCESS);
+
+ if (!sock->tlsstream.server &&
+ (sock->tlsstream.state == TLS_INIT ||
+ sock->tlsstream.state == TLS_HANDSHAKE) &&
+ sock->connect_cb != NULL)
+ {
+ isc_nmhandle_t *handle = NULL;
+ INSIST(sock->statichandle == NULL);
+ handle = isc__nmhandle_get(sock, &sock->peer, &sock->iface);
+ tls_call_connect_cb(sock, handle, result);
+ isc__nmsocket_clearcb(sock);
+ isc_nmhandle_detach(&handle);
+ } else if (sock->recv_cb != NULL && sock->statichandle != NULL &&
+ (sock->recv_read || result == ISC_R_TIMEDOUT))
+ {
+ isc__nm_uvreq_t *req = NULL;
+ INSIST(VALID_NMHANDLE(sock->statichandle));
+ sock->recv_read = false;
+ req = isc__nm_uvreq_get(sock->mgr, sock);
+ req->cb.recv = sock->recv_cb;
+ req->cbarg = sock->recv_cbarg;
+ isc_nmhandle_attach(sock->statichandle, &req->handle);
+ if (result != ISC_R_TIMEDOUT) {
+ isc__nmsocket_clearcb(sock);
+ }
+ isc__nm_readcb(sock, req, result);
+ if (result == ISC_R_TIMEDOUT &&
+ (sock->outerhandle == NULL ||
+ isc__nmsocket_timer_running(sock->outerhandle->sock)))
+ {
+ destroy = false;
+ }
+ }
+
+ if (destroy) {
+ isc__nmsocket_prep_destroy(sock);
+ }
+}
+
+static void
+async_tls_do_bio(isc_nmsocket_t *sock) {
+ isc__netievent_tlsdobio_t *ievent =
+ isc__nm_get_netievent_tlsdobio(sock->mgr, sock);
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+}
+
+static int
+tls_send_outgoing(isc_nmsocket_t *sock, bool finish, isc_nmhandle_t *tlshandle,
+ isc_nm_cb_t cb, void *cbarg) {
+ isc_nmsocket_tls_send_req_t *send_req = NULL;
+ int pending;
+ int rv;
+ size_t len = 0;
+
+ if (inactive(sock)) {
+ if (cb != NULL) {
+ INSIST(VALID_NMHANDLE(tlshandle));
+ cb(tlshandle, ISC_R_CANCELED, cbarg);
+ }
+ return (0);
+ }
+
+ if (finish) {
+ tls_try_shutdown(sock->tlsstream.tls, false);
+ tls_keep_client_tls_session(sock);
+ }
+
+ pending = BIO_pending(sock->tlsstream.bio_out);
+ if (pending <= 0) {
+ return (pending);
+ }
+
+ /* TODO Should we keep track of these requests in a list? */
+ if ((unsigned int)pending > TLS_BUF_SIZE) {
+ pending = TLS_BUF_SIZE;
+ }
+
+ send_req = isc_mem_get(sock->mgr->mctx, sizeof(*send_req));
+ *send_req = (isc_nmsocket_tls_send_req_t){ .finish = finish,
+ .data.length = pending };
+
+ /* Let's try to avoid a memory allocation for small write requests */
+ if ((size_t)pending > sizeof(send_req->smallbuf)) {
+ send_req->data.base = isc_mem_get(sock->mgr->mctx, pending);
+ } else {
+ send_req->data.base = &send_req->smallbuf[0];
+ }
+
+ isc__nmsocket_attach(sock, &send_req->tlssock);
+ if (cb != NULL) {
+ send_req->cb = cb;
+ send_req->cbarg = cbarg;
+ isc_nmhandle_attach(tlshandle, &send_req->handle);
+ }
+
+ rv = BIO_read_ex(sock->tlsstream.bio_out, send_req->data.base, pending,
+ &len);
+ /* There's something pending, read must succeed */
+ RUNTIME_CHECK(rv == 1);
+
+ INSIST(VALID_NMHANDLE(sock->outerhandle));
+
+ sock->tlsstream.nsending++;
+ isc_nm_send(sock->outerhandle, &send_req->data, tls_senddone, send_req);
+
+ return (pending);
+}
+
+static int
+tls_process_outgoing(isc_nmsocket_t *sock, bool finish,
+ isc__nm_uvreq_t *send_data) {
+ int pending;
+
+ bool received_shutdown = ((SSL_get_shutdown(sock->tlsstream.tls) &
+ SSL_RECEIVED_SHUTDOWN) != 0);
+ bool sent_shutdown = ((SSL_get_shutdown(sock->tlsstream.tls) &
+ SSL_SENT_SHUTDOWN) != 0);
+
+ if (received_shutdown && !sent_shutdown) {
+ finish = true;
+ }
+
+ /* Data from TLS to network */
+ if (send_data != NULL) {
+ pending = tls_send_outgoing(sock, finish, send_data->handle,
+ send_data->cb.send,
+ send_data->cbarg);
+ } else {
+ pending = tls_send_outgoing(sock, finish, NULL, NULL, NULL);
+ }
+
+ return (pending);
+}
+
+static int
+tls_try_handshake(isc_nmsocket_t *sock, isc_result_t *presult) {
+ int rv = 0;
+ isc_nmhandle_t *tlshandle = NULL;
+
+ REQUIRE(sock->tlsstream.state == TLS_HANDSHAKE);
+
+ if (SSL_is_init_finished(sock->tlsstream.tls) == 1) {
+ return (0);
+ }
+
+ rv = SSL_do_handshake(sock->tlsstream.tls);
+ if (rv == 1) {
+ isc_result_t result = ISC_R_SUCCESS;
+ INSIST(SSL_is_init_finished(sock->tlsstream.tls) == 1);
+ INSIST(sock->statichandle == NULL);
+ isc__nmsocket_log_tls_session_reuse(sock, sock->tlsstream.tls);
+ tlshandle = isc__nmhandle_get(sock, &sock->peer, &sock->iface);
+
+ if (isc__nm_closing(sock)) {
+ result = ISC_R_SHUTTINGDOWN;
+ }
+
+ if (sock->tlsstream.server) {
+ if (isc__nmsocket_closing(sock->listener)) {
+ result = ISC_R_CANCELED;
+ } else if (result == ISC_R_SUCCESS) {
+ result = sock->listener->accept_cb(
+ tlshandle, result,
+ sock->listener->accept_cbarg);
+ }
+ } else {
+ tls_call_connect_cb(sock, tlshandle, result);
+ }
+ isc_nmhandle_detach(&tlshandle);
+ sock->tlsstream.state = TLS_IO;
+
+ if (presult != NULL) {
+ *presult = result;
+ }
+ }
+
+ return (rv);
+}
+
+static bool
+tls_try_to_close_unused_socket(isc_nmsocket_t *sock) {
+ if (sock->tlsstream.state > TLS_HANDSHAKE &&
+ sock->statichandle == NULL && sock->tlsstream.nsending == 0)
+ {
+ /*
+ * It seems that no action on the socket has been
+ * scheduled on some point after the handshake, let's
+ * close the connection.
+ */
+ isc__nmsocket_prep_destroy(sock);
+ return (true);
+ }
+
+ return (false);
+}
+
+static void
+tls_do_bio(isc_nmsocket_t *sock, isc_region_t *received_data,
+ isc__nm_uvreq_t *send_data, bool finish) {
+ isc_result_t result = ISC_R_SUCCESS;
+ int pending, tls_status = SSL_ERROR_NONE;
+ int rv = 0;
+ size_t len = 0;
+ int saved_errno = 0;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ /* We will resume read if TLS layer wants us to */
+ if (sock->tlsstream.reading && sock->outerhandle) {
+ REQUIRE(VALID_NMHANDLE(sock->outerhandle));
+ isc_nm_pauseread(sock->outerhandle);
+ }
+
+ /*
+ * Clear the TLS error queue so that SSL_get_error() and SSL I/O
+ * routine calls will not get affected by prior error statuses.
+ *
+ * See here:
+ * https://www.openssl.org/docs/man3.0/man3/SSL_get_error.html
+ *
+ * In particular, it mentions the following:
+ *
+ * The current thread's error queue must be empty before the
+ * TLS/SSL I/O operation is attempted, or SSL_get_error() will not
+ * work reliably.
+ *
+ * As we use the result of SSL_get_error() to decide on I/O
+ * operations, we need to ensure that it works reliably by
+ * cleaning the error queue.
+ *
+ * The sum of details: https://stackoverflow.com/a/37980911
+ */
+ ERR_clear_error();
+
+ if (sock->tlsstream.state == TLS_INIT) {
+ INSIST(received_data == NULL && send_data == NULL);
+ if (sock->tlsstream.server) {
+ SSL_set_accept_state(sock->tlsstream.tls);
+ } else {
+ SSL_set_connect_state(sock->tlsstream.tls);
+ }
+ sock->tlsstream.state = TLS_HANDSHAKE;
+ rv = tls_try_handshake(sock, NULL);
+ INSIST(SSL_is_init_finished(sock->tlsstream.tls) == 0);
+ } else if (sock->tlsstream.state == TLS_CLOSED) {
+ return;
+ } else { /* initialised and doing I/O */
+ if (received_data != NULL) {
+ INSIST(send_data == NULL);
+ rv = BIO_write_ex(sock->tlsstream.bio_in,
+ received_data->base,
+ received_data->length, &len);
+ if (rv <= 0 || len != received_data->length) {
+ result = ISC_R_TLSERROR;
+#if defined(NETMGR_TRACE) && defined(NETMGR_TRACE_VERBOSE)
+ saved_errno = errno;
+#endif
+ goto error;
+ }
+
+ /*
+ * Only after doing the IO we can check whether SSL
+ * handshake is done.
+ */
+ if (sock->tlsstream.state == TLS_HANDSHAKE) {
+ isc_result_t hs_result = ISC_R_UNSET;
+ rv = tls_try_handshake(sock, &hs_result);
+ if (sock->tlsstream.state == TLS_IO &&
+ hs_result != ISC_R_SUCCESS)
+ {
+ /*
+ * The accept callback has been called
+ * unsuccessfully. Let's try to shut
+ * down the TLS connection gracefully.
+ */
+ INSIST(SSL_is_init_finished(
+ sock->tlsstream.tls) ==
+ 1);
+ finish = true;
+ }
+ }
+ } else if (send_data != NULL) {
+ INSIST(received_data == NULL);
+ INSIST(sock->tlsstream.state > TLS_HANDSHAKE);
+ bool received_shutdown =
+ ((SSL_get_shutdown(sock->tlsstream.tls) &
+ SSL_RECEIVED_SHUTDOWN) != 0);
+ bool sent_shutdown =
+ ((SSL_get_shutdown(sock->tlsstream.tls) &
+ SSL_SENT_SHUTDOWN) != 0);
+ rv = SSL_write_ex(sock->tlsstream.tls,
+ send_data->uvbuf.base,
+ send_data->uvbuf.len, &len);
+ if (rv != 1 || len != send_data->uvbuf.len) {
+ result = received_shutdown || sent_shutdown
+ ? ISC_R_CANCELED
+ : ISC_R_TLSERROR;
+ send_data->cb.send(send_data->handle, result,
+ send_data->cbarg);
+ send_data = NULL;
+ return;
+ }
+ }
+
+ /* Decrypt and pass data from network to client */
+ if (sock->tlsstream.state >= TLS_IO && sock->recv_cb != NULL &&
+ !atomic_load(&sock->readpaused) &&
+ sock->statichandle != NULL && !finish)
+ {
+ uint8_t recv_buf[TLS_BUF_SIZE];
+ INSIST(sock->tlsstream.state > TLS_HANDSHAKE);
+ while ((rv = SSL_read_ex(sock->tlsstream.tls, recv_buf,
+ TLS_BUF_SIZE, &len)) == 1)
+ {
+ isc_region_t region;
+ region = (isc_region_t){ .base = &recv_buf[0],
+ .length = len };
+
+ INSIST(VALID_NMHANDLE(sock->statichandle));
+ sock->recv_cb(sock->statichandle, ISC_R_SUCCESS,
+ &region, sock->recv_cbarg);
+ /* The handle could have been detached in
+ * sock->recv_cb, making the sock->statichandle
+ * nullified (it happens in netmgr.c). If it is
+ * the case, then it means that we are not
+ * interested in keeping the connection alive
+ * anymore. Let's shut down the SSL session,
+ * send what we have in the SSL buffers,
+ * and close the connection.
+ */
+ if (sock->statichandle == NULL) {
+ finish = true;
+ break;
+ } else if (sock->recv_cb == NULL) {
+ /*
+ * The 'sock->recv_cb' might have been
+ * nullified during the call to
+ * 'sock->recv_cb'. That could happen,
+ * indirectly when wrapping up.
+ *
+ * In this case, let's close the TLS
+ * connection.
+ */
+ finish = true;
+ break;
+ } else if (atomic_load(&sock->readpaused)) {
+ /*
+ * Reading has been paused from withing
+ * the context of read callback - stop
+ * processing incoming data.
+ */
+ break;
+ }
+ }
+ }
+ }
+ errno = 0;
+ tls_status = SSL_get_error(sock->tlsstream.tls, rv);
+ saved_errno = errno;
+
+ /* See "BUGS" section at:
+ * https://www.openssl.org/docs/man1.1.1/man3/SSL_get_error.html
+ *
+ * It is mentioned there that when TLS status equals
+ * SSL_ERROR_SYSCALL AND errno == 0 it means that underlying
+ * transport layer returned EOF prematurely. However, we are
+ * managing the transport ourselves, so we should just resume
+ * reading from the TCP socket.
+ *
+ * It seems that this case has been handled properly on modern
+ * versions of OpenSSL. That being said, the situation goes in
+ * line with the manual: it is briefly mentioned there that
+ * SSL_ERROR_SYSCALL might be returned not only in a case of
+ * low-level errors (like system call failures).
+ */
+ if (tls_status == SSL_ERROR_SYSCALL && saved_errno == 0 &&
+ received_data == NULL && send_data == NULL && finish == false)
+ {
+ tls_status = SSL_ERROR_WANT_READ;
+ }
+
+ pending = tls_process_outgoing(sock, finish, send_data);
+ if (pending > 0 && tls_status != SSL_ERROR_SSL) {
+ /* We'll continue in tls_senddone */
+ return;
+ }
+
+ switch (tls_status) {
+ case SSL_ERROR_NONE:
+ case SSL_ERROR_ZERO_RETURN:
+ (void)tls_try_to_close_unused_socket(sock);
+ return;
+ case SSL_ERROR_WANT_WRITE:
+ if (sock->tlsstream.nsending == 0) {
+ /*
+ * Launch tls_do_bio asynchronously. If we're sending
+ * already the send callback will call it.
+ */
+ async_tls_do_bio(sock);
+ }
+ return;
+ case SSL_ERROR_WANT_READ:
+ if (tls_try_to_close_unused_socket(sock) ||
+ sock->outerhandle == NULL || atomic_load(&sock->readpaused))
+ {
+ return;
+ }
+
+ INSIST(VALID_NMHANDLE(sock->outerhandle));
+
+ if (sock->tlsstream.reading) {
+ isc_nm_resumeread(sock->outerhandle);
+ } else if (sock->tlsstream.state == TLS_HANDSHAKE) {
+ sock->tlsstream.reading = true;
+ isc_nm_read(sock->outerhandle, tls_readcb, sock);
+ }
+ return;
+ default:
+ result = tls_error_to_result(tls_status, sock->tlsstream.state,
+ sock->tlsstream.tls);
+ break;
+ }
+
+error:
+#if defined(NETMGR_TRACE) && defined(NETMGR_TRACE_VERBOSE)
+ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_NETMGR,
+ ISC_LOG_NOTICE,
+ "SSL error in BIO: %d %s (errno: %d). Arguments: "
+ "received_data: %p, "
+ "send_data: %p, finish: %s",
+ tls_status, isc_result_totext(result), saved_errno,
+ received_data, send_data, finish ? "true" : "false");
+#endif
+ tls_failed_read_cb(sock, result);
+}
+
+static void
+tls_readcb(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *region,
+ void *cbarg) {
+ isc_nmsocket_t *tlssock = (isc_nmsocket_t *)cbarg;
+
+ REQUIRE(VALID_NMSOCK(tlssock));
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(tlssock->tid == isc_nm_tid());
+
+ if (result != ISC_R_SUCCESS) {
+ tls_failed_read_cb(tlssock, result);
+ return;
+ }
+
+ tls_do_bio(tlssock, region, NULL, false);
+}
+
+static isc_result_t
+initialize_tls(isc_nmsocket_t *sock, bool server) {
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ sock->tlsstream.bio_in = BIO_new(BIO_s_mem());
+ if (sock->tlsstream.bio_in == NULL) {
+ isc_tls_free(&sock->tlsstream.tls);
+ return (ISC_R_TLSERROR);
+ }
+ sock->tlsstream.bio_out = BIO_new(BIO_s_mem());
+ if (sock->tlsstream.bio_out == NULL) {
+ BIO_free_all(sock->tlsstream.bio_in);
+ sock->tlsstream.bio_in = NULL;
+ isc_tls_free(&sock->tlsstream.tls);
+ return (ISC_R_TLSERROR);
+ }
+
+ if (BIO_set_mem_eof_return(sock->tlsstream.bio_in, EOF) != 1 ||
+ BIO_set_mem_eof_return(sock->tlsstream.bio_out, EOF) != 1)
+ {
+ goto error;
+ }
+
+ SSL_set_bio(sock->tlsstream.tls, sock->tlsstream.bio_in,
+ sock->tlsstream.bio_out);
+ sock->tlsstream.server = server;
+ sock->tlsstream.nsending = 0;
+ sock->tlsstream.state = TLS_INIT;
+ return (ISC_R_SUCCESS);
+error:
+ isc_tls_free(&sock->tlsstream.tls);
+ sock->tlsstream.bio_out = sock->tlsstream.bio_in = NULL;
+ return (ISC_R_TLSERROR);
+}
+
+static isc_result_t
+tlslisten_acceptcb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
+ isc_nmsocket_t *tlslistensock = (isc_nmsocket_t *)cbarg;
+ isc_nmsocket_t *tlssock = NULL;
+ isc_tlsctx_t *tlsctx = NULL;
+ int tid;
+
+ /* If accept() was unsuccessful we can't do anything */
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+ REQUIRE(VALID_NMSOCK(tlslistensock));
+ REQUIRE(tlslistensock->type == isc_nm_tlslistener);
+
+ if (isc__nmsocket_closing(handle->sock) ||
+ isc__nmsocket_closing(tlslistensock) ||
+ !atomic_load(&tlslistensock->listening))
+ {
+ return (ISC_R_CANCELED);
+ }
+
+ /*
+ * We need to create a 'wrapper' tlssocket for this connection.
+ */
+ tlssock = isc_mem_get(handle->sock->mgr->mctx, sizeof(*tlssock));
+ isc__nmsocket_init(tlssock, handle->sock->mgr, isc_nm_tlssocket,
+ &handle->sock->iface);
+
+ tid = isc_nm_tid();
+ /* We need to initialize SSL now to reference SSL_CTX properly */
+ tlsctx = tls_get_listener_tlsctx(tlslistensock, tid);
+ RUNTIME_CHECK(tlsctx != NULL);
+ isc_tlsctx_attach(tlsctx, &tlssock->tlsstream.ctx);
+ tlssock->tlsstream.tls = isc_tls_create(tlssock->tlsstream.ctx);
+ if (tlssock->tlsstream.tls == NULL) {
+ atomic_store(&tlssock->closed, true);
+ isc_tlsctx_free(&tlssock->tlsstream.ctx);
+ isc__nmsocket_detach(&tlssock);
+ return (ISC_R_TLSERROR);
+ }
+
+ tlssock->extrahandlesize = tlslistensock->extrahandlesize;
+ isc__nmsocket_attach(tlslistensock, &tlssock->listener);
+ isc_nmhandle_attach(handle, &tlssock->outerhandle);
+ tlssock->peer = handle->sock->peer;
+ tlssock->read_timeout = atomic_load(&handle->sock->mgr->init);
+ tlssock->tid = tid;
+
+ /*
+ * Hold a reference to tlssock in the TCP socket: it will
+ * detached in isc__nm_tls_cleanup_data().
+ */
+ handle->sock->tlsstream.tlssocket = tlssock;
+
+ result = initialize_tls(tlssock, true);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ /* TODO: catch failure code, detach tlssock, and log the error */
+
+ tls_do_bio(tlssock, NULL, NULL, false);
+ return (result);
+}
+
+isc_result_t
+isc_nm_listentls(isc_nm_t *mgr, isc_sockaddr_t *iface,
+ isc_nm_accept_cb_t accept_cb, void *accept_cbarg,
+ size_t extrahandlesize, int backlog, isc_quota_t *quota,
+ SSL_CTX *sslctx, isc_nmsocket_t **sockp) {
+ isc_result_t result;
+ isc_nmsocket_t *tlssock = NULL;
+ isc_nmsocket_t *tsock = NULL;
+
+ REQUIRE(VALID_NM(mgr));
+ if (atomic_load(&mgr->closing)) {
+ return (ISC_R_SHUTTINGDOWN);
+ }
+
+ tlssock = isc_mem_get(mgr->mctx, sizeof(*tlssock));
+
+ isc__nmsocket_init(tlssock, mgr, isc_nm_tlslistener, iface);
+ tlssock->result = ISC_R_UNSET;
+ tlssock->accept_cb = accept_cb;
+ tlssock->accept_cbarg = accept_cbarg;
+ tlssock->extrahandlesize = extrahandlesize;
+ tls_init_listener_tlsctx(tlssock, sslctx);
+ tlssock->tlsstream.tls = NULL;
+
+ /*
+ * tlssock will be a TLS 'wrapper' around an unencrypted stream.
+ * We set tlssock->outer to a socket listening for a TCP connection.
+ */
+ result = isc_nm_listentcp(mgr, iface, tlslisten_acceptcb, tlssock,
+ extrahandlesize, backlog, quota,
+ &tlssock->outer);
+ if (result != ISC_R_SUCCESS) {
+ atomic_store(&tlssock->closed, true);
+ isc__nmsocket_detach(&tlssock);
+ return (result);
+ }
+
+ /* wait for listen result */
+ isc__nmsocket_attach(tlssock->outer, &tsock);
+ tlssock->result = result;
+ atomic_store(&tlssock->active, true);
+ INSIST(tlssock->outer->tlsstream.tlslistener == NULL);
+ isc__nmsocket_attach(tlssock, &tlssock->outer->tlsstream.tlslistener);
+ isc__nmsocket_detach(&tsock);
+ INSIST(result != ISC_R_UNSET);
+ tlssock->nchildren = tlssock->outer->nchildren;
+
+ isc__nmsocket_barrier_init(tlssock);
+ atomic_init(&tlssock->rchildren, tlssock->nchildren);
+
+ if (result == ISC_R_SUCCESS) {
+ atomic_store(&tlssock->listening, true);
+ *sockp = tlssock;
+ }
+
+ return (result);
+}
+
+void
+isc__nm_async_tlssend(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tlssend_t *ievent = (isc__netievent_tlssend_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+ isc__nm_uvreq_t *req = ievent->req;
+
+ REQUIRE(VALID_UVREQ(req));
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ UNUSED(worker);
+
+ ievent->req = NULL;
+
+ if (inactive(sock)) {
+ req->cb.send(req->handle, ISC_R_CANCELED, req->cbarg);
+ goto done;
+ }
+
+ tls_do_bio(sock, NULL, req, false);
+done:
+ isc__nm_uvreq_put(&req, sock);
+ return;
+}
+
+void
+isc__nm_tls_send(isc_nmhandle_t *handle, const isc_region_t *region,
+ isc_nm_cb_t cb, void *cbarg) {
+ isc__netievent_tlssend_t *ievent = NULL;
+ isc__nm_uvreq_t *uvreq = NULL;
+ isc_nmsocket_t *sock = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ sock = handle->sock;
+
+ REQUIRE(sock->type == isc_nm_tlssocket);
+
+ uvreq = isc__nm_uvreq_get(sock->mgr, sock);
+ isc_nmhandle_attach(handle, &uvreq->handle);
+ uvreq->cb.send = cb;
+ uvreq->cbarg = cbarg;
+ uvreq->uvbuf.base = (char *)region->base;
+ uvreq->uvbuf.len = region->length;
+
+ /*
+ * We need to create an event and pass it using async channel
+ */
+ ievent = isc__nm_get_netievent_tlssend(sock->mgr, sock, uvreq);
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+}
+
+void
+isc__nm_async_tlsstartread(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tlsstartread_t *ievent =
+ (isc__netievent_tlsstartread_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ UNUSED(worker);
+
+ tls_do_bio(sock, NULL, NULL, false);
+}
+
+void
+isc__nm_tls_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
+ isc__netievent_tlsstartread_t *ievent = NULL;
+ isc_nmsocket_t *sock = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ sock = handle->sock;
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->statichandle == handle);
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(sock->recv_cb == NULL);
+
+ if (inactive(sock)) {
+ cb(handle, ISC_R_CANCELED, NULL, cbarg);
+ return;
+ }
+
+ sock->recv_cb = cb;
+ sock->recv_cbarg = cbarg;
+ sock->recv_read = true;
+
+ ievent = isc__nm_get_netievent_tlsstartread(sock->mgr, sock);
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+}
+
+void
+isc__nm_tls_pauseread(isc_nmhandle_t *handle) {
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ if (atomic_compare_exchange_strong(&handle->sock->readpaused,
+ &(bool){ false }, true))
+ {
+ if (handle->sock->outerhandle != NULL) {
+ isc_nm_pauseread(handle->sock->outerhandle);
+ }
+ }
+}
+
+void
+isc__nm_tls_resumeread(isc_nmhandle_t *handle) {
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ if (!atomic_compare_exchange_strong(&handle->sock->readpaused,
+ &(bool){ true }, false))
+ {
+ if (inactive(handle->sock)) {
+ return;
+ }
+
+ async_tls_do_bio(handle->sock);
+ }
+}
+
+static void
+tls_close_direct(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ /*
+ * At this point we're certain that there are no
+ * external references, we can close everything.
+ */
+ if (sock->outerhandle != NULL) {
+ isc_nm_pauseread(sock->outerhandle);
+ isc__nmsocket_clearcb(sock->outerhandle->sock);
+ isc_nmhandle_detach(&sock->outerhandle);
+ }
+
+ if (sock->listener != NULL) {
+ isc__nmsocket_detach(&sock->listener);
+ }
+
+ /* Further cleanup performed in isc__nm_tls_cleanup_data() */
+ atomic_store(&sock->closed, true);
+ atomic_store(&sock->active, false);
+ sock->tlsstream.state = TLS_CLOSED;
+}
+
+void
+isc__nm_tls_close(isc_nmsocket_t *sock) {
+ isc__netievent_tlsclose_t *ievent = NULL;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_tlssocket);
+
+ if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false },
+ true))
+ {
+ return;
+ }
+
+ ievent = isc__nm_get_netievent_tlsclose(sock->mgr, sock);
+ isc__nm_maybe_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+}
+
+void
+isc__nm_async_tlsclose(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tlsclose_t *ievent = (isc__netievent_tlsclose_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+
+ REQUIRE(ievent->sock->tid == isc_nm_tid());
+
+ UNUSED(worker);
+
+ tls_close_direct(sock);
+}
+
+void
+isc__nm_tls_stoplistening(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_tlslistener);
+
+ isc__nmsocket_stop(sock);
+}
+
+static void
+tcp_connected(isc_nmhandle_t *handle, isc_result_t result, void *cbarg);
+
+void
+isc_nm_tlsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
+ isc_nm_cb_t cb, void *cbarg, isc_tlsctx_t *ctx,
+ isc_tlsctx_client_session_cache_t *client_sess_cache,
+ unsigned int timeout, size_t extrahandlesize) {
+ isc_nmsocket_t *nsock = NULL;
+#if defined(NETMGR_TRACE) && defined(NETMGR_TRACE_VERBOSE)
+ fprintf(stderr, "TLS: isc_nm_tlsconnect(): in net thread: %s\n",
+ isc__nm_in_netthread() ? "yes" : "no");
+#endif /* NETMGR_TRACE */
+
+ REQUIRE(VALID_NM(mgr));
+
+ if (atomic_load(&mgr->closing)) {
+ cb(NULL, ISC_R_SHUTTINGDOWN, cbarg);
+ return;
+ }
+
+ nsock = isc_mem_get(mgr->mctx, sizeof(*nsock));
+ isc__nmsocket_init(nsock, mgr, isc_nm_tlssocket, local);
+ nsock->extrahandlesize = extrahandlesize;
+ nsock->result = ISC_R_UNSET;
+ nsock->connect_cb = cb;
+ nsock->connect_cbarg = cbarg;
+ nsock->connect_timeout = timeout;
+ isc_tlsctx_attach(ctx, &nsock->tlsstream.ctx);
+ atomic_init(&nsock->client, true);
+ if (client_sess_cache != NULL) {
+ INSIST(isc_tlsctx_client_session_cache_getctx(
+ client_sess_cache) == ctx);
+ isc_tlsctx_client_session_cache_attach(
+ client_sess_cache, &nsock->tlsstream.client_sess_cache);
+ }
+
+ isc_nm_tcpconnect(mgr, local, peer, tcp_connected, nsock,
+ nsock->connect_timeout, 0);
+}
+
+static void
+tcp_connected(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
+ isc_nmsocket_t *tlssock = (isc_nmsocket_t *)cbarg;
+ isc_nmhandle_t *tlshandle = NULL;
+
+ REQUIRE(VALID_NMSOCK(tlssock));
+
+ tlssock->tid = isc_nm_tid();
+ if (result != ISC_R_SUCCESS) {
+ goto error;
+ }
+
+ INSIST(VALID_NMHANDLE(handle));
+
+ tlssock->iface = handle->sock->iface;
+ tlssock->peer = handle->sock->peer;
+ if (isc__nm_closing(tlssock)) {
+ result = ISC_R_SHUTTINGDOWN;
+ goto error;
+ }
+
+ /*
+ * We need to initialize SSL now to reference SSL_CTX properly.
+ */
+ tlssock->tlsstream.tls = isc_tls_create(tlssock->tlsstream.ctx);
+ if (tlssock->tlsstream.tls == NULL) {
+ result = ISC_R_TLSERROR;
+ goto error;
+ }
+
+ result = initialize_tls(tlssock, false);
+ if (result != ISC_R_SUCCESS) {
+ goto error;
+ }
+ tlssock->peer = isc_nmhandle_peeraddr(handle);
+ isc_nmhandle_attach(handle, &tlssock->outerhandle);
+ atomic_store(&tlssock->active, true);
+
+ if (tlssock->tlsstream.client_sess_cache != NULL) {
+ isc_tlsctx_client_session_cache_reuse_sockaddr(
+ tlssock->tlsstream.client_sess_cache, &tlssock->peer,
+ tlssock->tlsstream.tls);
+ }
+
+ /*
+ * Hold a reference to tlssock in the TCP socket: it will
+ * detached in isc__nm_tls_cleanup_data().
+ */
+ handle->sock->tlsstream.tlssocket = tlssock;
+
+ tls_do_bio(tlssock, NULL, NULL, false);
+ return;
+error:
+ tlshandle = isc__nmhandle_get(tlssock, NULL, NULL);
+ atomic_store(&tlssock->closed, true);
+ tls_call_connect_cb(tlssock, tlshandle, result);
+ isc_nmhandle_detach(&tlshandle);
+ isc__nmsocket_detach(&tlssock);
+}
+
+static void
+tls_cancelread(isc_nmsocket_t *sock) {
+ if (!inactive(sock) && sock->tlsstream.state == TLS_IO) {
+ tls_do_bio(sock, NULL, NULL, true);
+ } else if (sock->outerhandle != NULL) {
+ sock->tlsstream.reading = false;
+ isc_nm_cancelread(sock->outerhandle);
+ }
+}
+
+void
+isc__nm_tls_cancelread(isc_nmhandle_t *handle) {
+ isc_nmsocket_t *sock = NULL;
+ isc__netievent_tlscancel_t *ievent = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ sock = handle->sock;
+
+ REQUIRE(sock->type == isc_nm_tlssocket);
+
+ if (sock->tid == isc_nm_tid()) {
+ tls_cancelread(sock);
+ } else {
+ ievent = isc__nm_get_netievent_tlscancel(sock->mgr, sock,
+ handle);
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+ }
+}
+
+void
+isc__nm_async_tlscancel(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tlscancel_t *ievent = (isc__netievent_tlscancel_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(worker->id == sock->tid);
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ UNUSED(worker);
+ tls_cancelread(sock);
+}
+
+void
+isc__nm_async_tlsdobio(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_tlsdobio_t *ievent = (isc__netievent_tlsdobio_t *)ev0;
+
+ UNUSED(worker);
+
+ tls_do_bio(ievent->sock, NULL, NULL, false);
+}
+
+void
+isc__nm_tls_cleanup_data(isc_nmsocket_t *sock) {
+ if (sock->type == isc_nm_tcplistener &&
+ sock->tlsstream.tlslistener != NULL)
+ {
+ isc__nmsocket_detach(&sock->tlsstream.tlslistener);
+ } else if (sock->type == isc_nm_tlslistener) {
+ tls_cleanup_listener_tlsctx(sock);
+ } else if (sock->type == isc_nm_tlssocket) {
+ if (sock->tlsstream.tls != NULL) {
+ /*
+ * Let's shut down the TLS session properly so that
+ * the session will remain resumable, if required.
+ */
+ tls_try_shutdown(sock->tlsstream.tls, true);
+ tls_keep_client_tls_session(sock);
+ isc_tls_free(&sock->tlsstream.tls);
+ /* These are destroyed when we free SSL */
+ sock->tlsstream.bio_out = NULL;
+ sock->tlsstream.bio_in = NULL;
+ }
+ if (sock->tlsstream.ctx != NULL) {
+ isc_tlsctx_free(&sock->tlsstream.ctx);
+ }
+ if (sock->tlsstream.client_sess_cache != NULL) {
+ INSIST(atomic_load(&sock->client));
+ isc_tlsctx_client_session_cache_detach(
+ &sock->tlsstream.client_sess_cache);
+ }
+ } else if (sock->type == isc_nm_tcpsocket &&
+ sock->tlsstream.tlssocket != NULL)
+ {
+ /*
+ * The TLS socket can't be destroyed until its underlying TCP
+ * socket is, to avoid possible use-after-free errors.
+ */
+ isc__nmsocket_detach(&sock->tlsstream.tlssocket);
+ }
+}
+
+void
+isc__nm_tls_cleartimeout(isc_nmhandle_t *handle) {
+ isc_nmsocket_t *sock = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+ REQUIRE(handle->sock->type == isc_nm_tlssocket);
+
+ sock = handle->sock;
+ if (sock->outerhandle != NULL) {
+ INSIST(VALID_NMHANDLE(sock->outerhandle));
+ isc_nmhandle_cleartimeout(sock->outerhandle);
+ }
+}
+
+void
+isc__nm_tls_settimeout(isc_nmhandle_t *handle, uint32_t timeout) {
+ isc_nmsocket_t *sock = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+ REQUIRE(handle->sock->type == isc_nm_tlssocket);
+
+ sock = handle->sock;
+ if (sock->outerhandle != NULL) {
+ INSIST(VALID_NMHANDLE(sock->outerhandle));
+ isc_nmhandle_settimeout(sock->outerhandle, timeout);
+ }
+}
+
+void
+isc__nmhandle_tls_keepalive(isc_nmhandle_t *handle, bool value) {
+ isc_nmsocket_t *sock = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+ REQUIRE(handle->sock->type == isc_nm_tlssocket);
+
+ sock = handle->sock;
+ if (sock->outerhandle != NULL) {
+ INSIST(VALID_NMHANDLE(sock->outerhandle));
+
+ isc_nmhandle_keepalive(sock->outerhandle, value);
+ }
+}
+
+void
+isc__nmhandle_tls_setwritetimeout(isc_nmhandle_t *handle,
+ uint64_t write_timeout) {
+ isc_nmsocket_t *sock = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+ REQUIRE(handle->sock->type == isc_nm_tlssocket);
+
+ sock = handle->sock;
+ if (sock->outerhandle != NULL) {
+ INSIST(VALID_NMHANDLE(sock->outerhandle));
+
+ isc_nmhandle_setwritetimeout(sock->outerhandle, write_timeout);
+ }
+}
+
+const char *
+isc__nm_tls_verify_tls_peer_result_string(const isc_nmhandle_t *handle) {
+ isc_nmsocket_t *sock = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+ REQUIRE(handle->sock->type == isc_nm_tlssocket);
+
+ sock = handle->sock;
+ if (sock->tlsstream.tls == NULL) {
+ return (NULL);
+ }
+
+ return (isc_tls_verify_peer_result_string(sock->tlsstream.tls));
+}
+
+static void
+tls_init_listener_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *ctx) {
+ size_t nworkers;
+
+ REQUIRE(VALID_NM(listener->mgr));
+ REQUIRE(ctx != NULL);
+
+ nworkers = (size_t)listener->mgr->nworkers;
+ INSIST(nworkers > 0);
+
+ listener->tlsstream.listener_tls_ctx = isc_mem_get(
+ listener->mgr->mctx, sizeof(isc_tlsctx_t *) * nworkers);
+ listener->tlsstream.n_listener_tls_ctx = nworkers;
+ for (size_t i = 0; i < nworkers; i++) {
+ listener->tlsstream.listener_tls_ctx[i] = NULL;
+ isc_tlsctx_attach(ctx,
+ &listener->tlsstream.listener_tls_ctx[i]);
+ }
+}
+
+static void
+tls_cleanup_listener_tlsctx(isc_nmsocket_t *listener) {
+ REQUIRE(VALID_NM(listener->mgr));
+
+ if (listener->tlsstream.listener_tls_ctx == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < listener->tlsstream.n_listener_tls_ctx; i++) {
+ isc_tlsctx_free(&listener->tlsstream.listener_tls_ctx[i]);
+ }
+ isc_mem_put(listener->mgr->mctx, listener->tlsstream.listener_tls_ctx,
+ sizeof(isc_tlsctx_t *) *
+ listener->tlsstream.n_listener_tls_ctx);
+ listener->tlsstream.n_listener_tls_ctx = 0;
+}
+
+static isc_tlsctx_t *
+tls_get_listener_tlsctx(isc_nmsocket_t *listener, const int tid) {
+ REQUIRE(VALID_NM(listener->mgr));
+ REQUIRE(tid >= 0);
+
+ if (listener->tlsstream.listener_tls_ctx == NULL) {
+ return (NULL);
+ }
+
+ return (listener->tlsstream.listener_tls_ctx[tid]);
+}
+
+void
+isc__nm_async_tls_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx,
+ const int tid) {
+ REQUIRE(tid >= 0);
+
+ isc_tlsctx_free(&listener->tlsstream.listener_tls_ctx[tid]);
+ isc_tlsctx_attach(tlsctx, &listener->tlsstream.listener_tls_ctx[tid]);
+}
+
+static void
+tls_keep_client_tls_session(isc_nmsocket_t *sock) {
+ /*
+ * Ensure that the isc_tls_t is being accessed from
+ * within the worker thread the socket is bound to.
+ */
+ REQUIRE(sock->tid == isc_nm_tid());
+ if (sock->tlsstream.client_sess_cache != NULL &&
+ sock->tlsstream.client_session_saved == false)
+ {
+ INSIST(atomic_load(&sock->client));
+ isc_tlsctx_client_session_cache_keep_sockaddr(
+ sock->tlsstream.client_sess_cache, &sock->peer,
+ sock->tlsstream.tls);
+ sock->tlsstream.client_session_saved = true;
+ }
+}
+
+static void
+tls_try_shutdown(isc_tls_t *tls, const bool force) {
+ if (force) {
+ (void)SSL_set_shutdown(tls, SSL_SENT_SHUTDOWN);
+ } else if ((SSL_get_shutdown(tls) & SSL_SENT_SHUTDOWN) == 0) {
+ (void)SSL_shutdown(tls);
+ }
+}
diff --git a/lib/isc/netmgr/udp.c b/lib/isc/netmgr/udp.c
new file mode 100644
index 0000000..1a0ee16
--- /dev/null
+++ b/lib/isc/netmgr/udp.c
@@ -0,0 +1,1405 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <unistd.h>
+#include <uv.h>
+
+#include <isc/atomic.h>
+#include <isc/barrier.h>
+#include <isc/buffer.h>
+#include <isc/condition.h>
+#include <isc/errno.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/netmgr.h>
+#include <isc/random.h>
+#include <isc/refcount.h>
+#include <isc/region.h>
+#include <isc/result.h>
+#include <isc/sockaddr.h>
+#include <isc/thread.h>
+#include <isc/util.h>
+
+#include "netmgr-int.h"
+#include "uv-compat.h"
+
+#ifdef HAVE_NET_ROUTE_H
+#include <net/route.h>
+#if defined(RTM_VERSION) && defined(RTM_NEWADDR) && defined(RTM_DELADDR)
+#define USE_ROUTE_SOCKET 1
+#define ROUTE_SOCKET_PF PF_ROUTE
+#define ROUTE_SOCKET_PROTOCOL 0
+#define MSGHDR rt_msghdr
+#define MSGTYPE rtm_type
+#endif /* if defined(RTM_VERSION) && defined(RTM_NEWADDR) && \
+ * defined(RTM_DELADDR) */
+#endif /* ifdef HAVE_NET_ROUTE_H */
+
+#if defined(HAVE_LINUX_NETLINK_H) && defined(HAVE_LINUX_RTNETLINK_H)
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#if defined(RTM_NEWADDR) && defined(RTM_DELADDR)
+#define USE_ROUTE_SOCKET 1
+#define USE_NETLINK 1
+#define ROUTE_SOCKET_PF PF_NETLINK
+#define ROUTE_SOCKET_PROTOCOL NETLINK_ROUTE
+#define MSGHDR nlmsghdr
+#define MSGTYPE nlmsg_type
+#endif /* if defined(RTM_NEWADDR) && defined(RTM_DELADDR) */
+#endif /* if defined(HAVE_LINUX_NETLINK_H) && defined(HAVE_LINUX_RTNETLINK_H) \
+ */
+
+static isc_result_t
+udp_send_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req,
+ isc_sockaddr_t *peer);
+
+static void
+udp_recv_cb(uv_udp_t *handle, ssize_t nrecv, const uv_buf_t *buf,
+ const struct sockaddr *addr, unsigned flags);
+
+static void
+udp_send_cb(uv_udp_send_t *req, int status);
+
+static void
+udp_close_cb(uv_handle_t *handle);
+
+static void
+read_timer_close_cb(uv_handle_t *handle);
+
+static void
+udp_close_direct(isc_nmsocket_t *sock);
+
+static void
+stop_udp_parent(isc_nmsocket_t *sock);
+static void
+stop_udp_child(isc_nmsocket_t *sock);
+
+static uv_os_sock_t
+isc__nm_udp_lb_socket(isc_nm_t *mgr, sa_family_t sa_family) {
+ isc_result_t result;
+ uv_os_sock_t sock;
+
+ result = isc__nm_socket(sa_family, SOCK_DGRAM, 0, &sock);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ (void)isc__nm_socket_incoming_cpu(sock);
+ (void)isc__nm_socket_disable_pmtud(sock, sa_family);
+ (void)isc__nm_socket_v6only(sock, sa_family);
+
+ result = isc__nm_socket_reuse(sock);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ if (mgr->load_balance_sockets) {
+ result = isc__nm_socket_reuse_lb(sock);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ }
+
+ return (sock);
+}
+
+static void
+start_udp_child(isc_nm_t *mgr, isc_sockaddr_t *iface, isc_nmsocket_t *sock,
+ uv_os_sock_t fd, int tid) {
+ isc_nmsocket_t *csock;
+ isc__netievent_udplisten_t *ievent = NULL;
+
+ csock = &sock->children[tid];
+
+ isc__nmsocket_init(csock, mgr, isc_nm_udpsocket, iface);
+ csock->parent = sock;
+ csock->iface = sock->iface;
+ atomic_init(&csock->reading, true);
+ csock->recv_cb = sock->recv_cb;
+ csock->recv_cbarg = sock->recv_cbarg;
+ csock->extrahandlesize = sock->extrahandlesize;
+ csock->tid = tid;
+
+ if (mgr->load_balance_sockets) {
+ UNUSED(fd);
+ csock->fd = isc__nm_udp_lb_socket(mgr,
+ iface->type.sa.sa_family);
+ } else {
+ csock->fd = dup(fd);
+ }
+ REQUIRE(csock->fd >= 0);
+
+ ievent = isc__nm_get_netievent_udplisten(mgr, csock);
+ isc__nm_maybe_enqueue_ievent(&mgr->workers[tid],
+ (isc__netievent_t *)ievent);
+}
+
+static void
+enqueue_stoplistening(isc_nmsocket_t *sock) {
+ isc__netievent_udpstop_t *ievent =
+ isc__nm_get_netievent_udpstop(sock->mgr, sock);
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+}
+
+isc_result_t
+isc_nm_listenudp(isc_nm_t *mgr, isc_sockaddr_t *iface, isc_nm_recv_cb_t cb,
+ void *cbarg, size_t extrahandlesize, isc_nmsocket_t **sockp) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_nmsocket_t *sock = NULL;
+ size_t children_size = 0;
+ REQUIRE(VALID_NM(mgr));
+ uv_os_sock_t fd = -1;
+
+ /*
+ * We are creating mgr->nworkers duplicated sockets, one
+ * socket for each worker thread.
+ */
+ sock = isc_mem_get(mgr->mctx, sizeof(isc_nmsocket_t));
+ isc__nmsocket_init(sock, mgr, isc_nm_udplistener, iface);
+
+ atomic_init(&sock->rchildren, 0);
+ sock->nchildren = mgr->nworkers;
+ children_size = sock->nchildren * sizeof(sock->children[0]);
+ sock->children = isc_mem_get(mgr->mctx, children_size);
+ memset(sock->children, 0, children_size);
+
+ sock->recv_cb = cb;
+ sock->recv_cbarg = cbarg;
+ sock->extrahandlesize = extrahandlesize;
+ sock->result = ISC_R_UNSET;
+
+ sock->tid = 0;
+ sock->fd = -1;
+
+ if (!mgr->load_balance_sockets) {
+ fd = isc__nm_udp_lb_socket(mgr, iface->type.sa.sa_family);
+ }
+
+ isc_barrier_init(&sock->startlistening, sock->nchildren);
+
+ for (size_t i = 0; i < sock->nchildren; i++) {
+ if ((int)i == isc_nm_tid()) {
+ continue;
+ }
+ start_udp_child(mgr, iface, sock, fd, i);
+ }
+
+ if (isc__nm_in_netthread()) {
+ start_udp_child(mgr, iface, sock, fd, isc_nm_tid());
+ }
+
+ if (!mgr->load_balance_sockets) {
+ isc__nm_closesocket(fd);
+ }
+
+ LOCK(&sock->lock);
+ while (atomic_load(&sock->rchildren) != sock->nchildren) {
+ WAIT(&sock->cond, &sock->lock);
+ }
+ result = sock->result;
+ atomic_store(&sock->active, true);
+ UNLOCK(&sock->lock);
+
+ INSIST(result != ISC_R_UNSET);
+
+ if (result == ISC_R_SUCCESS) {
+ REQUIRE(atomic_load(&sock->rchildren) == sock->nchildren);
+ *sockp = sock;
+ } else {
+ atomic_store(&sock->active, false);
+ enqueue_stoplistening(sock);
+ isc_nmsocket_close(&sock);
+ }
+
+ return (result);
+}
+
+#ifdef USE_ROUTE_SOCKET
+static isc_result_t
+route_socket(uv_os_sock_t *fdp) {
+ isc_result_t result;
+ uv_os_sock_t fd;
+#ifdef USE_NETLINK
+ struct sockaddr_nl sa;
+ int r;
+#endif
+
+ result = isc__nm_socket(ROUTE_SOCKET_PF, SOCK_RAW,
+ ROUTE_SOCKET_PROTOCOL, &fd);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+#ifdef USE_NETLINK
+ sa.nl_family = PF_NETLINK;
+ sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;
+ r = bind(fd, (struct sockaddr *)&sa, sizeof(sa));
+ if (r < 0) {
+ isc__nm_closesocket(fd);
+ return (isc_errno_toresult(r));
+ }
+#endif
+
+ *fdp = fd;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+route_connect_direct(isc_nmsocket_t *sock) {
+ isc__networker_t *worker = NULL;
+ isc_result_t result = ISC_R_UNSET;
+ int r;
+
+ REQUIRE(isc__nm_in_netthread());
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ worker = &sock->mgr->workers[isc_nm_tid()];
+
+ atomic_store(&sock->connecting, true);
+
+ r = uv_udp_init(&worker->loop, &sock->uv_handle.udp);
+ UV_RUNTIME_CHECK(uv_udp_init, r);
+ uv_handle_set_data(&sock->uv_handle.handle, sock);
+
+ r = uv_timer_init(&worker->loop, &sock->read_timer);
+ UV_RUNTIME_CHECK(uv_timer_init, r);
+ uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock);
+
+ if (isc__nm_closing(sock)) {
+ result = ISC_R_SHUTTINGDOWN;
+ goto error;
+ }
+
+ r = uv_udp_open(&sock->uv_handle.udp, sock->fd);
+ if (r != 0) {
+ goto done;
+ }
+
+ isc__nm_set_network_buffers(sock->mgr, &sock->uv_handle.handle);
+
+ atomic_store(&sock->connecting, false);
+ atomic_store(&sock->connected, true);
+
+done:
+ result = isc__nm_uverr2result(r);
+error:
+
+ LOCK(&sock->lock);
+ sock->result = result;
+ SIGNAL(&sock->cond);
+ if (!atomic_load(&sock->active)) {
+ WAIT(&sock->scond, &sock->lock);
+ }
+ INSIST(atomic_load(&sock->active));
+ UNLOCK(&sock->lock);
+
+ return (result);
+}
+
+/*
+ * Asynchronous 'udpconnect' call handler: open a new UDP socket and
+ * call the 'open' callback with a handle.
+ */
+void
+isc__nm_async_routeconnect(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_routeconnect_t *ievent =
+ (isc__netievent_routeconnect_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+ isc__nm_uvreq_t *req = ievent->req;
+ isc_result_t result;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_udpsocket);
+ REQUIRE(sock->parent == NULL);
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ result = route_connect_direct(sock);
+ if (result != ISC_R_SUCCESS) {
+ atomic_store(&sock->active, false);
+ isc__nm_udp_close(sock);
+ isc__nm_connectcb(sock, req, result, true);
+ } else {
+ /*
+ * The callback has to be called after the socket has been
+ * initialized
+ */
+ isc__nm_connectcb(sock, req, ISC_R_SUCCESS, true);
+ }
+
+ /*
+ * The sock is now attached to the handle.
+ */
+ isc__nmsocket_detach(&sock);
+}
+#endif /* USE_ROUTE_SOCKET */
+
+isc_result_t
+isc_nm_routeconnect(isc_nm_t *mgr, isc_nm_cb_t cb, void *cbarg,
+ size_t extrahandlesize) {
+#ifdef USE_ROUTE_SOCKET
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_nmsocket_t *sock = NULL;
+ isc__netievent_udpconnect_t *event = NULL;
+ isc__nm_uvreq_t *req = NULL;
+
+ REQUIRE(VALID_NM(mgr));
+
+ sock = isc_mem_get(mgr->mctx, sizeof(*sock));
+ isc__nmsocket_init(sock, mgr, isc_nm_udpsocket, NULL);
+
+ sock->connect_cb = cb;
+ sock->connect_cbarg = cbarg;
+ sock->extrahandlesize = extrahandlesize;
+ sock->result = ISC_R_UNSET;
+ atomic_init(&sock->client, true);
+ sock->route_sock = true;
+
+ req = isc__nm_uvreq_get(mgr, sock);
+ req->cb.connect = cb;
+ req->cbarg = cbarg;
+ req->handle = isc__nmhandle_get(sock, NULL, NULL);
+
+ result = route_socket(&sock->fd);
+ if (result != ISC_R_SUCCESS) {
+ if (isc__nm_in_netthread()) {
+ sock->tid = isc_nm_tid();
+ }
+ isc__nmsocket_clearcb(sock);
+ isc__nm_connectcb(sock, req, result, true);
+ atomic_store(&sock->closed, true);
+ isc__nmsocket_detach(&sock);
+ return (result);
+ }
+
+ event = isc__nm_get_netievent_routeconnect(mgr, sock, req);
+
+ if (isc__nm_in_netthread()) {
+ atomic_store(&sock->active, true);
+ sock->tid = isc_nm_tid();
+ isc__nm_async_routeconnect(&mgr->workers[sock->tid],
+ (isc__netievent_t *)event);
+ isc__nm_put_netievent_routeconnect(mgr, event);
+ } else {
+ atomic_init(&sock->active, false);
+ sock->tid = 0;
+ isc__nm_enqueue_ievent(&mgr->workers[sock->tid],
+ (isc__netievent_t *)event);
+ }
+ LOCK(&sock->lock);
+ while (sock->result == ISC_R_UNSET) {
+ WAIT(&sock->cond, &sock->lock);
+ }
+ atomic_store(&sock->active, true);
+ BROADCAST(&sock->scond);
+ UNLOCK(&sock->lock);
+
+ return (sock->result);
+#else /* USE_ROUTE_SOCKET */
+ UNUSED(mgr);
+ UNUSED(cb);
+ UNUSED(cbarg);
+ UNUSED(extrahandlesize);
+ return (ISC_R_NOTIMPLEMENTED);
+#endif /* USE_ROUTE_SOCKET */
+}
+
+/*
+ * Asynchronous 'udplisten' call handler: start listening on a UDP socket.
+ */
+void
+isc__nm_async_udplisten(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_udplisten_t *ievent = (isc__netievent_udplisten_t *)ev0;
+ isc_nmsocket_t *sock = NULL;
+ int r, uv_bind_flags = 0;
+ int uv_init_flags = 0;
+ sa_family_t sa_family;
+ isc_result_t result = ISC_R_UNSET;
+ isc_nm_t *mgr = NULL;
+
+ REQUIRE(VALID_NMSOCK(ievent->sock));
+ REQUIRE(ievent->sock->tid == isc_nm_tid());
+ REQUIRE(VALID_NMSOCK(ievent->sock->parent));
+
+ sock = ievent->sock;
+ sa_family = sock->iface.type.sa.sa_family;
+ mgr = sock->mgr;
+
+ REQUIRE(sock->type == isc_nm_udpsocket);
+ REQUIRE(sock->parent != NULL);
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ (void)isc__nm_socket_min_mtu(sock->fd, sa_family);
+
+#if HAVE_DECL_UV_UDP_RECVMMSG
+ uv_init_flags |= UV_UDP_RECVMMSG;
+#endif
+ r = uv_udp_init_ex(&worker->loop, &sock->uv_handle.udp, uv_init_flags);
+ UV_RUNTIME_CHECK(uv_udp_init_ex, r);
+ uv_handle_set_data(&sock->uv_handle.handle, sock);
+ /* This keeps the socket alive after everything else is gone */
+ isc__nmsocket_attach(sock, &(isc_nmsocket_t *){ NULL });
+
+ r = uv_timer_init(&worker->loop, &sock->read_timer);
+ UV_RUNTIME_CHECK(uv_timer_init, r);
+ uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock);
+
+ LOCK(&sock->parent->lock);
+
+ r = uv_udp_open(&sock->uv_handle.udp, sock->fd);
+ if (r < 0) {
+ isc__nm_closesocket(sock->fd);
+ isc__nm_incstats(sock, STATID_OPENFAIL);
+ goto done;
+ }
+ isc__nm_incstats(sock, STATID_OPEN);
+
+ if (sa_family == AF_INET6) {
+ uv_bind_flags |= UV_UDP_IPV6ONLY;
+ }
+
+ if (mgr->load_balance_sockets) {
+ r = isc_uv_udp_freebind(&sock->uv_handle.udp,
+ &sock->parent->iface.type.sa,
+ uv_bind_flags);
+ if (r < 0) {
+ isc__nm_incstats(sock, STATID_BINDFAIL);
+ goto done;
+ }
+ } else {
+ if (sock->parent->fd == -1) {
+ /* This thread is first, bind the socket */
+ r = isc_uv_udp_freebind(&sock->uv_handle.udp,
+ &sock->parent->iface.type.sa,
+ uv_bind_flags);
+ if (r < 0) {
+ isc__nm_incstats(sock, STATID_BINDFAIL);
+ goto done;
+ }
+ sock->parent->uv_handle.udp.flags =
+ sock->uv_handle.udp.flags;
+ sock->parent->fd = sock->fd;
+ } else {
+ /* The socket is already bound, just copy the flags */
+ sock->uv_handle.udp.flags =
+ sock->parent->uv_handle.udp.flags;
+ }
+ }
+
+ isc__nm_set_network_buffers(sock->mgr, &sock->uv_handle.handle);
+
+ r = uv_udp_recv_start(&sock->uv_handle.udp, isc__nm_alloc_cb,
+ udp_recv_cb);
+ if (r != 0) {
+ isc__nm_incstats(sock, STATID_BINDFAIL);
+ goto done;
+ }
+
+ atomic_store(&sock->listening, true);
+
+done:
+ result = isc__nm_uverr2result(r);
+ atomic_fetch_add(&sock->parent->rchildren, 1);
+ if (sock->parent->result == ISC_R_UNSET) {
+ sock->parent->result = result;
+ }
+ SIGNAL(&sock->parent->cond);
+ UNLOCK(&sock->parent->lock);
+
+ isc_barrier_wait(&sock->parent->startlistening);
+}
+
+void
+isc__nm_udp_stoplistening(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_udplistener);
+
+ if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false },
+ true))
+ {
+ UNREACHABLE();
+ }
+
+ if (!isc__nm_in_netthread()) {
+ enqueue_stoplistening(sock);
+ } else {
+ stop_udp_parent(sock);
+ }
+}
+
+/*
+ * Asynchronous 'udpstop' call handler: stop listening on a UDP socket.
+ */
+void
+isc__nm_async_udpstop(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_udpstop_t *ievent = (isc__netievent_udpstop_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ if (sock->parent != NULL) {
+ stop_udp_child(sock);
+ return;
+ }
+
+ stop_udp_parent(sock);
+}
+
+/*
+ * udp_recv_cb handles incoming UDP packet from uv. The buffer here is
+ * reused for a series of packets, so we need to allocate a new one.
+ * This new one can be reused to send the response then.
+ */
+static void
+udp_recv_cb(uv_udp_t *handle, ssize_t nrecv, const uv_buf_t *buf,
+ const struct sockaddr *addr, unsigned flags) {
+ isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)handle);
+ isc__nm_uvreq_t *req = NULL;
+ uint32_t maxudp;
+ isc_result_t result;
+ isc_sockaddr_t sockaddr, *sa = NULL;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(atomic_load(&sock->reading));
+
+ /*
+ * When using recvmmsg(2), if no errors occur, there will be a final
+ * callback with nrecv set to 0, addr set to NULL and the buffer
+ * pointing at the initially allocated data with the UV_UDP_MMSG_CHUNK
+ * flag cleared and the UV_UDP_MMSG_FREE flag set.
+ */
+#if HAVE_DECL_UV_UDP_MMSG_FREE
+ if ((flags & UV_UDP_MMSG_FREE) == UV_UDP_MMSG_FREE) {
+ INSIST(nrecv == 0);
+ INSIST(addr == NULL);
+ goto free;
+ }
+#else
+ UNUSED(flags);
+#endif
+
+ /*
+ * - If we're simulating a firewall blocking UDP packets
+ * bigger than 'maxudp' bytes for testing purposes.
+ */
+ maxudp = atomic_load(&sock->mgr->maxudp);
+ if ((maxudp != 0 && (uint32_t)nrecv > maxudp)) {
+ /*
+ * We need to keep the read_cb intact in case, so the
+ * readtimeout_cb can trigger and not crash because of
+ * missing read_req.
+ */
+ goto free;
+ }
+
+ /*
+ * - If there was a networking error.
+ */
+ if (nrecv < 0) {
+ isc__nm_failed_read_cb(sock, isc__nm_uverr2result(nrecv),
+ false);
+ goto free;
+ }
+
+ /*
+ * - If addr == NULL, in which case it's the end of stream;
+ * we can free the buffer and bail.
+ */
+ if (addr == NULL) {
+ isc__nm_failed_read_cb(sock, ISC_R_EOF, false);
+ goto free;
+ }
+
+ /*
+ * - If the socket is no longer active.
+ */
+ if (!isc__nmsocket_active(sock)) {
+ isc__nm_failed_read_cb(sock, ISC_R_CANCELED, false);
+ goto free;
+ }
+
+ if (!sock->route_sock) {
+ result = isc_sockaddr_fromsockaddr(&sockaddr, addr);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ sa = &sockaddr;
+ }
+
+ req = isc__nm_get_read_req(sock, sa);
+
+ /*
+ * The callback will be called synchronously, because result is
+ * ISC_R_SUCCESS, so we are ok of passing the buf directly.
+ */
+ req->uvbuf.base = buf->base;
+ req->uvbuf.len = nrecv;
+
+ sock->recv_read = false;
+
+ REQUIRE(!sock->processing);
+ sock->processing = true;
+ isc__nm_readcb(sock, req, ISC_R_SUCCESS);
+ sock->processing = false;
+
+free:
+#if HAVE_DECL_UV_UDP_MMSG_CHUNK
+ /*
+ * When using recvmmsg(2), chunks will have the UV_UDP_MMSG_CHUNK flag
+ * set, those must not be freed.
+ */
+ if ((flags & UV_UDP_MMSG_CHUNK) == UV_UDP_MMSG_CHUNK) {
+ return;
+ }
+#endif
+
+ /*
+ * When using recvmmsg(2), if a UDP socket error occurs, nrecv will be <
+ * 0. In either scenario, the callee can now safely free the provided
+ * buffer.
+ */
+ if (nrecv < 0) {
+ /*
+ * The buffer may be a null buffer on error.
+ */
+ if (buf->base == NULL && buf->len == 0) {
+ return;
+ }
+ }
+
+ isc__nm_free_uvbuf(sock, buf);
+}
+
+/*
+ * Send the data in 'region' to a peer via a UDP socket. We try to find
+ * a proper sibling/child socket so that we won't have to jump to
+ * another thread.
+ */
+void
+isc__nm_udp_send(isc_nmhandle_t *handle, const isc_region_t *region,
+ isc_nm_cb_t cb, void *cbarg) {
+ isc_nmsocket_t *sock = handle->sock;
+ isc_nmsocket_t *rsock = NULL;
+ isc_sockaddr_t *peer = &handle->peer;
+ isc__nm_uvreq_t *uvreq = NULL;
+ uint32_t maxudp = atomic_load(&sock->mgr->maxudp);
+ int ntid;
+
+ INSIST(sock->type == isc_nm_udpsocket);
+
+ /*
+ * We're simulating a firewall blocking UDP packets bigger than
+ * 'maxudp' bytes, for testing purposes.
+ *
+ * The client would ordinarily have unreferenced the handle
+ * in the callback, but that won't happen in this case, so
+ * we need to do so here.
+ */
+ if (maxudp != 0 && region->length > maxudp) {
+ isc_nmhandle_detach(&handle);
+ return;
+ }
+
+ if (atomic_load(&sock->client)) {
+ /*
+ * When we are sending from the client socket, we directly use
+ * the socket provided.
+ */
+ rsock = sock;
+ goto send;
+ } else {
+ /*
+ * When we are sending from the server socket, we either use the
+ * socket associated with the network thread we are in, or we
+ * use the thread from the socket associated with the handle.
+ */
+ INSIST(sock->parent != NULL);
+
+ if (isc__nm_in_netthread()) {
+ ntid = isc_nm_tid();
+ } else {
+ ntid = sock->tid;
+ }
+ rsock = &sock->parent->children[ntid];
+ }
+
+send:
+ uvreq = isc__nm_uvreq_get(rsock->mgr, rsock);
+ uvreq->uvbuf.base = (char *)region->base;
+ uvreq->uvbuf.len = region->length;
+
+ isc_nmhandle_attach(handle, &uvreq->handle);
+
+ uvreq->cb.send = cb;
+ uvreq->cbarg = cbarg;
+
+ if (isc_nm_tid() == rsock->tid) {
+ REQUIRE(rsock->tid == isc_nm_tid());
+ isc__netievent_udpsend_t ievent = { .sock = rsock,
+ .req = uvreq,
+ .peer = *peer };
+
+ isc__nm_async_udpsend(NULL, (isc__netievent_t *)&ievent);
+ } else {
+ isc__netievent_udpsend_t *ievent =
+ isc__nm_get_netievent_udpsend(sock->mgr, rsock);
+ ievent->peer = *peer;
+ ievent->req = uvreq;
+
+ isc__nm_enqueue_ievent(&sock->mgr->workers[rsock->tid],
+ (isc__netievent_t *)ievent);
+ }
+}
+
+/*
+ * Asynchronous 'udpsend' event handler: send a packet on a UDP socket.
+ */
+void
+isc__nm_async_udpsend(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc_result_t result;
+ isc__netievent_udpsend_t *ievent = (isc__netievent_udpsend_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+ isc__nm_uvreq_t *uvreq = ievent->req;
+
+ REQUIRE(sock->type == isc_nm_udpsocket);
+ REQUIRE(sock->tid == isc_nm_tid());
+ UNUSED(worker);
+
+ if (isc__nmsocket_closing(sock)) {
+ isc__nm_failed_send_cb(sock, uvreq, ISC_R_CANCELED);
+ return;
+ }
+
+ result = udp_send_direct(sock, uvreq, &ievent->peer);
+ if (result != ISC_R_SUCCESS) {
+ isc__nm_incstats(sock, STATID_SENDFAIL);
+ isc__nm_failed_send_cb(sock, uvreq, result);
+ }
+}
+
+static void
+udp_send_cb(uv_udp_send_t *req, int status) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc__nm_uvreq_t *uvreq = uv_handle_get_data((uv_handle_t *)req);
+ isc_nmsocket_t *sock = NULL;
+
+ REQUIRE(VALID_UVREQ(uvreq));
+ REQUIRE(VALID_NMHANDLE(uvreq->handle));
+
+ sock = uvreq->sock;
+
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ if (status < 0) {
+ result = isc__nm_uverr2result(status);
+ isc__nm_incstats(sock, STATID_SENDFAIL);
+ }
+
+ isc__nm_sendcb(sock, uvreq, result, false);
+}
+
+/*
+ * udp_send_direct sends buf to a peer on a socket. Sock has to be in
+ * the same thread as the callee.
+ */
+static isc_result_t
+udp_send_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req,
+ isc_sockaddr_t *peer) {
+ const struct sockaddr *sa = &peer->type.sa;
+ int r;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(VALID_UVREQ(req));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(sock->type == isc_nm_udpsocket);
+
+ if (isc__nmsocket_closing(sock)) {
+ return (ISC_R_CANCELED);
+ }
+
+#if UV_VERSION_HEX >= UV_VERSION(1, 27, 0)
+ /*
+ * If we used uv_udp_connect() (and not the shim version for
+ * older versions of libuv), then the peer address has to be
+ * set to NULL or else uv_udp_send() could fail or assert,
+ * depending on the libuv version.
+ */
+ if (atomic_load(&sock->connected)) {
+ sa = NULL;
+ }
+#endif
+
+ r = uv_udp_send(&req->uv_req.udp_send, &sock->uv_handle.udp,
+ &req->uvbuf, 1, sa, udp_send_cb);
+ if (r < 0) {
+ return (isc__nm_uverr2result(r));
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+udp_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) {
+ isc__networker_t *worker = NULL;
+ int uv_bind_flags = UV_UDP_REUSEADDR;
+ isc_result_t result = ISC_R_UNSET;
+ int r;
+
+ REQUIRE(isc__nm_in_netthread());
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ worker = &sock->mgr->workers[isc_nm_tid()];
+
+ atomic_store(&sock->connecting, true);
+
+ r = uv_udp_init(&worker->loop, &sock->uv_handle.udp);
+ UV_RUNTIME_CHECK(uv_udp_init, r);
+ uv_handle_set_data(&sock->uv_handle.handle, sock);
+
+ r = uv_timer_init(&worker->loop, &sock->read_timer);
+ UV_RUNTIME_CHECK(uv_timer_init, r);
+ uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock);
+
+ if (isc__nm_closing(sock)) {
+ result = ISC_R_SHUTTINGDOWN;
+ goto error;
+ }
+
+ r = uv_udp_open(&sock->uv_handle.udp, sock->fd);
+ if (r != 0) {
+ isc__nm_incstats(sock, STATID_OPENFAIL);
+ goto done;
+ }
+ isc__nm_incstats(sock, STATID_OPEN);
+
+ if (sock->iface.type.sa.sa_family == AF_INET6) {
+ uv_bind_flags |= UV_UDP_IPV6ONLY;
+ }
+
+ r = uv_udp_bind(&sock->uv_handle.udp, &sock->iface.type.sa,
+ uv_bind_flags);
+ if (r != 0) {
+ isc__nm_incstats(sock, STATID_BINDFAIL);
+ goto done;
+ }
+
+ isc__nm_set_network_buffers(sock->mgr, &sock->uv_handle.handle);
+
+ /*
+ * On FreeBSD the UDP connect() call sometimes results in a
+ * spurious transient EADDRINUSE. Try a few more times before
+ * giving up.
+ */
+ do {
+ r = isc_uv_udp_connect(&sock->uv_handle.udp,
+ &req->peer.type.sa);
+ } while (r == UV_EADDRINUSE && --req->connect_tries > 0);
+ if (r != 0) {
+ isc__nm_incstats(sock, STATID_CONNECTFAIL);
+ goto done;
+ }
+ isc__nm_incstats(sock, STATID_CONNECT);
+
+ atomic_store(&sock->connecting, false);
+ atomic_store(&sock->connected, true);
+
+done:
+ result = isc__nm_uverr2result(r);
+error:
+
+ LOCK(&sock->lock);
+ sock->result = result;
+ SIGNAL(&sock->cond);
+ if (!atomic_load(&sock->active)) {
+ WAIT(&sock->scond, &sock->lock);
+ }
+ INSIST(atomic_load(&sock->active));
+ UNLOCK(&sock->lock);
+
+ return (result);
+}
+
+/*
+ * Asynchronous 'udpconnect' call handler: open a new UDP socket and
+ * call the 'open' callback with a handle.
+ */
+void
+isc__nm_async_udpconnect(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_udpconnect_t *ievent =
+ (isc__netievent_udpconnect_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+ isc__nm_uvreq_t *req = ievent->req;
+ isc_result_t result;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_udpsocket);
+ REQUIRE(sock->parent == NULL);
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ result = udp_connect_direct(sock, req);
+ if (result != ISC_R_SUCCESS) {
+ atomic_store(&sock->active, false);
+ isc__nm_udp_close(sock);
+ isc__nm_connectcb(sock, req, result, true);
+ } else {
+ /*
+ * The callback has to be called after the socket has been
+ * initialized
+ */
+ isc__nm_connectcb(sock, req, ISC_R_SUCCESS, true);
+ }
+
+ /*
+ * The sock is now attached to the handle.
+ */
+ isc__nmsocket_detach(&sock);
+}
+
+void
+isc_nm_udpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
+ isc_nm_cb_t cb, void *cbarg, unsigned int timeout,
+ size_t extrahandlesize) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_nmsocket_t *sock = NULL;
+ isc__netievent_udpconnect_t *event = NULL;
+ isc__nm_uvreq_t *req = NULL;
+ sa_family_t sa_family;
+
+ REQUIRE(VALID_NM(mgr));
+ REQUIRE(local != NULL);
+ REQUIRE(peer != NULL);
+
+ sa_family = peer->type.sa.sa_family;
+
+ sock = isc_mem_get(mgr->mctx, sizeof(isc_nmsocket_t));
+ isc__nmsocket_init(sock, mgr, isc_nm_udpsocket, local);
+
+ sock->connect_cb = cb;
+ sock->connect_cbarg = cbarg;
+ sock->read_timeout = timeout;
+ sock->extrahandlesize = extrahandlesize;
+ sock->peer = *peer;
+ sock->result = ISC_R_UNSET;
+ atomic_init(&sock->client, true);
+
+ req = isc__nm_uvreq_get(mgr, sock);
+ req->cb.connect = cb;
+ req->cbarg = cbarg;
+ req->peer = *peer;
+ req->local = *local;
+ req->handle = isc__nmhandle_get(sock, &req->peer, &sock->iface);
+
+ result = isc__nm_socket(sa_family, SOCK_DGRAM, 0, &sock->fd);
+ if (result != ISC_R_SUCCESS) {
+ if (isc__nm_in_netthread()) {
+ sock->tid = isc_nm_tid();
+ }
+ isc__nmsocket_clearcb(sock);
+ isc__nm_connectcb(sock, req, result, true);
+ atomic_store(&sock->closed, true);
+ isc__nmsocket_detach(&sock);
+ return;
+ }
+
+ result = isc__nm_socket_reuse(sock->fd);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS ||
+ result == ISC_R_NOTIMPLEMENTED);
+
+ result = isc__nm_socket_reuse_lb(sock->fd);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS ||
+ result == ISC_R_NOTIMPLEMENTED);
+
+ (void)isc__nm_socket_incoming_cpu(sock->fd);
+
+ (void)isc__nm_socket_disable_pmtud(sock->fd, sa_family);
+
+ (void)isc__nm_socket_min_mtu(sock->fd, sa_family);
+
+ event = isc__nm_get_netievent_udpconnect(mgr, sock, req);
+
+ if (isc__nm_in_netthread()) {
+ atomic_store(&sock->active, true);
+ sock->tid = isc_nm_tid();
+ isc__nm_async_udpconnect(&mgr->workers[sock->tid],
+ (isc__netievent_t *)event);
+ isc__nm_put_netievent_udpconnect(mgr, event);
+ } else {
+ atomic_init(&sock->active, false);
+ sock->tid = isc_random_uniform(mgr->nworkers);
+ isc__nm_enqueue_ievent(&mgr->workers[sock->tid],
+ (isc__netievent_t *)event);
+ }
+ LOCK(&sock->lock);
+ while (sock->result == ISC_R_UNSET) {
+ WAIT(&sock->cond, &sock->lock);
+ }
+ atomic_store(&sock->active, true);
+ BROADCAST(&sock->scond);
+ UNLOCK(&sock->lock);
+}
+
+void
+isc__nm_udp_read_cb(uv_udp_t *handle, ssize_t nrecv, const uv_buf_t *buf,
+ const struct sockaddr *addr, unsigned flags) {
+ isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)handle);
+ REQUIRE(VALID_NMSOCK(sock));
+
+ udp_recv_cb(handle, nrecv, buf, addr, flags);
+ /*
+ * If a caller calls isc_nm_read() on a listening socket, we can
+ * get here, but we MUST NOT stop reading from the listener
+ * socket. The only difference between listener and connected
+ * sockets is that the former has sock->parent set and later
+ * does not.
+ */
+ if (!sock->parent) {
+ isc__nmsocket_timer_stop(sock);
+ isc__nm_stop_reading(sock);
+ }
+}
+
+void
+isc__nm_udp_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(result != ISC_R_SUCCESS);
+
+ if (atomic_load(&sock->client)) {
+ isc__nmsocket_timer_stop(sock);
+ isc__nm_stop_reading(sock);
+
+ if (!sock->recv_read) {
+ goto destroy;
+ }
+ sock->recv_read = false;
+
+ if (sock->recv_cb != NULL) {
+ isc__nm_uvreq_t *req = isc__nm_get_read_req(sock, NULL);
+ isc__nmsocket_clearcb(sock);
+ isc__nm_readcb(sock, req, result);
+ }
+
+ destroy:
+ isc__nmsocket_prep_destroy(sock);
+ return;
+ }
+
+ /*
+ * For UDP server socket, we don't have child socket via
+ * "accept", so we:
+ * - we continue to read
+ * - we don't clear the callbacks
+ * - we don't destroy it (only stoplistening could do that)
+ */
+ if (!sock->recv_read) {
+ return;
+ }
+ sock->recv_read = false;
+
+ if (sock->recv_cb != NULL) {
+ isc__nm_uvreq_t *req = isc__nm_get_read_req(sock, NULL);
+ isc__nm_readcb(sock, req, result);
+ }
+}
+
+/*
+ * Asynchronous 'udpread' call handler: start or resume reading on a
+ * socket; pause reading and call the 'recv' callback after each
+ * datagram.
+ */
+void
+isc__nm_async_udpread(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_udpread_t *ievent = (isc__netievent_udpread_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+ isc_result_t result;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ if (isc__nm_closing(sock)) {
+ result = ISC_R_SHUTTINGDOWN;
+ } else if (isc__nmsocket_closing(sock)) {
+ result = ISC_R_CANCELED;
+ } else {
+ result = isc__nm_start_reading(sock);
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ atomic_store(&sock->reading, true);
+ isc__nm_failed_read_cb(sock, result, false);
+ return;
+ }
+
+ isc__nmsocket_timer_start(sock);
+}
+
+void
+isc__nm_udp_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
+ REQUIRE(VALID_NMHANDLE(handle));
+ REQUIRE(VALID_NMSOCK(handle->sock));
+
+ isc_nmsocket_t *sock = handle->sock;
+
+ REQUIRE(sock->type == isc_nm_udpsocket);
+ REQUIRE(sock->statichandle == handle);
+ REQUIRE(!sock->recv_read);
+
+ sock->recv_cb = cb;
+ sock->recv_cbarg = cbarg;
+ sock->recv_read = true;
+
+ if (!atomic_load(&sock->reading) && sock->tid == isc_nm_tid()) {
+ isc__netievent_udpread_t ievent = { .sock = sock };
+ isc__nm_async_udpread(NULL, (isc__netievent_t *)&ievent);
+ } else {
+ isc__netievent_udpread_t *ievent =
+ isc__nm_get_netievent_udpread(sock->mgr, sock);
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+ }
+}
+
+static void
+udp_stop_cb(uv_handle_t *handle) {
+ isc_nmsocket_t *sock = uv_handle_get_data(handle);
+ uv_handle_set_data(handle, NULL);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(atomic_load(&sock->closing));
+
+ if (!atomic_compare_exchange_strong(&sock->closed, &(bool){ false },
+ true))
+ {
+ UNREACHABLE();
+ }
+
+ isc__nm_incstats(sock, STATID_CLOSE);
+
+ atomic_store(&sock->listening, false);
+
+ isc__nmsocket_detach(&sock);
+}
+
+static void
+udp_close_cb(uv_handle_t *handle) {
+ isc_nmsocket_t *sock = uv_handle_get_data(handle);
+ uv_handle_set_data(handle, NULL);
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(atomic_load(&sock->closing));
+
+ if (!atomic_compare_exchange_strong(&sock->closed, &(bool){ false },
+ true))
+ {
+ UNREACHABLE();
+ }
+
+ isc__nm_incstats(sock, STATID_CLOSE);
+
+ if (sock->server != NULL) {
+ isc__nmsocket_detach(&sock->server);
+ }
+
+ atomic_store(&sock->connected, false);
+ atomic_store(&sock->listening, false);
+
+ isc__nmsocket_prep_destroy(sock);
+}
+
+static void
+read_timer_close_cb(uv_handle_t *handle) {
+ isc_nmsocket_t *sock = uv_handle_get_data(handle);
+ uv_handle_set_data(handle, NULL);
+
+ if (sock->parent) {
+ uv_close(&sock->uv_handle.handle, udp_stop_cb);
+ } else {
+ uv_close(&sock->uv_handle.handle, udp_close_cb);
+ }
+}
+
+static void
+stop_udp_child(isc_nmsocket_t *sock) {
+ REQUIRE(sock->type == isc_nm_udpsocket);
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false },
+ true))
+ {
+ return;
+ }
+
+ udp_close_direct(sock);
+
+ atomic_fetch_sub(&sock->parent->rchildren, 1);
+
+ isc_barrier_wait(&sock->parent->stoplistening);
+}
+
+static void
+stop_udp_parent(isc_nmsocket_t *sock) {
+ isc_nmsocket_t *csock = NULL;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(sock->type == isc_nm_udplistener);
+
+ isc_barrier_init(&sock->stoplistening, sock->nchildren);
+
+ for (size_t i = 0; i < sock->nchildren; i++) {
+ csock = &sock->children[i];
+ REQUIRE(VALID_NMSOCK(csock));
+
+ if ((int)i == isc_nm_tid()) {
+ /*
+ * We need to schedule closing the other sockets first
+ */
+ continue;
+ }
+
+ atomic_store(&csock->active, false);
+ enqueue_stoplistening(csock);
+ }
+
+ csock = &sock->children[isc_nm_tid()];
+ atomic_store(&csock->active, false);
+ stop_udp_child(csock);
+
+ atomic_store(&sock->closed, true);
+ isc__nmsocket_prep_destroy(sock);
+}
+
+static void
+udp_close_direct(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+
+ uv_handle_set_data((uv_handle_t *)&sock->read_timer, sock);
+ uv_close((uv_handle_t *)&sock->read_timer, read_timer_close_cb);
+}
+
+void
+isc__nm_async_udpclose(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_udpclose_t *ievent = (isc__netievent_udpclose_t *)ev0;
+ isc_nmsocket_t *sock = ievent->sock;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ UNUSED(worker);
+
+ udp_close_direct(sock);
+}
+
+void
+isc__nm_udp_close(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_udpsocket);
+ REQUIRE(!isc__nmsocket_active(sock));
+
+ if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false },
+ true))
+ {
+ return;
+ }
+
+ if (sock->tid == isc_nm_tid()) {
+ udp_close_direct(sock);
+ } else {
+ isc__netievent_udpclose_t *ievent =
+ isc__nm_get_netievent_udpclose(sock->mgr, sock);
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+ }
+}
+
+void
+isc__nm_udp_shutdown(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(sock->type == isc_nm_udpsocket);
+
+ /*
+ * If the socket is active, mark it inactive and
+ * continue. If it isn't active, stop now.
+ */
+ if (!isc__nmsocket_deactivate(sock)) {
+ return;
+ }
+
+ /*
+ * If the socket is connecting, the cancel will happen in the
+ * async_udpconnect() due socket being inactive now.
+ */
+ if (atomic_load(&sock->connecting)) {
+ return;
+ }
+
+ /*
+ * When the client detaches the last handle, the
+ * sock->statichandle would be NULL, in that case, nobody is
+ * interested in the callback.
+ */
+ if (sock->statichandle != NULL) {
+ if (isc__nm_closing(sock)) {
+ isc__nm_failed_read_cb(sock, ISC_R_SHUTTINGDOWN, false);
+ } else {
+ isc__nm_failed_read_cb(sock, ISC_R_CANCELED, false);
+ }
+ return;
+ }
+
+ /*
+ * Otherwise, we just send the socket to abyss...
+ */
+ if (sock->parent == NULL) {
+ isc__nmsocket_prep_destroy(sock);
+ }
+}
+
+void
+isc__nm_udp_cancelread(isc_nmhandle_t *handle) {
+ isc_nmsocket_t *sock = NULL;
+ isc__netievent_udpcancel_t *ievent = NULL;
+
+ REQUIRE(VALID_NMHANDLE(handle));
+
+ sock = handle->sock;
+
+ REQUIRE(VALID_NMSOCK(sock));
+ REQUIRE(sock->type == isc_nm_udpsocket);
+
+ ievent = isc__nm_get_netievent_udpcancel(sock->mgr, sock, handle);
+
+ isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+ (isc__netievent_t *)ievent);
+}
+
+void
+isc__nm_async_udpcancel(isc__networker_t *worker, isc__netievent_t *ev0) {
+ isc__netievent_udpcancel_t *ievent = (isc__netievent_udpcancel_t *)ev0;
+ isc_nmsocket_t *sock = NULL;
+
+ UNUSED(worker);
+
+ REQUIRE(VALID_NMSOCK(ievent->sock));
+
+ sock = ievent->sock;
+
+ REQUIRE(sock->tid == isc_nm_tid());
+ REQUIRE(atomic_load(&sock->client));
+
+ isc__nm_failed_read_cb(sock, ISC_R_EOF, false);
+}
diff --git a/lib/isc/netmgr/uv-compat.c b/lib/isc/netmgr/uv-compat.c
new file mode 100644
index 0000000..b7c0f7b
--- /dev/null
+++ b/lib/isc/netmgr/uv-compat.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include "uv-compat.h"
+#include <unistd.h>
+
+#include <isc/util.h>
+
+#include "netmgr-int.h"
+
+#if UV_VERSION_HEX < UV_VERSION(1, 27, 0)
+int
+isc_uv_udp_connect(uv_udp_t *handle, const struct sockaddr *addr) {
+ int err = 0;
+
+ do {
+ int addrlen = (addr->sa_family == AF_INET)
+ ? sizeof(struct sockaddr_in)
+ : sizeof(struct sockaddr_in6);
+ err = connect(handle->io_watcher.fd, addr, addrlen);
+ } while (err == -1 && errno == EINTR);
+
+ if (err) {
+#if UV_VERSION_HEX >= UV_VERSION(1, 10, 0)
+ return (uv_translate_sys_error(errno));
+#else
+ return (-errno);
+#endif /* UV_VERSION_HEX >= UV_VERSION(1, 10, 0) */
+ }
+
+ return (0);
+}
+#endif /* UV_VERSION_HEX < UV_VERSION(1, 27, 0) */
+
+#if UV_VERSION_HEX < UV_VERSION(1, 32, 0)
+int
+uv_tcp_close_reset(uv_tcp_t *handle, uv_close_cb close_cb) {
+ if (setsockopt(handle->io_watcher.fd, SOL_SOCKET, SO_LINGER,
+ &(struct linger){ 1, 0 }, sizeof(struct linger)) == -1)
+ {
+#if UV_VERSION_HEX >= UV_VERSION(1, 10, 0)
+ return (uv_translate_sys_error(errno));
+#else
+ return (-errno);
+#endif /* UV_VERSION_HEX >= UV_VERSION(1, 10, 0) */
+ }
+
+ uv_close((uv_handle_t *)handle, close_cb);
+ return (0);
+}
+#endif /* UV_VERSION_HEX < UV_VERSION(1, 32, 0) */
+
+int
+isc_uv_udp_freebind(uv_udp_t *handle, const struct sockaddr *addr,
+ unsigned int flags) {
+ int r;
+ uv_os_sock_t fd;
+
+ r = uv_fileno((const uv_handle_t *)handle, (uv_os_fd_t *)&fd);
+ if (r < 0) {
+ return (r);
+ }
+
+ r = uv_udp_bind(handle, addr, flags);
+ if (r == UV_EADDRNOTAVAIL &&
+ isc__nm_socket_freebind(fd, addr->sa_family) == ISC_R_SUCCESS)
+ {
+ /*
+ * Retry binding with IP_FREEBIND (or equivalent option) if the
+ * address is not available. This helps with IPv6 tentative
+ * addresses which are reported by the route socket, although
+ * named is not yet able to properly bind to them.
+ */
+ r = uv_udp_bind(handle, addr, flags);
+ }
+
+ return (r);
+}
+
+static int
+isc__uv_tcp_bind_now(uv_tcp_t *handle, const struct sockaddr *addr,
+ unsigned int flags) {
+ int r;
+ struct sockaddr_storage sname;
+ int snamelen = sizeof(sname);
+
+ r = uv_tcp_bind(handle, addr, flags);
+ if (r < 0) {
+ return (r);
+ }
+
+ /*
+ * uv_tcp_bind() uses a delayed error, initially returning
+ * success even if bind() fails. By calling uv_tcp_getsockname()
+ * here we can find out whether the bind() call was successful.
+ */
+ r = uv_tcp_getsockname(handle, (struct sockaddr *)&sname, &snamelen);
+ if (r < 0) {
+ return (r);
+ }
+
+ return (0);
+}
+
+int
+isc_uv_tcp_freebind(uv_tcp_t *handle, const struct sockaddr *addr,
+ unsigned int flags) {
+ int r;
+ uv_os_sock_t fd;
+
+ r = uv_fileno((const uv_handle_t *)handle, (uv_os_fd_t *)&fd);
+ if (r < 0) {
+ return (r);
+ }
+
+ r = isc__uv_tcp_bind_now(handle, addr, flags);
+ if (r == UV_EADDRNOTAVAIL &&
+ isc__nm_socket_freebind(fd, addr->sa_family) == ISC_R_SUCCESS)
+ {
+ /*
+ * Retry binding with IP_FREEBIND (or equivalent option) if the
+ * address is not available. This helps with IPv6 tentative
+ * addresses which are reported by the route socket, although
+ * named is not yet able to properly bind to them.
+ */
+ r = isc__uv_tcp_bind_now(handle, addr, flags);
+ }
+
+ return (r);
+}
diff --git a/lib/isc/netmgr/uv-compat.h b/lib/isc/netmgr/uv-compat.h
new file mode 100644
index 0000000..3a10387
--- /dev/null
+++ b/lib/isc/netmgr/uv-compat.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+#include <uv.h>
+
+/*
+ * These functions were introduced in newer libuv, but we still
+ * want BIND9 compile on older ones so we emulate them.
+ * They're inline to avoid conflicts when running with a newer
+ * library version.
+ */
+
+#define UV_VERSION(major, minor, patch) ((major << 16) | (minor << 8) | (patch))
+
+/*
+ * Copied verbatim from libuv/src/version.c
+ */
+
+#define UV_STRINGIFY(v) UV_STRINGIFY_HELPER(v)
+#define UV_STRINGIFY_HELPER(v) #v
+
+#define UV_VERSION_STRING_BASE \
+ UV_STRINGIFY(UV_VERSION_MAJOR) \
+ "." UV_STRINGIFY(UV_VERSION_MINOR) "." UV_STRINGIFY(UV_VERSION_PATCH)
+
+#if UV_VERSION_IS_RELEASE
+#define UV_VERSION_STRING UV_VERSION_STRING_BASE
+#else
+#define UV_VERSION_STRING UV_VERSION_STRING_BASE "-" UV_VERSION_SUFFIX
+#endif
+
+#if !defined(UV__ERR)
+#define UV__ERR(x) (-(x))
+#endif
+
+#if UV_VERSION_HEX < UV_VERSION(1, 19, 0)
+static inline void *
+uv_handle_get_data(const uv_handle_t *handle) {
+ return (handle->data);
+}
+
+static inline void
+uv_handle_set_data(uv_handle_t *handle, void *data) {
+ handle->data = data;
+}
+
+static inline void *
+uv_req_get_data(const uv_req_t *req) {
+ return (req->data);
+}
+
+static inline void
+uv_req_set_data(uv_req_t *req, void *data) {
+ req->data = data;
+}
+#endif /* UV_VERSION_HEX < UV_VERSION(1, 19, 0) */
+
+#if UV_VERSION_HEX < UV_VERSION(1, 32, 0)
+int
+uv_tcp_close_reset(uv_tcp_t *handle, uv_close_cb close_cb);
+#endif
+
+#if UV_VERSION_HEX < UV_VERSION(1, 34, 0)
+#define uv_sleep(msec) usleep(msec * 1000)
+#endif /* UV_VERSION_HEX < UV_VERSION(1, 34, 0) */
+
+#if UV_VERSION_HEX < UV_VERSION(1, 27, 0)
+int
+isc_uv_udp_connect(uv_udp_t *handle, const struct sockaddr *addr);
+/*%<
+ * Associate the UDP handle to a remote address and port, so every message sent
+ * by this handle is automatically sent to that destination.
+ *
+ * NOTE: This is just a limited shim for uv_udp_connect() as it requires the
+ * handle to be bound.
+ */
+#else /* UV_VERSION_HEX < UV_VERSION(1, 27, 0) */
+#define isc_uv_udp_connect uv_udp_connect
+#endif /* UV_VERSION_HEX < UV_VERSION(1, 27, 0) */
+
+#if UV_VERSION_HEX < UV_VERSION(1, 12, 0)
+#include <stdlib.h>
+#include <string.h>
+
+static inline int
+uv_os_getenv(const char *name, char *buffer, size_t *size) {
+ size_t len;
+ char *buf = getenv(name);
+
+ if (buf == NULL) {
+ return (UV_ENOENT);
+ }
+
+ len = strlen(buf) + 1;
+ if (len > *size) {
+ *size = len;
+ return (UV_ENOBUFS);
+ }
+
+ *size = len;
+ memmove(buffer, buf, len);
+
+ return (0);
+}
+
+#define uv_os_setenv(name, value) setenv(name, value, 0)
+#endif /* UV_VERSION_HEX < UV_VERSION(1, 12, 0) */
+
+int
+isc_uv_udp_freebind(uv_udp_t *handle, const struct sockaddr *addr,
+ unsigned int flags);
+
+int
+isc_uv_tcp_freebind(uv_tcp_t *handle, const struct sockaddr *addr,
+ unsigned int flags);
diff --git a/lib/isc/netmgr/uverr2result.c b/lib/isc/netmgr/uverr2result.c
new file mode 100644
index 0000000..9f16ea8
--- /dev/null
+++ b/lib/isc/netmgr/uverr2result.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <stdbool.h>
+#include <uv.h>
+
+#include <isc/result.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include "netmgr-int.h"
+
+/*%
+ * Convert a libuv error value into an isc_result_t. The
+ * list of supported error values is not complete; new users
+ * of this function should add any expected errors that are
+ * not already there.
+ */
+isc_result_t
+isc___nm_uverr2result(int uverr, bool dolog, const char *file,
+ unsigned int line, const char *func) {
+ switch (uverr) {
+ case 0:
+ return (ISC_R_SUCCESS);
+ case UV_ENOTDIR:
+ case UV_ELOOP:
+ case UV_EINVAL: /* XXX sometimes this is not for files */
+ case UV_ENAMETOOLONG:
+ case UV_EBADF:
+ return (ISC_R_INVALIDFILE);
+ case UV_ENOENT:
+ return (ISC_R_FILENOTFOUND);
+ case UV_EAGAIN:
+ return (ISC_R_NOCONN);
+ case UV_EACCES:
+ case UV_EPERM:
+ return (ISC_R_NOPERM);
+ case UV_EEXIST:
+ return (ISC_R_FILEEXISTS);
+ case UV_EIO:
+ return (ISC_R_IOERROR);
+ case UV_ENOMEM:
+ return (ISC_R_NOMEMORY);
+ case UV_ENFILE:
+ case UV_EMFILE:
+ return (ISC_R_TOOMANYOPENFILES);
+ case UV_ENOSPC:
+ return (ISC_R_DISCFULL);
+ case UV_EPIPE:
+ case UV_ECONNRESET:
+ case UV_ECONNABORTED:
+ return (ISC_R_CONNECTIONRESET);
+ case UV_ENOTCONN:
+ return (ISC_R_NOTCONNECTED);
+ case UV_ETIMEDOUT:
+ return (ISC_R_TIMEDOUT);
+ case UV_ENOBUFS:
+ return (ISC_R_NORESOURCES);
+ case UV_EAFNOSUPPORT:
+ return (ISC_R_FAMILYNOSUPPORT);
+ case UV_ENETDOWN:
+ return (ISC_R_NETDOWN);
+ case UV_EHOSTDOWN:
+ return (ISC_R_HOSTDOWN);
+ case UV_ENETUNREACH:
+ return (ISC_R_NETUNREACH);
+ case UV_EHOSTUNREACH:
+ return (ISC_R_HOSTUNREACH);
+ case UV_EADDRINUSE:
+ return (ISC_R_ADDRINUSE);
+ case UV_EADDRNOTAVAIL:
+ return (ISC_R_ADDRNOTAVAIL);
+ case UV_ECONNREFUSED:
+ return (ISC_R_CONNREFUSED);
+ case UV_ECANCELED:
+ return (ISC_R_CANCELED);
+ case UV_EOF:
+ return (ISC_R_EOF);
+ case UV_EMSGSIZE:
+ return (ISC_R_MAXSIZE);
+ case UV_ENOTSUP:
+ return (ISC_R_FAMILYNOSUPPORT);
+ case UV_ENOPROTOOPT:
+ case UV_EPROTONOSUPPORT:
+ return (ISC_R_INVALIDPROTO);
+ default:
+ if (dolog) {
+ UNEXPECTED_ERROR("unable to convert libuv error code "
+ "in %s (%s:%d) to isc_result: %d: %s",
+ func, file, line, uverr,
+ uv_strerror(uverr));
+ }
+ return (ISC_R_UNEXPECTED);
+ }
+}
diff --git a/lib/isc/netmgr_p.h b/lib/isc/netmgr_p.h
new file mode 100644
index 0000000..73171a9
--- /dev/null
+++ b/lib/isc/netmgr_p.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <isc/mem.h>
+#include <isc/result.h>
+
+void
+isc__netmgr_create(isc_mem_t *mctx, uint32_t workers, isc_nm_t **netgmrp);
+/*%<
+ * Creates a new network manager with 'workers' worker threads,
+ * and starts it running.
+ */
+
+void
+isc__netmgr_destroy(isc_nm_t **netmgrp);
+/*%<
+ * Similar to isc_nm_detach(), but actively waits for all other references
+ * to be gone before returning.
+ */
+
+void
+isc__netmgr_shutdown(isc_nm_t *mgr);
+/*%<
+ * Shut down all active connections, freeing associated resources;
+ * prevent new connections from being established.
+ */
diff --git a/lib/isc/netscope.c b/lib/isc/netscope.c
new file mode 100644
index 0000000..69c3e9a
--- /dev/null
+++ b/lib/isc/netscope.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include <isc/net.h>
+#include <isc/netscope.h>
+#include <isc/result.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+isc_result_t
+isc_netscope_pton(int af, char *scopename, void *addr, uint32_t *zoneid) {
+ char *ep;
+#ifdef HAVE_IF_NAMETOINDEX
+ unsigned int ifid;
+ struct in6_addr *in6;
+#endif /* ifdef HAVE_IF_NAMETOINDEX */
+ uint32_t zone = 0;
+ uint64_t llz;
+
+#ifndef HAVE_IF_NAMETOINDEX
+ UNUSED(addr);
+#endif
+
+ /* at this moment, we only support AF_INET6 */
+ if (af != AF_INET6) {
+ return (ISC_R_FAILURE);
+ }
+
+ /*
+ * Basically, "names" are more stable than numeric IDs in terms
+ * of renumbering, and are more preferred. However, since there
+ * is no standard naming convention and APIs to deal with the
+ * names. Thus, we only handle the case of link-local
+ * addresses, for which we use interface names as link names,
+ * assuming one to one mapping between interfaces and links.
+ */
+#ifdef HAVE_IF_NAMETOINDEX
+ in6 = (struct in6_addr *)addr;
+ if (IN6_IS_ADDR_LINKLOCAL(in6) &&
+ (ifid = if_nametoindex((const char *)scopename)) != 0)
+ {
+ zone = (uint32_t)ifid;
+ } else {
+#endif /* ifdef HAVE_IF_NAMETOINDEX */
+ llz = strtoull(scopename, &ep, 10);
+ if (ep == scopename) {
+ return (ISC_R_FAILURE);
+ }
+
+ /* check overflow */
+ zone = (uint32_t)(llz & 0xffffffffUL);
+ if (zone != llz) {
+ return (ISC_R_FAILURE);
+ }
+#ifdef HAVE_IF_NAMETOINDEX
+ }
+#endif /* ifdef HAVE_IF_NAMETOINDEX */
+
+ *zoneid = zone;
+ return (ISC_R_SUCCESS);
+}
diff --git a/lib/isc/nonce.c b/lib/isc/nonce.c
new file mode 100644
index 0000000..4c2baff
--- /dev/null
+++ b/lib/isc/nonce.c
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <isc/nonce.h>
+
+#include "entropy_private.h"
+
+void
+isc_nonce_buf(void *buf, size_t buflen) {
+ isc_entropy_get(buf, buflen);
+}
diff --git a/lib/isc/openssl_shim.c b/lib/isc/openssl_shim.c
new file mode 100644
index 0000000..b8dbfaa
--- /dev/null
+++ b/lib/isc/openssl_shim.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/crypto.h>
+#include <openssl/engine.h>
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+#include <openssl/opensslv.h>
+#include <openssl/ssl.h>
+
+#include "openssl_shim.h"
+
+#if !HAVE_CRYPTO_ZALLOC
+void *
+CRYPTO_zalloc(size_t num, const char *file, int line) {
+ void *ret = CRYPTO_malloc(num, file, line);
+ if (ret != NULL) {
+ memset(ret, 0, num);
+ }
+ return (ret);
+}
+#endif /* if !HAVE_CRYPTO_ZALLOC */
+
+#if !HAVE_EVP_CIPHER_CTX_NEW
+EVP_CIPHER_CTX *
+EVP_CIPHER_CTX_new(void) {
+ EVP_CIPHER_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx));
+ return (ctx);
+}
+#endif /* if !HAVE_EVP_CIPHER_CTX_NEW */
+
+#if !HAVE_EVP_CIPHER_CTX_FREE
+void
+EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *ctx) {
+ if (ctx != NULL) {
+ EVP_CIPHER_CTX_cleanup(ctx);
+ OPENSSL_free(ctx);
+ }
+}
+#endif /* if !HAVE_EVP_CIPHER_CTX_FREE */
+
+#if !HAVE_EVP_MD_CTX_RESET
+int
+EVP_MD_CTX_reset(EVP_MD_CTX *ctx) {
+ return (EVP_MD_CTX_cleanup(ctx));
+}
+#endif /* if !HAVE_EVP_MD_CTX_RESET */
+
+#if !HAVE_SSL_READ_EX
+int
+SSL_read_ex(SSL *ssl, void *buf, size_t num, size_t *readbytes) {
+ int rv = SSL_read(ssl, buf, num);
+ if (rv > 0) {
+ *readbytes = rv;
+ rv = 1;
+ }
+
+ return (rv);
+}
+#endif
+
+#if !HAVE_SSL_PEEK_EX
+int
+SSL_peek_ex(SSL *ssl, void *buf, size_t num, size_t *readbytes) {
+ int rv = SSL_peek(ssl, buf, num);
+ if (rv > 0) {
+ *readbytes = rv;
+ rv = 1;
+ }
+
+ return (rv);
+}
+#endif
+
+#if !HAVE_SSL_WRITE_EX
+int
+SSL_write_ex(SSL *ssl, const void *buf, size_t num, size_t *written) {
+ int rv = SSL_write(ssl, buf, num);
+ if (rv > 0) {
+ *written = rv;
+ rv = 1;
+ }
+
+ return (rv);
+}
+#endif
+
+#if !HAVE_BIO_READ_EX
+int
+BIO_read_ex(BIO *b, void *data, size_t dlen, size_t *readbytes) {
+ int rv = BIO_read(b, data, dlen);
+ if (rv > 0) {
+ *readbytes = rv;
+ rv = 1;
+ }
+
+ return (rv);
+}
+#endif
+
+#if !HAVE_BIO_WRITE_EX
+int
+BIO_write_ex(BIO *b, const void *data, size_t dlen, size_t *written) {
+ int rv = BIO_write(b, data, dlen);
+ if (rv > 0) {
+ *written = rv;
+ rv = 1;
+ }
+
+ return (rv);
+}
+#endif
+
+#if !HAVE_OPENSSL_INIT_CRYPTO
+int
+OPENSSL_init_crypto(uint64_t opts, const void *settings) {
+ (void)settings;
+
+ if ((opts & OPENSSL_INIT_NO_LOAD_CRYPTO_STRINGS) == 0) {
+ ERR_load_crypto_strings();
+ }
+
+ if ((opts & (OPENSSL_INIT_NO_ADD_ALL_CIPHERS |
+ OPENSSL_INIT_NO_ADD_ALL_CIPHERS)) == 0)
+ {
+ OpenSSL_add_all_algorithms();
+ } else if ((opts & OPENSSL_INIT_NO_ADD_ALL_CIPHERS) == 0) {
+ OpenSSL_add_all_digests();
+ } else if ((opts & OPENSSL_INIT_NO_ADD_ALL_CIPHERS) == 0) {
+ OpenSSL_add_all_ciphers();
+ }
+
+ return (1);
+}
+#endif
+
+#if !HAVE_OPENSSL_INIT_SSL
+int
+OPENSSL_init_ssl(uint64_t opts, const void *settings) {
+ OPENSSL_init_crypto(opts, settings);
+
+ SSL_library_init();
+
+ if ((opts & OPENSSL_INIT_NO_LOAD_SSL_STRINGS) == 0) {
+ SSL_load_error_strings();
+ }
+
+ return (1);
+}
+#endif
+
+#if !HAVE_OPENSSL_CLEANUP
+void
+OPENSSL_cleanup(void) {
+ return;
+}
+#endif
+
+#if !HAVE_SSL_CTX_UP_REF
+int
+SSL_CTX_up_ref(SSL_CTX *ctx) {
+ return (CRYPTO_add(&ctx->references, 1, CRYPTO_LOCK_SSL_CTX) > 0);
+}
+#endif /* !HAVE_SSL_CTX_UP_REF */
+
+#if !HAVE_X509_STORE_UP_REF
+
+int
+X509_STORE_up_ref(X509_STORE *store) {
+ return (CRYPTO_add(&store->references, 1, CRYPTO_LOCK_X509_STORE) > 0);
+}
+
+#endif /* !HAVE_OPENSSL_CLEANUP */
+
+#if !HAVE_SSL_CTX_SET1_CERT_STORE
+
+void
+SSL_CTX_set1_cert_store(SSL_CTX *ctx, X509_STORE *store) {
+ (void)X509_STORE_up_ref(store);
+
+ SSL_CTX_set_cert_store(ctx, store);
+}
+
+#endif /* !HAVE_SSL_CTX_SET1_CERT_STORE */
diff --git a/lib/isc/openssl_shim.h b/lib/isc/openssl_shim.h
new file mode 100644
index 0000000..c0abd14
--- /dev/null
+++ b/lib/isc/openssl_shim.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <openssl/crypto.h>
+#include <openssl/engine.h>
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+#include <openssl/opensslv.h>
+#include <openssl/ssl.h>
+
+#if !HAVE_CRYPTO_ZALLOC
+void *
+CRYPTO_zalloc(size_t num, const char *file, int line);
+#endif /* if !HAVE_CRYPTO_ZALLOC */
+
+#if !defined(OPENSSL_zalloc)
+#define OPENSSL_zalloc(num) CRYPTO_zalloc(num, __FILE__, __LINE__)
+#endif
+
+#if !HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
+#define EVP_PKEY_new_raw_private_key(type, e, key, keylen) \
+ EVP_PKEY_new_mac_key(type, e, key, (int)(keylen))
+#endif /* if !HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY */
+
+#if !HAVE_EVP_CIPHER_CTX_NEW
+EVP_CIPHER_CTX *
+EVP_CIPHER_CTX_new(void);
+#endif /* if !HAVE_EVP_CIPHER_CTX_NEW */
+
+#if !HAVE_EVP_CIPHER_CTX_FREE
+void
+EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *ctx);
+#endif /* if !HAVE_EVP_CIPHER_CTX_FREE */
+
+#if !HAVE_EVP_MD_CTX_NEW
+#define EVP_MD_CTX_new EVP_MD_CTX_create
+#endif /* if !HAVE_EVP_MD_CTX_NEW */
+
+#if !HAVE_EVP_MD_CTX_FREE
+#define EVP_MD_CTX_free EVP_MD_CTX_destroy
+#endif /* if !HAVE_EVP_MD_CTX_FREE */
+
+#if !HAVE_EVP_MD_CTX_RESET
+int
+EVP_MD_CTX_reset(EVP_MD_CTX *ctx);
+#endif /* if !HAVE_EVP_MD_CTX_RESET */
+
+#if !HAVE_EVP_MD_CTX_GET0_MD
+#define EVP_MD_CTX_get0_md EVP_MD_CTX_md
+#endif /* if !HAVE_EVP_MD_CTX_GET0_MD */
+
+#if !HAVE_SSL_READ_EX
+int
+SSL_read_ex(SSL *ssl, void *buf, size_t num, size_t *readbytes);
+#endif
+
+#if !HAVE_SSL_PEEK_EX
+int
+SSL_peek_ex(SSL *ssl, void *buf, size_t num, size_t *readbytes);
+#endif
+
+#if !HAVE_SSL_WRITE_EX
+int
+SSL_write_ex(SSL *ssl, const void *buf, size_t num, size_t *written);
+#endif
+
+#if !HAVE_BIO_READ_EX
+int
+BIO_read_ex(BIO *b, void *data, size_t dlen, size_t *readbytes);
+#endif
+
+#if !HAVE_BIO_WRITE_EX
+int
+BIO_write_ex(BIO *b, const void *data, size_t dlen, size_t *written);
+#endif
+
+#if !HAVE_OPENSSL_INIT_CRYPTO
+
+#define OPENSSL_INIT_NO_LOAD_CRYPTO_STRINGS 0x00000001L
+#define OPENSSL_INIT_LOAD_CRYPTO_STRINGS 0x00000002L
+#define OPENSSL_INIT_ADD_ALL_CIPHERS 0x00000004L
+#define OPENSSL_INIT_ADD_ALL_DIGESTS 0x00000008L
+#define OPENSSL_INIT_NO_ADD_ALL_CIPHERS 0x00000010L
+#define OPENSSL_INIT_NO_ADD_ALL_DIGESTS 0x00000020L
+
+int
+OPENSSL_init_crypto(uint64_t opts, const void *settings);
+#endif
+
+#if !HAVE_OPENSSL_INIT_SSL
+#define OPENSSL_INIT_NO_LOAD_SSL_STRINGS 0x00100000L
+#define OPENSSL_INIT_LOAD_SSL_STRINGS 0x00200000L
+
+int
+OPENSSL_init_ssl(uint64_t opts, const void *settings);
+
+#endif
+
+#if !HAVE_OPENSSL_CLEANUP
+void
+OPENSSL_cleanup(void);
+#endif
+
+#if !HAVE_TLS_SERVER_METHOD
+#define TLS_server_method SSLv23_server_method
+#endif
+
+#if !HAVE_TLS_CLIENT_METHOD
+#define TLS_client_method SSLv23_client_method
+#endif
+
+#if !HAVE_SSL_CTX_UP_REF
+int
+SSL_CTX_up_ref(SSL_CTX *store);
+#endif /* !HAVE_SSL_CTX_UP_REF */
+
+#if !HAVE_X509_STORE_UP_REF
+int
+X509_STORE_up_ref(X509_STORE *v);
+#endif /* !HAVE_OPENSSL_CLEANUP */
+
+#if !HAVE_SSL_CTX_SET1_CERT_STORE
+void
+SSL_CTX_set1_cert_store(SSL_CTX *ctx, X509_STORE *store);
+#endif /* !HAVE_SSL_CTX_SET1_CERT_STORE */
diff --git a/lib/isc/os.c b/lib/isc/os.c
new file mode 100644
index 0000000..0ba0fab
--- /dev/null
+++ b/lib/isc/os.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <inttypes.h>
+#include <sys/stat.h>
+
+#include <isc/os.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+#include "os_p.h"
+
+static unsigned int isc__os_ncpus = 0;
+static unsigned long isc__os_cacheline = ISC_OS_CACHELINE_SIZE;
+static mode_t isc__os_umask = 0;
+
+#ifdef HAVE_SYSCONF
+
+#include <unistd.h>
+
+static long
+sysconf_ncpus(void) {
+#if defined(_SC_NPROCESSORS_ONLN)
+ return (sysconf((_SC_NPROCESSORS_ONLN)));
+#elif defined(_SC_NPROC_ONLN)
+ return (sysconf((_SC_NPROC_ONLN)));
+#else /* if defined(_SC_NPROCESSORS_ONLN) */
+ return (0);
+#endif /* if defined(_SC_NPROCESSORS_ONLN) */
+}
+#endif /* HAVE_SYSCONF */
+
+#if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_SYSCTLBYNAME)
+#include <sys/param.h> /* for NetBSD */
+#include <sys/sysctl.h>
+#include <sys/types.h> /* for FreeBSD */
+
+static int
+sysctl_ncpus(void) {
+ int ncpu, result;
+ size_t len;
+
+ len = sizeof(ncpu);
+ result = sysctlbyname("hw.ncpu", &ncpu, &len, 0, 0);
+ if (result != -1) {
+ return (ncpu);
+ }
+ return (0);
+}
+#endif /* if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_SYSCTLBYNAME) */
+
+static void
+ncpus_initialize(void) {
+#if defined(HAVE_SYSCONF)
+ isc__os_ncpus = sysconf_ncpus();
+#endif /* if defined(HAVE_SYSCONF) */
+#if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_SYSCTLBYNAME)
+ if (isc__os_ncpus <= 0) {
+ isc__os_ncpus = sysctl_ncpus();
+ }
+#endif /* if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_SYSCTLBYNAME) */
+ if (isc__os_ncpus == 0) {
+ isc__os_ncpus = 1;
+ }
+}
+
+static void
+umask_initialize(void) {
+ isc__os_umask = umask(0);
+ (void)umask(isc__os_umask);
+}
+
+unsigned int
+isc_os_ncpus(void) {
+ return (isc__os_ncpus);
+}
+
+unsigned long
+isc_os_cacheline(void) {
+ return (isc__os_cacheline);
+}
+
+mode_t
+isc_os_umask(void) {
+ return (isc__os_umask);
+}
+
+void
+isc__os_initialize(void) {
+ umask_initialize();
+ ncpus_initialize();
+#if defined(HAVE_SYSCONF) && defined(_SC_LEVEL1_DCACHE_LINESIZE)
+ long s = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
+ if (s > 0 && (unsigned long)s > isc__os_cacheline) {
+ isc__os_cacheline = s;
+ }
+#endif
+}
+
+void
+isc__os_shutdown(void) {
+ /* empty, but defined for completeness */;
+}
diff --git a/lib/isc/os_p.h b/lib/isc/os_p.h
new file mode 100644
index 0000000..c39bc23
--- /dev/null
+++ b/lib/isc/os_p.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <stdio.h>
+
+#include <isc/os.h>
+
+/*! \file */
+
+void
+isc__os_initialize(void);
+
+void
+isc__os_shutdown(void);
diff --git a/lib/isc/parseint.c b/lib/isc/parseint.c
new file mode 100644
index 0000000..da6c281
--- /dev/null
+++ b/lib/isc/parseint.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <ctype.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include <isc/parseint.h>
+#include <isc/result.h>
+
+isc_result_t
+isc_parse_uint32(uint32_t *uip, const char *string, int base) {
+ unsigned long n;
+ uint32_t r;
+ char *e;
+ if (!isalnum((unsigned char)(string[0]))) {
+ return (ISC_R_BADNUMBER);
+ }
+ errno = 0;
+ n = strtoul(string, &e, base);
+ if (*e != '\0') {
+ return (ISC_R_BADNUMBER);
+ }
+ /*
+ * Where long is 64 bits we need to convert to 32 bits then test for
+ * equality. This is a no-op on 32 bit machines and a good compiler
+ * will optimise it away.
+ */
+ r = (uint32_t)n;
+ if ((n == ULONG_MAX && errno == ERANGE) || (n != (unsigned long)r)) {
+ return (ISC_R_RANGE);
+ }
+ *uip = r;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_parse_uint16(uint16_t *uip, const char *string, int base) {
+ uint32_t val;
+ isc_result_t result;
+ result = isc_parse_uint32(&val, string, base);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ if (val > 0xFFFF) {
+ return (ISC_R_RANGE);
+ }
+ *uip = (uint16_t)val;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_parse_uint8(uint8_t *uip, const char *string, int base) {
+ uint32_t val;
+ isc_result_t result;
+ result = isc_parse_uint32(&val, string, base);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ if (val > 0xFF) {
+ return (ISC_R_RANGE);
+ }
+ *uip = (uint8_t)val;
+ return (ISC_R_SUCCESS);
+}
diff --git a/lib/isc/picohttpparser.c b/lib/isc/picohttpparser.c
new file mode 100644
index 0000000..d82fd01
--- /dev/null
+++ b/lib/isc/picohttpparser.c
@@ -0,0 +1,727 @@
+/*
+ * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
+ * Shigeo Mitsunari
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * The software is licensed under either the MIT License (below) or the Perl
+ * license.
+ *
+ * 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 <assert.h>
+#include <stddef.h>
+#include <string.h>
+#ifdef __SSE4_2__
+#ifdef _MSC_VER
+#include <nmmintrin.h>
+#else
+#include <x86intrin.h>
+#endif
+#endif
+#include "picohttpparser.h"
+
+#if __GNUC__ >= 3
+#define likely(x) __builtin_expect(!!(x), 1)
+#define unlikely(x) __builtin_expect(!!(x), 0)
+#else
+#define likely(x) (x)
+#define unlikely(x) (x)
+#endif
+
+#ifdef _MSC_VER
+#define ALIGNED(n) _declspec(align(n))
+#else
+#define ALIGNED(n) __attribute__((aligned(n)))
+#endif
+
+#define IS_PRINTABLE_ASCII(c) ((unsigned char)(c)-040u < 0137u)
+
+#define CHECK_EOF() \
+ if (buf == buf_end) { \
+ *ret = -2; \
+ return NULL; \
+ }
+
+#define EXPECT_CHAR_NO_CHECK(ch) \
+ if (*buf++ != ch) { \
+ *ret = -1; \
+ return NULL; \
+ }
+
+#define EXPECT_CHAR(ch) \
+ CHECK_EOF(); \
+ EXPECT_CHAR_NO_CHECK(ch);
+
+#define ADVANCE_TOKEN(tok, toklen) \
+ do { \
+ const char *tok_start = buf; \
+ static const char ALIGNED(16) \
+ ranges2[16] = "\000\040\177\177"; \
+ int found2; \
+ buf = findchar_fast(buf, buf_end, ranges2, 4, &found2); \
+ if (!found2) { \
+ CHECK_EOF(); \
+ } \
+ while (1) { \
+ if (*buf == ' ') { \
+ break; \
+ } else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { \
+ if ((unsigned char)*buf < '\040' || \
+ *buf == '\177') \
+ { \
+ *ret = -1; \
+ return NULL; \
+ } \
+ } \
+ ++buf; \
+ CHECK_EOF(); \
+ } \
+ tok = tok_start; \
+ toklen = buf - tok_start; \
+ } while (0)
+
+static const char *token_char_map =
+ "\0\0\0\0\0\0\0\0\0\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\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0"
+ "\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1"
+ "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\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";
+
+static const char *
+findchar_fast(const char *buf, const char *buf_end, const char *ranges,
+ size_t ranges_size, int *found) {
+ *found = 0;
+#if __SSE4_2__
+ if (likely(buf_end - buf >= 16)) {
+ __m128i ranges16 = _mm_loadu_si128((const __m128i *)ranges);
+
+ size_t left = (buf_end - buf) & ~15;
+ do {
+ __m128i b16 = _mm_loadu_si128((const __m128i *)buf);
+ int r = _mm_cmpestri(ranges16, ranges_size, b16, 16,
+ _SIDD_LEAST_SIGNIFICANT |
+ _SIDD_CMP_RANGES |
+ _SIDD_UBYTE_OPS);
+ if (unlikely(r != 16)) {
+ buf += r;
+ *found = 1;
+ break;
+ }
+ buf += 16;
+ left -= 16;
+ } while (likely(left != 0));
+ }
+#else
+ /* suppress unused parameter warning */
+ (void)buf_end;
+ (void)ranges;
+ (void)ranges_size;
+#endif
+ return buf;
+}
+
+static const char *
+get_token_to_eol(const char *buf, const char *buf_end, const char **token,
+ size_t *token_len, int *ret) {
+ const char *token_start = buf;
+
+#ifdef __SSE4_2__
+ static const char ALIGNED(16)
+ ranges1[16] = "\0\010" /* allow HT */
+ "\012\037" /* allow SP and up to but not including
+ DEL */
+ "\177\177"; /* allow chars w. MSB set */
+ int found;
+ buf = findchar_fast(buf, buf_end, ranges1, 6, &found);
+ if (found)
+ goto FOUND_CTL;
+#else
+ /* find non-printable char within the next 8 bytes, this is the hottest
+ * code; manually inlined */
+ while (likely(buf_end - buf >= 8)) {
+#define DOIT() \
+ do { \
+ if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \
+ goto NonPrintable; \
+ ++buf; \
+ } while (0)
+ DOIT();
+ DOIT();
+ DOIT();
+ DOIT();
+ DOIT();
+ DOIT();
+ DOIT();
+ DOIT();
+#undef DOIT
+ continue;
+ NonPrintable:
+ if ((likely((unsigned char)*buf < '\040') &&
+ likely(*buf != '\011')) ||
+ unlikely(*buf == '\177'))
+ {
+ goto FOUND_CTL;
+ }
+ ++buf;
+ }
+#endif
+ for (;; ++buf) {
+ CHECK_EOF();
+ if (unlikely(!IS_PRINTABLE_ASCII(*buf))) {
+ if ((likely((unsigned char)*buf < '\040') &&
+ likely(*buf != '\011')) ||
+ unlikely(*buf == '\177'))
+ {
+ goto FOUND_CTL;
+ }
+ }
+ }
+FOUND_CTL:
+ if (likely(*buf == '\015')) {
+ ++buf;
+ EXPECT_CHAR('\012');
+ *token_len = buf - 2 - token_start;
+ } else if (*buf == '\012') {
+ *token_len = buf - token_start;
+ ++buf;
+ } else {
+ *ret = -1;
+ return NULL;
+ }
+ *token = token_start;
+
+ return buf;
+}
+
+static const char *
+is_complete(const char *buf, const char *buf_end, size_t last_len, int *ret) {
+ int ret_cnt = 0;
+ buf = last_len < 3 ? buf : buf + last_len - 3;
+
+ while (1) {
+ CHECK_EOF();
+ if (*buf == '\015') {
+ ++buf;
+ CHECK_EOF();
+ EXPECT_CHAR('\012');
+ ++ret_cnt;
+ } else if (*buf == '\012') {
+ ++buf;
+ ++ret_cnt;
+ } else {
+ ++buf;
+ ret_cnt = 0;
+ }
+ if (ret_cnt == 2) {
+ return buf;
+ }
+ }
+
+ *ret = -2;
+ return NULL;
+}
+
+#define PARSE_INT(valp_, mul_) \
+ if (*buf < '0' || '9' < *buf) { \
+ buf++; \
+ *ret = -1; \
+ return NULL; \
+ } \
+ *(valp_) = (mul_) * (*buf++ - '0');
+
+#define PARSE_INT_3(valp_) \
+ do { \
+ int res_ = 0; \
+ PARSE_INT(&res_, 100) \
+ *valp_ = res_; \
+ PARSE_INT(&res_, 10) \
+ *valp_ += res_; \
+ PARSE_INT(&res_, 1) \
+ *valp_ += res_; \
+ } while (0)
+
+/* returned pointer is always within [buf, buf_end), or null */
+static const char *
+parse_token(const char *buf, const char *buf_end, const char **token,
+ size_t *token_len, char next_char, int *ret) {
+ /* We use pcmpestri to detect non-token characters. This instruction can
+ * take no more than eight character ranges (8*2*8=128 bits that is the
+ * size of a SSE register). Due to this restriction, characters `|` and
+ * `~` are handled in the slow loop. */
+ static const char ALIGNED(16) ranges[] = "\x00 " /* control chars and up
+ to SP */
+ "\"\"" /* 0x22 */
+ "()" /* 0x28,0x29 */
+ ",," /* 0x2c */
+ "//" /* 0x2f */
+ ":@" /* 0x3a-0x40 */
+ "[]" /* 0x5b-0x5d */
+ "{\xff"; /* 0x7b-0xff */
+ const char *buf_start = buf;
+ int found;
+ buf = findchar_fast(buf, buf_end, ranges, sizeof(ranges) - 1, &found);
+ if (!found) {
+ CHECK_EOF();
+ }
+ while (1) {
+ if (*buf == next_char) {
+ break;
+ } else if (!token_char_map[(unsigned char)*buf]) {
+ *ret = -1;
+ return NULL;
+ }
+ ++buf;
+ CHECK_EOF();
+ }
+ *token = buf_start;
+ *token_len = buf - buf_start;
+ return buf;
+}
+
+/* returned pointer is always within [buf, buf_end), or null */
+static const char *
+parse_http_version(const char *buf, const char *buf_end, int *minor_version,
+ int *ret) {
+ /* we want at least [HTTP/1.<two chars>] to try to parse */
+ if (buf_end - buf < 9) {
+ *ret = -2;
+ return NULL;
+ }
+ EXPECT_CHAR_NO_CHECK('H');
+ EXPECT_CHAR_NO_CHECK('T');
+ EXPECT_CHAR_NO_CHECK('T');
+ EXPECT_CHAR_NO_CHECK('P');
+ EXPECT_CHAR_NO_CHECK('/');
+ EXPECT_CHAR_NO_CHECK('1');
+ EXPECT_CHAR_NO_CHECK('.');
+ PARSE_INT(minor_version, 1);
+ return buf;
+}
+
+static const char *
+parse_headers(const char *buf, const char *buf_end, struct phr_header *headers,
+ size_t *num_headers, size_t max_headers, int *ret) {
+ for (;; ++*num_headers) {
+ CHECK_EOF();
+ if (*buf == '\015') {
+ ++buf;
+ EXPECT_CHAR('\012');
+ break;
+ } else if (*buf == '\012') {
+ ++buf;
+ break;
+ }
+ if (*num_headers == max_headers) {
+ *ret = -1;
+ return NULL;
+ }
+ if (!(*num_headers != 0 && (*buf == ' ' || *buf == '\t'))) {
+ /* parsing name, but do not discard SP before colon, see
+ * http://www.mozilla.org/security/announce/2006/mfsa2006-33.html
+ */
+ if ((buf = parse_token(buf, buf_end,
+ &headers[*num_headers].name,
+ &headers[*num_headers].name_len,
+ ':', ret)) == NULL)
+ {
+ return NULL;
+ }
+ if (headers[*num_headers].name_len == 0) {
+ *ret = -1;
+ return NULL;
+ }
+ ++buf;
+ for (;; ++buf) {
+ CHECK_EOF();
+ if (!(*buf == ' ' || *buf == '\t')) {
+ break;
+ }
+ }
+ } else {
+ headers[*num_headers].name = NULL;
+ headers[*num_headers].name_len = 0;
+ }
+ const char *value;
+ size_t value_len;
+ if ((buf = get_token_to_eol(buf, buf_end, &value, &value_len,
+ ret)) == NULL)
+ {
+ return NULL;
+ }
+ /* remove trailing SPs and HTABs */
+ const char *value_end = value + value_len;
+ for (; value_end != value; --value_end) {
+ const char c = *(value_end - 1);
+ if (!(c == ' ' || c == '\t')) {
+ break;
+ }
+ }
+ headers[*num_headers].value = value;
+ headers[*num_headers].value_len = value_end - value;
+ }
+ return buf;
+}
+
+static const char *
+parse_request(const char *buf, const char *buf_end, const char **method,
+ size_t *method_len, const char **path, size_t *path_len,
+ int *minor_version, struct phr_header *headers,
+ size_t *num_headers, size_t max_headers, int *ret) {
+ /* skip first empty line (some clients add CRLF after POST content) */
+ CHECK_EOF();
+ if (*buf == '\015') {
+ ++buf;
+ EXPECT_CHAR('\012');
+ } else if (*buf == '\012') {
+ ++buf;
+ }
+
+ /* parse request line */
+ if ((buf = parse_token(buf, buf_end, method, method_len, ' ', ret)) ==
+ NULL)
+ {
+ return NULL;
+ }
+ do {
+ ++buf;
+ CHECK_EOF();
+ } while (*buf == ' ');
+ ADVANCE_TOKEN(*path, *path_len);
+ do {
+ ++buf;
+ CHECK_EOF();
+ } while (*buf == ' ');
+ if (*method_len == 0 || *path_len == 0) {
+ *ret = -1;
+ return NULL;
+ }
+ if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) ==
+ NULL)
+ {
+ return NULL;
+ }
+ if (*buf == '\015') {
+ ++buf;
+ EXPECT_CHAR('\012');
+ } else if (*buf == '\012') {
+ ++buf;
+ } else {
+ *ret = -1;
+ return NULL;
+ }
+
+ return parse_headers(buf, buf_end, headers, num_headers, max_headers,
+ ret);
+}
+
+int
+phr_parse_request(const char *buf_start, size_t len, const char **method,
+ size_t *method_len, const char **path, size_t *path_len,
+ int *minor_version, struct phr_header *headers,
+ size_t *num_headers, size_t last_len) {
+ const char *buf = buf_start, *buf_end = buf_start + len;
+ size_t max_headers = *num_headers;
+ int r = -1;
+
+ *method = NULL;
+ *method_len = 0;
+ *path = NULL;
+ *path_len = 0;
+ *minor_version = -1;
+ *num_headers = 0;
+
+ /* if last_len != 0, check if the request is complete (a fast
+ countermeasure againt slowloris */
+ if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
+ return r;
+ }
+
+ if ((buf = parse_request(buf, buf_end, method, method_len, path,
+ path_len, minor_version, headers, num_headers,
+ max_headers, &r)) == NULL)
+ {
+ return r;
+ }
+
+ return (int)(buf - buf_start);
+}
+
+static const char *
+parse_response(const char *buf, const char *buf_end, int *minor_version,
+ int *status, const char **msg, size_t *msg_len,
+ struct phr_header *headers, size_t *num_headers,
+ size_t max_headers, int *ret) {
+ /* parse "HTTP/1.x" */
+ if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) ==
+ NULL)
+ {
+ return NULL;
+ }
+ /* skip space */
+ if (*buf != ' ') {
+ *ret = -1;
+ return NULL;
+ }
+ do {
+ ++buf;
+ CHECK_EOF();
+ } while (*buf == ' ');
+ /* parse status code, we want at least [:digit:][:digit:][:digit:]<other
+ * char> to try to parse */
+ if (buf_end - buf < 4) {
+ *ret = -2;
+ return NULL;
+ }
+ PARSE_INT_3(status);
+
+ /* get message including preceding space */
+ if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) {
+ return NULL;
+ }
+ if (*msg_len == 0) {
+ /* ok */
+ } else if (**msg == ' ') {
+ /* Remove preceding space. Successful return from
+ * `get_token_to_eol` guarantees that we would hit something
+ * other than SP before running past the end of the given
+ * buffer. */
+ do {
+ ++*msg;
+ --*msg_len;
+ } while (**msg == ' ');
+ } else {
+ /* garbage found after status code */
+ *ret = -1;
+ return NULL;
+ }
+
+ return parse_headers(buf, buf_end, headers, num_headers, max_headers,
+ ret);
+}
+
+int
+phr_parse_response(const char *buf_start, size_t len, int *minor_version,
+ int *status, const char **msg, size_t *msg_len,
+ struct phr_header *headers, size_t *num_headers,
+ size_t last_len) {
+ const char *buf = buf_start, *buf_end = buf + len;
+ size_t max_headers = *num_headers;
+ int r;
+
+ *minor_version = -1;
+ *status = 0;
+ *msg = NULL;
+ *msg_len = 0;
+ *num_headers = 0;
+
+ /* if last_len != 0, check if the response is complete (a fast
+ countermeasure against slowloris */
+ if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
+ return r;
+ }
+
+ if ((buf = parse_response(buf, buf_end, minor_version, status, msg,
+ msg_len, headers, num_headers, max_headers,
+ &r)) == NULL)
+ {
+ return r;
+ }
+
+ return (int)(buf - buf_start);
+}
+
+int
+phr_parse_headers(const char *buf_start, size_t len, struct phr_header *headers,
+ size_t *num_headers, size_t last_len) {
+ const char *buf = buf_start, *buf_end = buf + len;
+ size_t max_headers = *num_headers;
+ int r;
+
+ *num_headers = 0;
+
+ /* if last_len != 0, check if the response is complete (a fast
+ countermeasure against slowloris */
+ if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
+ return r;
+ }
+
+ if ((buf = parse_headers(buf, buf_end, headers, num_headers,
+ max_headers, &r)) == NULL)
+ {
+ return r;
+ }
+
+ return (int)(buf - buf_start);
+}
+
+enum {
+ CHUNKED_IN_CHUNK_SIZE,
+ CHUNKED_IN_CHUNK_EXT,
+ CHUNKED_IN_CHUNK_DATA,
+ CHUNKED_IN_CHUNK_CRLF,
+ CHUNKED_IN_TRAILERS_LINE_HEAD,
+ CHUNKED_IN_TRAILERS_LINE_MIDDLE
+};
+
+static int
+decode_hex(int ch) {
+ if ('0' <= ch && ch <= '9') {
+ return ch - '0';
+ } else if ('A' <= ch && ch <= 'F') {
+ return ch - 'A' + 0xa;
+ } else if ('a' <= ch && ch <= 'f') {
+ return ch - 'a' + 0xa;
+ } else {
+ return -1;
+ }
+}
+
+ssize_t
+phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf,
+ size_t *_bufsz) {
+ size_t dst = 0, src = 0, bufsz = *_bufsz;
+ ssize_t ret = -2; /* incomplete */
+
+ while (1) {
+ switch (decoder->_state) {
+ case CHUNKED_IN_CHUNK_SIZE:
+ for (;; ++src) {
+ int v;
+ if (src == bufsz)
+ goto Exit;
+ if ((v = decode_hex(buf[src])) == -1) {
+ if (decoder->_hex_count == 0) {
+ ret = -1;
+ goto Exit;
+ }
+ break;
+ }
+ if (decoder->_hex_count == sizeof(size_t) * 2) {
+ ret = -1;
+ goto Exit;
+ }
+ decoder->bytes_left_in_chunk =
+ decoder->bytes_left_in_chunk * 16 + v;
+ ++decoder->_hex_count;
+ }
+ decoder->_hex_count = 0;
+ decoder->_state = CHUNKED_IN_CHUNK_EXT;
+ /* fallthru */
+ case CHUNKED_IN_CHUNK_EXT:
+ /* RFC 7230 A.2 "Line folding in chunk extensions is
+ * disallowed" */
+ for (;; ++src) {
+ if (src == bufsz)
+ goto Exit;
+ if (buf[src] == '\012')
+ break;
+ }
+ ++src;
+ if (decoder->bytes_left_in_chunk == 0) {
+ if (decoder->consume_trailer) {
+ decoder->_state =
+ CHUNKED_IN_TRAILERS_LINE_HEAD;
+ break;
+ } else {
+ goto Complete;
+ }
+ }
+ decoder->_state = CHUNKED_IN_CHUNK_DATA;
+ /* fallthru */
+ case CHUNKED_IN_CHUNK_DATA: {
+ size_t avail = bufsz - src;
+ if (avail < decoder->bytes_left_in_chunk) {
+ if (dst != src)
+ memmove(buf + dst, buf + src, avail);
+ src += avail;
+ dst += avail;
+ decoder->bytes_left_in_chunk -= avail;
+ goto Exit;
+ }
+ if (dst != src)
+ memmove(buf + dst, buf + src,
+ decoder->bytes_left_in_chunk);
+ src += decoder->bytes_left_in_chunk;
+ dst += decoder->bytes_left_in_chunk;
+ decoder->bytes_left_in_chunk = 0;
+ decoder->_state = CHUNKED_IN_CHUNK_CRLF;
+ }
+ /* fallthru */
+ case CHUNKED_IN_CHUNK_CRLF:
+ for (;; ++src) {
+ if (src == bufsz)
+ goto Exit;
+ if (buf[src] != '\015')
+ break;
+ }
+ if (buf[src] != '\012') {
+ ret = -1;
+ goto Exit;
+ }
+ ++src;
+ decoder->_state = CHUNKED_IN_CHUNK_SIZE;
+ break;
+ case CHUNKED_IN_TRAILERS_LINE_HEAD:
+ for (;; ++src) {
+ if (src == bufsz)
+ goto Exit;
+ if (buf[src] != '\015')
+ break;
+ }
+ if (buf[src++] == '\012')
+ goto Complete;
+ decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE;
+ /* fallthru */
+ case CHUNKED_IN_TRAILERS_LINE_MIDDLE:
+ for (;; ++src) {
+ if (src == bufsz)
+ goto Exit;
+ if (buf[src] == '\012')
+ break;
+ }
+ ++src;
+ decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
+ break;
+ default:
+ assert(!"decoder is corrupt");
+ }
+ }
+
+Complete:
+ ret = bufsz - src;
+Exit:
+ if (dst != src)
+ memmove(buf + dst, buf + src, bufsz - src);
+ *_bufsz = dst;
+ return ret;
+}
+
+int
+phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder) {
+ return decoder->_state == CHUNKED_IN_CHUNK_DATA;
+}
+
+#undef CHECK_EOF
+#undef EXPECT_CHAR
+#undef ADVANCE_TOKEN
diff --git a/lib/isc/picohttpparser.h b/lib/isc/picohttpparser.h
new file mode 100644
index 0000000..0657cb2
--- /dev/null
+++ b/lib/isc/picohttpparser.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
+ * Shigeo Mitsunari
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * The software is licensed under either the MIT License (below) or the Perl
+ * license.
+ *
+ * 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 picohttpparser_h
+#define picohttpparser_h
+
+#include <sys/types.h>
+
+#ifdef _MSC_VER
+#define ssize_t intptr_t
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* contains name and value of a header (name == NULL if is a continuing line
+ * of a multiline header */
+struct phr_header {
+ const char *name;
+ size_t name_len;
+ const char *value;
+ size_t value_len;
+};
+
+/* returns number of bytes consumed if successful, -2 if request is partial,
+ * -1 if failed */
+int
+phr_parse_request(const char *buf, size_t len, const char **method,
+ size_t *method_len, const char **path, size_t *path_len,
+ int *minor_version, struct phr_header *headers,
+ size_t *num_headers, size_t last_len);
+
+/* ditto */
+int
+phr_parse_response(const char *_buf, size_t len, int *minor_version,
+ int *status, const char **msg, size_t *msg_len,
+ struct phr_header *headers, size_t *num_headers,
+ size_t last_len);
+
+/* ditto */
+int
+phr_parse_headers(const char *buf, size_t len, struct phr_header *headers,
+ size_t *num_headers, size_t last_len);
+
+/* should be zero-filled before start */
+struct phr_chunked_decoder {
+ size_t bytes_left_in_chunk; /* number of bytes left in current chunk */
+ char consume_trailer; /* if trailing headers should be consumed */
+ char _hex_count;
+ char _state;
+};
+
+/* the function rewrites the buffer given as (buf, bufsz) removing the chunked-
+ * encoding headers. When the function returns without an error, bufsz is
+ * updated to the length of the decoded data available. Applications should
+ * repeatedly call the function while it returns -2 (incomplete) every time
+ * supplying newly arrived data. If the end of the chunked-encoded data is
+ * found, the function returns a non-negative number indicating the number of
+ * octets left undecoded, that starts from the offset returned by `*bufsz`.
+ * Returns -1 on error.
+ */
+ssize_t
+phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf,
+ size_t *bufsz);
+
+/* returns if the chunked decoder is in middle of chunked data */
+int
+phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/isc/pool.c b/lib/isc/pool.c
new file mode 100644
index 0000000..5a5fb12
--- /dev/null
+++ b/lib/isc/pool.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <string.h>
+
+#include <isc/mem.h>
+#include <isc/pool.h>
+#include <isc/random.h>
+#include <isc/util.h>
+
+/***
+ *** Types.
+ ***/
+
+struct isc_pool {
+ isc_mem_t *mctx;
+ unsigned int count;
+ isc_pooldeallocator_t free;
+ isc_poolinitializer_t init;
+ void *initarg;
+ void **pool;
+};
+
+/***
+ *** Functions.
+ ***/
+
+static isc_result_t
+alloc_pool(isc_mem_t *mctx, unsigned int count, isc_pool_t **poolp) {
+ isc_pool_t *pool;
+
+ pool = isc_mem_get(mctx, sizeof(*pool));
+ pool->count = count;
+ pool->free = NULL;
+ pool->init = NULL;
+ pool->initarg = NULL;
+ pool->mctx = NULL;
+ isc_mem_attach(mctx, &pool->mctx);
+ pool->pool = isc_mem_get(mctx, count * sizeof(void *));
+ memset(pool->pool, 0, count * sizeof(void *));
+
+ *poolp = pool;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_pool_create(isc_mem_t *mctx, unsigned int count,
+ isc_pooldeallocator_t release, isc_poolinitializer_t init,
+ void *initarg, isc_pool_t **poolp) {
+ isc_pool_t *pool = NULL;
+ isc_result_t result;
+ unsigned int i;
+
+ INSIST(count > 0);
+
+ /* Allocate the pool structure */
+ result = alloc_pool(mctx, count, &pool);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ pool->free = release;
+ pool->init = init;
+ pool->initarg = initarg;
+
+ /* Populate the pool */
+ for (i = 0; i < count; i++) {
+ result = init(&pool->pool[i], initarg);
+ if (result != ISC_R_SUCCESS) {
+ isc_pool_destroy(&pool);
+ return (result);
+ }
+ }
+
+ *poolp = pool;
+ return (ISC_R_SUCCESS);
+}
+
+void *
+isc_pool_get(isc_pool_t *pool) {
+ return (pool->pool[isc_random_uniform(pool->count)]);
+}
+
+int
+isc_pool_count(isc_pool_t *pool) {
+ REQUIRE(pool != NULL);
+ return (pool->count);
+}
+
+isc_result_t
+isc_pool_expand(isc_pool_t **sourcep, unsigned int count,
+ isc_pool_t **targetp) {
+ isc_result_t result;
+ isc_pool_t *pool;
+
+ REQUIRE(sourcep != NULL && *sourcep != NULL);
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ pool = *sourcep;
+ *sourcep = NULL;
+ if (count > pool->count) {
+ isc_pool_t *newpool = NULL;
+ unsigned int i;
+
+ /* Allocate a new pool structure */
+ result = alloc_pool(pool->mctx, count, &newpool);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ newpool->free = pool->free;
+ newpool->init = pool->init;
+ newpool->initarg = pool->initarg;
+
+ /* Populate the new entries */
+ for (i = pool->count; i < count; i++) {
+ result = newpool->init(&newpool->pool[i],
+ newpool->initarg);
+ if (result != ISC_R_SUCCESS) {
+ isc_pool_destroy(&newpool);
+ return (result);
+ }
+ }
+
+ /* Copy over the objects from the old pool */
+ for (i = 0; i < pool->count; i++) {
+ newpool->pool[i] = pool->pool[i];
+ pool->pool[i] = NULL;
+ }
+
+ isc_pool_destroy(&pool);
+ pool = newpool;
+ }
+
+ *targetp = pool;
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_pool_destroy(isc_pool_t **poolp) {
+ unsigned int i;
+ isc_pool_t *pool = *poolp;
+ *poolp = NULL;
+ for (i = 0; i < pool->count; i++) {
+ if (pool->free != NULL && pool->pool[i] != NULL) {
+ pool->free(&pool->pool[i]);
+ }
+ }
+ isc_mem_put(pool->mctx, pool->pool, pool->count * sizeof(void *));
+ isc_mem_putanddetach(&pool->mctx, pool, sizeof(*pool));
+}
diff --git a/lib/isc/portset.c b/lib/isc/portset.c
new file mode 100644
index 0000000..52b96f5
--- /dev/null
+++ b/lib/isc/portset.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/mem.h>
+#include <isc/portset.h>
+#include <isc/string.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+#define ISC_PORTSET_BUFSIZE (65536 / (sizeof(uint32_t) * 8))
+
+/*%
+ * Internal representation of portset. It's an array of 32-bit integers, each
+ * bit corresponding to a single port in the ascending order. For example,
+ * the second most significant bit of buf[0] corresponds to port 1.
+ */
+struct isc_portset {
+ unsigned int nports; /*%< number of ports in the set */
+ uint32_t buf[ISC_PORTSET_BUFSIZE];
+};
+
+static bool
+portset_isset(isc_portset_t *portset, in_port_t port) {
+ return ((portset->buf[port >> 5] & ((uint32_t)1 << (port & 31))) != 0);
+}
+
+static void
+portset_add(isc_portset_t *portset, in_port_t port) {
+ if (!portset_isset(portset, port)) {
+ portset->nports++;
+ portset->buf[port >> 5] |= ((uint32_t)1 << (port & 31));
+ }
+}
+
+static void
+portset_remove(isc_portset_t *portset, in_port_t port) {
+ if (portset_isset(portset, port)) {
+ portset->nports--;
+ portset->buf[port >> 5] &= ~((uint32_t)1 << (port & 31));
+ }
+}
+
+isc_result_t
+isc_portset_create(isc_mem_t *mctx, isc_portset_t **portsetp) {
+ isc_portset_t *portset;
+
+ REQUIRE(portsetp != NULL && *portsetp == NULL);
+
+ portset = isc_mem_get(mctx, sizeof(*portset));
+
+ /* Make the set 'empty' by default */
+ memset(portset, 0, sizeof(*portset));
+ *portsetp = portset;
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_portset_destroy(isc_mem_t *mctx, isc_portset_t **portsetp) {
+ isc_portset_t *portset;
+
+ REQUIRE(portsetp != NULL);
+ portset = *portsetp;
+
+ isc_mem_put(mctx, portset, sizeof(*portset));
+}
+
+bool
+isc_portset_isset(isc_portset_t *portset, in_port_t port) {
+ REQUIRE(portset != NULL);
+
+ return (portset_isset(portset, port));
+}
+
+unsigned int
+isc_portset_nports(isc_portset_t *portset) {
+ REQUIRE(portset != NULL);
+
+ return (portset->nports);
+}
+
+void
+isc_portset_add(isc_portset_t *portset, in_port_t port) {
+ REQUIRE(portset != NULL);
+
+ portset_add(portset, port);
+}
+
+void
+isc_portset_remove(isc_portset_t *portset, in_port_t port) {
+ portset_remove(portset, port);
+}
+
+void
+isc_portset_addrange(isc_portset_t *portset, in_port_t port_lo,
+ in_port_t port_hi) {
+ in_port_t p;
+
+ REQUIRE(portset != NULL);
+ REQUIRE(port_lo <= port_hi);
+
+ p = port_lo;
+ do {
+ portset_add(portset, p);
+ } while (p++ < port_hi);
+}
+
+void
+isc_portset_removerange(isc_portset_t *portset, in_port_t port_lo,
+ in_port_t port_hi) {
+ in_port_t p;
+
+ REQUIRE(portset != NULL);
+ REQUIRE(port_lo <= port_hi);
+
+ p = port_lo;
+ do {
+ portset_remove(portset, p);
+ } while (p++ < port_hi);
+}
diff --git a/lib/isc/quota.c b/lib/isc/quota.c
new file mode 100644
index 0000000..5465db5
--- /dev/null
+++ b/lib/isc/quota.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <stddef.h>
+
+#include <isc/atomic.h>
+#include <isc/quota.h>
+#include <isc/util.h>
+
+#define QUOTA_MAGIC ISC_MAGIC('Q', 'U', 'O', 'T')
+#define VALID_QUOTA(p) ISC_MAGIC_VALID(p, QUOTA_MAGIC)
+
+#define QUOTA_CB_MAGIC ISC_MAGIC('Q', 'T', 'C', 'B')
+#define VALID_QUOTA_CB(p) ISC_MAGIC_VALID(p, QUOTA_CB_MAGIC)
+
+void
+isc_quota_init(isc_quota_t *quota, unsigned int max) {
+ atomic_init(&quota->max, max);
+ atomic_init(&quota->used, 0);
+ atomic_init(&quota->soft, 0);
+ atomic_init(&quota->waiting, 0);
+ ISC_LIST_INIT(quota->cbs);
+ isc_mutex_init(&quota->cblock);
+ ISC_LINK_INIT(quota, link);
+ quota->magic = QUOTA_MAGIC;
+}
+
+void
+isc_quota_destroy(isc_quota_t *quota) {
+ REQUIRE(VALID_QUOTA(quota));
+ quota->magic = 0;
+
+ INSIST(atomic_load(&quota->used) == 0);
+ INSIST(atomic_load(&quota->waiting) == 0);
+ INSIST(ISC_LIST_EMPTY(quota->cbs));
+ atomic_store_release(&quota->max, 0);
+ atomic_store_release(&quota->used, 0);
+ atomic_store_release(&quota->soft, 0);
+ isc_mutex_destroy(&quota->cblock);
+}
+
+void
+isc_quota_soft(isc_quota_t *quota, unsigned int soft) {
+ REQUIRE(VALID_QUOTA(quota));
+ atomic_store_release(&quota->soft, soft);
+}
+
+void
+isc_quota_max(isc_quota_t *quota, unsigned int max) {
+ REQUIRE(VALID_QUOTA(quota));
+ atomic_store_release(&quota->max, max);
+}
+
+unsigned int
+isc_quota_getmax(isc_quota_t *quota) {
+ REQUIRE(VALID_QUOTA(quota));
+ return (atomic_load_relaxed(&quota->max));
+}
+
+unsigned int
+isc_quota_getsoft(isc_quota_t *quota) {
+ REQUIRE(VALID_QUOTA(quota));
+ return (atomic_load_relaxed(&quota->soft));
+}
+
+unsigned int
+isc_quota_getused(isc_quota_t *quota) {
+ REQUIRE(VALID_QUOTA(quota));
+ return (atomic_load_relaxed(&quota->used));
+}
+
+static isc_result_t
+quota_reserve(isc_quota_t *quota) {
+ isc_result_t result;
+ uint_fast32_t max = atomic_load_acquire(&quota->max);
+ uint_fast32_t soft = atomic_load_acquire(&quota->soft);
+ uint_fast32_t used = atomic_load_acquire(&quota->used);
+ do {
+ if (max != 0 && used >= max) {
+ return (ISC_R_QUOTA);
+ }
+ if (soft != 0 && used >= soft) {
+ result = ISC_R_SOFTQUOTA;
+ } else {
+ result = ISC_R_SUCCESS;
+ }
+ } while (!atomic_compare_exchange_weak_acq_rel(&quota->used, &used,
+ used + 1));
+ return (result);
+}
+
+/* Must be quota->cbslock locked */
+static void
+enqueue(isc_quota_t *quota, isc_quota_cb_t *cb) {
+ REQUIRE(cb != NULL);
+ ISC_LIST_ENQUEUE(quota->cbs, cb, link);
+ atomic_fetch_add_release(&quota->waiting, 1);
+}
+
+/* Must be quota->cbslock locked */
+static isc_quota_cb_t *
+dequeue(isc_quota_t *quota) {
+ isc_quota_cb_t *cb = ISC_LIST_HEAD(quota->cbs);
+ INSIST(cb != NULL);
+ ISC_LIST_DEQUEUE(quota->cbs, cb, link);
+ atomic_fetch_sub_relaxed(&quota->waiting, 1);
+ return (cb);
+}
+
+static void
+quota_release(isc_quota_t *quota) {
+ uint_fast32_t used;
+
+ /*
+ * This is opportunistic - we might race with a failing quota_attach_cb
+ * and not detect that something is waiting, but eventually someone will
+ * be releasing quota and will detect it, so we don't need to worry -
+ * and we're saving a lot by not locking cblock every time.
+ */
+
+ if (atomic_load_acquire(&quota->waiting) > 0) {
+ isc_quota_cb_t *cb = NULL;
+ LOCK(&quota->cblock);
+ if (atomic_load_relaxed(&quota->waiting) > 0) {
+ cb = dequeue(quota);
+ }
+ UNLOCK(&quota->cblock);
+ if (cb != NULL) {
+ cb->cb_func(quota, cb->data);
+ return;
+ }
+ }
+
+ used = atomic_fetch_sub_release(&quota->used, 1);
+ INSIST(used > 0);
+}
+
+static isc_result_t
+doattach(isc_quota_t *quota, isc_quota_t **p) {
+ isc_result_t result;
+ REQUIRE(p != NULL && *p == NULL);
+
+ result = quota_reserve(quota);
+ if (result == ISC_R_SUCCESS || result == ISC_R_SOFTQUOTA) {
+ *p = quota;
+ }
+
+ return (result);
+}
+
+isc_result_t
+isc_quota_attach(isc_quota_t *quota, isc_quota_t **quotap) {
+ REQUIRE(VALID_QUOTA(quota));
+ REQUIRE(quotap != NULL && *quotap == NULL);
+
+ return (isc_quota_attach_cb(quota, quotap, NULL));
+}
+
+isc_result_t
+isc_quota_attach_cb(isc_quota_t *quota, isc_quota_t **quotap,
+ isc_quota_cb_t *cb) {
+ REQUIRE(VALID_QUOTA(quota));
+ REQUIRE(cb == NULL || VALID_QUOTA_CB(cb));
+ REQUIRE(quotap != NULL && *quotap == NULL);
+
+ isc_result_t result = doattach(quota, quotap);
+ if (result == ISC_R_QUOTA && cb != NULL) {
+ LOCK(&quota->cblock);
+ enqueue(quota, cb);
+ UNLOCK(&quota->cblock);
+ }
+ return (result);
+}
+
+void
+isc_quota_cb_init(isc_quota_cb_t *cb, isc_quota_cb_func_t cb_func, void *data) {
+ ISC_LINK_INIT(cb, link);
+ cb->cb_func = cb_func;
+ cb->data = data;
+ cb->magic = QUOTA_CB_MAGIC;
+}
+
+void
+isc_quota_detach(isc_quota_t **quotap) {
+ REQUIRE(quotap != NULL && VALID_QUOTA(*quotap));
+ isc_quota_t *quota = *quotap;
+ *quotap = NULL;
+
+ quota_release(quota);
+}
diff --git a/lib/isc/radix.c b/lib/isc/radix.c
new file mode 100644
index 0000000..54c0383
--- /dev/null
+++ b/lib/isc/radix.c
@@ -0,0 +1,706 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * This source was adapted from MRT's RCS Ids:
+ * Id: radix.c,v 1.10.2.1 1999/11/29 05:16:24 masaki Exp
+ * Id: prefix.c,v 1.37.2.9 2000/03/10 02:53:19 labovit Exp
+ */
+
+#include <inttypes.h>
+
+#include <isc/mem.h>
+#include <isc/radix.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+#define BIT_TEST(f, b) (((f) & (b)) != 0)
+
+static isc_result_t
+_new_prefix(isc_mem_t *mctx, isc_prefix_t **target, int family, void *dest,
+ int bitlen);
+
+static void
+_deref_prefix(isc_prefix_t *prefix);
+
+static isc_result_t
+_ref_prefix(isc_mem_t *mctx, isc_prefix_t **target, isc_prefix_t *prefix);
+
+static int
+_comp_with_mask(void *addr, void *dest, u_int mask);
+
+static void
+_clear_radix(isc_radix_tree_t *radix, isc_radix_destroyfunc_t func);
+
+static isc_result_t
+_new_prefix(isc_mem_t *mctx, isc_prefix_t **target, int family, void *dest,
+ int bitlen) {
+ isc_prefix_t *prefix;
+
+ REQUIRE(target != NULL);
+
+ if (family != AF_INET6 && family != AF_INET && family != AF_UNSPEC) {
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+
+ prefix = isc_mem_get(mctx, sizeof(isc_prefix_t));
+
+ if (family == AF_INET6) {
+ prefix->bitlen = (bitlen >= 0) ? bitlen : 128;
+ memmove(&prefix->add.sin6, dest, 16);
+ } else {
+ /* AF_UNSPEC is "any" or "none"--treat it as AF_INET */
+ prefix->bitlen = (bitlen >= 0) ? bitlen : 32;
+ memmove(&prefix->add.sin, dest, 4);
+ }
+
+ prefix->family = family;
+ prefix->mctx = NULL;
+ isc_mem_attach(mctx, &prefix->mctx);
+
+ isc_refcount_init(&prefix->refcount, 1);
+
+ *target = prefix;
+ return (ISC_R_SUCCESS);
+}
+
+static void
+_deref_prefix(isc_prefix_t *prefix) {
+ if (prefix != NULL) {
+ if (isc_refcount_decrement(&prefix->refcount) == 1) {
+ isc_refcount_destroy(&prefix->refcount);
+ isc_mem_putanddetach(&prefix->mctx, prefix,
+ sizeof(isc_prefix_t));
+ }
+ }
+}
+
+static isc_result_t
+_ref_prefix(isc_mem_t *mctx, isc_prefix_t **target, isc_prefix_t *prefix) {
+ INSIST(prefix != NULL);
+ INSIST((prefix->family == AF_INET && prefix->bitlen <= 32) ||
+ (prefix->family == AF_INET6 && prefix->bitlen <= 128) ||
+ (prefix->family == AF_UNSPEC && prefix->bitlen == 0));
+ REQUIRE(target != NULL && *target == NULL);
+
+ /*
+ * If this prefix is a static allocation, copy it into new memory.
+ * (Note, the refcount still has to be destroyed by the calling
+ * routine.)
+ */
+ if (isc_refcount_current(&prefix->refcount) == 0) {
+ isc_result_t ret;
+ ret = _new_prefix(mctx, target, prefix->family, &prefix->add,
+ prefix->bitlen);
+ return (ret);
+ }
+
+ isc_refcount_increment(&prefix->refcount);
+
+ *target = prefix;
+ return (ISC_R_SUCCESS);
+}
+
+static int
+_comp_with_mask(void *addr, void *dest, u_int mask) {
+ /* Mask length of zero matches everything */
+ if (mask == 0) {
+ return (1);
+ }
+
+ if (memcmp(addr, dest, mask / 8) == 0) {
+ u_int n = mask / 8;
+ u_int m = ((~0U) << (8 - (mask % 8)));
+
+ if ((mask % 8) == 0 ||
+ (((u_char *)addr)[n] & m) == (((u_char *)dest)[n] & m))
+ {
+ return (1);
+ }
+ }
+ return (0);
+}
+
+isc_result_t
+isc_radix_create(isc_mem_t *mctx, isc_radix_tree_t **target, int maxbits) {
+ isc_radix_tree_t *radix;
+
+ REQUIRE(target != NULL && *target == NULL);
+
+ radix = isc_mem_get(mctx, sizeof(isc_radix_tree_t));
+
+ radix->mctx = NULL;
+ isc_mem_attach(mctx, &radix->mctx);
+ radix->maxbits = maxbits;
+ radix->head = NULL;
+ radix->num_active_node = 0;
+ radix->num_added_node = 0;
+ RUNTIME_CHECK(maxbits <= RADIX_MAXBITS); /* XXX */
+ radix->magic = RADIX_TREE_MAGIC;
+ *target = radix;
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * if func is supplied, it will be called as func(node->data)
+ * before deleting the node
+ */
+
+static void
+_clear_radix(isc_radix_tree_t *radix, isc_radix_destroyfunc_t func) {
+ REQUIRE(radix != NULL);
+
+ if (radix->head != NULL) {
+ isc_radix_node_t *Xstack[RADIX_MAXBITS + 1];
+ isc_radix_node_t **Xsp = Xstack;
+ isc_radix_node_t *Xrn = radix->head;
+
+ while (Xrn != NULL) {
+ isc_radix_node_t *l = Xrn->l;
+ isc_radix_node_t *r = Xrn->r;
+
+ if (Xrn->prefix != NULL) {
+ _deref_prefix(Xrn->prefix);
+ if (func != NULL) {
+ func(Xrn->data);
+ }
+ } else {
+ INSIST(Xrn->data[RADIX_V4] == NULL &&
+ Xrn->data[RADIX_V6] == NULL);
+ }
+
+ isc_mem_put(radix->mctx, Xrn, sizeof(*Xrn));
+ radix->num_active_node--;
+
+ if (l != NULL) {
+ if (r != NULL) {
+ *Xsp++ = r;
+ }
+ Xrn = l;
+ } else if (r != NULL) {
+ Xrn = r;
+ } else if (Xsp != Xstack) {
+ Xrn = *(--Xsp);
+ } else {
+ Xrn = NULL;
+ }
+ }
+ }
+ RUNTIME_CHECK(radix->num_active_node == 0);
+}
+
+void
+isc_radix_destroy(isc_radix_tree_t *radix, isc_radix_destroyfunc_t func) {
+ REQUIRE(radix != NULL);
+ _clear_radix(radix, func);
+ isc_mem_putanddetach(&radix->mctx, radix, sizeof(*radix));
+}
+
+/*
+ * func will be called as func(node->prefix, node->data)
+ */
+void
+isc_radix_process(isc_radix_tree_t *radix, isc_radix_processfunc_t func) {
+ isc_radix_node_t *node;
+
+ REQUIRE(func != NULL);
+
+ RADIX_WALK(radix->head, node) { func(node->prefix, node->data); }
+ RADIX_WALK_END;
+}
+
+isc_result_t
+isc_radix_search(isc_radix_tree_t *radix, isc_radix_node_t **target,
+ isc_prefix_t *prefix) {
+ isc_radix_node_t *node;
+ isc_radix_node_t *stack[RADIX_MAXBITS + 1];
+ u_char *addr;
+ uint32_t bitlen;
+ int tfam = -1, cnt = 0;
+
+ REQUIRE(radix != NULL);
+ REQUIRE(prefix != NULL);
+ REQUIRE(target != NULL && *target == NULL);
+ RUNTIME_CHECK(prefix->bitlen <= radix->maxbits);
+
+ *target = NULL;
+
+ node = radix->head;
+
+ if (node == NULL) {
+ return (ISC_R_NOTFOUND);
+ }
+
+ addr = isc_prefix_touchar(prefix);
+ bitlen = prefix->bitlen;
+
+ while (node != NULL && node->bit < bitlen) {
+ if (node->prefix) {
+ stack[cnt++] = node;
+ }
+
+ if (BIT_TEST(addr[node->bit >> 3], 0x80 >> (node->bit & 0x07)))
+ {
+ node = node->r;
+ } else {
+ node = node->l;
+ }
+ }
+
+ if (node != NULL && node->prefix) {
+ stack[cnt++] = node;
+ }
+
+ while (cnt-- > 0) {
+ node = stack[cnt];
+
+ if (prefix->bitlen < node->bit) {
+ continue;
+ }
+
+ if (_comp_with_mask(isc_prefix_tochar(node->prefix),
+ isc_prefix_tochar(prefix),
+ node->prefix->bitlen))
+ {
+ int fam = ISC_RADIX_FAMILY(prefix);
+ if (node->node_num[fam] != -1 &&
+ ((*target == NULL) ||
+ (*target)->node_num[tfam] > node->node_num[fam]))
+ {
+ *target = node;
+ tfam = fam;
+ }
+ }
+ }
+
+ if (*target == NULL) {
+ return (ISC_R_NOTFOUND);
+ } else {
+ return (ISC_R_SUCCESS);
+ }
+}
+
+isc_result_t
+isc_radix_insert(isc_radix_tree_t *radix, isc_radix_node_t **target,
+ isc_radix_node_t *source, isc_prefix_t *prefix) {
+ isc_radix_node_t *node, *new_node, *parent, *glue = NULL;
+ u_char *addr, *test_addr;
+ uint32_t bitlen, fam, check_bit, differ_bit;
+ uint32_t i, j, r;
+ isc_result_t result;
+
+ REQUIRE(radix != NULL);
+ REQUIRE(target != NULL && *target == NULL);
+ REQUIRE(prefix != NULL || (source != NULL && source->prefix != NULL));
+ RUNTIME_CHECK(prefix == NULL || prefix->bitlen <= radix->maxbits);
+
+ if (prefix == NULL) {
+ prefix = source->prefix;
+ }
+
+ INSIST(prefix != NULL);
+
+ bitlen = prefix->bitlen;
+ fam = prefix->family;
+
+ if (radix->head == NULL) {
+ node = isc_mem_get(radix->mctx, sizeof(isc_radix_node_t));
+ node->bit = bitlen;
+ for (i = 0; i < RADIX_FAMILIES; i++) {
+ node->node_num[i] = -1;
+ }
+ node->prefix = NULL;
+ result = _ref_prefix(radix->mctx, &node->prefix, prefix);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(radix->mctx, node,
+ sizeof(isc_radix_node_t));
+ return (result);
+ }
+ node->parent = NULL;
+ node->l = node->r = NULL;
+ if (source != NULL) {
+ /*
+ * If source is non-NULL, then we're merging in a
+ * node from an existing radix tree. To keep
+ * the node_num values consistent, the calling
+ * function will add the total number of nodes
+ * added to num_added_node at the end of
+ * the merge operation--we don't do it here.
+ */
+ for (i = 0; i < RADIX_FAMILIES; i++) {
+ if (source->node_num[i] != -1) {
+ node->node_num[i] =
+ radix->num_added_node +
+ source->node_num[i];
+ }
+ node->data[i] = source->data[i];
+ }
+ } else {
+ int next = ++radix->num_added_node;
+ if (fam == AF_UNSPEC) {
+ /* "any" or "none" */
+ for (i = 0; i < RADIX_FAMILIES; i++) {
+ node->node_num[i] = next;
+ }
+ } else {
+ node->node_num[ISC_RADIX_FAMILY(prefix)] = next;
+ }
+
+ memset(node->data, 0, sizeof(node->data));
+ }
+ radix->head = node;
+ radix->num_active_node++;
+ *target = node;
+ return (ISC_R_SUCCESS);
+ }
+
+ addr = isc_prefix_touchar(prefix);
+ node = radix->head;
+
+ while (node->bit < bitlen || node->prefix == NULL) {
+ if (node->bit < radix->maxbits &&
+ BIT_TEST(addr[node->bit >> 3], 0x80 >> (node->bit & 0x07)))
+ {
+ if (node->r == NULL) {
+ break;
+ }
+ node = node->r;
+ } else {
+ if (node->l == NULL) {
+ break;
+ }
+ node = node->l;
+ }
+
+ INSIST(node != NULL);
+ }
+
+ INSIST(node->prefix != NULL);
+
+ test_addr = isc_prefix_touchar(node->prefix);
+ /* Find the first bit different. */
+ check_bit = (node->bit < bitlen) ? node->bit : bitlen;
+ differ_bit = 0;
+ for (i = 0; i * 8 < check_bit; i++) {
+ if ((r = (addr[i] ^ test_addr[i])) == 0) {
+ differ_bit = (i + 1) * 8;
+ continue;
+ }
+ /* I know the better way, but for now. */
+ for (j = 0; j < 8; j++) {
+ if (BIT_TEST(r, (0x80 >> j))) {
+ break;
+ }
+ }
+ /* Must be found. */
+ INSIST(j < 8);
+ differ_bit = i * 8 + j;
+ break;
+ }
+
+ if (differ_bit > check_bit) {
+ differ_bit = check_bit;
+ }
+
+ parent = node->parent;
+ while (parent != NULL && parent->bit >= differ_bit) {
+ node = parent;
+ parent = node->parent;
+ }
+
+ if (differ_bit == bitlen && node->bit == bitlen) {
+ if (node->prefix != NULL) {
+ /* Set node_num only if it hasn't been set before */
+ if (source != NULL) {
+ /* Merging nodes */
+ for (i = 0; i < RADIX_FAMILIES; i++) {
+ if (node->node_num[i] == -1 &&
+ source->node_num[i] != -1)
+ {
+ node->node_num[i] =
+ radix->num_added_node +
+ source->node_num[i];
+ node->data[i] = source->data[i];
+ }
+ }
+ } else {
+ if (fam == AF_UNSPEC) {
+ /* "any" or "none" */
+ int next = radix->num_added_node + 1;
+ for (i = 0; i < RADIX_FAMILIES; i++) {
+ if (node->node_num[i] == -1) {
+ node->node_num[i] =
+ next;
+ radix->num_added_node =
+ next;
+ }
+ }
+ } else {
+ int foff = ISC_RADIX_FAMILY(prefix);
+ if (node->node_num[foff] == -1) {
+ node->node_num[foff] =
+ ++radix->num_added_node;
+ }
+ }
+ }
+ *target = node;
+ return (ISC_R_SUCCESS);
+ } else {
+ result = _ref_prefix(radix->mctx, &node->prefix,
+ prefix);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ }
+ INSIST(node->data[RADIX_V4] == NULL &&
+ node->node_num[RADIX_V4] == -1 &&
+ node->data[RADIX_V4] == NULL &&
+ node->node_num[RADIX_V4] == -1);
+ if (source != NULL) {
+ /* Merging node */
+ for (i = 0; i < RADIX_FAMILIES; i++) {
+ int cur = radix->num_added_node;
+ if (source->node_num[i] != -1) {
+ node->node_num[i] =
+ source->node_num[i] + cur;
+ node->data[i] = source->data[i];
+ }
+ }
+ } else {
+ int next = ++radix->num_added_node;
+ if (fam == AF_UNSPEC) {
+ /* "any" or "none" */
+ for (i = 0; i < RADIX_FAMILIES; i++) {
+ node->node_num[i] = next;
+ }
+ } else {
+ node->node_num[ISC_RADIX_FAMILY(prefix)] = next;
+ }
+ }
+ *target = node;
+ return (ISC_R_SUCCESS);
+ }
+
+ new_node = isc_mem_get(radix->mctx, sizeof(isc_radix_node_t));
+ if (node->bit != differ_bit && bitlen != differ_bit) {
+ glue = isc_mem_get(radix->mctx, sizeof(isc_radix_node_t));
+ }
+ new_node->bit = bitlen;
+ new_node->prefix = NULL;
+ result = _ref_prefix(radix->mctx, &new_node->prefix, prefix);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(radix->mctx, new_node, sizeof(isc_radix_node_t));
+ if (glue != NULL) {
+ isc_mem_put(radix->mctx, glue,
+ sizeof(isc_radix_node_t));
+ }
+ return (result);
+ }
+ new_node->parent = NULL;
+ new_node->l = new_node->r = NULL;
+ for (i = 0; i < RADIX_FAMILIES; i++) {
+ new_node->node_num[i] = -1;
+ new_node->data[i] = NULL;
+ }
+ radix->num_active_node++;
+
+ if (source != NULL) {
+ /* Merging node */
+ for (i = 0; i < RADIX_FAMILIES; i++) {
+ int cur = radix->num_added_node;
+ if (source->node_num[i] != -1) {
+ new_node->node_num[i] = source->node_num[i] +
+ cur;
+ new_node->data[i] = source->data[i];
+ }
+ }
+ } else {
+ int next = ++radix->num_added_node;
+ if (fam == AF_UNSPEC) {
+ /* "any" or "none" */
+ for (i = 0; i < RADIX_FAMILIES; i++) {
+ new_node->node_num[i] = next;
+ }
+ } else {
+ new_node->node_num[ISC_RADIX_FAMILY(prefix)] = next;
+ }
+ memset(new_node->data, 0, sizeof(new_node->data));
+ }
+
+ if (node->bit == differ_bit) {
+ INSIST(glue == NULL);
+ new_node->parent = node;
+ if (node->bit < radix->maxbits &&
+ BIT_TEST(addr[node->bit >> 3], 0x80 >> (node->bit & 0x07)))
+ {
+ INSIST(node->r == NULL);
+ node->r = new_node;
+ } else {
+ INSIST(node->l == NULL);
+ node->l = new_node;
+ }
+ *target = new_node;
+ return (ISC_R_SUCCESS);
+ }
+
+ if (bitlen == differ_bit) {
+ INSIST(glue == NULL);
+ if (bitlen < radix->maxbits &&
+ BIT_TEST(test_addr[bitlen >> 3], 0x80 >> (bitlen & 0x07)))
+ {
+ new_node->r = node;
+ } else {
+ new_node->l = node;
+ }
+ new_node->parent = node->parent;
+ if (node->parent == NULL) {
+ INSIST(radix->head == node);
+ radix->head = new_node;
+ } else if (node->parent->r == node) {
+ node->parent->r = new_node;
+ } else {
+ node->parent->l = new_node;
+ }
+ node->parent = new_node;
+ } else {
+ INSIST(glue != NULL);
+ glue->bit = differ_bit;
+ glue->prefix = NULL;
+ glue->parent = node->parent;
+ for (i = 0; i < RADIX_FAMILIES; i++) {
+ glue->data[i] = NULL;
+ glue->node_num[i] = -1;
+ }
+ radix->num_active_node++;
+ if (differ_bit < radix->maxbits &&
+ BIT_TEST(addr[differ_bit >> 3], 0x80 >> (differ_bit & 07)))
+ {
+ glue->r = new_node;
+ glue->l = node;
+ } else {
+ glue->r = node;
+ glue->l = new_node;
+ }
+ new_node->parent = glue;
+
+ if (node->parent == NULL) {
+ INSIST(radix->head == node);
+ radix->head = glue;
+ } else if (node->parent->r == node) {
+ node->parent->r = glue;
+ } else {
+ node->parent->l = glue;
+ }
+ node->parent = glue;
+ }
+
+ *target = new_node;
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_radix_remove(isc_radix_tree_t *radix, isc_radix_node_t *node) {
+ isc_radix_node_t *parent, *child;
+
+ REQUIRE(radix != NULL);
+ REQUIRE(node != NULL);
+
+ if (node->r && node->l) {
+ /*
+ * This might be a placeholder node -- have to check and
+ * make sure there is a prefix associated with it!
+ */
+ if (node->prefix != NULL) {
+ _deref_prefix(node->prefix);
+ }
+
+ node->prefix = NULL;
+ memset(node->data, 0, sizeof(node->data));
+ return;
+ }
+
+ if (node->r == NULL && node->l == NULL) {
+ parent = node->parent;
+ _deref_prefix(node->prefix);
+
+ if (parent == NULL) {
+ INSIST(radix->head == node);
+ radix->head = NULL;
+ isc_mem_put(radix->mctx, node, sizeof(*node));
+ radix->num_active_node--;
+ return;
+ }
+
+ if (parent->r == node) {
+ parent->r = NULL;
+ child = parent->l;
+ } else {
+ INSIST(parent->l == node);
+ parent->l = NULL;
+ child = parent->r;
+ }
+
+ isc_mem_put(radix->mctx, node, sizeof(*node));
+ radix->num_active_node--;
+
+ if (parent->prefix) {
+ return;
+ }
+
+ /* We need to remove parent too. */
+ if (parent->parent == NULL) {
+ INSIST(radix->head == parent);
+ radix->head = child;
+ } else if (parent->parent->r == parent) {
+ parent->parent->r = child;
+ } else {
+ INSIST(parent->parent->l == parent);
+ parent->parent->l = child;
+ }
+
+ child->parent = parent->parent;
+ isc_mem_put(radix->mctx, parent, sizeof(*parent));
+ radix->num_active_node--;
+ return;
+ }
+
+ if (node->r) {
+ child = node->r;
+ } else {
+ INSIST(node->l != NULL);
+ child = node->l;
+ }
+
+ parent = node->parent;
+ child->parent = parent;
+
+ _deref_prefix(node->prefix);
+
+ if (parent == NULL) {
+ INSIST(radix->head == node);
+ radix->head = child;
+ isc_mem_put(radix->mctx, node, sizeof(*node));
+ radix->num_active_node--;
+ return;
+ }
+
+ if (parent->r == node) {
+ parent->r = child;
+ } else {
+ INSIST(parent->l == node);
+ parent->l = child;
+ }
+
+ isc_mem_put(radix->mctx, node, sizeof(*node));
+ radix->num_active_node--;
+}
diff --git a/lib/isc/random.c b/lib/isc/random.c
new file mode 100644
index 0000000..7eead66
--- /dev/null
+++ b/lib/isc/random.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Portions of isc_random_uniform():
+ *
+ * Copyright (c) 1996, David Mazieres <dm@uun.org>
+ * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
+ *
+ * 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 <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <isc/once.h>
+#include <isc/random.h>
+#include <isc/result.h>
+#include <isc/thread.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+#include "entropy_private.h"
+
+/*
+ * The specific implementation for PRNG is included as a C file
+ * that has to provide a static variable named seed, and a function
+ * uint32_t next(void) that provides next random number.
+ *
+ * The implementation must be thread-safe.
+ */
+
+/*
+ * Two contestants have been considered: the xoroshiro family of the
+ * functions by Villa&Blackman, and PCG by O'Neill. After
+ * consideration, the xoshiro128starstar function has been chosen as
+ * the uint32_t random number provider because it is very fast and has
+ * good enough properties for our usage pattern.
+ */
+
+/*
+ * Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org)
+ *
+ * To the extent possible under law, the author has dedicated all
+ * copyright and related and neighboring rights to this software to the
+ * public domain worldwide. This software is distributed without any
+ * warranty.
+ *
+ * See <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+/*
+ * This is xoshiro128** 1.0, our 32-bit all-purpose, rock-solid generator.
+ * It has excellent (sub-ns) speed, a state size (128 bits) that is large
+ * enough for mild parallelism, and it passes all tests we are aware of.
+ *
+ * For generating just single-precision (i.e., 32-bit) floating-point
+ * numbers, xoshiro128+ is even faster.
+ *
+ * The state must be seeded so that it is not everywhere zero.
+ */
+static thread_local uint32_t seed[4] = { 0 };
+
+static uint32_t
+rotl(const uint32_t x, int k) {
+ return ((x << k) | (x >> (32 - k)));
+}
+
+static uint32_t
+next(void) {
+ uint32_t result_starstar, t;
+
+ result_starstar = rotl(seed[0] * 5, 7) * 9;
+ t = seed[1] << 9;
+
+ seed[2] ^= seed[0];
+ seed[3] ^= seed[1];
+ seed[1] ^= seed[2];
+ seed[0] ^= seed[3];
+
+ seed[2] ^= t;
+
+ seed[3] = rotl(seed[3], 11);
+
+ return (result_starstar);
+}
+
+static thread_local isc_once_t isc_random_once = ISC_ONCE_INIT;
+
+static void
+isc_random_initialize(void) {
+ int useed[4] = { 0, 0, 0, 1 };
+#if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ /*
+ * Set a constant seed to help in problem reproduction should fuzzing
+ * find a crash or a hang. The seed array must be non-zero else
+ * xoshiro128starstar will generate an infinite series of zeroes.
+ */
+#else /* if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
+ isc_entropy_get(useed, sizeof(useed));
+#endif /* if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
+ memmove(seed, useed, sizeof(seed));
+}
+
+uint8_t
+isc_random8(void) {
+ RUNTIME_CHECK(isc_once_do(&isc_random_once, isc_random_initialize) ==
+ ISC_R_SUCCESS);
+ return (next() & 0xff);
+}
+
+uint16_t
+isc_random16(void) {
+ RUNTIME_CHECK(isc_once_do(&isc_random_once, isc_random_initialize) ==
+ ISC_R_SUCCESS);
+ return (next() & 0xffff);
+}
+
+uint32_t
+isc_random32(void) {
+ RUNTIME_CHECK(isc_once_do(&isc_random_once, isc_random_initialize) ==
+ ISC_R_SUCCESS);
+ return (next());
+}
+
+void
+isc_random_buf(void *buf, size_t buflen) {
+ int i;
+ uint32_t r;
+
+ REQUIRE(buf != NULL);
+ REQUIRE(buflen > 0);
+
+ RUNTIME_CHECK(isc_once_do(&isc_random_once, isc_random_initialize) ==
+ ISC_R_SUCCESS);
+
+ for (i = 0; i + sizeof(r) <= buflen; i += sizeof(r)) {
+ r = next();
+ memmove((uint8_t *)buf + i, &r, sizeof(r));
+ }
+ r = next();
+ memmove((uint8_t *)buf + i, &r, buflen % sizeof(r));
+ return;
+}
+
+uint32_t
+isc_random_uniform(uint32_t upper_bound) {
+ /* Copy of arc4random_uniform from OpenBSD */
+ uint32_t r, min;
+
+ RUNTIME_CHECK(isc_once_do(&isc_random_once, isc_random_initialize) ==
+ ISC_R_SUCCESS);
+
+ if (upper_bound < 2) {
+ return (0);
+ }
+
+#if (ULONG_MAX > 0xffffffffUL)
+ min = 0x100000000UL % upper_bound;
+#else /* if (ULONG_MAX > 0xffffffffUL) */
+ /* Calculate (2**32 % upper_bound) avoiding 64-bit math */
+ if (upper_bound > 0x80000000) {
+ min = 1 + ~upper_bound; /* 2**32 - upper_bound */
+ } else {
+ /* (2**32 - (x * 2)) % x == 2**32 % x when x <= 2**31 */
+ min = ((0xffffffff - (upper_bound * 2)) + 1) % upper_bound;
+ }
+#endif /* if (ULONG_MAX > 0xffffffffUL) */
+
+ /*
+ * This could theoretically loop forever but each retry has
+ * p > 0.5 (worst case, usually far better) of selecting a
+ * number inside the range we need, so it should rarely need
+ * to re-roll.
+ */
+ for (;;) {
+ r = next();
+ if (r >= min) {
+ break;
+ }
+ }
+
+ return (r % upper_bound);
+}
diff --git a/lib/isc/ratelimiter.c b/lib/isc/ratelimiter.c
new file mode 100644
index 0000000..84a848a
--- /dev/null
+++ b/lib/isc/ratelimiter.c
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/mem.h>
+#include <isc/ratelimiter.h>
+#include <isc/refcount.h>
+#include <isc/task.h>
+#include <isc/time.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+typedef enum {
+ isc_ratelimiter_stalled = 0,
+ isc_ratelimiter_ratelimited = 1,
+ isc_ratelimiter_idle = 2,
+ isc_ratelimiter_shuttingdown = 3
+} isc_ratelimiter_state_t;
+
+struct isc_ratelimiter {
+ isc_mem_t *mctx;
+ isc_mutex_t lock;
+ isc_refcount_t references;
+ isc_task_t *task;
+ isc_timer_t *timer;
+ isc_interval_t interval;
+ uint32_t pertic;
+ bool pushpop;
+ isc_ratelimiter_state_t state;
+ isc_event_t shutdownevent;
+ ISC_LIST(isc_event_t) pending;
+};
+
+#define ISC_RATELIMITEREVENT_SHUTDOWN (ISC_EVENTCLASS_RATELIMITER + 1)
+
+static void
+ratelimiter_tick(isc_task_t *task, isc_event_t *event);
+
+static void
+ratelimiter_shutdowncomplete(isc_task_t *task, isc_event_t *event);
+
+isc_result_t
+isc_ratelimiter_create(isc_mem_t *mctx, isc_timermgr_t *timermgr,
+ isc_task_t *task, isc_ratelimiter_t **ratelimiterp) {
+ isc_result_t result;
+ isc_ratelimiter_t *rl;
+ INSIST(ratelimiterp != NULL && *ratelimiterp == NULL);
+
+ rl = isc_mem_get(mctx, sizeof(*rl));
+ *rl = (isc_ratelimiter_t){
+ .mctx = mctx,
+ .task = task,
+ .pertic = 1,
+ .state = isc_ratelimiter_idle,
+ };
+
+ isc_refcount_init(&rl->references, 1);
+ isc_interval_set(&rl->interval, 0, 0);
+ ISC_LIST_INIT(rl->pending);
+
+ isc_mutex_init(&rl->lock);
+
+ result = isc_timer_create(timermgr, isc_timertype_inactive, NULL, NULL,
+ rl->task, ratelimiter_tick, rl, &rl->timer);
+ if (result != ISC_R_SUCCESS) {
+ goto free_mutex;
+ }
+
+ /*
+ * Increment the reference count to indicate that we may
+ * (soon) have events outstanding.
+ */
+ isc_refcount_increment(&rl->references);
+
+ ISC_EVENT_INIT(&rl->shutdownevent, sizeof(isc_event_t), 0, NULL,
+ ISC_RATELIMITEREVENT_SHUTDOWN,
+ ratelimiter_shutdowncomplete, rl, rl, NULL, NULL);
+
+ *ratelimiterp = rl;
+ return (ISC_R_SUCCESS);
+
+free_mutex:
+ isc_refcount_decrementz(&rl->references);
+ isc_refcount_destroy(&rl->references);
+ isc_mutex_destroy(&rl->lock);
+ isc_mem_put(mctx, rl, sizeof(*rl));
+ return (result);
+}
+
+isc_result_t
+isc_ratelimiter_setinterval(isc_ratelimiter_t *rl, isc_interval_t *interval) {
+ isc_result_t result = ISC_R_SUCCESS;
+
+ REQUIRE(rl != NULL);
+ REQUIRE(interval != NULL);
+
+ LOCK(&rl->lock);
+ rl->interval = *interval;
+ /*
+ * If the timer is currently running, change its rate.
+ */
+ if (rl->state == isc_ratelimiter_ratelimited) {
+ result = isc_timer_reset(rl->timer, isc_timertype_ticker, NULL,
+ &rl->interval, false);
+ }
+ UNLOCK(&rl->lock);
+ return (result);
+}
+
+void
+isc_ratelimiter_setpertic(isc_ratelimiter_t *rl, uint32_t pertic) {
+ REQUIRE(rl != NULL);
+
+ if (pertic == 0) {
+ pertic = 1;
+ }
+ rl->pertic = pertic;
+}
+
+void
+isc_ratelimiter_setpushpop(isc_ratelimiter_t *rl, bool pushpop) {
+ REQUIRE(rl != NULL);
+
+ rl->pushpop = pushpop;
+}
+
+isc_result_t
+isc_ratelimiter_enqueue(isc_ratelimiter_t *rl, isc_task_t *task,
+ isc_event_t **eventp) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_event_t *ev;
+
+ REQUIRE(rl != NULL);
+ REQUIRE(task != NULL);
+ REQUIRE(eventp != NULL && *eventp != NULL);
+ ev = *eventp;
+ REQUIRE(ev->ev_sender == NULL);
+
+ LOCK(&rl->lock);
+ if (rl->state == isc_ratelimiter_ratelimited ||
+ rl->state == isc_ratelimiter_stalled)
+ {
+ ev->ev_sender = task;
+ *eventp = NULL;
+ if (rl->pushpop) {
+ ISC_LIST_PREPEND(rl->pending, ev, ev_ratelink);
+ } else {
+ ISC_LIST_APPEND(rl->pending, ev, ev_ratelink);
+ }
+ } else if (rl->state == isc_ratelimiter_idle) {
+ result = isc_timer_reset(rl->timer, isc_timertype_ticker, NULL,
+ &rl->interval, false);
+ if (result == ISC_R_SUCCESS) {
+ ev->ev_sender = task;
+ rl->state = isc_ratelimiter_ratelimited;
+ }
+ } else {
+ INSIST(rl->state == isc_ratelimiter_shuttingdown);
+ result = ISC_R_SHUTTINGDOWN;
+ }
+ UNLOCK(&rl->lock);
+ if (*eventp != NULL && result == ISC_R_SUCCESS) {
+ isc_task_send(task, eventp);
+ }
+ return (result);
+}
+
+isc_result_t
+isc_ratelimiter_dequeue(isc_ratelimiter_t *rl, isc_event_t *event) {
+ isc_result_t result = ISC_R_SUCCESS;
+
+ REQUIRE(rl != NULL);
+ REQUIRE(event != NULL);
+
+ LOCK(&rl->lock);
+ if (ISC_LINK_LINKED(event, ev_ratelink)) {
+ ISC_LIST_UNLINK(rl->pending, event, ev_ratelink);
+ event->ev_sender = NULL;
+ } else {
+ result = ISC_R_NOTFOUND;
+ }
+ UNLOCK(&rl->lock);
+ return (result);
+}
+
+static void
+ratelimiter_tick(isc_task_t *task, isc_event_t *event) {
+ isc_ratelimiter_t *rl = (isc_ratelimiter_t *)event->ev_arg;
+ isc_event_t *p;
+ uint32_t pertic;
+
+ UNUSED(task);
+
+ isc_event_free(&event);
+
+ pertic = rl->pertic;
+ while (pertic != 0) {
+ pertic--;
+ LOCK(&rl->lock);
+ p = ISC_LIST_HEAD(rl->pending);
+ if (p != NULL) {
+ /*
+ * There is work to do. Let's do it after unlocking.
+ */
+ ISC_LIST_UNLINK(rl->pending, p, ev_ratelink);
+ } else {
+ /*
+ * No work left to do. Stop the timer so that we don't
+ * waste resources by having it fire periodically.
+ */
+ isc_result_t result = isc_timer_reset(
+ rl->timer, isc_timertype_inactive, NULL, NULL,
+ false);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ rl->state = isc_ratelimiter_idle;
+ pertic = 0; /* Force the loop to exit. */
+ }
+ UNLOCK(&rl->lock);
+ if (p != NULL) {
+ isc_task_t *evtask = p->ev_sender;
+ isc_task_send(evtask, &p);
+ }
+ INSIST(p == NULL);
+ }
+}
+
+void
+isc_ratelimiter_shutdown(isc_ratelimiter_t *rl) {
+ isc_event_t *ev;
+ isc_task_t *task;
+ isc_result_t result;
+
+ REQUIRE(rl != NULL);
+
+ LOCK(&rl->lock);
+ rl->state = isc_ratelimiter_shuttingdown;
+ (void)isc_timer_reset(rl->timer, isc_timertype_inactive, NULL, NULL,
+ false);
+ while ((ev = ISC_LIST_HEAD(rl->pending)) != NULL) {
+ task = ev->ev_sender;
+ ISC_LIST_UNLINK(rl->pending, ev, ev_ratelink);
+ ev->ev_attributes |= ISC_EVENTATTR_CANCELED;
+ isc_task_send(task, &ev);
+ }
+ task = NULL;
+ isc_task_attach(rl->task, &task);
+
+ result = isc_timer_reset(rl->timer, isc_timertype_inactive, NULL, NULL,
+ true);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ isc_timer_destroy(&rl->timer);
+
+ /*
+ * Send an event to our task. The delivery of this event
+ * indicates that no more timer events will be delivered.
+ */
+ ev = &rl->shutdownevent;
+ isc_task_send(rl->task, &ev);
+
+ UNLOCK(&rl->lock);
+}
+
+static void
+ratelimiter_shutdowncomplete(isc_task_t *task, isc_event_t *event) {
+ isc_ratelimiter_t *rl = (isc_ratelimiter_t *)event->ev_arg;
+
+ UNUSED(task);
+
+ isc_ratelimiter_detach(&rl);
+ isc_task_detach(&task);
+}
+
+static void
+ratelimiter_free(isc_ratelimiter_t *rl) {
+ isc_refcount_destroy(&rl->references);
+ isc_mutex_destroy(&rl->lock);
+ isc_mem_put(rl->mctx, rl, sizeof(*rl));
+}
+
+void
+isc_ratelimiter_attach(isc_ratelimiter_t *source, isc_ratelimiter_t **target) {
+ REQUIRE(source != NULL);
+ REQUIRE(target != NULL && *target == NULL);
+
+ isc_refcount_increment(&source->references);
+
+ *target = source;
+}
+
+void
+isc_ratelimiter_detach(isc_ratelimiter_t **rlp) {
+ isc_ratelimiter_t *rl;
+
+ REQUIRE(rlp != NULL && *rlp != NULL);
+
+ rl = *rlp;
+ *rlp = NULL;
+
+ if (isc_refcount_decrement(&rl->references) == 1) {
+ ratelimiter_free(rl);
+ }
+}
+
+isc_result_t
+isc_ratelimiter_stall(isc_ratelimiter_t *rl) {
+ isc_result_t result = ISC_R_SUCCESS;
+
+ REQUIRE(rl != NULL);
+
+ LOCK(&rl->lock);
+ switch (rl->state) {
+ case isc_ratelimiter_shuttingdown:
+ result = ISC_R_SHUTTINGDOWN;
+ break;
+ case isc_ratelimiter_ratelimited:
+ result = isc_timer_reset(rl->timer, isc_timertype_inactive,
+ NULL, NULL, false);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ FALLTHROUGH;
+ case isc_ratelimiter_idle:
+ case isc_ratelimiter_stalled:
+ rl->state = isc_ratelimiter_stalled;
+ break;
+ }
+ UNLOCK(&rl->lock);
+ return (result);
+}
+
+isc_result_t
+isc_ratelimiter_release(isc_ratelimiter_t *rl) {
+ isc_result_t result = ISC_R_SUCCESS;
+
+ REQUIRE(rl != NULL);
+
+ LOCK(&rl->lock);
+ switch (rl->state) {
+ case isc_ratelimiter_shuttingdown:
+ result = ISC_R_SHUTTINGDOWN;
+ break;
+ case isc_ratelimiter_stalled:
+ if (!ISC_LIST_EMPTY(rl->pending)) {
+ result = isc_timer_reset(rl->timer,
+ isc_timertype_ticker, NULL,
+ &rl->interval, false);
+ if (result == ISC_R_SUCCESS) {
+ rl->state = isc_ratelimiter_ratelimited;
+ }
+ } else {
+ rl->state = isc_ratelimiter_idle;
+ }
+ break;
+ case isc_ratelimiter_ratelimited:
+ case isc_ratelimiter_idle:
+ break;
+ }
+ UNLOCK(&rl->lock);
+ return (result);
+}
diff --git a/lib/isc/regex.c b/lib/isc/regex.c
new file mode 100644
index 0000000..f7a3f5e
--- /dev/null
+++ b/lib/isc/regex.c
@@ -0,0 +1,436 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <stdbool.h>
+
+#include <isc/file.h>
+#include <isc/print.h>
+#include <isc/regex.h>
+#include <isc/string.h>
+
+#if VALREGEX_REPORT_REASON
+#define FAIL(x) \
+ do { \
+ reason = (x); \
+ goto error; \
+ } while (0)
+#else /* if VALREGEX_REPORT_REASON */
+#define FAIL(x) goto error
+#endif /* if VALREGEX_REPORT_REASON */
+
+/*
+ * Validate the regular expression 'C' locale.
+ */
+int
+isc_regex_validate(const char *c) {
+ enum {
+ none,
+ parse_bracket,
+ parse_bound,
+ parse_ce,
+ parse_ec,
+ parse_cc
+ } state = none;
+ /* Well known character classes. */
+ const char *cc[] = { ":alnum:", ":digit:", ":punct:", ":alpha:",
+ ":graph:", ":space:", ":blank:", ":lower:",
+ ":upper:", ":cntrl:", ":print:", ":xdigit:" };
+ bool seen_comma = false;
+ bool seen_high = false;
+ bool seen_char = false;
+ bool seen_ec = false;
+ bool seen_ce = false;
+ bool have_atom = false;
+ int group = 0;
+ int range = 0;
+ int sub = 0;
+ bool empty_ok = false;
+ bool neg = false;
+ bool was_multiple = false;
+ unsigned int low = 0;
+ unsigned int high = 0;
+ const char *ccname = NULL;
+ int range_start = 0;
+#if VALREGEX_REPORT_REASON
+ const char *reason = "";
+#endif /* if VALREGEX_REPORT_REASON */
+
+ if (c == NULL || *c == 0) {
+ FAIL("empty string");
+ }
+
+ while (c != NULL && *c != 0) {
+ switch (state) {
+ case none:
+ switch (*c) {
+ case '\\': /* make literal */
+ ++c;
+ switch (*c) {
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if ((*c - '0') > sub) {
+ FAIL("bad back reference");
+ }
+ have_atom = true;
+ was_multiple = false;
+ break;
+ case 0:
+ FAIL("escaped end-of-string");
+ default:
+ goto literal;
+ }
+ ++c;
+ break;
+ case '[': /* bracket start */
+ ++c;
+ neg = false;
+ was_multiple = false;
+ seen_char = false;
+ state = parse_bracket;
+ break;
+ case '{': /* bound start */
+ switch (c[1]) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (!have_atom) {
+ FAIL("no atom");
+ }
+ if (was_multiple) {
+ FAIL("was multiple");
+ }
+ seen_comma = false;
+ seen_high = false;
+ low = high = 0;
+ state = parse_bound;
+ break;
+ default:
+ goto literal;
+ }
+ ++c;
+ have_atom = true;
+ was_multiple = true;
+ break;
+ case '}':
+ goto literal;
+ case '(': /* group start */
+ have_atom = false;
+ was_multiple = false;
+ empty_ok = true;
+ ++group;
+ ++sub;
+ ++c;
+ break;
+ case ')': /* group end */
+ if (group && !have_atom && !empty_ok) {
+ FAIL("empty alternative");
+ }
+ have_atom = true;
+ was_multiple = false;
+ if (group != 0) {
+ --group;
+ }
+ ++c;
+ break;
+ case '|': /* alternative separator */
+ if (!have_atom) {
+ FAIL("no atom");
+ }
+ have_atom = false;
+ empty_ok = false;
+ was_multiple = false;
+ ++c;
+ break;
+ case '^':
+ case '$':
+ have_atom = true;
+ was_multiple = true;
+ ++c;
+ break;
+ case '+':
+ case '*':
+ case '?':
+ if (was_multiple) {
+ FAIL("was multiple");
+ }
+ if (!have_atom) {
+ FAIL("no atom");
+ }
+ have_atom = true;
+ was_multiple = true;
+ ++c;
+ break;
+ case '.':
+ default:
+ literal:
+ have_atom = true;
+ was_multiple = false;
+ ++c;
+ break;
+ }
+ break;
+ case parse_bound:
+ switch (*c) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (!seen_comma) {
+ low = low * 10 + *c - '0';
+ if (low > 255) {
+ FAIL("lower bound too big");
+ }
+ } else {
+ seen_high = true;
+ high = high * 10 + *c - '0';
+ if (high > 255) {
+ FAIL("upper bound too big");
+ }
+ }
+ ++c;
+ break;
+ case ',':
+ if (seen_comma) {
+ FAIL("multiple commas");
+ }
+ seen_comma = true;
+ ++c;
+ break;
+ default:
+ case '{':
+ FAIL("non digit/comma");
+ case '}':
+ if (seen_high && low > high) {
+ FAIL("bad parse bound");
+ }
+ seen_comma = false;
+ state = none;
+ ++c;
+ break;
+ }
+ break;
+ case parse_bracket:
+ switch (*c) {
+ case '^':
+ if (seen_char || neg) {
+ goto inside;
+ }
+ neg = true;
+ ++c;
+ break;
+ case '-':
+ if (range == 2) {
+ goto inside;
+ }
+ if (!seen_char) {
+ goto inside;
+ }
+ if (range == 1) {
+ FAIL("bad range");
+ }
+ range = 2;
+ ++c;
+ break;
+ case '[':
+ ++c;
+ switch (*c) {
+ case '.': /* collating element */
+ if (range != 0) {
+ --range;
+ }
+ ++c;
+ state = parse_ce;
+ seen_ce = false;
+ break;
+ case '=': /* equivalence class */
+ if (range == 2) {
+ FAIL("equivalence class in "
+ "range");
+ }
+ ++c;
+ state = parse_ec;
+ seen_ec = false;
+ break;
+ case ':': /* character class */
+ if (range == 2) {
+ FAIL("character class in "
+ "range");
+ }
+ ccname = c;
+ ++c;
+ state = parse_cc;
+ break;
+ }
+ seen_char = true;
+ break;
+ case ']':
+ if (!c[1] && !seen_char) {
+ FAIL("unfinished brace");
+ }
+ if (!seen_char) {
+ goto inside;
+ }
+ ++c;
+ range = 0;
+ have_atom = true;
+ state = none;
+ break;
+ default:
+ inside:
+ seen_char = true;
+ if (range == 2 && (*c & 0xff) < range_start) {
+ FAIL("out of order range");
+ }
+ if (range != 0) {
+ --range;
+ }
+ range_start = *c & 0xff;
+ ++c;
+ break;
+ }
+ break;
+ case parse_ce:
+ switch (*c) {
+ case '.':
+ ++c;
+ switch (*c) {
+ case ']':
+ if (!seen_ce) {
+ FAIL("empty ce");
+ }
+ ++c;
+ state = parse_bracket;
+ break;
+ default:
+ if (seen_ce) {
+ range_start = 256;
+ } else {
+ range_start = '.';
+ }
+ seen_ce = true;
+ break;
+ }
+ break;
+ default:
+ if (seen_ce) {
+ range_start = 256;
+ } else {
+ range_start = *c;
+ }
+ seen_ce = true;
+ ++c;
+ break;
+ }
+ break;
+ case parse_ec:
+ switch (*c) {
+ case '=':
+ ++c;
+ switch (*c) {
+ case ']':
+ if (!seen_ec) {
+ FAIL("no ec");
+ }
+ ++c;
+ state = parse_bracket;
+ break;
+ default:
+ seen_ec = true;
+ break;
+ }
+ break;
+ default:
+ seen_ec = true;
+ ++c;
+ break;
+ }
+ break;
+ case parse_cc:
+ switch (*c) {
+ case ':':
+ ++c;
+ switch (*c) {
+ case ']': {
+ unsigned int i;
+ bool found = false;
+ for (i = 0;
+ i < sizeof(cc) / sizeof(*cc); i++)
+ {
+ unsigned int len;
+ len = strlen(cc[i]);
+ if (len !=
+ (unsigned int)(c - ccname))
+ {
+ continue;
+ }
+ if (strncmp(cc[i], ccname, len))
+ {
+ continue;
+ }
+ found = true;
+ }
+ if (!found) {
+ FAIL("unknown cc");
+ }
+ ++c;
+ state = parse_bracket;
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ default:
+ ++c;
+ break;
+ }
+ break;
+ }
+ }
+ if (group != 0) {
+ FAIL("group open");
+ }
+ if (state != none) {
+ FAIL("incomplete");
+ }
+ if (!have_atom) {
+ FAIL("no atom");
+ }
+ return (sub);
+
+error:
+#if VALREGEX_REPORT_REASON
+ fprintf(stderr, "%s\n", reason);
+#endif /* if VALREGEX_REPORT_REASON */
+ return (-1);
+}
diff --git a/lib/isc/region.c b/lib/isc/region.c
new file mode 100644
index 0000000..675f4ec
--- /dev/null
+++ b/lib/isc/region.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/region.h>
+#include <isc/util.h>
+
+int
+isc_region_compare(isc_region_t *r1, isc_region_t *r2) {
+ unsigned int l;
+ int result;
+
+ REQUIRE(r1 != NULL);
+ REQUIRE(r2 != NULL);
+
+ l = (r1->length < r2->length) ? r1->length : r2->length;
+
+ if ((result = memcmp(r1->base, r2->base, l)) != 0) {
+ return ((result < 0) ? -1 : 1);
+ } else {
+ return ((r1->length == r2->length) ? 0
+ : (r1->length < r2->length) ? -1
+ : 1);
+ }
+}
diff --git a/lib/isc/resource.c b/lib/isc/resource.c
new file mode 100644
index 0000000..d83762c
--- /dev/null
+++ b/lib/isc/resource.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <sys/resource.h>
+#include <sys/time.h> /* Required on some systems for <sys/resource.h>. */
+#include <sys/types.h>
+
+#include <isc/resource.h>
+#include <isc/result.h>
+#include <isc/util.h>
+
+#ifdef __linux__
+#include <linux/fs.h> /* To get the large NR_OPEN. */
+#endif /* ifdef __linux__ */
+
+#include "errno2result.h"
+
+static isc_result_t
+resource2rlim(isc_resource_t resource, int *rlim_resource) {
+ isc_result_t result = ISC_R_SUCCESS;
+
+ switch (resource) {
+ case isc_resource_coresize:
+ *rlim_resource = RLIMIT_CORE;
+ break;
+ case isc_resource_cputime:
+ *rlim_resource = RLIMIT_CPU;
+ break;
+ case isc_resource_datasize:
+ *rlim_resource = RLIMIT_DATA;
+ break;
+ case isc_resource_filesize:
+ *rlim_resource = RLIMIT_FSIZE;
+ break;
+ case isc_resource_lockedmemory:
+#ifdef RLIMIT_MEMLOCK
+ *rlim_resource = RLIMIT_MEMLOCK;
+#else /* ifdef RLIMIT_MEMLOCK */
+ result = ISC_R_NOTIMPLEMENTED;
+#endif /* ifdef RLIMIT_MEMLOCK */
+ break;
+ case isc_resource_openfiles:
+#ifdef RLIMIT_NOFILE
+ *rlim_resource = RLIMIT_NOFILE;
+#else /* ifdef RLIMIT_NOFILE */
+ result = ISC_R_NOTIMPLEMENTED;
+#endif /* ifdef RLIMIT_NOFILE */
+ break;
+ case isc_resource_processes:
+#ifdef RLIMIT_NPROC
+ *rlim_resource = RLIMIT_NPROC;
+#else /* ifdef RLIMIT_NPROC */
+ result = ISC_R_NOTIMPLEMENTED;
+#endif /* ifdef RLIMIT_NPROC */
+ break;
+ case isc_resource_residentsize:
+#ifdef RLIMIT_RSS
+ *rlim_resource = RLIMIT_RSS;
+#else /* ifdef RLIMIT_RSS */
+ result = ISC_R_NOTIMPLEMENTED;
+#endif /* ifdef RLIMIT_RSS */
+ break;
+ case isc_resource_stacksize:
+ *rlim_resource = RLIMIT_STACK;
+ break;
+ default:
+ /*
+ * This test is not very robust if isc_resource_t
+ * changes, but generates a clear assertion message.
+ */
+ REQUIRE(resource >= isc_resource_coresize &&
+ resource <= isc_resource_stacksize);
+
+ result = ISC_R_RANGE;
+ break;
+ }
+
+ return (result);
+}
+
+isc_result_t
+isc_resource_setlimit(isc_resource_t resource, isc_resourcevalue_t value) {
+ struct rlimit rl;
+ rlim_t rlim_value;
+ int unixresult;
+ int unixresource;
+ isc_result_t result;
+
+ result = resource2rlim(resource, &unixresource);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ if (value == ISC_RESOURCE_UNLIMITED) {
+ rlim_value = RLIM_INFINITY;
+ } else {
+ /*
+ * isc_resourcevalue_t was chosen as an unsigned 64 bit
+ * integer so that it could contain the maximum range of
+ * reasonable values. Unfortunately, this exceeds the typical
+ * range on Unix systems. Ensure the range of
+ * rlim_t is not overflowed.
+ */
+ isc_resourcevalue_t rlim_max;
+ bool rlim_t_is_signed = (((double)(rlim_t)-1) < 0);
+
+ if (rlim_t_is_signed) {
+ rlim_max = ~((rlim_t)1 << (sizeof(rlim_t) * 8 - 1));
+ } else {
+ rlim_max = (rlim_t)-1;
+ }
+
+ if (value > rlim_max) {
+ value = rlim_max;
+ }
+
+ rlim_value = value;
+ }
+
+ rl.rlim_cur = rl.rlim_max = rlim_value;
+ unixresult = setrlimit(unixresource, &rl);
+
+ if (unixresult == 0) {
+ return (ISC_R_SUCCESS);
+ }
+
+#if defined(OPEN_MAX) && defined(__APPLE__)
+ /*
+ * The Darwin kernel doesn't accept RLIM_INFINITY for rlim_cur; the
+ * maximum possible value is OPEN_MAX. BIND8 used to use
+ * sysconf(_SC_OPEN_MAX) for such a case, but this value is much
+ * smaller than OPEN_MAX and is not really effective.
+ */
+ if (resource == isc_resource_openfiles && rlim_value == RLIM_INFINITY) {
+ rl.rlim_cur = OPEN_MAX;
+ unixresult = setrlimit(unixresource, &rl);
+ if (unixresult == 0) {
+ return (ISC_R_SUCCESS);
+ }
+ }
+#elif defined(__linux__)
+#ifndef NR_OPEN
+#define NR_OPEN (1024 * 1024)
+#endif /* ifndef NR_OPEN */
+
+ /*
+ * Some Linux kernels don't accept RLIM_INFINIT; the maximum
+ * possible value is the NR_OPEN defined in linux/fs.h.
+ */
+ if (resource == isc_resource_openfiles && rlim_value == RLIM_INFINITY) {
+ rl.rlim_cur = rl.rlim_max = NR_OPEN;
+ unixresult = setrlimit(unixresource, &rl);
+ if (unixresult == 0) {
+ return (ISC_R_SUCCESS);
+ }
+ }
+#endif /* if defined(OPEN_MAX) && defined(__APPLE__) */
+ if (resource == isc_resource_openfiles && rlim_value == RLIM_INFINITY) {
+ if (getrlimit(unixresource, &rl) == 0) {
+ rl.rlim_cur = rl.rlim_max;
+ unixresult = setrlimit(unixresource, &rl);
+ if (unixresult == 0) {
+ return (ISC_R_SUCCESS);
+ }
+ }
+ }
+ return (isc__errno2result(errno));
+}
+
+isc_result_t
+isc_resource_getlimit(isc_resource_t resource, isc_resourcevalue_t *value) {
+ int unixresource;
+ struct rlimit rl;
+ isc_result_t result;
+
+ result = resource2rlim(resource, &unixresource);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ if (getrlimit(unixresource, &rl) != 0) {
+ return (isc__errno2result(errno));
+ }
+
+ *value = rl.rlim_max;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_resource_getcurlimit(isc_resource_t resource, isc_resourcevalue_t *value) {
+ int unixresource;
+ struct rlimit rl;
+ isc_result_t result;
+
+ result = resource2rlim(resource, &unixresource);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ if (getrlimit(unixresource, &rl) != 0) {
+ return (isc__errno2result(errno));
+ }
+
+ *value = rl.rlim_cur;
+ return (ISC_R_SUCCESS);
+}
diff --git a/lib/isc/result.c b/lib/isc/result.c
new file mode 100644
index 0000000..edbc8fd
--- /dev/null
+++ b/lib/isc/result.c
@@ -0,0 +1,540 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include <isc/once.h>
+#include <isc/util.h>
+
+static const char *description[ISC_R_NRESULTS] = {
+ [ISC_R_SUCCESS] = "success",
+ [ISC_R_NOMEMORY] = "out of memory",
+ [ISC_R_TIMEDOUT] = "timed out",
+ [ISC_R_NOTHREADS] = "no available threads",
+ [ISC_R_ADDRNOTAVAIL] = "address not available",
+ [ISC_R_ADDRINUSE] = "address in use",
+ [ISC_R_NOPERM] = "permission denied",
+ [ISC_R_NOCONN] = "no pending connections",
+ [ISC_R_NETUNREACH] = "network unreachable",
+ [ISC_R_HOSTUNREACH] = "host unreachable",
+ [ISC_R_NETDOWN] = "network down",
+ [ISC_R_HOSTDOWN] = "host down",
+ [ISC_R_CONNREFUSED] = "connection refused",
+ [ISC_R_NORESOURCES] = "not enough free resources",
+ [ISC_R_EOF] = "end of file",
+ [ISC_R_BOUND] = "socket already bound",
+ [ISC_R_RELOAD] = "reload",
+ [ISC_R_LOCKBUSY] = "lock busy",
+ [ISC_R_EXISTS] = "already exists",
+ [ISC_R_NOSPACE] = "ran out of space",
+ [ISC_R_CANCELED] = "operation canceled",
+ [ISC_R_NOTBOUND] = "socket is not bound",
+ [ISC_R_SHUTTINGDOWN] = "shutting down",
+ [ISC_R_NOTFOUND] = "not found",
+ [ISC_R_UNEXPECTEDEND] = "unexpected end of input",
+ [ISC_R_FAILURE] = "failure",
+ [ISC_R_IOERROR] = "I/O error",
+ [ISC_R_NOTIMPLEMENTED] = "not implemented",
+ [ISC_R_UNBALANCED] = "unbalanced parentheses",
+ [ISC_R_NOMORE] = "no more",
+ [ISC_R_INVALIDFILE] = "invalid file",
+ [ISC_R_BADBASE64] = "bad base64 encoding",
+ [ISC_R_UNEXPECTEDTOKEN] = "unexpected token",
+ [ISC_R_QUOTA] = "quota reached",
+ [ISC_R_UNEXPECTED] = "unexpected error",
+ [ISC_R_ALREADYRUNNING] = "already running",
+ [ISC_R_IGNORE] = "ignore",
+ [ISC_R_MASKNONCONTIG] = "address mask not contiguous",
+ [ISC_R_FILENOTFOUND] = "file not found",
+ [ISC_R_FILEEXISTS] = "file already exists",
+ [ISC_R_NOTCONNECTED] = "socket is not connected",
+ [ISC_R_RANGE] = "out of range",
+ [ISC_R_NOENTROPY] = "out of entropy",
+ [ISC_R_MULTICAST] = "invalid use of multicast address",
+ [ISC_R_NOTFILE] = "not a file",
+ [ISC_R_NOTDIRECTORY] = "not a directory",
+ [ISC_R_EMPTY] = "queue is empty",
+ [ISC_R_FAMILYMISMATCH] = "address family mismatch",
+ [ISC_R_FAMILYNOSUPPORT] = "address family not supported",
+ [ISC_R_BADHEX] = "bad hex encoding",
+ [ISC_R_TOOMANYOPENFILES] = "too many open files",
+ [ISC_R_NOTBLOCKING] = "not blocking",
+ [ISC_R_UNBALANCEDQUOTES] = "unbalanced quotes",
+ [ISC_R_INPROGRESS] = "operation in progress",
+ [ISC_R_CONNECTIONRESET] = "connection reset",
+ [ISC_R_SOFTQUOTA] = "soft quota reached",
+ [ISC_R_BADNUMBER] = "not a valid number",
+ [ISC_R_DISABLED] = "disabled",
+ [ISC_R_MAXSIZE] = "max size",
+ [ISC_R_BADADDRESSFORM] = "invalid address format",
+ [ISC_R_BADBASE32] = "bad base32 encoding",
+ [ISC_R_UNSET] = "unset",
+ [ISC_R_MULTIPLE] = "multiple",
+ [ISC_R_WOULDBLOCK] = "would block",
+ [ISC_R_COMPLETE] = "complete",
+ [ISC_R_CRYPTOFAILURE] = "crypto failure",
+ [ISC_R_DISCQUOTA] = "disc quota",
+ [ISC_R_DISCFULL] = "disc full",
+ [ISC_R_DEFAULT] = "default",
+ [ISC_R_IPV4PREFIX] = "IPv4 prefix",
+ [ISC_R_TLSERROR] = "TLS error",
+ [ISC_R_TLSBADPEERCERT] = "TLS peer certificate verification failed",
+ [ISC_R_HTTP2ALPNERROR] = "ALPN for HTTP/2 failed",
+ [ISC_R_DOTALPNERROR] = "ALPN for DoT failed",
+ [ISC_R_INVALIDPROTO] = "invalid protocol",
+
+ [DNS_R_LABELTOOLONG] = "label too long",
+ [DNS_R_BADESCAPE] = "bad escape",
+ [DNS_R_EMPTYLABEL] = "empty label",
+ [DNS_R_BADDOTTEDQUAD] = "bad dotted quad",
+ [DNS_R_INVALIDNS] = "invalid NS owner name (wildcard)",
+ [DNS_R_UNKNOWN] = "unknown class/type",
+ [DNS_R_BADLABELTYPE] = "bad label type",
+ [DNS_R_BADPOINTER] = "bad compression pointer",
+ [DNS_R_TOOMANYHOPS] = "too many hops",
+ [DNS_R_DISALLOWED] = "disallowed (by application policy)",
+ [DNS_R_EXTRATOKEN] = "extra input text",
+ [DNS_R_EXTRADATA] = "extra input data",
+ [DNS_R_TEXTTOOLONG] = "text too long",
+ [DNS_R_NOTZONETOP] = "not at top of zone",
+ [DNS_R_SYNTAX] = "syntax error",
+ [DNS_R_BADCKSUM] = "bad checksum",
+ [DNS_R_BADAAAA] = "bad IPv6 address",
+ [DNS_R_NOOWNER] = "no owner",
+ [DNS_R_NOTTL] = "no ttl",
+ [DNS_R_BADCLASS] = "bad class",
+ [DNS_R_NAMETOOLONG] = "name too long",
+ [DNS_R_PARTIALMATCH] = "partial match",
+ [DNS_R_NEWORIGIN] = "new origin",
+ [DNS_R_UNCHANGED] = "unchanged",
+ [DNS_R_BADTTL] = "bad ttl",
+ [DNS_R_NOREDATA] = "more data needed/to be rendered",
+ [DNS_R_CONTINUE] = "continue",
+ [DNS_R_DELEGATION] = "delegation",
+ [DNS_R_GLUE] = "glue",
+ [DNS_R_DNAME] = "dname",
+ [DNS_R_CNAME] = "cname",
+ [DNS_R_BADDB] = "bad database",
+ [DNS_R_ZONECUT] = "zonecut",
+ [DNS_R_BADZONE] = "bad zone",
+ [DNS_R_MOREDATA] = "more data",
+ [DNS_R_UPTODATE] = "up to date",
+ [DNS_R_TSIGVERIFYFAILURE] = "tsig verify failure",
+ [DNS_R_TSIGERRORSET] = "tsig indicates error",
+ [DNS_R_SIGINVALID] = "RRSIG failed to verify",
+ [DNS_R_SIGEXPIRED] = "RRSIG has expired",
+ [DNS_R_SIGFUTURE] = "RRSIG validity period has not begun",
+ [DNS_R_KEYUNAUTHORIZED] = "key is unauthorized to sign data",
+ [DNS_R_INVALIDTIME] = "invalid time",
+ [DNS_R_EXPECTEDTSIG] = "expected a TSIG or SIG(0)",
+ [DNS_R_UNEXPECTEDTSIG] = "did not expect a TSIG or SIG(0)",
+ [DNS_R_INVALIDTKEY] = "TKEY is unacceptable",
+ [DNS_R_HINT] = "hint",
+ [DNS_R_DROP] = "drop",
+ [DNS_R_NOTLOADED] = "zone not loaded",
+ [DNS_R_NCACHENXDOMAIN] = "ncache nxdomain",
+ [DNS_R_NCACHENXRRSET] = "ncache nxrrset",
+ [DNS_R_WAIT] = "wait",
+ [DNS_R_NOTVERIFIEDYET] = "not verified yet",
+ [DNS_R_NOIDENTITY] = "no identity",
+ [DNS_R_NOJOURNAL] = "no journal",
+ [DNS_R_ALIAS] = "alias",
+ [DNS_R_USETCP] = "use TCP",
+ [DNS_R_NOVALIDSIG] = "no valid RRSIG",
+ [DNS_R_NOVALIDNSEC] = "no valid NSEC",
+ [DNS_R_NOTINSECURE] = "insecurity proof failed",
+ [DNS_R_UNKNOWNSERVICE] = "unknown service",
+ [DNS_R_RECOVERABLE] = "recoverable error occurred",
+ [DNS_R_UNKNOWNOPT] = "unknown opt attribute record",
+ [DNS_R_UNEXPECTEDID] = "unexpected message id",
+ [DNS_R_SEENINCLUDE] = "seen include file",
+ [DNS_R_NOTEXACT] = "not exact",
+ [DNS_R_BLACKHOLED] = "address blackholed",
+ [DNS_R_BADALG] = "bad algorithm",
+ [DNS_R_METATYPE] = "invalid use of a meta type",
+ [DNS_R_CNAMEANDOTHER] = "CNAME and other data",
+ [DNS_R_SINGLETON] = "multiple RRs of singleton type",
+ [DNS_R_HINTNXRRSET] = "hint nxrrset",
+ [DNS_R_NOMASTERFILE] = "no master file configured",
+ [DNS_R_UNKNOWNPROTO] = "unknown protocol",
+ [DNS_R_CLOCKSKEW] = "clocks are unsynchronized",
+ [DNS_R_BADIXFR] = "IXFR failed",
+ [DNS_R_NOTAUTHORITATIVE] = "not authoritative",
+ [DNS_R_NOVALIDKEY] = "no valid KEY",
+ [DNS_R_OBSOLETE] = "obsolete",
+ [DNS_R_FROZEN] = "already frozen",
+ [DNS_R_UNKNOWNFLAG] = "unknown flag",
+ [DNS_R_EXPECTEDRESPONSE] = "expected a response",
+ [DNS_R_NOVALIDDS] = "no valid DS",
+ [DNS_R_NSISADDRESS] = "NS is an address",
+ [DNS_R_REMOTEFORMERR] = "received FORMERR",
+ [DNS_R_TRUNCATEDTCP] = "truncated TCP response",
+ [DNS_R_LAME] = "lame server detected",
+ [DNS_R_UNEXPECTEDRCODE] = "unexpected RCODE",
+ [DNS_R_UNEXPECTEDOPCODE] = "unexpected OPCODE",
+ [DNS_R_CHASEDSSERVERS] = "chase DS servers",
+ [DNS_R_EMPTYNAME] = "empty name",
+ [DNS_R_EMPTYWILD] = "empty wild",
+ [DNS_R_BADBITMAP] = "bad bitmap",
+ [DNS_R_FROMWILDCARD] = "from wildcard",
+ [DNS_R_BADOWNERNAME] = "bad owner name (check-names)",
+ [DNS_R_BADNAME] = "bad name (check-names)",
+ [DNS_R_DYNAMIC] = "dynamic zone",
+ [DNS_R_UNKNOWNCOMMAND] = "unknown command",
+ [DNS_R_MUSTBESECURE] = "must-be-secure",
+ [DNS_R_COVERINGNSEC] = "covering NSEC record returned",
+ [DNS_R_MXISADDRESS] = "MX is an address",
+ [DNS_R_DUPLICATE] = "duplicate query",
+ [DNS_R_INVALIDNSEC3] = "invalid NSEC3 owner name (wildcard)",
+ [DNS_R_NOTPRIMARY] = "not primary",
+ [DNS_R_BROKENCHAIN] = "broken trust chain",
+ [DNS_R_EXPIRED] = "expired",
+ [DNS_R_NOTDYNAMIC] = "not dynamic",
+ [DNS_R_BADEUI] = "bad EUI",
+ [DNS_R_NTACOVERED] = "covered by negative trust anchor",
+ [DNS_R_BADCDS] = "bad CDS",
+ [DNS_R_BADCDNSKEY] = "bad CDNSKEY",
+ [DNS_R_OPTERR] = "malformed OPT option",
+ [DNS_R_BADDNSTAP] = "malformed DNSTAP data",
+ [DNS_R_BADTSIG] = "TSIG in wrong location",
+ [DNS_R_BADSIG0] = "SIG(0) in wrong location",
+ [DNS_R_TOOMANYRECORDS] = "too many records",
+ [DNS_R_VERIFYFAILURE] = "verify failure",
+ [DNS_R_ATZONETOP] = "at top of zone",
+ [DNS_R_NOKEYMATCH] = "no matching key found",
+ [DNS_R_TOOMANYKEYS] = "too many keys matching",
+ [DNS_R_KEYNOTACTIVE] = "key is not actively signing",
+ [DNS_R_NSEC3ITERRANGE] = "NSEC3 iterations out of range",
+ [DNS_R_NSEC3SALTRANGE] = "NSEC3 salt length too high",
+ [DNS_R_NSEC3BADALG] = "cannot use NSEC3 with key algorithm",
+ [DNS_R_NSEC3RESALT] = "NSEC3 resalt",
+ [DNS_R_INCONSISTENTRR] = "inconsistent resource record",
+ [DNS_R_NOALPN] = "no ALPN",
+
+ [DST_R_UNSUPPORTEDALG] = "algorithm is unsupported",
+ [DST_R_CRYPTOFAILURE] = "crypto failure",
+ [DST_R_NOCRYPTO] = "built with no crypto support",
+ [DST_R_NULLKEY] = "illegal operation for a null key",
+ [DST_R_INVALIDPUBLICKEY] = "public key is invalid",
+ [DST_R_INVALIDPRIVATEKEY] = "private key is invalid",
+ [DST_R_WRITEERROR] = "error occurred writing key to disk",
+ [DST_R_INVALIDPARAM] = "invalid algorithm specific parameter",
+ [DST_R_SIGNFAILURE] = "sign failure",
+ [DST_R_VERIFYFAILURE] = "verify failure",
+ [DST_R_NOTPUBLICKEY] = "not a public key",
+ [DST_R_NOTPRIVATEKEY] = "not a private key",
+ [DST_R_KEYCANNOTCOMPUTESECRET] = "not a key that can compute a secret",
+ [DST_R_COMPUTESECRETFAILURE] = "failure computing a shared secret",
+ [DST_R_NORANDOMNESS] = "no randomness available",
+ [DST_R_BADKEYTYPE] = "bad key type",
+ [DST_R_NOENGINE] = "no engine",
+ [DST_R_EXTERNALKEY] = "illegal operation for an external key",
+
+ [DNS_R_NOERROR] = "NOERROR",
+ [DNS_R_FORMERR] = "FORMERR",
+ [DNS_R_SERVFAIL] = "SERVFAIL",
+ [DNS_R_NXDOMAIN] = "NXDOMAIN",
+ [DNS_R_NOTIMP] = "NOTIMP",
+ [DNS_R_REFUSED] = "REFUSED",
+ [DNS_R_YXDOMAIN] = "YXDOMAIN",
+ [DNS_R_YXRRSET] = "YXRRSET",
+ [DNS_R_NXRRSET] = "NXRRSET",
+ [DNS_R_NOTAUTH] = "NOTAUTH",
+ [DNS_R_NOTZONE] = "NOTZONE",
+ [DNS_R_RCODE11] = "<rcode 11>",
+ [DNS_R_RCODE12] = "<rcode 12>",
+ [DNS_R_RCODE13] = "<rcode 13>",
+ [DNS_R_RCODE14] = "<rcode 14>",
+ [DNS_R_RCODE15] = "<rcode 15>",
+ [DNS_R_BADVERS] = "BADVERS",
+ [DNS_R_BADCOOKIE] = "BADCOOKIE",
+
+ [ISCCC_R_UNKNOWNVERSION] = "unknown version",
+ [ISCCC_R_SYNTAX] = "syntax error",
+ [ISCCC_R_BADAUTH] = "bad auth",
+ [ISCCC_R_EXPIRED] = "expired",
+ [ISCCC_R_CLOCKSKEW] = "clock skew",
+ [ISCCC_R_DUPLICATE] = "duplicate",
+ [ISCCC_R_MAXDEPTH] = "max depth",
+};
+
+static const char *identifier[ISC_R_NRESULTS] = {
+ [ISC_R_SUCCESS] = "ISC_R_SUCCESS",
+ [ISC_R_NOMEMORY] = "ISC_R_NOMEMORY",
+ [ISC_R_TIMEDOUT] = "ISC_R_TIMEDOUT",
+ [ISC_R_NOTHREADS] = "ISC_R_NOTHREADS",
+ [ISC_R_ADDRNOTAVAIL] = "ISC_R_ADDRNOTAVAIL",
+ [ISC_R_ADDRINUSE] = "ISC_R_ADDRINUSE",
+ [ISC_R_NOPERM] = "ISC_R_NOPERM",
+ [ISC_R_NOCONN] = "ISC_R_NOCONN",
+ [ISC_R_NETUNREACH] = "ISC_R_NETUNREACH",
+ [ISC_R_HOSTUNREACH] = "ISC_R_HOSTUNREACH",
+ [ISC_R_NETDOWN] = "ISC_R_NETDOWN",
+ [ISC_R_HOSTDOWN] = "ISC_R_HOSTDOWN",
+ [ISC_R_CONNREFUSED] = "ISC_R_CONNREFUSED",
+ [ISC_R_NORESOURCES] = "ISC_R_NORESOURCES",
+ [ISC_R_EOF] = "ISC_R_EOF",
+ [ISC_R_BOUND] = "ISC_R_BOUND",
+ [ISC_R_RELOAD] = "ISC_R_RELOAD",
+ [ISC_R_LOCKBUSY] = "ISC_R_LOCKBUSY",
+ [ISC_R_EXISTS] = "ISC_R_EXISTS",
+ [ISC_R_NOSPACE] = "ISC_R_NOSPACE",
+ [ISC_R_CANCELED] = "ISC_R_CANCELED",
+ [ISC_R_NOTBOUND] = "ISC_R_NOTBOUND",
+ [ISC_R_SHUTTINGDOWN] = "ISC_R_SHUTTINGDOWN",
+ [ISC_R_NOTFOUND] = "ISC_R_NOTFOUND",
+ [ISC_R_UNEXPECTEDEND] = "ISC_R_UNEXPECTEDEND",
+ [ISC_R_FAILURE] = "ISC_R_FAILURE",
+ [ISC_R_IOERROR] = "ISC_R_IOERROR",
+ [ISC_R_NOTIMPLEMENTED] = "ISC_R_NOTIMPLEMENTED",
+ [ISC_R_UNBALANCED] = "ISC_R_UNBALANCED",
+ [ISC_R_NOMORE] = "ISC_R_NOMORE",
+ [ISC_R_INVALIDFILE] = "ISC_R_INVALIDFILE",
+ [ISC_R_BADBASE64] = "ISC_R_BADBASE64",
+ [ISC_R_UNEXPECTEDTOKEN] = "ISC_R_UNEXPECTEDTOKEN",
+ [ISC_R_QUOTA] = "ISC_R_QUOTA",
+ [ISC_R_UNEXPECTED] = "ISC_R_UNEXPECTED",
+ [ISC_R_ALREADYRUNNING] = "ISC_R_ALREADYRUNNING",
+ [ISC_R_IGNORE] = "ISC_R_IGNORE",
+ [ISC_R_MASKNONCONTIG] = "ISC_R_MASKNONCONTIG",
+ [ISC_R_FILENOTFOUND] = "ISC_R_FILENOTFOUND",
+ [ISC_R_FILEEXISTS] = "ISC_R_FILEEXISTS",
+ [ISC_R_NOTCONNECTED] = "ISC_R_NOTCONNECTED",
+ [ISC_R_RANGE] = "ISC_R_RANGE",
+ [ISC_R_NOENTROPY] = "ISC_R_NOENTROPY",
+ [ISC_R_MULTICAST] = "ISC_R_MULTICAST",
+ [ISC_R_NOTFILE] = "ISC_R_NOTFILE",
+ [ISC_R_NOTDIRECTORY] = "ISC_R_NOTDIRECTORY",
+ [ISC_R_EMPTY] = "ISC_R_EMPTY",
+ [ISC_R_FAMILYMISMATCH] = "ISC_R_FAMILYMISMATCH",
+ [ISC_R_FAMILYNOSUPPORT] = "ISC_R_FAMILYNOSUPPORT",
+ [ISC_R_BADHEX] = "ISC_R_BADHEX",
+ [ISC_R_TOOMANYOPENFILES] = "ISC_R_TOOMANYOPENFILES",
+ [ISC_R_NOTBLOCKING] = "ISC_R_NOTBLOCKING",
+ [ISC_R_UNBALANCEDQUOTES] = "ISC_R_UNBALANCEDQUOTES",
+ [ISC_R_INPROGRESS] = "ISC_R_INPROGRESS",
+ [ISC_R_CONNECTIONRESET] = "ISC_R_CONNECTIONRESET",
+ [ISC_R_SOFTQUOTA] = "ISC_R_SOFTQUOTA",
+ [ISC_R_BADNUMBER] = "ISC_R_BADNUMBER",
+ [ISC_R_DISABLED] = "ISC_R_DISABLED",
+ [ISC_R_MAXSIZE] = "ISC_R_MAXSIZE",
+ [ISC_R_BADADDRESSFORM] = "ISC_R_BADADDRESSFORM",
+ [ISC_R_BADBASE32] = "ISC_R_BADBASE32",
+ [ISC_R_UNSET] = "ISC_R_UNSET",
+ [ISC_R_MULTIPLE] = "ISC_R_MULTIPLE",
+ [ISC_R_WOULDBLOCK] = "ISC_R_WOULDBLOCK",
+ [ISC_R_COMPLETE] = "ISC_R_COMPLETE",
+ [ISC_R_CRYPTOFAILURE] = "ISC_R_CRYPTOFAILURE",
+ [ISC_R_DISCQUOTA] = "ISC_R_DISCQUOTA",
+ [ISC_R_DISCFULL] = "ISC_R_DISCFULL",
+ [ISC_R_DEFAULT] = "ISC_R_DEFAULT",
+ [ISC_R_IPV4PREFIX] = "ISC_R_IPV4PREFIX",
+ [ISC_R_TLSERROR] = "ISC_R_TLSERROR",
+ [ISC_R_TLSBADPEERCERT] = "ISC_R_TLSBADPEERCERT",
+ [ISC_R_HTTP2ALPNERROR] = "ISC_R_HTTP2ALPNERROR",
+ [ISC_R_DOTALPNERROR] = "ISC_R_DOTALPNERROR",
+ [DNS_R_LABELTOOLONG] = "DNS_R_LABELTOOLONG",
+ [DNS_R_BADESCAPE] = "DNS_R_BADESCAPE",
+ [DNS_R_EMPTYLABEL] = "DNS_R_EMPTYLABEL",
+ [DNS_R_BADDOTTEDQUAD] = "DNS_R_BADDOTTEDQUAD",
+ [DNS_R_INVALIDNS] = "DNS_R_INVALIDNS",
+ [DNS_R_UNKNOWN] = "DNS_R_UNKNOWN",
+ [DNS_R_BADLABELTYPE] = "DNS_R_BADLABELTYPE",
+ [DNS_R_BADPOINTER] = "DNS_R_BADPOINTER",
+ [DNS_R_TOOMANYHOPS] = "DNS_R_TOOMANYHOPS",
+ [DNS_R_DISALLOWED] = "DNS_R_DISALLOWED",
+ [DNS_R_EXTRATOKEN] = "DNS_R_EXTRATOKEN",
+ [DNS_R_EXTRADATA] = "DNS_R_EXTRADATA",
+ [DNS_R_TEXTTOOLONG] = "DNS_R_TEXTTOOLONG",
+ [DNS_R_NOTZONETOP] = "DNS_R_NOTZONETOP",
+ [DNS_R_SYNTAX] = "DNS_R_SYNTAX",
+ [DNS_R_BADCKSUM] = "DNS_R_BADCKSUM",
+ [DNS_R_BADAAAA] = "DNS_R_BADAAAA",
+ [DNS_R_NOOWNER] = "DNS_R_NOOWNER",
+ [DNS_R_NOTTL] = "DNS_R_NOTTL",
+ [DNS_R_BADCLASS] = "DNS_R_BADCLASS",
+ [DNS_R_NAMETOOLONG] = "DNS_R_NAMETOOLONG",
+ [DNS_R_PARTIALMATCH] = "DNS_R_PARTIALMATCH",
+ [DNS_R_NEWORIGIN] = "DNS_R_NEWORIGIN",
+ [DNS_R_UNCHANGED] = "DNS_R_UNCHANGED",
+ [DNS_R_BADTTL] = "DNS_R_BADTTL",
+ [DNS_R_NOREDATA] = "DNS_R_NOREDATA",
+ [DNS_R_CONTINUE] = "DNS_R_CONTINUE",
+ [DNS_R_DELEGATION] = "DNS_R_DELEGATION",
+ [DNS_R_GLUE] = "DNS_R_GLUE",
+ [DNS_R_DNAME] = "DNS_R_DNAME",
+ [DNS_R_CNAME] = "DNS_R_CNAME",
+ [DNS_R_BADDB] = "DNS_R_BADDB",
+ [DNS_R_ZONECUT] = "DNS_R_ZONECUT",
+ [DNS_R_BADZONE] = "DNS_R_BADZONE",
+ [DNS_R_MOREDATA] = "DNS_R_MOREDATA",
+ [DNS_R_UPTODATE] = "DNS_R_UPTODATE",
+ [DNS_R_TSIGVERIFYFAILURE] = "DNS_R_TSIGVERIFYFAILURE",
+ [DNS_R_TSIGERRORSET] = "DNS_R_TSIGERRORSET",
+ [DNS_R_SIGINVALID] = "DNS_R_SIGINVALID",
+ [DNS_R_SIGEXPIRED] = "DNS_R_SIGEXPIRED",
+ [DNS_R_SIGFUTURE] = "DNS_R_SIGFUTURE",
+ [DNS_R_KEYUNAUTHORIZED] = "DNS_R_KEYUNAUTHORIZED",
+ [DNS_R_INVALIDTIME] = "DNS_R_INVALIDTIME",
+ [DNS_R_EXPECTEDTSIG] = "DNS_R_EXPECTEDTSIG",
+ [DNS_R_UNEXPECTEDTSIG] = "DNS_R_UNEXPECTEDTSIG",
+ [DNS_R_INVALIDTKEY] = "DNS_R_INVALIDTKEY",
+ [DNS_R_HINT] = "DNS_R_HINT",
+ [DNS_R_DROP] = "DNS_R_DROP",
+ [DNS_R_NOTLOADED] = "DNS_R_NOTLOADED",
+ [DNS_R_NCACHENXDOMAIN] = "DNS_R_NCACHENXDOMAIN",
+ [DNS_R_NCACHENXRRSET] = "DNS_R_NCACHENXRRSET",
+ [DNS_R_WAIT] = "DNS_R_WAIT",
+ [DNS_R_NOTVERIFIEDYET] = "DNS_R_NOTVERIFIEDYET",
+ [DNS_R_NOIDENTITY] = "DNS_R_NOIDENTITY",
+ [DNS_R_NOJOURNAL] = "DNS_R_NOJOURNAL",
+ [DNS_R_ALIAS] = "DNS_R_ALIAS",
+ [DNS_R_USETCP] = "DNS_R_USETCP",
+ [DNS_R_NOVALIDSIG] = "DNS_R_NOVALIDSIG",
+ [DNS_R_NOVALIDNSEC] = "DNS_R_NOVALIDNSEC",
+ [DNS_R_NOTINSECURE] = "DNS_R_NOTINSECURE",
+ [DNS_R_UNKNOWNSERVICE] = "DNS_R_UNKNOWNSERVICE",
+ [DNS_R_RECOVERABLE] = "DNS_R_RECOVERABLE",
+ [DNS_R_UNKNOWNOPT] = "DNS_R_UNKNOWNOPT",
+ [DNS_R_UNEXPECTEDID] = "DNS_R_UNEXPECTEDID",
+ [DNS_R_SEENINCLUDE] = "DNS_R_SEENINCLUDE",
+ [DNS_R_NOTEXACT] = "DNS_R_NOTEXACT",
+ [DNS_R_BLACKHOLED] = "DNS_R_BLACKHOLED",
+ [DNS_R_BADALG] = "DNS_R_BADALG",
+ [DNS_R_METATYPE] = "DNS_R_METATYPE",
+ [DNS_R_CNAMEANDOTHER] = "DNS_R_CNAMEANDOTHER",
+ [DNS_R_SINGLETON] = "DNS_R_SINGLETON",
+ [DNS_R_HINTNXRRSET] = "DNS_R_HINTNXRRSET",
+ [DNS_R_NOMASTERFILE] = "DNS_R_NOMASTERFILE",
+ [DNS_R_UNKNOWNPROTO] = "DNS_R_UNKNOWNPROTO",
+ [DNS_R_CLOCKSKEW] = "DNS_R_CLOCKSKEW",
+ [DNS_R_BADIXFR] = "DNS_R_BADIXFR",
+ [DNS_R_NOTAUTHORITATIVE] = "DNS_R_NOTAUTHORITATIVE",
+ [DNS_R_NOVALIDKEY] = "DNS_R_NOVALIDKEY",
+ [DNS_R_OBSOLETE] = "DNS_R_OBSOLETE",
+ [DNS_R_FROZEN] = "DNS_R_FROZEN",
+ [DNS_R_UNKNOWNFLAG] = "DNS_R_UNKNOWNFLAG",
+ [DNS_R_EXPECTEDRESPONSE] = "DNS_R_EXPECTEDRESPONSE",
+ [DNS_R_NOVALIDDS] = "DNS_R_NOVALIDDS",
+ [DNS_R_NSISADDRESS] = "DNS_R_NSISADDRESS",
+ [DNS_R_REMOTEFORMERR] = "DNS_R_REMOTEFORMERR",
+ [DNS_R_TRUNCATEDTCP] = "DNS_R_TRUNCATEDTCP",
+ [DNS_R_LAME] = "DNS_R_LAME",
+ [DNS_R_UNEXPECTEDRCODE] = "DNS_R_UNEXPECTEDRCODE",
+ [DNS_R_UNEXPECTEDOPCODE] = "DNS_R_UNEXPECTEDOPCODE",
+ [DNS_R_CHASEDSSERVERS] = "DNS_R_CHASEDSSERVERS",
+ [DNS_R_EMPTYNAME] = "DNS_R_EMPTYNAME",
+ [DNS_R_EMPTYWILD] = "DNS_R_EMPTYWILD",
+ [DNS_R_BADBITMAP] = "DNS_R_BADBITMAP",
+ [DNS_R_FROMWILDCARD] = "DNS_R_FROMWILDCARD",
+ [DNS_R_BADOWNERNAME] = "DNS_R_BADOWNERNAME",
+ [DNS_R_BADNAME] = "DNS_R_BADNAME",
+ [DNS_R_DYNAMIC] = "DNS_R_DYNAMIC",
+ [DNS_R_UNKNOWNCOMMAND] = "DNS_R_UNKNOWNCOMMAND",
+ [DNS_R_MUSTBESECURE] = "DNS_R_MUSTBESECURE",
+ [DNS_R_COVERINGNSEC] = "DNS_R_COVERINGNSEC",
+ [DNS_R_MXISADDRESS] = "DNS_R_MXISADDRESS",
+ [DNS_R_DUPLICATE] = "DNS_R_DUPLICATE",
+ [DNS_R_INVALIDNSEC3] = "DNS_R_INVALIDNSEC3",
+ [DNS_R_NOTPRIMARY] = "DNS_R_NOTPRIMARY",
+ [DNS_R_BROKENCHAIN] = "DNS_R_BROKENCHAIN",
+ [DNS_R_EXPIRED] = "DNS_R_EXPIRED",
+ [DNS_R_NOTDYNAMIC] = "DNS_R_NOTDYNAMIC",
+ [DNS_R_BADEUI] = "DNS_R_BADEUI",
+ [DNS_R_NTACOVERED] = "DNS_R_NTACOVERED",
+ [DNS_R_BADCDS] = "DNS_R_BADCDS",
+ [DNS_R_BADCDNSKEY] = "DNS_R_BADCDNSKEY",
+ [DNS_R_OPTERR] = "DNS_R_OPTERR",
+ [DNS_R_BADDNSTAP] = "DNS_R_BADDNSTAP",
+ [DNS_R_BADTSIG] = "DNS_R_BADTSIG",
+ [DNS_R_BADSIG0] = "DNS_R_BADSIG0",
+ [DNS_R_TOOMANYRECORDS] = "DNS_R_TOOMANYRECORDS",
+ [DNS_R_VERIFYFAILURE] = "DNS_R_VERIFYFAILURE",
+ [DNS_R_ATZONETOP] = "DNS_R_ATZONETOP",
+ [DNS_R_NOKEYMATCH] = "DNS_R_NOKEYMATCH",
+ [DNS_R_TOOMANYKEYS] = "DNS_R_TOOMANYKEYS",
+ [DNS_R_KEYNOTACTIVE] = "DNS_R_KEYNOTACTIVE",
+ [DNS_R_NSEC3ITERRANGE] = "DNS_R_NSEC3ITERRANGE",
+ [DNS_R_NSEC3SALTRANGE] = "DNS_R_NSEC3SALTRANGE",
+ [DNS_R_NSEC3BADALG] = "DNS_R_NSEC3BADALG",
+ [DNS_R_NSEC3RESALT] = "DNS_R_NSEC3RESALT",
+ [DNS_R_INCONSISTENTRR] = "DNS_R_INCONSISTENTRR",
+ [DNS_R_NOALPN] = "DNS_R_NOALPN",
+
+ [DST_R_UNSUPPORTEDALG] = "DST_R_UNSUPPORTEDALG",
+ [DST_R_CRYPTOFAILURE] = "DST_R_CRYPTOFAILURE",
+ [DST_R_NOCRYPTO] = "DST_R_NOCRYPTO",
+ [DST_R_NULLKEY] = "DST_R_NULLKEY",
+ [DST_R_INVALIDPUBLICKEY] = "DST_R_INVALIDPUBLICKEY",
+ [DST_R_INVALIDPRIVATEKEY] = "DST_R_INVALIDPRIVATEKEY",
+ [DST_R_WRITEERROR] = "DST_R_WRITEERROR",
+ [DST_R_INVALIDPARAM] = "DST_R_INVALIDPARAM",
+ [DST_R_SIGNFAILURE] = "DST_R_SIGNFAILURE",
+ [DST_R_VERIFYFAILURE] = "DST_R_VERIFYFAILURE",
+ [DST_R_NOTPUBLICKEY] = "DST_R_NOTPUBLICKEY",
+ [DST_R_NOTPRIVATEKEY] = "DST_R_NOTPRIVATEKEY",
+ [DST_R_KEYCANNOTCOMPUTESECRET] = "DST_R_KEYCANNOTCOMPUTESECRET",
+ [DST_R_COMPUTESECRETFAILURE] = "DST_R_COMPUTESECRETFAILURE",
+ [DST_R_NORANDOMNESS] = "DST_R_NORANDOMNESS",
+ [DST_R_BADKEYTYPE] = "DST_R_BADKEYTYPE",
+ [DST_R_NOENGINE] = "DST_R_NOENGINE",
+ [DST_R_EXTERNALKEY] = "DST_R_EXTERNALKEY",
+
+ [DNS_R_NOERROR] = "DNS_R_NOERROR",
+ [DNS_R_FORMERR] = "DNS_R_FORMERR",
+ [DNS_R_SERVFAIL] = "DNS_R_SERVFAIL",
+ [DNS_R_NXDOMAIN] = "DNS_R_NXDOMAIN",
+ [DNS_R_NOTIMP] = "DNS_R_NOTIMP",
+ [DNS_R_REFUSED] = "DNS_R_REFUSED",
+ [DNS_R_YXDOMAIN] = "DNS_R_YXDOMAIN",
+ [DNS_R_YXRRSET] = "DNS_R_YXRRSET",
+ [DNS_R_NXRRSET] = "DNS_R_NXRRSET",
+ [DNS_R_NOTAUTH] = "DNS_R_NOTAUTH",
+ [DNS_R_NOTZONE] = "DNS_R_NOTZONE",
+ [DNS_R_RCODE11] = "DNS_R_RCODE11",
+ [DNS_R_RCODE12] = "RNS_R_RCODE12",
+ [DNS_R_RCODE13] = "DNS_R_RCODE13",
+ [DNS_R_RCODE14] = "DNS_R_RCODE14",
+ [DNS_R_RCODE15] = "DNS_R_RCODE15",
+ [DNS_R_BADVERS] = "DNS_R_BADVERS",
+ [DNS_R_BADCOOKIE] = "DNS_R_BADCOOKIE",
+
+ [ISCCC_R_UNKNOWNVERSION] = "ISCCC_R_UNKNOWNVERSION",
+ [ISCCC_R_SYNTAX] = "ISCCC_R_SYNTAX",
+ [ISCCC_R_BADAUTH] = "ISCCC_R_BADAUTH",
+ [ISCCC_R_EXPIRED] = "ISCCC_R_EXPIRED",
+ [ISCCC_R_CLOCKSKEW] = "ISCCC_R_CLOCKSKEW",
+ [ISCCC_R_DUPLICATE] = "ISCCC_R_DUPLICATE",
+ [ISCCC_R_MAXDEPTH] = "ISCCC_R_MAXDEPTH",
+};
+
+STATIC_ASSERT((DNS_R_SERVFAIL - DNS_R_NOERROR == 2),
+ "DNS_R_NOERROR has wrong value");
+
+STATIC_ASSERT((DNS_R_BADVERS - DNS_R_NOERROR == 16),
+ "DNS_R_BADVERS has wrong value");
+
+STATIC_ASSERT((ISC_R_NRESULTS < INT32_MAX), "result.h enum too big");
+
+const char *
+isc_result_totext(isc_result_t result) {
+ return (description[result]);
+}
+
+const char *
+isc_result_toid(isc_result_t result) {
+ return (identifier[result]);
+}
diff --git a/lib/isc/rwlock.c b/lib/isc/rwlock.c
new file mode 100644
index 0000000..156b469
--- /dev/null
+++ b/lib/isc/rwlock.c
@@ -0,0 +1,644 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#if defined(sun) && (defined(__sparc) || defined(__sparc__))
+#include <synch.h> /* for smt_pause(3c) */
+#endif /* if defined(sun) && (defined(__sparc) || defined(__sparc__)) */
+
+#include <isc/atomic.h>
+#include <isc/magic.h>
+#include <isc/print.h>
+#include <isc/rwlock.h>
+#include <isc/util.h>
+
+#if USE_PTHREAD_RWLOCK
+
+#include <errno.h>
+#include <pthread.h>
+
+void
+isc_rwlock_init(isc_rwlock_t *rwl, unsigned int read_quota,
+ unsigned int write_quota) {
+ UNUSED(read_quota);
+ UNUSED(write_quota);
+ REQUIRE(pthread_rwlock_init(&rwl->rwlock, NULL) == 0);
+ atomic_init(&rwl->downgrade, false);
+}
+
+isc_result_t
+isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
+ switch (type) {
+ case isc_rwlocktype_read:
+ REQUIRE(pthread_rwlock_rdlock(&rwl->rwlock) == 0);
+ break;
+ case isc_rwlocktype_write:
+ while (true) {
+ REQUIRE(pthread_rwlock_wrlock(&rwl->rwlock) == 0);
+ /* Unlock if in middle of downgrade operation */
+ if (atomic_load_acquire(&rwl->downgrade)) {
+ REQUIRE(pthread_rwlock_unlock(&rwl->rwlock) ==
+ 0);
+ while (atomic_load_acquire(&rwl->downgrade)) {
+ }
+ continue;
+ }
+ break;
+ }
+ break;
+ default:
+ UNREACHABLE();
+ }
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
+ int ret = 0;
+ switch (type) {
+ case isc_rwlocktype_read:
+ ret = pthread_rwlock_tryrdlock(&rwl->rwlock);
+ break;
+ case isc_rwlocktype_write:
+ ret = pthread_rwlock_trywrlock(&rwl->rwlock);
+ if ((ret == 0) && atomic_load_acquire(&rwl->downgrade)) {
+ isc_rwlock_unlock(rwl, type);
+ return (ISC_R_LOCKBUSY);
+ }
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ switch (ret) {
+ case 0:
+ return (ISC_R_SUCCESS);
+ case EBUSY:
+ return (ISC_R_LOCKBUSY);
+ case EAGAIN:
+ return (ISC_R_LOCKBUSY);
+ default:
+ UNREACHABLE();
+ }
+}
+
+isc_result_t
+isc_rwlock_unlock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
+ UNUSED(type);
+ REQUIRE(pthread_rwlock_unlock(&rwl->rwlock) == 0);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_rwlock_tryupgrade(isc_rwlock_t *rwl) {
+ UNUSED(rwl);
+ return (ISC_R_LOCKBUSY);
+}
+
+void
+isc_rwlock_downgrade(isc_rwlock_t *rwl) {
+ isc_result_t result;
+ atomic_store_release(&rwl->downgrade, true);
+ result = isc_rwlock_unlock(rwl, isc_rwlocktype_write);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ result = isc_rwlock_lock(rwl, isc_rwlocktype_read);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ atomic_store_release(&rwl->downgrade, false);
+}
+
+void
+isc_rwlock_destroy(isc_rwlock_t *rwl) {
+ pthread_rwlock_destroy(&rwl->rwlock);
+}
+
+#else /* if USE_PTHREAD_RWLOCK */
+
+#define RWLOCK_MAGIC ISC_MAGIC('R', 'W', 'L', 'k')
+#define VALID_RWLOCK(rwl) ISC_MAGIC_VALID(rwl, RWLOCK_MAGIC)
+
+#ifndef RWLOCK_DEFAULT_READ_QUOTA
+#define RWLOCK_DEFAULT_READ_QUOTA 4
+#endif /* ifndef RWLOCK_DEFAULT_READ_QUOTA */
+
+#ifndef RWLOCK_DEFAULT_WRITE_QUOTA
+#define RWLOCK_DEFAULT_WRITE_QUOTA 4
+#endif /* ifndef RWLOCK_DEFAULT_WRITE_QUOTA */
+
+#ifndef RWLOCK_MAX_ADAPTIVE_COUNT
+#define RWLOCK_MAX_ADAPTIVE_COUNT 100
+#endif /* ifndef RWLOCK_MAX_ADAPTIVE_COUNT */
+
+#if defined(_MSC_VER)
+#include <intrin.h>
+#define isc_rwlock_pause() YieldProcessor()
+#elif defined(__x86_64__)
+#include <immintrin.h>
+#define isc_rwlock_pause() _mm_pause()
+#elif defined(__i386__)
+#define isc_rwlock_pause() __asm__ __volatile__("rep; nop")
+#elif defined(__ia64__)
+#define isc_rwlock_pause() __asm__ __volatile__("hint @pause")
+#elif defined(__arm__) && HAVE_ARM_YIELD
+#define isc_rwlock_pause() __asm__ __volatile__("yield")
+#elif defined(sun) && (defined(__sparc) || defined(__sparc__))
+#define isc_rwlock_pause() smt_pause()
+#elif (defined(__sparc) || defined(__sparc__)) && HAVE_SPARC_PAUSE
+#define isc_rwlock_pause() __asm__ __volatile__("pause")
+#elif defined(__ppc__) || defined(_ARCH_PPC) || defined(_ARCH_PWR) || \
+ defined(_ARCH_PWR2) || defined(_POWER)
+#define isc_rwlock_pause() __asm__ volatile("or 27,27,27")
+#else /* if defined(_MSC_VER) */
+#define isc_rwlock_pause()
+#endif /* if defined(_MSC_VER) */
+
+static isc_result_t
+isc__rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type);
+
+#ifdef ISC_RWLOCK_TRACE
+#include <stdio.h> /* Required for fprintf/stderr. */
+
+#include <isc/thread.h> /* Required for isc_thread_self(). */
+
+static void
+print_lock(const char *operation, isc_rwlock_t *rwl, isc_rwlocktype_t type) {
+ fprintf(stderr,
+ "rwlock %p thread %" PRIuPTR " %s(%s): "
+ "write_requests=%u, write_completions=%u, "
+ "cnt_and_flag=0x%x, readers_waiting=%u, "
+ "write_granted=%u, write_quota=%u\n",
+ rwl, isc_thread_self(), operation,
+ (type == isc_rwlocktype_read ? "read" : "write"),
+ atomic_load_acquire(&rwl->write_requests),
+ atomic_load_acquire(&rwl->write_completions),
+ atomic_load_acquire(&rwl->cnt_and_flag), rwl->readers_waiting,
+ atomic_load_acquire(&rwl->write_granted), rwl->write_quota);
+}
+#endif /* ISC_RWLOCK_TRACE */
+
+void
+isc_rwlock_init(isc_rwlock_t *rwl, unsigned int read_quota,
+ unsigned int write_quota) {
+ REQUIRE(rwl != NULL);
+
+ /*
+ * In case there's trouble initializing, we zero magic now. If all
+ * goes well, we'll set it to RWLOCK_MAGIC.
+ */
+ rwl->magic = 0;
+
+ atomic_init(&rwl->spins, 0);
+ atomic_init(&rwl->write_requests, 0);
+ atomic_init(&rwl->write_completions, 0);
+ atomic_init(&rwl->cnt_and_flag, 0);
+ rwl->readers_waiting = 0;
+ atomic_init(&rwl->write_granted, 0);
+ if (read_quota != 0) {
+ UNEXPECTED_ERROR("read quota is not supported");
+ }
+ if (write_quota == 0) {
+ write_quota = RWLOCK_DEFAULT_WRITE_QUOTA;
+ }
+ rwl->write_quota = write_quota;
+
+ isc_mutex_init(&rwl->lock);
+
+ isc_condition_init(&rwl->readable);
+ isc_condition_init(&rwl->writeable);
+
+ rwl->magic = RWLOCK_MAGIC;
+}
+
+void
+isc_rwlock_destroy(isc_rwlock_t *rwl) {
+ REQUIRE(VALID_RWLOCK(rwl));
+
+ REQUIRE(atomic_load_acquire(&rwl->write_requests) ==
+ atomic_load_acquire(&rwl->write_completions) &&
+ atomic_load_acquire(&rwl->cnt_and_flag) == 0 &&
+ rwl->readers_waiting == 0);
+
+ rwl->magic = 0;
+ (void)isc_condition_destroy(&rwl->readable);
+ (void)isc_condition_destroy(&rwl->writeable);
+ isc_mutex_destroy(&rwl->lock);
+}
+
+/*
+ * When some architecture-dependent atomic operations are available,
+ * rwlock can be more efficient than the generic algorithm defined below.
+ * The basic algorithm is described in the following URL:
+ * http://www.cs.rochester.edu/u/scott/synchronization/pseudocode/rw.html
+ *
+ * The key is to use the following integer variables modified atomically:
+ * write_requests, write_completions, and cnt_and_flag.
+ *
+ * write_requests and write_completions act as a waiting queue for writers
+ * in order to ensure the FIFO order. Both variables begin with the initial
+ * value of 0. When a new writer tries to get a write lock, it increments
+ * write_requests and gets the previous value of the variable as a "ticket".
+ * When write_completions reaches the ticket number, the new writer can start
+ * writing. When the writer completes its work, it increments
+ * write_completions so that another new writer can start working. If the
+ * write_requests is not equal to write_completions, it means a writer is now
+ * working or waiting. In this case, a new readers cannot start reading, or
+ * in other words, this algorithm basically prefers writers.
+ *
+ * cnt_and_flag is a "lock" shared by all readers and writers. This integer
+ * variable is a kind of structure with two members: writer_flag (1 bit) and
+ * reader_count (31 bits). The writer_flag shows whether a writer is working,
+ * and the reader_count shows the number of readers currently working or almost
+ * ready for working. A writer who has the current "ticket" tries to get the
+ * lock by exclusively setting the writer_flag to 1, provided that the whole
+ * 32-bit is 0 (meaning no readers or writers working). On the other hand,
+ * a new reader tries to increment the "reader_count" field provided that
+ * the writer_flag is 0 (meaning there is no writer working).
+ *
+ * If some of the above operations fail, the reader or the writer sleeps
+ * until the related condition changes. When a working reader or writer
+ * completes its work, some readers or writers are sleeping, and the condition
+ * that suspended the reader or writer has changed, it wakes up the sleeping
+ * readers or writers.
+ *
+ * As already noted, this algorithm basically prefers writers. In order to
+ * prevent readers from starving, however, the algorithm also introduces the
+ * "writer quota" (Q). When Q consecutive writers have completed their work,
+ * suspending readers, the last writer will wake up the readers, even if a new
+ * writer is waiting.
+ *
+ * Implementation specific note: due to the combination of atomic operations
+ * and a mutex lock, ordering between the atomic operation and locks can be
+ * very sensitive in some cases. In particular, it is generally very important
+ * to check the atomic variable that requires a reader or writer to sleep after
+ * locking the mutex and before actually sleeping; otherwise, it could be very
+ * likely to cause a deadlock. For example, assume "var" is a variable
+ * atomically modified, then the corresponding code would be:
+ * if (var == need_sleep) {
+ * LOCK(lock);
+ * if (var == need_sleep)
+ * WAIT(cond, lock);
+ * UNLOCK(lock);
+ * }
+ * The second check is important, since "var" is protected by the atomic
+ * operation, not by the mutex, and can be changed just before sleeping.
+ * (The first "if" could be omitted, but this is also important in order to
+ * make the code efficient by avoiding the use of the mutex unless it is
+ * really necessary.)
+ */
+
+#define WRITER_ACTIVE 0x1
+#define READER_INCR 0x2
+
+static isc_result_t
+isc__rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
+ int32_t cntflag;
+
+ REQUIRE(VALID_RWLOCK(rwl));
+
+#ifdef ISC_RWLOCK_TRACE
+ print_lock("prelock", rwl, type);
+#endif /* ifdef ISC_RWLOCK_TRACE */
+
+ if (type == isc_rwlocktype_read) {
+ if (atomic_load_acquire(&rwl->write_requests) !=
+ atomic_load_acquire(&rwl->write_completions))
+ {
+ /* there is a waiting or active writer */
+ LOCK(&rwl->lock);
+ if (atomic_load_acquire(&rwl->write_requests) !=
+ atomic_load_acquire(&rwl->write_completions))
+ {
+ rwl->readers_waiting++;
+ WAIT(&rwl->readable, &rwl->lock);
+ rwl->readers_waiting--;
+ }
+ UNLOCK(&rwl->lock);
+ }
+
+ cntflag = atomic_fetch_add_release(&rwl->cnt_and_flag,
+ READER_INCR);
+ POST(cntflag);
+ while (1) {
+ if ((atomic_load_acquire(&rwl->cnt_and_flag) &
+ WRITER_ACTIVE) == 0)
+ {
+ break;
+ }
+
+ /* A writer is still working */
+ LOCK(&rwl->lock);
+ rwl->readers_waiting++;
+ if ((atomic_load_acquire(&rwl->cnt_and_flag) &
+ WRITER_ACTIVE) != 0)
+ {
+ WAIT(&rwl->readable, &rwl->lock);
+ }
+ rwl->readers_waiting--;
+ UNLOCK(&rwl->lock);
+
+ /*
+ * Typically, the reader should be able to get a lock
+ * at this stage:
+ * (1) there should have been no pending writer when
+ * the reader was trying to increment the
+ * counter; otherwise, the writer should be in
+ * the waiting queue, preventing the reader from
+ * proceeding to this point.
+ * (2) once the reader increments the counter, no
+ * more writer can get a lock.
+ * Still, it is possible another writer can work at
+ * this point, e.g. in the following scenario:
+ * A previous writer unlocks the writer lock.
+ * This reader proceeds to point (1).
+ * A new writer appears, and gets a new lock before
+ * the reader increments the counter.
+ * The reader then increments the counter.
+ * The previous writer notices there is a waiting
+ * reader who is almost ready, and wakes it up.
+ * So, the reader needs to confirm whether it can now
+ * read explicitly (thus we loop). Note that this is
+ * not an infinite process, since the reader has
+ * incremented the counter at this point.
+ */
+ }
+
+ /*
+ * If we are temporarily preferred to writers due to the writer
+ * quota, reset the condition (race among readers doesn't
+ * matter).
+ */
+ atomic_store_release(&rwl->write_granted, 0);
+ } else {
+ int32_t prev_writer;
+
+ /* enter the waiting queue, and wait for our turn */
+ prev_writer = atomic_fetch_add_release(&rwl->write_requests, 1);
+ while (atomic_load_acquire(&rwl->write_completions) !=
+ prev_writer)
+ {
+ LOCK(&rwl->lock);
+ if (atomic_load_acquire(&rwl->write_completions) !=
+ prev_writer)
+ {
+ WAIT(&rwl->writeable, &rwl->lock);
+ UNLOCK(&rwl->lock);
+ continue;
+ }
+ UNLOCK(&rwl->lock);
+ break;
+ }
+
+ while (!atomic_compare_exchange_weak_acq_rel(
+ &rwl->cnt_and_flag, &(int_fast32_t){ 0 },
+ WRITER_ACTIVE))
+ {
+ /* Another active reader or writer is working. */
+ LOCK(&rwl->lock);
+ if (atomic_load_acquire(&rwl->cnt_and_flag) != 0) {
+ WAIT(&rwl->writeable, &rwl->lock);
+ }
+ UNLOCK(&rwl->lock);
+ }
+
+ INSIST((atomic_load_acquire(&rwl->cnt_and_flag) &
+ WRITER_ACTIVE));
+ atomic_fetch_add_release(&rwl->write_granted, 1);
+ }
+
+#ifdef ISC_RWLOCK_TRACE
+ print_lock("postlock", rwl, type);
+#endif /* ifdef ISC_RWLOCK_TRACE */
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
+ int32_t cnt = 0;
+ int32_t spins = atomic_load_acquire(&rwl->spins) * 2 + 10;
+ int32_t max_cnt = ISC_MAX(spins, RWLOCK_MAX_ADAPTIVE_COUNT);
+ isc_result_t result = ISC_R_SUCCESS;
+
+ do {
+ if (cnt++ >= max_cnt) {
+ result = isc__rwlock_lock(rwl, type);
+ break;
+ }
+ isc_rwlock_pause();
+ } while (isc_rwlock_trylock(rwl, type) != ISC_R_SUCCESS);
+
+ atomic_fetch_add_release(&rwl->spins, (cnt - spins) / 8);
+
+ return (result);
+}
+
+isc_result_t
+isc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
+ int32_t cntflag;
+
+ REQUIRE(VALID_RWLOCK(rwl));
+
+#ifdef ISC_RWLOCK_TRACE
+ print_lock("prelock", rwl, type);
+#endif /* ifdef ISC_RWLOCK_TRACE */
+
+ if (type == isc_rwlocktype_read) {
+ /* If a writer is waiting or working, we fail. */
+ if (atomic_load_acquire(&rwl->write_requests) !=
+ atomic_load_acquire(&rwl->write_completions))
+ {
+ return (ISC_R_LOCKBUSY);
+ }
+
+ /* Otherwise, be ready for reading. */
+ cntflag = atomic_fetch_add_release(&rwl->cnt_and_flag,
+ READER_INCR);
+ if ((cntflag & WRITER_ACTIVE) != 0) {
+ /*
+ * A writer is working. We lose, and cancel the read
+ * request.
+ */
+ cntflag = atomic_fetch_sub_release(&rwl->cnt_and_flag,
+ READER_INCR);
+ /*
+ * If no other readers are waiting and we've suspended
+ * new writers in this short period, wake them up.
+ */
+ if (cntflag == READER_INCR &&
+ atomic_load_acquire(&rwl->write_completions) !=
+ atomic_load_acquire(&rwl->write_requests))
+ {
+ LOCK(&rwl->lock);
+ BROADCAST(&rwl->writeable);
+ UNLOCK(&rwl->lock);
+ }
+
+ return (ISC_R_LOCKBUSY);
+ }
+ } else {
+ /* Try locking without entering the waiting queue. */
+ int_fast32_t zero = 0;
+ if (!atomic_compare_exchange_strong_acq_rel(
+ &rwl->cnt_and_flag, &zero, WRITER_ACTIVE))
+ {
+ return (ISC_R_LOCKBUSY);
+ }
+
+ /*
+ * XXXJT: jump into the queue, possibly breaking the writer
+ * order.
+ */
+ atomic_fetch_sub_release(&rwl->write_completions, 1);
+ atomic_fetch_add_release(&rwl->write_granted, 1);
+ }
+
+#ifdef ISC_RWLOCK_TRACE
+ print_lock("postlock", rwl, type);
+#endif /* ifdef ISC_RWLOCK_TRACE */
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_rwlock_tryupgrade(isc_rwlock_t *rwl) {
+ REQUIRE(VALID_RWLOCK(rwl));
+
+ int_fast32_t reader_incr = READER_INCR;
+
+ /* Try to acquire write access. */
+ atomic_compare_exchange_strong_acq_rel(&rwl->cnt_and_flag, &reader_incr,
+ WRITER_ACTIVE);
+ /*
+ * There must have been no writer, and there must have
+ * been at least one reader.
+ */
+ INSIST((reader_incr & WRITER_ACTIVE) == 0 &&
+ (reader_incr & ~WRITER_ACTIVE) != 0);
+
+ if (reader_incr == READER_INCR) {
+ /*
+ * We are the only reader and have been upgraded.
+ * Now jump into the head of the writer waiting queue.
+ */
+ atomic_fetch_sub_release(&rwl->write_completions, 1);
+ } else {
+ return (ISC_R_LOCKBUSY);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_rwlock_downgrade(isc_rwlock_t *rwl) {
+ int32_t prev_readers;
+
+ REQUIRE(VALID_RWLOCK(rwl));
+
+ /* Become an active reader. */
+ prev_readers = atomic_fetch_add_release(&rwl->cnt_and_flag,
+ READER_INCR);
+ /* We must have been a writer. */
+ INSIST((prev_readers & WRITER_ACTIVE) != 0);
+
+ /* Complete write */
+ atomic_fetch_sub_release(&rwl->cnt_and_flag, WRITER_ACTIVE);
+ atomic_fetch_add_release(&rwl->write_completions, 1);
+
+ /* Resume other readers */
+ LOCK(&rwl->lock);
+ if (rwl->readers_waiting > 0) {
+ BROADCAST(&rwl->readable);
+ }
+ UNLOCK(&rwl->lock);
+}
+
+isc_result_t
+isc_rwlock_unlock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
+ int32_t prev_cnt;
+
+ REQUIRE(VALID_RWLOCK(rwl));
+
+#ifdef ISC_RWLOCK_TRACE
+ print_lock("preunlock", rwl, type);
+#endif /* ifdef ISC_RWLOCK_TRACE */
+
+ if (type == isc_rwlocktype_read) {
+ prev_cnt = atomic_fetch_sub_release(&rwl->cnt_and_flag,
+ READER_INCR);
+ /*
+ * If we're the last reader and any writers are waiting, wake
+ * them up. We need to wake up all of them to ensure the
+ * FIFO order.
+ */
+ if (prev_cnt == READER_INCR &&
+ atomic_load_acquire(&rwl->write_completions) !=
+ atomic_load_acquire(&rwl->write_requests))
+ {
+ LOCK(&rwl->lock);
+ BROADCAST(&rwl->writeable);
+ UNLOCK(&rwl->lock);
+ }
+ } else {
+ bool wakeup_writers = true;
+
+ /*
+ * Reset the flag, and (implicitly) tell other writers
+ * we are done.
+ */
+ atomic_fetch_sub_release(&rwl->cnt_and_flag, WRITER_ACTIVE);
+ atomic_fetch_add_release(&rwl->write_completions, 1);
+
+ if ((atomic_load_acquire(&rwl->write_granted) >=
+ rwl->write_quota) ||
+ (atomic_load_acquire(&rwl->write_requests) ==
+ atomic_load_acquire(&rwl->write_completions)) ||
+ (atomic_load_acquire(&rwl->cnt_and_flag) & ~WRITER_ACTIVE))
+ {
+ /*
+ * We have passed the write quota, no writer is
+ * waiting, or some readers are almost ready, pending
+ * possible writers. Note that the last case can
+ * happen even if write_requests != write_completions
+ * (which means a new writer in the queue), so we need
+ * to catch the case explicitly.
+ */
+ LOCK(&rwl->lock);
+ if (rwl->readers_waiting > 0) {
+ wakeup_writers = false;
+ BROADCAST(&rwl->readable);
+ }
+ UNLOCK(&rwl->lock);
+ }
+
+ if ((atomic_load_acquire(&rwl->write_requests) !=
+ atomic_load_acquire(&rwl->write_completions)) &&
+ wakeup_writers)
+ {
+ LOCK(&rwl->lock);
+ BROADCAST(&rwl->writeable);
+ UNLOCK(&rwl->lock);
+ }
+ }
+
+#ifdef ISC_RWLOCK_TRACE
+ print_lock("postunlock", rwl, type);
+#endif /* ifdef ISC_RWLOCK_TRACE */
+
+ return (ISC_R_SUCCESS);
+}
+
+#endif /* USE_PTHREAD_RWLOCK */
diff --git a/lib/isc/safe.c b/lib/isc/safe.c
new file mode 100644
index 0000000..988034d
--- /dev/null
+++ b/lib/isc/safe.c
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <openssl/crypto.h>
+
+#include <isc/safe.h>
+
+int
+isc_safe_memequal(const void *s1, const void *s2, size_t len) {
+ return (!CRYPTO_memcmp(s1, s2, len));
+}
+
+void
+isc_safe_memwipe(void *ptr, size_t len) {
+ OPENSSL_cleanse(ptr, len);
+}
diff --git a/lib/isc/serial.c b/lib/isc/serial.c
new file mode 100644
index 0000000..5ede64b
--- /dev/null
+++ b/lib/isc/serial.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/serial.h>
+
+bool
+isc_serial_lt(uint32_t a, uint32_t b) {
+ /*
+ * Undefined => false
+ */
+ if (a == (b ^ 0x80000000U)) {
+ return (false);
+ }
+ return (((int32_t)(a - b) < 0) ? true : false);
+}
+
+bool
+isc_serial_gt(uint32_t a, uint32_t b) {
+ return (((int32_t)(a - b) > 0) ? true : false);
+}
+
+bool
+isc_serial_le(uint32_t a, uint32_t b) {
+ return ((a == b) ? true : isc_serial_lt(a, b));
+}
+
+bool
+isc_serial_ge(uint32_t a, uint32_t b) {
+ return ((a == b) ? true : isc_serial_gt(a, b));
+}
+
+bool
+isc_serial_eq(uint32_t a, uint32_t b) {
+ return ((a == b) ? true : false);
+}
+
+bool
+isc_serial_ne(uint32_t a, uint32_t b) {
+ return ((a != b) ? true : false);
+}
diff --git a/lib/isc/siphash.c b/lib/isc/siphash.c
new file mode 100644
index 0000000..a6e60cf
--- /dev/null
+++ b/lib/isc/siphash.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <inttypes.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <isc/endian.h>
+#include <isc/siphash.h>
+#include <isc/util.h>
+
+/*
+ * The implementation is based on SipHash reference C implementation by
+ *
+ * Copyright (c) 2012-2016 Jean-Philippe Aumasson
+ * <jeanphilippe.aumasson@gmail.com> Copyright (c) 2012-2014 Daniel J. Bernstein
+ * <djb@cr.yp.to>
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty. You should
+ * have received a copy of the CC0 Public Domain Dedication along with this
+ * software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#define cROUNDS 2
+#define dROUNDS 4
+
+#define ROTATE64(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
+
+#define HALF_ROUND64(a, b, c, d, s, t) \
+ a += b; \
+ c += d; \
+ b = ROTATE64(b, s) ^ a; \
+ d = ROTATE64(d, t) ^ c; \
+ a = ROTATE64(a, 32);
+
+#define FULL_ROUND64(v0, v1, v2, v3) \
+ HALF_ROUND64(v0, v1, v2, v3, 13, 16); \
+ HALF_ROUND64(v2, v1, v0, v3, 17, 21);
+
+#define SIPROUND FULL_ROUND64
+
+#define ROTATE32(x, b) (uint32_t)(((x) << (b)) | ((x) >> (32 - (b))))
+
+#define HALF_ROUND32(a, b, c, d, s, t) \
+ a += b; \
+ c += d; \
+ b = ROTATE32(b, s) ^ a; \
+ d = ROTATE32(d, t) ^ c; \
+ a = ROTATE32(a, 16);
+
+#define FULL_ROUND32(v0, v1, v2, v3) \
+ HALF_ROUND32(v0, v1, v2, v3, 5, 8); \
+ HALF_ROUND32(v2, v1, v0, v3, 13, 7);
+
+#define HALFSIPROUND FULL_ROUND32
+
+#define U32TO8_LE(p, v) \
+ (p)[0] = (uint8_t)((v)); \
+ (p)[1] = (uint8_t)((v) >> 8); \
+ (p)[2] = (uint8_t)((v) >> 16); \
+ (p)[3] = (uint8_t)((v) >> 24);
+
+#define U8TO32_LE(p) \
+ (((uint32_t)((p)[0])) | ((uint32_t)((p)[1]) << 8) | \
+ ((uint32_t)((p)[2]) << 16) | ((uint32_t)((p)[3]) << 24))
+
+#define U64TO8_LE(p, v) \
+ U32TO8_LE((p), (uint32_t)((v))); \
+ U32TO8_LE((p) + 4, (uint32_t)((v) >> 32));
+
+#define U8TO64_LE(p) \
+ (((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) | \
+ ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \
+ ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | \
+ ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56))
+
+void
+isc_siphash24(const uint8_t *k, const uint8_t *in, const size_t inlen,
+ uint8_t *out) {
+ REQUIRE(k != NULL);
+ REQUIRE(out != NULL);
+ REQUIRE(inlen == 0 || in != NULL);
+
+ uint64_t k0 = U8TO64_LE(k);
+ uint64_t k1 = U8TO64_LE(k + 8);
+
+ uint64_t v0 = UINT64_C(0x736f6d6570736575) ^ k0;
+ uint64_t v1 = UINT64_C(0x646f72616e646f6d) ^ k1;
+ uint64_t v2 = UINT64_C(0x6c7967656e657261) ^ k0;
+ uint64_t v3 = UINT64_C(0x7465646279746573) ^ k1;
+
+ uint64_t b = ((uint64_t)inlen) << 56;
+
+ const uint8_t *end = (in == NULL)
+ ? NULL
+ : in + inlen - (inlen % sizeof(uint64_t));
+ const size_t left = inlen & 7;
+
+ for (; in != end; in += 8) {
+ uint64_t m = U8TO64_LE(in);
+
+ v3 ^= m;
+
+ for (size_t i = 0; i < cROUNDS; ++i) {
+ SIPROUND(v0, v1, v2, v3);
+ }
+
+ v0 ^= m;
+ }
+
+ switch (left) {
+ case 7:
+ b |= ((uint64_t)in[6]) << 48;
+ FALLTHROUGH;
+ case 6:
+ b |= ((uint64_t)in[5]) << 40;
+ FALLTHROUGH;
+ case 5:
+ b |= ((uint64_t)in[4]) << 32;
+ FALLTHROUGH;
+ case 4:
+ b |= ((uint64_t)in[3]) << 24;
+ FALLTHROUGH;
+ case 3:
+ b |= ((uint64_t)in[2]) << 16;
+ FALLTHROUGH;
+ case 2:
+ b |= ((uint64_t)in[1]) << 8;
+ FALLTHROUGH;
+ case 1:
+ b |= ((uint64_t)in[0]);
+ FALLTHROUGH;
+ case 0:
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ v3 ^= b;
+
+ for (size_t i = 0; i < cROUNDS; ++i) {
+ SIPROUND(v0, v1, v2, v3);
+ }
+
+ v0 ^= b;
+
+ v2 ^= 0xff;
+
+ for (size_t i = 0; i < dROUNDS; ++i) {
+ SIPROUND(v0, v1, v2, v3);
+ }
+
+ b = v0 ^ v1 ^ v2 ^ v3;
+
+ U64TO8_LE(out, b);
+}
+
+void
+isc_halfsiphash24(const uint8_t *k, const uint8_t *in, const size_t inlen,
+ uint8_t *out) {
+ REQUIRE(k != NULL);
+ REQUIRE(out != NULL);
+ REQUIRE(inlen == 0 || in != NULL);
+
+ uint32_t k0 = U8TO32_LE(k);
+ uint32_t k1 = U8TO32_LE(k + 4);
+
+ uint32_t v0 = UINT32_C(0x00000000) ^ k0;
+ uint32_t v1 = UINT32_C(0x00000000) ^ k1;
+ uint32_t v2 = UINT32_C(0x6c796765) ^ k0;
+ uint32_t v3 = UINT32_C(0x74656462) ^ k1;
+
+ uint32_t b = ((uint32_t)inlen) << 24;
+
+ const uint8_t *end = (in == NULL)
+ ? NULL
+ : in + inlen - (inlen % sizeof(uint32_t));
+ const int left = inlen & 3;
+
+ for (; in != end; in += 4) {
+ uint32_t m = U8TO32_LE(in);
+ v3 ^= m;
+
+ for (size_t i = 0; i < cROUNDS; ++i) {
+ HALFSIPROUND(v0, v1, v2, v3);
+ }
+
+ v0 ^= m;
+ }
+
+ switch (left) {
+ case 3:
+ b |= ((uint32_t)in[2]) << 16;
+ FALLTHROUGH;
+ case 2:
+ b |= ((uint32_t)in[1]) << 8;
+ FALLTHROUGH;
+ case 1:
+ b |= ((uint32_t)in[0]);
+ FALLTHROUGH;
+ case 0:
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ v3 ^= b;
+
+ for (size_t i = 0; i < cROUNDS; ++i) {
+ HALFSIPROUND(v0, v1, v2, v3);
+ }
+
+ v0 ^= b;
+
+ v2 ^= 0xff;
+
+ for (size_t i = 0; i < dROUNDS; ++i) {
+ HALFSIPROUND(v0, v1, v2, v3);
+ }
+
+ b = v1 ^ v3;
+ U32TO8_LE(out, b);
+}
diff --git a/lib/isc/sockaddr.c b/lib/isc/sockaddr.c
new file mode 100644
index 0000000..038e3ec
--- /dev/null
+++ b/lib/isc/sockaddr.c
@@ -0,0 +1,499 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <isc/buffer.h>
+#include <isc/hash.h>
+#include <isc/netaddr.h>
+#include <isc/print.h>
+#include <isc/region.h>
+#include <isc/sockaddr.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+bool
+isc_sockaddr_equal(const isc_sockaddr_t *a, const isc_sockaddr_t *b) {
+ return (isc_sockaddr_compare(a, b,
+ ISC_SOCKADDR_CMPADDR |
+ ISC_SOCKADDR_CMPPORT |
+ ISC_SOCKADDR_CMPSCOPE));
+}
+
+bool
+isc_sockaddr_eqaddr(const isc_sockaddr_t *a, const isc_sockaddr_t *b) {
+ return (isc_sockaddr_compare(
+ a, b, ISC_SOCKADDR_CMPADDR | ISC_SOCKADDR_CMPSCOPE));
+}
+
+bool
+isc_sockaddr_compare(const isc_sockaddr_t *a, const isc_sockaddr_t *b,
+ unsigned int flags) {
+ REQUIRE(a != NULL && b != NULL);
+
+ if (a->length != b->length) {
+ return (false);
+ }
+
+ /*
+ * We don't just memcmp because the sin_zero field isn't always
+ * zero.
+ */
+
+ if (a->type.sa.sa_family != b->type.sa.sa_family) {
+ return (false);
+ }
+ switch (a->type.sa.sa_family) {
+ case AF_INET:
+ if ((flags & ISC_SOCKADDR_CMPADDR) != 0 &&
+ memcmp(&a->type.sin.sin_addr, &b->type.sin.sin_addr,
+ sizeof(a->type.sin.sin_addr)) != 0)
+ {
+ return (false);
+ }
+ if ((flags & ISC_SOCKADDR_CMPPORT) != 0 &&
+ a->type.sin.sin_port != b->type.sin.sin_port)
+ {
+ return (false);
+ }
+ break;
+ case AF_INET6:
+ if ((flags & ISC_SOCKADDR_CMPADDR) != 0 &&
+ memcmp(&a->type.sin6.sin6_addr, &b->type.sin6.sin6_addr,
+ sizeof(a->type.sin6.sin6_addr)) != 0)
+ {
+ return (false);
+ }
+ /*
+ * If ISC_SOCKADDR_CMPSCOPEZERO is set then don't return
+ * false if one of the scopes in zero.
+ */
+ if ((flags & ISC_SOCKADDR_CMPSCOPE) != 0 &&
+ a->type.sin6.sin6_scope_id != b->type.sin6.sin6_scope_id &&
+ ((flags & ISC_SOCKADDR_CMPSCOPEZERO) == 0 ||
+ (a->type.sin6.sin6_scope_id != 0 &&
+ b->type.sin6.sin6_scope_id != 0)))
+ {
+ return (false);
+ }
+ if ((flags & ISC_SOCKADDR_CMPPORT) != 0 &&
+ a->type.sin6.sin6_port != b->type.sin6.sin6_port)
+ {
+ return (false);
+ }
+ break;
+ default:
+ if (memcmp(&a->type, &b->type, a->length) != 0) {
+ return (false);
+ }
+ }
+ return (true);
+}
+
+bool
+isc_sockaddr_eqaddrprefix(const isc_sockaddr_t *a, const isc_sockaddr_t *b,
+ unsigned int prefixlen) {
+ isc_netaddr_t na, nb;
+ isc_netaddr_fromsockaddr(&na, a);
+ isc_netaddr_fromsockaddr(&nb, b);
+ return (isc_netaddr_eqprefix(&na, &nb, prefixlen));
+}
+
+isc_result_t
+isc_sockaddr_totext(const isc_sockaddr_t *sockaddr, isc_buffer_t *target) {
+ isc_result_t result;
+ isc_netaddr_t netaddr;
+ char pbuf[sizeof("65000")];
+ unsigned int plen;
+ isc_region_t avail;
+
+ REQUIRE(sockaddr != NULL);
+
+ /*
+ * Do the port first, giving us the opportunity to check for
+ * unsupported address families before calling
+ * isc_netaddr_fromsockaddr().
+ */
+ switch (sockaddr->type.sa.sa_family) {
+ case AF_INET:
+ snprintf(pbuf, sizeof(pbuf), "%u",
+ ntohs(sockaddr->type.sin.sin_port));
+ break;
+ case AF_INET6:
+ snprintf(pbuf, sizeof(pbuf), "%u",
+ ntohs(sockaddr->type.sin6.sin6_port));
+ break;
+ case AF_UNIX:
+ plen = strlen(sockaddr->type.sunix.sun_path);
+ if (plen >= isc_buffer_availablelength(target)) {
+ return (ISC_R_NOSPACE);
+ }
+
+ isc_buffer_putmem(
+ target,
+ (const unsigned char *)sockaddr->type.sunix.sun_path,
+ plen);
+
+ /*
+ * Null terminate after used region.
+ */
+ isc_buffer_availableregion(target, &avail);
+ INSIST(avail.length >= 1);
+ avail.base[0] = '\0';
+
+ return (ISC_R_SUCCESS);
+ default:
+ return (ISC_R_FAILURE);
+ }
+
+ plen = strlen(pbuf);
+ INSIST(plen < sizeof(pbuf));
+
+ isc_netaddr_fromsockaddr(&netaddr, sockaddr);
+ result = isc_netaddr_totext(&netaddr, target);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ if (1 + plen + 1 > isc_buffer_availablelength(target)) {
+ return (ISC_R_NOSPACE);
+ }
+
+ isc_buffer_putmem(target, (const unsigned char *)"#", 1);
+ isc_buffer_putmem(target, (const unsigned char *)pbuf, plen);
+
+ /*
+ * Null terminate after used region.
+ */
+ isc_buffer_availableregion(target, &avail);
+ INSIST(avail.length >= 1);
+ avail.base[0] = '\0';
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_sockaddr_format(const isc_sockaddr_t *sa, char *array, unsigned int size) {
+ isc_result_t result;
+ isc_buffer_t buf;
+
+ if (size == 0U) {
+ return;
+ }
+
+ isc_buffer_init(&buf, array, size);
+ result = isc_sockaddr_totext(sa, &buf);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * The message is the same as in netaddr.c.
+ */
+ snprintf(array, size, "<unknown address, family %u>",
+ sa->type.sa.sa_family);
+ array[size - 1] = '\0';
+ }
+}
+
+unsigned int
+isc_sockaddr_hash(const isc_sockaddr_t *sockaddr, bool address_only) {
+ unsigned int length = 0;
+ const unsigned char *s = NULL;
+ unsigned int h = 0;
+ unsigned int p = 0;
+ const struct in6_addr *in6;
+
+ REQUIRE(sockaddr != NULL);
+
+ switch (sockaddr->type.sa.sa_family) {
+ case AF_INET:
+ s = (const unsigned char *)&sockaddr->type.sin.sin_addr;
+ p = ntohs(sockaddr->type.sin.sin_port);
+ length = sizeof(sockaddr->type.sin.sin_addr.s_addr);
+ break;
+ case AF_INET6:
+ in6 = &sockaddr->type.sin6.sin6_addr;
+ s = (const unsigned char *)in6;
+ if (IN6_IS_ADDR_V4MAPPED(in6)) {
+ s += 12;
+ length = sizeof(sockaddr->type.sin.sin_addr.s_addr);
+ } else {
+ length = sizeof(sockaddr->type.sin6.sin6_addr);
+ }
+ p = ntohs(sockaddr->type.sin6.sin6_port);
+ break;
+ default:
+ UNEXPECTED_ERROR("unknown address family: %d",
+ (int)sockaddr->type.sa.sa_family);
+ s = (const unsigned char *)&sockaddr->type;
+ length = sockaddr->length;
+ p = 0;
+ }
+
+ uint8_t buf[sizeof(struct sockaddr_storage) + sizeof(p)];
+ memmove(buf, s, length);
+ if (!address_only) {
+ memmove(buf + length, &p, sizeof(p));
+ h = isc_hash_function(buf, length + sizeof(p), true);
+ } else {
+ h = isc_hash_function(buf, length, true);
+ }
+
+ return (h);
+}
+
+void
+isc_sockaddr_any(isc_sockaddr_t *sockaddr) {
+ memset(sockaddr, 0, sizeof(*sockaddr));
+ sockaddr->type.sin.sin_family = AF_INET;
+ sockaddr->type.sin.sin_addr.s_addr = INADDR_ANY;
+ sockaddr->type.sin.sin_port = 0;
+ sockaddr->length = sizeof(sockaddr->type.sin);
+ ISC_LINK_INIT(sockaddr, link);
+}
+
+void
+isc_sockaddr_any6(isc_sockaddr_t *sockaddr) {
+ memset(sockaddr, 0, sizeof(*sockaddr));
+ sockaddr->type.sin6.sin6_family = AF_INET6;
+ sockaddr->type.sin6.sin6_addr = in6addr_any;
+ sockaddr->type.sin6.sin6_port = 0;
+ sockaddr->length = sizeof(sockaddr->type.sin6);
+ ISC_LINK_INIT(sockaddr, link);
+}
+
+void
+isc_sockaddr_fromin(isc_sockaddr_t *sockaddr, const struct in_addr *ina,
+ in_port_t port) {
+ memset(sockaddr, 0, sizeof(*sockaddr));
+ sockaddr->type.sin.sin_family = AF_INET;
+ sockaddr->type.sin.sin_addr = *ina;
+ sockaddr->type.sin.sin_port = htons(port);
+ sockaddr->length = sizeof(sockaddr->type.sin);
+ ISC_LINK_INIT(sockaddr, link);
+}
+
+void
+isc_sockaddr_anyofpf(isc_sockaddr_t *sockaddr, int pf) {
+ switch (pf) {
+ case AF_INET:
+ isc_sockaddr_any(sockaddr);
+ break;
+ case AF_INET6:
+ isc_sockaddr_any6(sockaddr);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+void
+isc_sockaddr_fromin6(isc_sockaddr_t *sockaddr, const struct in6_addr *ina6,
+ in_port_t port) {
+ memset(sockaddr, 0, sizeof(*sockaddr));
+ sockaddr->type.sin6.sin6_family = AF_INET6;
+ sockaddr->type.sin6.sin6_addr = *ina6;
+ sockaddr->type.sin6.sin6_port = htons(port);
+ sockaddr->length = sizeof(sockaddr->type.sin6);
+ ISC_LINK_INIT(sockaddr, link);
+}
+
+void
+isc_sockaddr_v6fromin(isc_sockaddr_t *sockaddr, const struct in_addr *ina,
+ in_port_t port) {
+ memset(sockaddr, 0, sizeof(*sockaddr));
+ sockaddr->type.sin6.sin6_family = AF_INET6;
+ sockaddr->type.sin6.sin6_addr.s6_addr[10] = 0xff;
+ sockaddr->type.sin6.sin6_addr.s6_addr[11] = 0xff;
+ memmove(&sockaddr->type.sin6.sin6_addr.s6_addr[12], ina, 4);
+ sockaddr->type.sin6.sin6_port = htons(port);
+ sockaddr->length = sizeof(sockaddr->type.sin6);
+ ISC_LINK_INIT(sockaddr, link);
+}
+
+int
+isc_sockaddr_pf(const isc_sockaddr_t *sockaddr) {
+ /*
+ * Get the protocol family of 'sockaddr'.
+ */
+
+#if (AF_INET == PF_INET && AF_INET6 == PF_INET6)
+ /*
+ * Assume that PF_xxx == AF_xxx for all AF and PF.
+ */
+ return (sockaddr->type.sa.sa_family);
+#else /* if (AF_INET == PF_INET && AF_INET6 == PF_INET6) */
+ switch (sockaddr->type.sa.sa_family) {
+ case AF_INET:
+ return (PF_INET);
+ case AF_INET6:
+ return (PF_INET6);
+ default:
+ FATAL_ERROR("unknown address family: %d",
+ (int)sockaddr->type.sa.sa_family);
+ }
+#endif /* if (AF_INET == PF_INET && AF_INET6 == PF_INET6) */
+}
+
+void
+isc_sockaddr_fromnetaddr(isc_sockaddr_t *sockaddr, const isc_netaddr_t *na,
+ in_port_t port) {
+ memset(sockaddr, 0, sizeof(*sockaddr));
+ sockaddr->type.sin.sin_family = na->family;
+ switch (na->family) {
+ case AF_INET:
+ sockaddr->length = sizeof(sockaddr->type.sin);
+ sockaddr->type.sin.sin_addr = na->type.in;
+ sockaddr->type.sin.sin_port = htons(port);
+ break;
+ case AF_INET6:
+ sockaddr->length = sizeof(sockaddr->type.sin6);
+ memmove(&sockaddr->type.sin6.sin6_addr, &na->type.in6, 16);
+ sockaddr->type.sin6.sin6_scope_id = isc_netaddr_getzone(na);
+ sockaddr->type.sin6.sin6_port = htons(port);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ ISC_LINK_INIT(sockaddr, link);
+}
+
+void
+isc_sockaddr_setport(isc_sockaddr_t *sockaddr, in_port_t port) {
+ switch (sockaddr->type.sa.sa_family) {
+ case AF_INET:
+ sockaddr->type.sin.sin_port = htons(port);
+ break;
+ case AF_INET6:
+ sockaddr->type.sin6.sin6_port = htons(port);
+ break;
+ default:
+ FATAL_ERROR("unknown address family: %d",
+ (int)sockaddr->type.sa.sa_family);
+ }
+}
+
+in_port_t
+isc_sockaddr_getport(const isc_sockaddr_t *sockaddr) {
+ in_port_t port = 0;
+
+ switch (sockaddr->type.sa.sa_family) {
+ case AF_INET:
+ port = ntohs(sockaddr->type.sin.sin_port);
+ break;
+ case AF_INET6:
+ port = ntohs(sockaddr->type.sin6.sin6_port);
+ break;
+ default:
+ FATAL_ERROR("unknown address family: %d",
+ (int)sockaddr->type.sa.sa_family);
+ }
+
+ return (port);
+}
+
+bool
+isc_sockaddr_ismulticast(const isc_sockaddr_t *sockaddr) {
+ isc_netaddr_t netaddr;
+
+ if (sockaddr->type.sa.sa_family == AF_INET ||
+ sockaddr->type.sa.sa_family == AF_INET6)
+ {
+ isc_netaddr_fromsockaddr(&netaddr, sockaddr);
+ return (isc_netaddr_ismulticast(&netaddr));
+ }
+ return (false);
+}
+
+bool
+isc_sockaddr_isexperimental(const isc_sockaddr_t *sockaddr) {
+ isc_netaddr_t netaddr;
+
+ if (sockaddr->type.sa.sa_family == AF_INET) {
+ isc_netaddr_fromsockaddr(&netaddr, sockaddr);
+ return (isc_netaddr_isexperimental(&netaddr));
+ }
+ return (false);
+}
+
+bool
+isc_sockaddr_issitelocal(const isc_sockaddr_t *sockaddr) {
+ isc_netaddr_t netaddr;
+
+ if (sockaddr->type.sa.sa_family == AF_INET6) {
+ isc_netaddr_fromsockaddr(&netaddr, sockaddr);
+ return (isc_netaddr_issitelocal(&netaddr));
+ }
+ return (false);
+}
+
+bool
+isc_sockaddr_islinklocal(const isc_sockaddr_t *sockaddr) {
+ isc_netaddr_t netaddr;
+
+ if (sockaddr->type.sa.sa_family == AF_INET6) {
+ isc_netaddr_fromsockaddr(&netaddr, sockaddr);
+ return (isc_netaddr_islinklocal(&netaddr));
+ }
+ return (false);
+}
+
+bool
+isc_sockaddr_isnetzero(const isc_sockaddr_t *sockaddr) {
+ isc_netaddr_t netaddr;
+
+ if (sockaddr->type.sa.sa_family == AF_INET) {
+ isc_netaddr_fromsockaddr(&netaddr, sockaddr);
+ return (isc_netaddr_isnetzero(&netaddr));
+ }
+ return (false);
+}
+
+isc_result_t
+isc_sockaddr_frompath(isc_sockaddr_t *sockaddr, const char *path) {
+ if (strlen(path) >= sizeof(sockaddr->type.sunix.sun_path)) {
+ return (ISC_R_NOSPACE);
+ }
+ memset(sockaddr, 0, sizeof(*sockaddr));
+ sockaddr->length = sizeof(sockaddr->type.sunix);
+ sockaddr->type.sunix.sun_family = AF_UNIX;
+ strlcpy(sockaddr->type.sunix.sun_path, path,
+ sizeof(sockaddr->type.sunix.sun_path));
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_sockaddr_fromsockaddr(isc_sockaddr_t *isa, const struct sockaddr *sa) {
+ unsigned int length = 0;
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ length = sizeof(isa->type.sin);
+ break;
+ case AF_INET6:
+ length = sizeof(isa->type.sin6);
+ break;
+ case AF_UNIX:
+ length = sizeof(isa->type.sunix);
+ break;
+ default:
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+
+ memset(isa, 0, sizeof(isc_sockaddr_t));
+ memmove(isa, sa, length);
+ isa->length = length;
+
+ return (ISC_R_SUCCESS);
+}
diff --git a/lib/isc/stats.c b/lib/isc/stats.c
new file mode 100644
index 0000000..3e4676c
--- /dev/null
+++ b/lib/isc/stats.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <inttypes.h>
+#include <string.h>
+
+#include <isc/atomic.h>
+#include <isc/buffer.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/refcount.h>
+#include <isc/stats.h>
+#include <isc/util.h>
+
+#define ISC_STATS_MAGIC ISC_MAGIC('S', 't', 'a', 't')
+#define ISC_STATS_VALID(x) ISC_MAGIC_VALID(x, ISC_STATS_MAGIC)
+
+typedef atomic_int_fast64_t isc__atomic_statcounter_t;
+
+struct isc_stats {
+ unsigned int magic;
+ isc_mem_t *mctx;
+ isc_refcount_t references;
+ int ncounters;
+ isc__atomic_statcounter_t *counters;
+};
+
+static isc_result_t
+create_stats(isc_mem_t *mctx, int ncounters, isc_stats_t **statsp) {
+ isc_stats_t *stats;
+ size_t counters_alloc_size;
+
+ REQUIRE(statsp != NULL && *statsp == NULL);
+
+ stats = isc_mem_get(mctx, sizeof(*stats));
+ counters_alloc_size = sizeof(isc__atomic_statcounter_t) * ncounters;
+ stats->counters = isc_mem_get(mctx, counters_alloc_size);
+ isc_refcount_init(&stats->references, 1);
+ for (int i = 0; i < ncounters; i++) {
+ atomic_init(&stats->counters[i], 0);
+ }
+ stats->mctx = NULL;
+ isc_mem_attach(mctx, &stats->mctx);
+ stats->ncounters = ncounters;
+ stats->magic = ISC_STATS_MAGIC;
+ *statsp = stats;
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_stats_attach(isc_stats_t *stats, isc_stats_t **statsp) {
+ REQUIRE(ISC_STATS_VALID(stats));
+ REQUIRE(statsp != NULL && *statsp == NULL);
+
+ isc_refcount_increment(&stats->references);
+ *statsp = stats;
+}
+
+void
+isc_stats_detach(isc_stats_t **statsp) {
+ isc_stats_t *stats;
+
+ REQUIRE(statsp != NULL && ISC_STATS_VALID(*statsp));
+
+ stats = *statsp;
+ *statsp = NULL;
+
+ if (isc_refcount_decrement(&stats->references) == 1) {
+ isc_refcount_destroy(&stats->references);
+ isc_mem_put(stats->mctx, stats->counters,
+ sizeof(isc__atomic_statcounter_t) *
+ stats->ncounters);
+ isc_mem_putanddetach(&stats->mctx, stats, sizeof(*stats));
+ }
+}
+
+int
+isc_stats_ncounters(isc_stats_t *stats) {
+ REQUIRE(ISC_STATS_VALID(stats));
+
+ return (stats->ncounters);
+}
+
+isc_result_t
+isc_stats_create(isc_mem_t *mctx, isc_stats_t **statsp, int ncounters) {
+ REQUIRE(statsp != NULL && *statsp == NULL);
+
+ return (create_stats(mctx, ncounters, statsp));
+}
+
+void
+isc_stats_increment(isc_stats_t *stats, isc_statscounter_t counter) {
+ REQUIRE(ISC_STATS_VALID(stats));
+ REQUIRE(counter < stats->ncounters);
+
+ atomic_fetch_add_relaxed(&stats->counters[counter], 1);
+}
+
+void
+isc_stats_decrement(isc_stats_t *stats, isc_statscounter_t counter) {
+ REQUIRE(ISC_STATS_VALID(stats));
+ REQUIRE(counter < stats->ncounters);
+ atomic_fetch_sub_release(&stats->counters[counter], 1);
+}
+
+void
+isc_stats_dump(isc_stats_t *stats, isc_stats_dumper_t dump_fn, void *arg,
+ unsigned int options) {
+ int i;
+
+ REQUIRE(ISC_STATS_VALID(stats));
+
+ for (i = 0; i < stats->ncounters; i++) {
+ uint32_t counter = atomic_load_acquire(&stats->counters[i]);
+ if ((options & ISC_STATSDUMP_VERBOSE) == 0 && counter == 0) {
+ continue;
+ }
+ dump_fn((isc_statscounter_t)i, counter, arg);
+ }
+}
+
+void
+isc_stats_set(isc_stats_t *stats, uint64_t val, isc_statscounter_t counter) {
+ REQUIRE(ISC_STATS_VALID(stats));
+ REQUIRE(counter < stats->ncounters);
+
+ atomic_store_release(&stats->counters[counter], val);
+}
+
+void
+isc_stats_update_if_greater(isc_stats_t *stats, isc_statscounter_t counter,
+ isc_statscounter_t value) {
+ REQUIRE(ISC_STATS_VALID(stats));
+ REQUIRE(counter < stats->ncounters);
+
+ isc_statscounter_t curr_value =
+ atomic_load_acquire(&stats->counters[counter]);
+ do {
+ if (curr_value >= value) {
+ break;
+ }
+ } while (!atomic_compare_exchange_weak_acq_rel(
+ &stats->counters[counter], &curr_value, value));
+}
+
+isc_statscounter_t
+isc_stats_get_counter(isc_stats_t *stats, isc_statscounter_t counter) {
+ REQUIRE(ISC_STATS_VALID(stats));
+ REQUIRE(counter < stats->ncounters);
+
+ return (atomic_load_acquire(&stats->counters[counter]));
+}
+
+void
+isc_stats_resize(isc_stats_t **statsp, int ncounters) {
+ isc_stats_t *stats;
+ size_t counters_alloc_size;
+ isc__atomic_statcounter_t *newcounters;
+
+ REQUIRE(statsp != NULL && *statsp != NULL);
+ REQUIRE(ISC_STATS_VALID(*statsp));
+ REQUIRE(ncounters > 0);
+
+ stats = *statsp;
+ if (stats->ncounters >= ncounters) {
+ /* We already have enough counters. */
+ return;
+ }
+
+ /* Grow number of counters. */
+ counters_alloc_size = sizeof(isc__atomic_statcounter_t) * ncounters;
+ newcounters = isc_mem_get(stats->mctx, counters_alloc_size);
+ for (int i = 0; i < ncounters; i++) {
+ atomic_init(&newcounters[i], 0);
+ }
+ for (int i = 0; i < stats->ncounters; i++) {
+ uint32_t counter = atomic_load_acquire(&stats->counters[i]);
+ atomic_store_release(&newcounters[i], counter);
+ }
+ isc_mem_put(stats->mctx, stats->counters,
+ sizeof(isc__atomic_statcounter_t) * stats->ncounters);
+ stats->counters = newcounters;
+ stats->ncounters = ncounters;
+}
diff --git a/lib/isc/stdio.c b/lib/isc/stdio.c
new file mode 100644
index 0000000..12ab678
--- /dev/null
+++ b/lib/isc/stdio.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <errno.h>
+#include <unistd.h>
+
+#include <isc/stat.h>
+#include <isc/stdio.h>
+#include <isc/util.h>
+
+#include "errno2result.h"
+
+isc_result_t
+isc_stdio_open(const char *filename, const char *mode, FILE **fp) {
+ FILE *f;
+
+ f = fopen(filename, mode);
+ if (f == NULL) {
+ return (isc__errno2result(errno));
+ }
+ *fp = f;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_stdio_close(FILE *f) {
+ int r;
+
+ r = fclose(f);
+ if (r == 0) {
+ return (ISC_R_SUCCESS);
+ } else {
+ return (isc__errno2result(errno));
+ }
+}
+
+isc_result_t
+isc_stdio_seek(FILE *f, off_t offset, int whence) {
+ int r;
+
+ r = fseeko(f, offset, whence);
+ if (r == 0) {
+ return (ISC_R_SUCCESS);
+ } else {
+ return (isc__errno2result(errno));
+ }
+}
+
+isc_result_t
+isc_stdio_tell(FILE *f, off_t *offsetp) {
+ off_t r;
+
+ REQUIRE(offsetp != NULL);
+
+ r = ftello(f);
+ if (r >= 0) {
+ *offsetp = r;
+ return (ISC_R_SUCCESS);
+ } else {
+ return (isc__errno2result(errno));
+ }
+}
+
+isc_result_t
+isc_stdio_read(void *ptr, size_t size, size_t nmemb, FILE *f, size_t *nret) {
+ isc_result_t result = ISC_R_SUCCESS;
+ size_t r;
+
+ clearerr(f);
+ r = fread(ptr, size, nmemb, f);
+ if (r != nmemb) {
+ if (feof(f)) {
+ result = ISC_R_EOF;
+ } else {
+ result = isc__errno2result(errno);
+ }
+ }
+ if (nret != NULL) {
+ *nret = r;
+ }
+ return (result);
+}
+
+isc_result_t
+isc_stdio_write(const void *ptr, size_t size, size_t nmemb, FILE *f,
+ size_t *nret) {
+ isc_result_t result = ISC_R_SUCCESS;
+ size_t r;
+
+ clearerr(f);
+ r = fwrite(ptr, size, nmemb, f);
+ if (r != nmemb) {
+ result = isc__errno2result(errno);
+ }
+ if (nret != NULL) {
+ *nret = r;
+ }
+ return (result);
+}
+
+isc_result_t
+isc_stdio_flush(FILE *f) {
+ int r;
+
+ r = fflush(f);
+ if (r == 0) {
+ return (ISC_R_SUCCESS);
+ } else {
+ return (isc__errno2result(errno));
+ }
+}
+
+/*
+ * OpenBSD has deprecated ENOTSUP in favor of EOPNOTSUPP.
+ */
+#if defined(EOPNOTSUPP) && !defined(ENOTSUP)
+#define ENOTSUP EOPNOTSUPP
+#endif /* if defined(EOPNOTSUPP) && !defined(ENOTSUP) */
+
+isc_result_t
+isc_stdio_sync(FILE *f) {
+ struct stat buf;
+ int r;
+
+ if (fstat(fileno(f), &buf) != 0) {
+ return (isc__errno2result(errno));
+ }
+
+ /*
+ * Only call fsync() on regular files.
+ */
+ if ((buf.st_mode & S_IFMT) != S_IFREG) {
+ return (ISC_R_SUCCESS);
+ }
+
+ r = fsync(fileno(f));
+ if (r == 0) {
+ return (ISC_R_SUCCESS);
+ } else {
+ return (isc__errno2result(errno));
+ }
+}
diff --git a/lib/isc/stdtime.c b/lib/isc/stdtime.c
new file mode 100644
index 0000000..b7cec81
--- /dev/null
+++ b/lib/isc/stdtime.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h> /* NULL */
+#include <stdlib.h> /* NULL */
+#include <syslog.h>
+#include <time.h>
+
+#include <isc/stdtime.h>
+#include <isc/time.h>
+#include <isc/util.h>
+
+#if defined(CLOCK_REALTIME_COARSE)
+#define CLOCKSOURCE CLOCK_REALTIME_COARSE
+#elif defined(CLOCK_REALTIME_FAST)
+#define CLOCKSOURCE CLOCK_REALTIME_FAST
+#else /* if defined(CLOCK_REALTIME_COARSE) */
+#define CLOCKSOURCE CLOCK_REALTIME
+#endif /* if defined(CLOCK_REALTIME_COARSE) */
+
+void
+isc_stdtime_get(isc_stdtime_t *t) {
+ REQUIRE(t != NULL);
+
+ struct timespec ts;
+
+ if (clock_gettime(CLOCKSOURCE, &ts) == -1) {
+ FATAL_SYSERROR(errno, "clock_gettime()");
+ }
+
+ REQUIRE(ts.tv_sec > 0 && ts.tv_nsec >= 0 && ts.tv_nsec < NS_PER_SEC);
+
+ *t = (isc_stdtime_t)ts.tv_sec;
+}
+
+void
+isc_stdtime_tostring(isc_stdtime_t t, char *out, size_t outlen) {
+ time_t when;
+
+ REQUIRE(out != NULL);
+ REQUIRE(outlen >= 26);
+
+ UNUSED(outlen);
+
+ /* time_t and isc_stdtime_t might be different sizes */
+ when = t;
+ INSIST((ctime_r(&when, out) != NULL));
+ *(out + strlen(out) - 1) = '\0';
+}
diff --git a/lib/isc/string.c b/lib/isc/string.c
new file mode 100644
index 0000000..09cf5d6
--- /dev/null
+++ b/lib/isc/string.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 2001 Mike Barcroft <mike@FreeBSD.org>
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * 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 University 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 REGENTS 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 REGENTS 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.
+ */
+
+/*! \file */
+
+#ifdef _GNU_SOURCE
+#undef _GNU_SOURCE
+#endif /* ifdef _GNU_SOURCE */
+#include <string.h>
+
+#include <isc/string.h> /* IWYU pragma: keep */
+
+#if !defined(HAVE_STRLCPY)
+size_t
+strlcpy(char *dst, const char *src, size_t size) {
+ char *d = dst;
+ const char *s = src;
+ size_t n = size;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0U && --n != 0U) {
+ do {
+ if ((*d++ = *s++) == 0) {
+ break;
+ }
+ } while (--n != 0U);
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0U) {
+ if (size != 0U) {
+ *d = '\0'; /* NUL-terminate dst */
+ }
+ while (*s++) {
+ }
+ }
+
+ return (s - src - 1); /* count does not include NUL */
+}
+#endif /* !defined(HAVE_STRLCPY) */
+
+#if !defined(HAVE_STRLCAT)
+size_t
+strlcat(char *dst, const char *src, size_t size) {
+ char *d = dst;
+ const char *s = src;
+ size_t n = size;
+ size_t dlen;
+
+ /* Find the end of dst and adjust bytes left but don't go past end */
+ while (n-- != 0U && *d != '\0') {
+ d++;
+ }
+ dlen = d - dst;
+ n = size - dlen;
+
+ if (n == 0U) {
+ return (dlen + strlen(s));
+ }
+ while (*s != '\0') {
+ if (n != 1U) {
+ *d++ = *s;
+ n--;
+ }
+ s++;
+ }
+ *d = '\0';
+
+ return (dlen + (s - src)); /* count does not include NUL */
+}
+#endif /* !defined(HAVE_STRLCAT) */
+
+#if !defined(HAVE_STRNSTR)
+char *
+strnstr(const char *s, const char *find, size_t slen) {
+ char c, sc;
+ size_t len;
+
+ if ((c = *find++) != '\0') {
+ len = strlen(find);
+ do {
+ do {
+ if (slen-- < 1 || (sc = *s++) == '\0')
+ return (NULL);
+ } while (sc != c);
+ if (len > slen)
+ return (NULL);
+ } while (strncmp(s, find, len) != 0);
+ s--;
+ }
+ return ((char *)s);
+}
+#endif
+
+int
+isc_string_strerror_r(int errnum, char *buf, size_t buflen) {
+ return (strerror_r(errnum, buf, buflen));
+}
diff --git a/lib/isc/symtab.c b/lib/isc/symtab.c
new file mode 100644
index 0000000..ff022a2
--- /dev/null
+++ b/lib/isc/symtab.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <ctype.h>
+#include <stdbool.h>
+
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/string.h>
+#include <isc/symtab.h>
+#include <isc/util.h>
+
+typedef struct elt {
+ char *key;
+ unsigned int type;
+ isc_symvalue_t value;
+ LINK(struct elt) link;
+} elt_t;
+
+typedef LIST(elt_t) eltlist_t;
+
+#define SYMTAB_MAGIC ISC_MAGIC('S', 'y', 'm', 'T')
+#define VALID_SYMTAB(st) ISC_MAGIC_VALID(st, SYMTAB_MAGIC)
+
+struct isc_symtab {
+ /* Unlocked. */
+ unsigned int magic;
+ isc_mem_t *mctx;
+ unsigned int size;
+ unsigned int count;
+ unsigned int maxload;
+ eltlist_t *table;
+ isc_symtabaction_t undefine_action;
+ void *undefine_arg;
+ bool case_sensitive;
+};
+
+isc_result_t
+isc_symtab_create(isc_mem_t *mctx, unsigned int size,
+ isc_symtabaction_t undefine_action, void *undefine_arg,
+ bool case_sensitive, isc_symtab_t **symtabp) {
+ isc_symtab_t *symtab;
+ unsigned int i;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(symtabp != NULL && *symtabp == NULL);
+ REQUIRE(size > 0); /* Should be prime. */
+
+ symtab = isc_mem_get(mctx, sizeof(*symtab));
+
+ symtab->mctx = NULL;
+ isc_mem_attach(mctx, &symtab->mctx);
+ symtab->table = isc_mem_get(mctx, size * sizeof(eltlist_t));
+ for (i = 0; i < size; i++) {
+ INIT_LIST(symtab->table[i]);
+ }
+ symtab->size = size;
+ symtab->count = 0;
+ symtab->maxload = size * 3 / 4;
+ symtab->undefine_action = undefine_action;
+ symtab->undefine_arg = undefine_arg;
+ symtab->case_sensitive = case_sensitive;
+ symtab->magic = SYMTAB_MAGIC;
+
+ *symtabp = symtab;
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_symtab_destroy(isc_symtab_t **symtabp) {
+ isc_symtab_t *symtab;
+ unsigned int i;
+ elt_t *elt, *nelt;
+
+ REQUIRE(symtabp != NULL);
+ symtab = *symtabp;
+ *symtabp = NULL;
+ REQUIRE(VALID_SYMTAB(symtab));
+
+ for (i = 0; i < symtab->size; i++) {
+ for (elt = HEAD(symtab->table[i]); elt != NULL; elt = nelt) {
+ nelt = NEXT(elt, link);
+ if (symtab->undefine_action != NULL) {
+ (symtab->undefine_action)(elt->key, elt->type,
+ elt->value,
+ symtab->undefine_arg);
+ }
+ isc_mem_put(symtab->mctx, elt, sizeof(*elt));
+ }
+ }
+ isc_mem_put(symtab->mctx, symtab->table,
+ symtab->size * sizeof(eltlist_t));
+ symtab->magic = 0;
+ isc_mem_putanddetach(&symtab->mctx, symtab, sizeof(*symtab));
+}
+
+static unsigned int
+hash(const char *key, bool case_sensitive) {
+ const char *s;
+ unsigned int h = 0;
+ int c;
+
+ /*
+ * This hash function is similar to the one Ousterhout
+ * uses in Tcl.
+ */
+
+ if (case_sensitive) {
+ for (s = key; *s != '\0'; s++) {
+ h += (h << 3) + *s;
+ }
+ } else {
+ for (s = key; *s != '\0'; s++) {
+ c = *s;
+ c = tolower((unsigned char)c);
+ h += (h << 3) + c;
+ }
+ }
+
+ return (h);
+}
+
+#define FIND(s, k, t, b, e) \
+ b = hash((k), (s)->case_sensitive) % (s)->size; \
+ if ((s)->case_sensitive) { \
+ for (e = HEAD((s)->table[b]); e != NULL; e = NEXT(e, link)) { \
+ if (((t) == 0 || e->type == (t)) && \
+ strcmp(e->key, (k)) == 0) \
+ break; \
+ } \
+ } else { \
+ for (e = HEAD((s)->table[b]); e != NULL; e = NEXT(e, link)) { \
+ if (((t) == 0 || e->type == (t)) && \
+ strcasecmp(e->key, (k)) == 0) \
+ break; \
+ } \
+ }
+
+isc_result_t
+isc_symtab_lookup(isc_symtab_t *symtab, const char *key, unsigned int type,
+ isc_symvalue_t *value) {
+ unsigned int bucket;
+ elt_t *elt;
+
+ REQUIRE(VALID_SYMTAB(symtab));
+ REQUIRE(key != NULL);
+
+ FIND(symtab, key, type, bucket, elt);
+
+ if (elt == NULL) {
+ return (ISC_R_NOTFOUND);
+ }
+
+ if (value != NULL) {
+ *value = elt->value;
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+grow_table(isc_symtab_t *symtab) {
+ eltlist_t *newtable;
+ unsigned int i, newsize, newmax;
+
+ REQUIRE(symtab != NULL);
+
+ newsize = symtab->size * 2;
+ newmax = newsize * 3 / 4;
+ INSIST(newsize > 0U && newmax > 0U);
+
+ newtable = isc_mem_get(symtab->mctx, newsize * sizeof(eltlist_t));
+
+ for (i = 0; i < newsize; i++) {
+ INIT_LIST(newtable[i]);
+ }
+
+ for (i = 0; i < symtab->size; i++) {
+ elt_t *elt, *nelt;
+
+ for (elt = HEAD(symtab->table[i]); elt != NULL; elt = nelt) {
+ unsigned int hv;
+
+ nelt = NEXT(elt, link);
+
+ UNLINK(symtab->table[i], elt, link);
+ hv = hash(elt->key, symtab->case_sensitive);
+ APPEND(newtable[hv % newsize], elt, link);
+ }
+ }
+
+ isc_mem_put(symtab->mctx, symtab->table,
+ symtab->size * sizeof(eltlist_t));
+
+ symtab->table = newtable;
+ symtab->size = newsize;
+ symtab->maxload = newmax;
+}
+
+isc_result_t
+isc_symtab_define(isc_symtab_t *symtab, const char *key, unsigned int type,
+ isc_symvalue_t value, isc_symexists_t exists_policy) {
+ unsigned int bucket;
+ elt_t *elt;
+
+ REQUIRE(VALID_SYMTAB(symtab));
+ REQUIRE(key != NULL);
+ REQUIRE(type != 0);
+
+ FIND(symtab, key, type, bucket, elt);
+
+ if (exists_policy != isc_symexists_add && elt != NULL) {
+ if (exists_policy == isc_symexists_reject) {
+ return (ISC_R_EXISTS);
+ }
+ INSIST(exists_policy == isc_symexists_replace);
+ UNLINK(symtab->table[bucket], elt, link);
+ if (symtab->undefine_action != NULL) {
+ (symtab->undefine_action)(elt->key, elt->type,
+ elt->value,
+ symtab->undefine_arg);
+ }
+ } else {
+ elt = isc_mem_get(symtab->mctx, sizeof(*elt));
+ ISC_LINK_INIT(elt, link);
+ symtab->count++;
+ }
+
+ /*
+ * Though the "key" can be const coming in, it is not stored as const
+ * so that the calling program can easily have writable access to
+ * it in its undefine_action function. In the event that it *was*
+ * truly const coming in and then the caller modified it anyway ...
+ * well, don't do that!
+ */
+ DE_CONST(key, elt->key);
+ elt->type = type;
+ elt->value = value;
+
+ /*
+ * We prepend so that the most recent definition will be found.
+ */
+ PREPEND(symtab->table[bucket], elt, link);
+
+ if (symtab->count > symtab->maxload) {
+ grow_table(symtab);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_symtab_undefine(isc_symtab_t *symtab, const char *key, unsigned int type) {
+ unsigned int bucket;
+ elt_t *elt;
+
+ REQUIRE(VALID_SYMTAB(symtab));
+ REQUIRE(key != NULL);
+
+ FIND(symtab, key, type, bucket, elt);
+
+ if (elt == NULL) {
+ return (ISC_R_NOTFOUND);
+ }
+
+ if (symtab->undefine_action != NULL) {
+ (symtab->undefine_action)(elt->key, elt->type, elt->value,
+ symtab->undefine_arg);
+ }
+ UNLINK(symtab->table[bucket], elt, link);
+ isc_mem_put(symtab->mctx, elt, sizeof(*elt));
+ symtab->count--;
+
+ return (ISC_R_SUCCESS);
+}
+
+unsigned int
+isc_symtab_count(isc_symtab_t *symtab) {
+ REQUIRE(VALID_SYMTAB(symtab));
+ return (symtab->count);
+}
diff --git a/lib/isc/syslog.c b/lib/isc/syslog.c
new file mode 100644
index 0000000..81b99ca
--- /dev/null
+++ b/lib/isc/syslog.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <stdlib.h>
+#include <syslog.h>
+
+#include <isc/result.h>
+#include <isc/string.h>
+#include <isc/syslog.h>
+#include <isc/util.h>
+
+static struct dsn_c_pvt_sfnt {
+ int val;
+ const char *strval;
+} facilities[] = { { LOG_KERN, "kern" },
+ { LOG_USER, "user" },
+ { LOG_MAIL, "mail" },
+ { LOG_DAEMON, "daemon" },
+ { LOG_AUTH, "auth" },
+ { LOG_SYSLOG, "syslog" },
+ { LOG_LPR, "lpr" },
+#ifdef LOG_NEWS
+ { LOG_NEWS, "news" },
+#endif /* ifdef LOG_NEWS */
+#ifdef LOG_UUCP
+ { LOG_UUCP, "uucp" },
+#endif /* ifdef LOG_UUCP */
+#ifdef LOG_CRON
+ { LOG_CRON, "cron" },
+#endif /* ifdef LOG_CRON */
+#ifdef LOG_AUTHPRIV
+ { LOG_AUTHPRIV, "authpriv" },
+#endif /* ifdef LOG_AUTHPRIV */
+#ifdef LOG_FTP
+ { LOG_FTP, "ftp" },
+#endif /* ifdef LOG_FTP */
+ { LOG_LOCAL0, "local0" },
+ { LOG_LOCAL1, "local1" },
+ { LOG_LOCAL2, "local2" },
+ { LOG_LOCAL3, "local3" },
+ { LOG_LOCAL4, "local4" },
+ { LOG_LOCAL5, "local5" },
+ { LOG_LOCAL6, "local6" },
+ { LOG_LOCAL7, "local7" },
+ { 0, NULL } };
+
+isc_result_t
+isc_syslog_facilityfromstring(const char *str, int *facilityp) {
+ int i;
+
+ REQUIRE(str != NULL);
+ REQUIRE(facilityp != NULL);
+
+ for (i = 0; facilities[i].strval != NULL; i++) {
+ if (strcasecmp(facilities[i].strval, str) == 0) {
+ *facilityp = facilities[i].val;
+ return (ISC_R_SUCCESS);
+ }
+ }
+ return (ISC_R_NOTFOUND);
+}
diff --git a/lib/isc/task.c b/lib/isc/task.c
new file mode 100644
index 0000000..439d430
--- /dev/null
+++ b/lib/isc/task.c
@@ -0,0 +1,1368 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+/*
+ * XXXRTH Need to document the states a task can be in, and the rules
+ * for changing states.
+ */
+
+#include <stdbool.h>
+#include <unistd.h>
+
+#include <isc/app.h>
+#include <isc/atomic.h>
+#include <isc/condition.h>
+#include <isc/event.h>
+#include <isc/log.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/once.h>
+#include <isc/print.h>
+#include <isc/random.h>
+#include <isc/refcount.h>
+#include <isc/string.h>
+#include <isc/task.h>
+#include <isc/thread.h>
+#include <isc/time.h>
+#include <isc/util.h>
+
+#ifdef HAVE_LIBXML2
+#include <libxml/xmlwriter.h>
+#define ISC_XMLCHAR (const xmlChar *)
+#endif /* HAVE_LIBXML2 */
+
+#ifdef HAVE_JSON_C
+#include <json_object.h>
+#endif /* HAVE_JSON_C */
+
+#include "task_p.h"
+
+/*
+ * Task manager is built around 'as little locking as possible' concept.
+ * Each thread has his own queue of tasks to be run, if a task is in running
+ * state it will stay on the runner it's currently on, if a task is in idle
+ * state it can be woken up on a specific runner with isc_task_sendto - that
+ * helps with data locality on CPU.
+ *
+ * To make load even some tasks (from task pools) are bound to specific
+ * queues using isc_task_create_bound. This way load balancing between
+ * CPUs/queues happens on the higher layer.
+ */
+
+#ifdef ISC_TASK_TRACE
+#define XTRACE(m) \
+ fprintf(stderr, "task %p thread %zu: %s\n", task, isc_tid_v, (m))
+#define XTTRACE(t, m) \
+ fprintf(stderr, "task %p thread %zu: %s\n", (t), isc_tid_v, (m))
+#define XTHREADTRACE(m) fprintf(stderr, "thread %zu: %s\n", isc_tid_v, (m))
+#else /* ifdef ISC_TASK_TRACE */
+#define XTRACE(m)
+#define XTTRACE(t, m)
+#define XTHREADTRACE(m)
+#endif /* ifdef ISC_TASK_TRACE */
+
+/***
+ *** Types.
+ ***/
+
+typedef enum {
+ task_state_idle, /* not doing anything, events queue empty */
+ task_state_ready, /* waiting in worker's queue */
+ task_state_running, /* actively processing events */
+ task_state_done /* shutting down, no events or references */
+} task_state_t;
+
+#if defined(HAVE_LIBXML2) || defined(HAVE_JSON_C)
+static const char *statenames[] = {
+ "idle",
+ "ready",
+ "running",
+ "done",
+};
+#endif /* if defined(HAVE_LIBXML2) || defined(HAVE_JSON_C) */
+
+#define TASK_MAGIC ISC_MAGIC('T', 'A', 'S', 'K')
+#define VALID_TASK(t) ISC_MAGIC_VALID(t, TASK_MAGIC)
+
+struct isc_task {
+ /* Not locked. */
+ unsigned int magic;
+ isc_taskmgr_t *manager;
+ isc_mutex_t lock;
+ /* Locked by task lock. */
+ int threadid;
+ task_state_t state;
+ isc_refcount_t references;
+ isc_refcount_t running;
+ isc_eventlist_t events;
+ isc_eventlist_t on_shutdown;
+ unsigned int nevents;
+ unsigned int quantum;
+ isc_stdtime_t now;
+ isc_time_t tnow;
+ char name[16];
+ void *tag;
+ bool bound;
+ /* Protected by atomics */
+ atomic_bool shuttingdown;
+ atomic_bool privileged;
+ /* Locked by task manager lock. */
+ LINK(isc_task_t) link;
+};
+
+#define TASK_SHUTTINGDOWN(t) (atomic_load_acquire(&(t)->shuttingdown))
+#define TASK_PRIVILEGED(t) (atomic_load_acquire(&(t)->privileged))
+
+#define TASK_MANAGER_MAGIC ISC_MAGIC('T', 'S', 'K', 'M')
+#define VALID_MANAGER(m) ISC_MAGIC_VALID(m, TASK_MANAGER_MAGIC)
+
+struct isc_taskmgr {
+ /* Not locked. */
+ unsigned int magic;
+ isc_refcount_t references;
+ isc_mem_t *mctx;
+ isc_mutex_t lock;
+ atomic_uint_fast32_t tasks_count;
+ isc_nm_t *netmgr;
+
+ /* Locked by task manager lock. */
+ unsigned int default_quantum;
+ LIST(isc_task_t) tasks;
+ atomic_uint_fast32_t mode;
+ atomic_bool exclusive_req;
+ bool exiting;
+ isc_task_t *excl;
+};
+
+#define DEFAULT_DEFAULT_QUANTUM 25
+
+/*%
+ * The following are intended for internal use (indicated by "isc__"
+ * prefix) but are not declared as static, allowing direct access from
+ * unit tests etc.
+ */
+
+bool
+isc_task_purgeevent(isc_task_t *task, isc_event_t *event);
+void
+isc_taskmgr_setexcltask(isc_taskmgr_t *mgr, isc_task_t *task);
+isc_result_t
+isc_taskmgr_excltask(isc_taskmgr_t *mgr, isc_task_t **taskp);
+
+/***
+ *** Tasks.
+ ***/
+
+static void
+task_finished(isc_task_t *task) {
+ isc_taskmgr_t *manager = task->manager;
+ isc_mem_t *mctx = manager->mctx;
+ REQUIRE(EMPTY(task->events));
+ REQUIRE(task->nevents == 0);
+ REQUIRE(EMPTY(task->on_shutdown));
+ REQUIRE(task->state == task_state_done);
+
+ XTRACE("task_finished");
+
+ isc_refcount_destroy(&task->running);
+ isc_refcount_destroy(&task->references);
+
+ LOCK(&manager->lock);
+ UNLINK(manager->tasks, task, link);
+ atomic_fetch_sub(&manager->tasks_count, 1);
+ UNLOCK(&manager->lock);
+
+ isc_mutex_destroy(&task->lock);
+ task->magic = 0;
+ isc_mem_put(mctx, task, sizeof(*task));
+
+ isc_taskmgr_detach(&manager);
+}
+
+isc_result_t
+isc_task_create(isc_taskmgr_t *manager, unsigned int quantum,
+ isc_task_t **taskp) {
+ return (isc_task_create_bound(manager, quantum, taskp, -1));
+}
+
+isc_result_t
+isc_task_create_bound(isc_taskmgr_t *manager, unsigned int quantum,
+ isc_task_t **taskp, int threadid) {
+ isc_task_t *task = NULL;
+ bool exiting;
+
+ REQUIRE(VALID_MANAGER(manager));
+ REQUIRE(taskp != NULL && *taskp == NULL);
+
+ XTRACE("isc_task_create");
+
+ task = isc_mem_get(manager->mctx, sizeof(*task));
+ *task = (isc_task_t){ 0 };
+
+ isc_taskmgr_attach(manager, &task->manager);
+
+ if (threadid == -1) {
+ /*
+ * Task is not pinned to a queue, it's threadid will be
+ * chosen when first task will be sent to it - either
+ * randomly or specified by isc_task_sendto.
+ */
+ task->bound = false;
+ task->threadid = -1;
+ } else {
+ /*
+ * Task is pinned to a queue, it'll always be run
+ * by a specific thread.
+ */
+ task->bound = true;
+ task->threadid = threadid;
+ }
+
+ isc_mutex_init(&task->lock);
+ task->state = task_state_idle;
+
+ isc_refcount_init(&task->references, 1);
+ isc_refcount_init(&task->running, 0);
+ INIT_LIST(task->events);
+ INIT_LIST(task->on_shutdown);
+ task->nevents = 0;
+ task->quantum = (quantum > 0) ? quantum : manager->default_quantum;
+ atomic_init(&task->shuttingdown, false);
+ atomic_init(&task->privileged, false);
+ task->now = 0;
+ isc_time_settoepoch(&task->tnow);
+ memset(task->name, 0, sizeof(task->name));
+ task->tag = NULL;
+ INIT_LINK(task, link);
+ task->magic = TASK_MAGIC;
+
+ LOCK(&manager->lock);
+ exiting = manager->exiting;
+ if (!exiting) {
+ APPEND(manager->tasks, task, link);
+ atomic_fetch_add(&manager->tasks_count, 1);
+ }
+ UNLOCK(&manager->lock);
+
+ if (exiting) {
+ isc_refcount_destroy(&task->running);
+ isc_refcount_decrement(&task->references);
+ isc_refcount_destroy(&task->references);
+ isc_mutex_destroy(&task->lock);
+ isc_taskmgr_detach(&task->manager);
+ isc_mem_put(manager->mctx, task, sizeof(*task));
+ return (ISC_R_SHUTTINGDOWN);
+ }
+
+ *taskp = task;
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_task_attach(isc_task_t *source, isc_task_t **targetp) {
+ /*
+ * Attach *targetp to source.
+ */
+
+ REQUIRE(VALID_TASK(source));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ XTTRACE(source, "isc_task_attach");
+
+ isc_refcount_increment(&source->references);
+
+ *targetp = source;
+}
+
+static bool
+task_shutdown(isc_task_t *task) {
+ bool was_idle = false;
+ isc_event_t *event, *prev;
+
+ /*
+ * Caller must be holding the task's lock.
+ */
+
+ XTRACE("task_shutdown");
+
+ if (atomic_compare_exchange_strong(&task->shuttingdown,
+ &(bool){ false }, true))
+ {
+ XTRACE("shutting down");
+ if (task->state == task_state_idle) {
+ INSIST(EMPTY(task->events));
+ task->state = task_state_ready;
+ was_idle = true;
+ }
+ INSIST(task->state == task_state_ready ||
+ task->state == task_state_running);
+
+ /*
+ * Note that we post shutdown events LIFO.
+ */
+ for (event = TAIL(task->on_shutdown); event != NULL;
+ event = prev)
+ {
+ prev = PREV(event, ev_link);
+ DEQUEUE(task->on_shutdown, event, ev_link);
+ ENQUEUE(task->events, event, ev_link);
+ task->nevents++;
+ }
+ }
+
+ return (was_idle);
+}
+
+/*
+ * Moves a task onto the appropriate run queue.
+ *
+ * Caller must NOT hold queue lock.
+ */
+static void
+task_ready(isc_task_t *task) {
+ isc_taskmgr_t *manager = task->manager;
+ REQUIRE(VALID_MANAGER(manager));
+
+ XTRACE("task_ready");
+
+ isc_refcount_increment0(&task->running);
+ LOCK(&task->lock);
+ isc_nm_task_enqueue(manager->netmgr, task, task->threadid);
+ UNLOCK(&task->lock);
+}
+
+void
+isc_task_ready(isc_task_t *task) {
+ task_ready(task);
+}
+
+static bool
+task_detach(isc_task_t *task) {
+ /*
+ * Caller must be holding the task lock.
+ */
+
+ XTRACE("detach");
+
+ if (isc_refcount_decrement(&task->references) == 1 &&
+ task->state == task_state_idle)
+ {
+ INSIST(EMPTY(task->events));
+ /*
+ * There are no references to this task, and no
+ * pending events. We could try to optimize and
+ * either initiate shutdown or clean up the task,
+ * depending on its state, but it's easier to just
+ * make the task ready and allow run() or the event
+ * loop to deal with shutting down and termination.
+ */
+ task->state = task_state_ready;
+ return (true);
+ }
+
+ return (false);
+}
+
+void
+isc_task_detach(isc_task_t **taskp) {
+ isc_task_t *task;
+ bool was_idle;
+
+ /*
+ * Detach *taskp from its task.
+ */
+
+ REQUIRE(taskp != NULL);
+ task = *taskp;
+ REQUIRE(VALID_TASK(task));
+
+ XTRACE("isc_task_detach");
+
+ LOCK(&task->lock);
+ was_idle = task_detach(task);
+ UNLOCK(&task->lock);
+
+ if (was_idle) {
+ task_ready(task);
+ }
+
+ *taskp = NULL;
+}
+
+static bool
+task_send(isc_task_t *task, isc_event_t **eventp, int c) {
+ bool was_idle = false;
+ isc_event_t *event;
+
+ /*
+ * Caller must be holding the task lock.
+ */
+
+ REQUIRE(eventp != NULL);
+ event = *eventp;
+ *eventp = NULL;
+ REQUIRE(event != NULL);
+ REQUIRE(event->ev_type > 0);
+ REQUIRE(task->state != task_state_done);
+ REQUIRE(!ISC_LINK_LINKED(event, ev_ratelink));
+
+ XTRACE("task_send");
+
+ if (task->bound) {
+ c = task->threadid;
+ } else if (c < 0) {
+ c = -1;
+ }
+
+ if (task->state == task_state_idle) {
+ was_idle = true;
+ task->threadid = c;
+ INSIST(EMPTY(task->events));
+ task->state = task_state_ready;
+ }
+ INSIST(task->state == task_state_ready ||
+ task->state == task_state_running);
+ ENQUEUE(task->events, event, ev_link);
+ task->nevents++;
+
+ return (was_idle);
+}
+
+void
+isc_task_send(isc_task_t *task, isc_event_t **eventp) {
+ isc_task_sendto(task, eventp, -1);
+}
+
+void
+isc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) {
+ isc_task_sendtoanddetach(taskp, eventp, -1);
+}
+
+void
+isc_task_sendto(isc_task_t *task, isc_event_t **eventp, int c) {
+ bool was_idle;
+
+ /*
+ * Send '*event' to 'task'.
+ */
+
+ REQUIRE(VALID_TASK(task));
+ XTRACE("isc_task_send");
+
+ /*
+ * We're trying hard to hold locks for as short a time as possible.
+ * We're also trying to hold as few locks as possible. This is why
+ * some processing is deferred until after the lock is released.
+ */
+ LOCK(&task->lock);
+ was_idle = task_send(task, eventp, c);
+ UNLOCK(&task->lock);
+
+ if (was_idle) {
+ /*
+ * We need to add this task to the ready queue.
+ *
+ * We've waited until now to do it because making a task
+ * ready requires locking the manager. If we tried to do
+ * this while holding the task lock, we could deadlock.
+ *
+ * We've changed the state to ready, so no one else will
+ * be trying to add this task to the ready queue. The
+ * only way to leave the ready state is by executing the
+ * task. It thus doesn't matter if events are added,
+ * removed, or a shutdown is started in the interval
+ * between the time we released the task lock, and the time
+ * we add the task to the ready queue.
+ */
+ task_ready(task);
+ }
+}
+
+void
+isc_task_sendtoanddetach(isc_task_t **taskp, isc_event_t **eventp, int c) {
+ bool idle1, idle2;
+ isc_task_t *task;
+
+ /*
+ * Send '*event' to '*taskp' and then detach '*taskp' from its
+ * task.
+ */
+
+ REQUIRE(taskp != NULL);
+ task = *taskp;
+ REQUIRE(VALID_TASK(task));
+ XTRACE("isc_task_sendanddetach");
+
+ LOCK(&task->lock);
+ idle1 = task_send(task, eventp, c);
+ idle2 = task_detach(task);
+ UNLOCK(&task->lock);
+
+ /*
+ * If idle1, then idle2 shouldn't be true as well since we're holding
+ * the task lock, and thus the task cannot switch from ready back to
+ * idle.
+ */
+ INSIST(!(idle1 && idle2));
+
+ if (idle1 || idle2) {
+ task_ready(task);
+ }
+
+ *taskp = NULL;
+}
+
+#define PURGE_OK(event) (((event)->ev_attributes & ISC_EVENTATTR_NOPURGE) == 0)
+
+static unsigned int
+dequeue_events(isc_task_t *task, void *sender, isc_eventtype_t first,
+ isc_eventtype_t last, void *tag, isc_eventlist_t *events,
+ bool purging) {
+ isc_event_t *event, *next_event;
+ unsigned int count = 0;
+
+ REQUIRE(VALID_TASK(task));
+ REQUIRE(last >= first);
+
+ XTRACE("dequeue_events");
+
+ /*
+ * Events matching 'sender', whose type is >= first and <= last, and
+ * whose tag is 'tag' will be dequeued. If 'purging', matching events
+ * which are marked as unpurgable will not be dequeued.
+ *
+ * sender == NULL means "any sender", and tag == NULL means "any tag".
+ */
+
+ LOCK(&task->lock);
+
+ for (event = HEAD(task->events); event != NULL; event = next_event) {
+ next_event = NEXT(event, ev_link);
+ if (event->ev_type >= first && event->ev_type <= last &&
+ (sender == NULL || event->ev_sender == sender) &&
+ (tag == NULL || event->ev_tag == tag) &&
+ (!purging || PURGE_OK(event)))
+ {
+ DEQUEUE(task->events, event, ev_link);
+ task->nevents--;
+ ENQUEUE(*events, event, ev_link);
+ count++;
+ }
+ }
+
+ UNLOCK(&task->lock);
+
+ return (count);
+}
+
+unsigned int
+isc_task_purgerange(isc_task_t *task, void *sender, isc_eventtype_t first,
+ isc_eventtype_t last, void *tag) {
+ unsigned int count;
+ isc_eventlist_t events;
+ isc_event_t *event, *next_event;
+ REQUIRE(VALID_TASK(task));
+
+ /*
+ * Purge events from a task's event queue.
+ */
+
+ XTRACE("isc_task_purgerange");
+
+ ISC_LIST_INIT(events);
+
+ count = dequeue_events(task, sender, first, last, tag, &events, true);
+
+ for (event = HEAD(events); event != NULL; event = next_event) {
+ next_event = NEXT(event, ev_link);
+ ISC_LIST_UNLINK(events, event, ev_link);
+ isc_event_free(&event);
+ }
+
+ /*
+ * Note that purging never changes the state of the task.
+ */
+
+ return (count);
+}
+
+unsigned int
+isc_task_purge(isc_task_t *task, void *sender, isc_eventtype_t type,
+ void *tag) {
+ /*
+ * Purge events from a task's event queue.
+ */
+ REQUIRE(VALID_TASK(task));
+
+ XTRACE("isc_task_purge");
+
+ return (isc_task_purgerange(task, sender, type, type, tag));
+}
+
+bool
+isc_task_purgeevent(isc_task_t *task, isc_event_t *event) {
+ bool found = false;
+
+ /*
+ * Purge 'event' from a task's event queue.
+ */
+
+ REQUIRE(VALID_TASK(task));
+
+ /*
+ * If 'event' is on the task's event queue, it will be purged,
+ * unless it is marked as unpurgeable. 'event' does not have to be
+ * on the task's event queue; in fact, it can even be an invalid
+ * pointer. Purging only occurs if the event is actually on the task's
+ * event queue.
+ *
+ * Purging never changes the state of the task.
+ */
+
+ LOCK(&task->lock);
+ if (ISC_LINK_LINKED(event, ev_link)) {
+ DEQUEUE(task->events, event, ev_link);
+ task->nevents--;
+ found = true;
+ }
+ UNLOCK(&task->lock);
+
+ if (!found) {
+ return (false);
+ }
+
+ isc_event_free(&event);
+
+ return (true);
+}
+
+unsigned int
+isc_task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type, void *tag,
+ isc_eventlist_t *events) {
+ /*
+ * Remove events from a task's event queue.
+ */
+
+ XTRACE("isc_task_unsend");
+
+ return (dequeue_events(task, sender, type, type, tag, events, false));
+}
+
+isc_result_t
+isc_task_onshutdown(isc_task_t *task, isc_taskaction_t action, void *arg) {
+ bool disallowed = false;
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_event_t *event;
+
+ /*
+ * Send a shutdown event with action 'action' and argument 'arg' when
+ * 'task' is shutdown.
+ */
+
+ REQUIRE(VALID_TASK(task));
+ REQUIRE(action != NULL);
+
+ event = isc_event_allocate(task->manager->mctx, NULL,
+ ISC_TASKEVENT_SHUTDOWN, action, arg,
+ sizeof(*event));
+
+ if (TASK_SHUTTINGDOWN(task)) {
+ disallowed = true;
+ result = ISC_R_SHUTTINGDOWN;
+ } else {
+ LOCK(&task->lock);
+ ENQUEUE(task->on_shutdown, event, ev_link);
+ UNLOCK(&task->lock);
+ }
+
+ if (disallowed) {
+ isc_mem_put(task->manager->mctx, event, sizeof(*event));
+ }
+
+ return (result);
+}
+
+void
+isc_task_shutdown(isc_task_t *task) {
+ bool was_idle;
+
+ /*
+ * Shutdown 'task'.
+ */
+
+ REQUIRE(VALID_TASK(task));
+
+ LOCK(&task->lock);
+ was_idle = task_shutdown(task);
+ UNLOCK(&task->lock);
+
+ if (was_idle) {
+ task_ready(task);
+ }
+}
+
+void
+isc_task_destroy(isc_task_t **taskp) {
+ /*
+ * Destroy '*taskp'.
+ */
+
+ REQUIRE(taskp != NULL);
+
+ isc_task_shutdown(*taskp);
+ isc_task_detach(taskp);
+}
+
+void
+isc_task_setname(isc_task_t *task, const char *name, void *tag) {
+ /*
+ * Name 'task'.
+ */
+
+ REQUIRE(VALID_TASK(task));
+
+ LOCK(&task->lock);
+ strlcpy(task->name, name, sizeof(task->name));
+ task->tag = tag;
+ UNLOCK(&task->lock);
+}
+
+const char *
+isc_task_getname(isc_task_t *task) {
+ REQUIRE(VALID_TASK(task));
+
+ return (task->name);
+}
+
+void *
+isc_task_gettag(isc_task_t *task) {
+ REQUIRE(VALID_TASK(task));
+
+ return (task->tag);
+}
+
+isc_nm_t *
+isc_task_getnetmgr(isc_task_t *task) {
+ REQUIRE(VALID_TASK(task));
+
+ return (task->manager->netmgr);
+}
+
+void
+isc_task_setquantum(isc_task_t *task, unsigned int quantum) {
+ REQUIRE(VALID_TASK(task));
+
+ LOCK(&task->lock);
+ task->quantum = (quantum > 0) ? quantum
+ : task->manager->default_quantum;
+ UNLOCK(&task->lock);
+}
+
+/***
+ *** Task Manager.
+ ***/
+
+static isc_result_t
+task_run(isc_task_t *task) {
+ unsigned int dispatch_count = 0;
+ bool finished = false;
+ isc_event_t *event = NULL;
+ isc_result_t result = ISC_R_SUCCESS;
+ uint32_t quantum;
+
+ REQUIRE(VALID_TASK(task));
+
+ LOCK(&task->lock);
+ quantum = task->quantum;
+
+ if (task->state != task_state_ready) {
+ goto done;
+ }
+
+ INSIST(task->state == task_state_ready);
+ task->state = task_state_running;
+ XTRACE("running");
+ XTRACE(task->name);
+ TIME_NOW(&task->tnow);
+ task->now = isc_time_seconds(&task->tnow);
+
+ while (true) {
+ if (!EMPTY(task->events)) {
+ event = HEAD(task->events);
+ DEQUEUE(task->events, event, ev_link);
+ task->nevents--;
+
+ /*
+ * Execute the event action.
+ */
+ XTRACE("execute action");
+ XTRACE(task->name);
+ if (event->ev_action != NULL) {
+ UNLOCK(&task->lock);
+ (event->ev_action)(task, event);
+ LOCK(&task->lock);
+ }
+ XTRACE("execution complete");
+ dispatch_count++;
+ }
+
+ if (isc_refcount_current(&task->references) == 0 &&
+ EMPTY(task->events) && !TASK_SHUTTINGDOWN(task))
+ {
+ /*
+ * There are no references and no pending events for
+ * this task, which means it will not become runnable
+ * again via an external action (such as sending an
+ * event or detaching).
+ *
+ * We initiate shutdown to prevent it from becoming a
+ * zombie.
+ *
+ * We do this here instead of in the "if
+ * EMPTY(task->events)" block below because:
+ *
+ * If we post no shutdown events, we want the task
+ * to finish.
+ *
+ * If we did post shutdown events, will still want
+ * the task's quantum to be applied.
+ */
+ INSIST(!task_shutdown(task));
+ }
+
+ if (EMPTY(task->events)) {
+ /*
+ * Nothing else to do for this task right now.
+ */
+ XTRACE("empty");
+ if (isc_refcount_current(&task->references) == 0 &&
+ TASK_SHUTTINGDOWN(task))
+ {
+ /*
+ * The task is done.
+ */
+ XTRACE("done");
+ task->state = task_state_done;
+ } else if (task->state == task_state_running) {
+ XTRACE("idling");
+ task->state = task_state_idle;
+ }
+ break;
+ } else if (dispatch_count >= quantum) {
+ /*
+ * Our quantum has expired, but there is more work to be
+ * done. We'll requeue it to the ready queue later.
+ *
+ * We don't check quantum until dispatching at least one
+ * event, so the minimum quantum is one.
+ */
+ XTRACE("quantum");
+ task->state = task_state_ready;
+ result = ISC_R_QUOTA;
+ break;
+ }
+ }
+
+done:
+ if (isc_refcount_decrement(&task->running) == 1 &&
+ task->state == task_state_done)
+ {
+ finished = true;
+ }
+ UNLOCK(&task->lock);
+
+ if (finished) {
+ task_finished(task);
+ }
+
+ return (result);
+}
+
+isc_result_t
+isc_task_run(isc_task_t *task) {
+ return (task_run(task));
+}
+
+static void
+manager_free(isc_taskmgr_t *manager) {
+ isc_refcount_destroy(&manager->references);
+ isc_nm_detach(&manager->netmgr);
+
+ isc_mutex_destroy(&manager->lock);
+ manager->magic = 0;
+ isc_mem_putanddetach(&manager->mctx, manager, sizeof(*manager));
+}
+
+void
+isc_taskmgr_attach(isc_taskmgr_t *source, isc_taskmgr_t **targetp) {
+ REQUIRE(VALID_MANAGER(source));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ isc_refcount_increment(&source->references);
+
+ *targetp = source;
+}
+
+void
+isc_taskmgr_detach(isc_taskmgr_t **managerp) {
+ REQUIRE(managerp != NULL);
+ REQUIRE(VALID_MANAGER(*managerp));
+
+ isc_taskmgr_t *manager = *managerp;
+ *managerp = NULL;
+
+ if (isc_refcount_decrement(&manager->references) == 1) {
+ manager_free(manager);
+ }
+}
+
+isc_result_t
+isc__taskmgr_create(isc_mem_t *mctx, unsigned int default_quantum, isc_nm_t *nm,
+ isc_taskmgr_t **managerp) {
+ isc_taskmgr_t *manager;
+
+ /*
+ * Create a new task manager.
+ */
+
+ REQUIRE(managerp != NULL && *managerp == NULL);
+ REQUIRE(nm != NULL);
+
+ manager = isc_mem_get(mctx, sizeof(*manager));
+ *manager = (isc_taskmgr_t){ .magic = TASK_MANAGER_MAGIC };
+
+ isc_mutex_init(&manager->lock);
+
+ if (default_quantum == 0) {
+ default_quantum = DEFAULT_DEFAULT_QUANTUM;
+ }
+ manager->default_quantum = default_quantum;
+
+ if (nm != NULL) {
+ isc_nm_attach(nm, &manager->netmgr);
+ }
+
+ INIT_LIST(manager->tasks);
+ atomic_init(&manager->mode, isc_taskmgrmode_normal);
+ atomic_init(&manager->exclusive_req, false);
+ atomic_init(&manager->tasks_count, 0);
+
+ isc_mem_attach(mctx, &manager->mctx);
+
+ isc_refcount_init(&manager->references, 1);
+
+ *managerp = manager;
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc__taskmgr_shutdown(isc_taskmgr_t *manager) {
+ isc_task_t *task;
+
+ REQUIRE(VALID_MANAGER(manager));
+
+ XTHREADTRACE("isc_taskmgr_shutdown");
+ /*
+ * Only one non-worker thread may ever call this routine.
+ * If a worker thread wants to initiate shutdown of the
+ * task manager, it should ask some non-worker thread to call
+ * isc_taskmgr_destroy(), e.g. by signalling a condition variable
+ * that the startup thread is sleeping on.
+ */
+
+ /*
+ * Unlike elsewhere, we're going to hold this lock a long time.
+ * We need to do so, because otherwise the list of tasks could
+ * change while we were traversing it.
+ *
+ * This is also the only function where we will hold both the
+ * task manager lock and a task lock at the same time.
+ */
+ LOCK(&manager->lock);
+ if (manager->excl != NULL) {
+ isc_task_detach((isc_task_t **)&manager->excl);
+ }
+
+ /*
+ * Make sure we only get called once.
+ */
+ INSIST(manager->exiting == false);
+ manager->exiting = true;
+
+ /*
+ * Post shutdown event(s) to every task (if they haven't already been
+ * posted).
+ */
+ for (task = HEAD(manager->tasks); task != NULL; task = NEXT(task, link))
+ {
+ bool was_idle;
+
+ LOCK(&task->lock);
+ was_idle = task_shutdown(task);
+ UNLOCK(&task->lock);
+
+ if (was_idle) {
+ task_ready(task);
+ }
+ }
+
+ UNLOCK(&manager->lock);
+}
+
+void
+isc__taskmgr_destroy(isc_taskmgr_t **managerp) {
+ REQUIRE(managerp != NULL && VALID_MANAGER(*managerp));
+ XTHREADTRACE("isc_taskmgr_destroy");
+
+#ifdef ISC_TASK_TRACE
+ int counter = 0;
+ while (isc_refcount_current(&(*managerp)->references) > 1 &&
+ counter++ < 1000)
+ {
+ usleep(10 * 1000);
+ }
+ INSIST(counter < 1000);
+#else
+ while (isc_refcount_current(&(*managerp)->references) > 1) {
+ usleep(10 * 1000);
+ }
+#endif
+
+ isc_taskmgr_detach(managerp);
+}
+
+void
+isc_taskmgr_setexcltask(isc_taskmgr_t *mgr, isc_task_t *task) {
+ REQUIRE(VALID_MANAGER(mgr));
+ REQUIRE(VALID_TASK(task));
+
+ LOCK(&task->lock);
+ REQUIRE(task->threadid == 0);
+ UNLOCK(&task->lock);
+
+ LOCK(&mgr->lock);
+ if (mgr->excl != NULL) {
+ isc_task_detach(&mgr->excl);
+ }
+ isc_task_attach(task, &mgr->excl);
+ UNLOCK(&mgr->lock);
+}
+
+isc_result_t
+isc_taskmgr_excltask(isc_taskmgr_t *mgr, isc_task_t **taskp) {
+ isc_result_t result;
+
+ REQUIRE(VALID_MANAGER(mgr));
+ REQUIRE(taskp != NULL && *taskp == NULL);
+
+ LOCK(&mgr->lock);
+ if (mgr->excl != NULL) {
+ isc_task_attach(mgr->excl, taskp);
+ result = ISC_R_SUCCESS;
+ } else if (mgr->exiting) {
+ result = ISC_R_SHUTTINGDOWN;
+ } else {
+ result = ISC_R_NOTFOUND;
+ }
+ UNLOCK(&mgr->lock);
+
+ return (result);
+}
+
+isc_result_t
+isc_task_beginexclusive(isc_task_t *task) {
+ isc_taskmgr_t *manager;
+
+ REQUIRE(VALID_TASK(task));
+
+ manager = task->manager;
+
+ REQUIRE(task->state == task_state_running);
+
+ LOCK(&manager->lock);
+ REQUIRE(task == manager->excl ||
+ (manager->exiting && manager->excl == NULL));
+ UNLOCK(&manager->lock);
+
+ if (!atomic_compare_exchange_strong(&manager->exclusive_req,
+ &(bool){ false }, true))
+ {
+ return (ISC_R_LOCKBUSY);
+ }
+
+ if (isc_log_wouldlog(isc_lctx, ISC_LOG_DEBUG(1))) {
+ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_OTHER, ISC_LOG_DEBUG(1),
+ "exclusive task mode: %s", "starting");
+ }
+
+ isc_nm_pause(manager->netmgr);
+
+ if (isc_log_wouldlog(isc_lctx, ISC_LOG_DEBUG(1))) {
+ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_OTHER, ISC_LOG_DEBUG(1),
+ "exclusive task mode: %s", "started");
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_task_endexclusive(isc_task_t *task) {
+ isc_taskmgr_t *manager = NULL;
+
+ REQUIRE(VALID_TASK(task));
+ REQUIRE(task->state == task_state_running);
+
+ manager = task->manager;
+
+ if (isc_log_wouldlog(isc_lctx, ISC_LOG_DEBUG(1))) {
+ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_OTHER, ISC_LOG_DEBUG(1),
+ "exclusive task mode: %s", "ending");
+ }
+
+ isc_nm_resume(manager->netmgr);
+
+ if (isc_log_wouldlog(isc_lctx, ISC_LOG_DEBUG(1))) {
+ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_OTHER, ISC_LOG_DEBUG(1),
+ "exclusive task mode: %s", "ended");
+ }
+
+ atomic_compare_exchange_enforced(&manager->exclusive_req,
+ &(bool){ true }, false);
+}
+
+void
+isc_taskmgr_setmode(isc_taskmgr_t *manager, isc_taskmgrmode_t mode) {
+ atomic_store(&manager->mode, mode);
+}
+
+isc_taskmgrmode_t
+isc_taskmgr_mode(isc_taskmgr_t *manager) {
+ return (atomic_load(&manager->mode));
+}
+
+void
+isc_task_setprivilege(isc_task_t *task, bool priv) {
+ REQUIRE(VALID_TASK(task));
+
+ atomic_store_release(&task->privileged, priv);
+}
+
+bool
+isc_task_getprivilege(isc_task_t *task) {
+ REQUIRE(VALID_TASK(task));
+
+ return (TASK_PRIVILEGED(task));
+}
+
+bool
+isc_task_privileged(isc_task_t *task) {
+ REQUIRE(VALID_TASK(task));
+
+ return (isc_taskmgr_mode(task->manager) && TASK_PRIVILEGED(task));
+}
+
+bool
+isc_task_exiting(isc_task_t *task) {
+ REQUIRE(VALID_TASK(task));
+
+ return (TASK_SHUTTINGDOWN(task));
+}
+
+#ifdef HAVE_LIBXML2
+#define TRY0(a) \
+ do { \
+ xmlrc = (a); \
+ if (xmlrc < 0) \
+ goto error; \
+ } while (0)
+int
+isc_taskmgr_renderxml(isc_taskmgr_t *mgr, void *writer0) {
+ isc_task_t *task = NULL;
+ int xmlrc;
+ xmlTextWriterPtr writer = (xmlTextWriterPtr)writer0;
+
+ LOCK(&mgr->lock);
+
+ /*
+ * Write out the thread-model, and some details about each depending
+ * on which type is enabled.
+ */
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "thread-model"));
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "type"));
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "threaded"));
+ TRY0(xmlTextWriterEndElement(writer)); /* type */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "default-quantum"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%d",
+ mgr->default_quantum));
+ TRY0(xmlTextWriterEndElement(writer)); /* default-quantum */
+
+ TRY0(xmlTextWriterEndElement(writer)); /* thread-model */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "tasks"));
+ task = ISC_LIST_HEAD(mgr->tasks);
+ while (task != NULL) {
+ LOCK(&task->lock);
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "task"));
+
+ if (task->name[0] != 0) {
+ TRY0(xmlTextWriterStartElement(writer,
+ ISC_XMLCHAR "name"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%s",
+ task->name));
+ TRY0(xmlTextWriterEndElement(writer)); /* name */
+ }
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "reference"
+ "s"));
+ TRY0(xmlTextWriterWriteFormatString(
+ writer, "%" PRIuFAST32,
+ isc_refcount_current(&task->references)));
+ TRY0(xmlTextWriterEndElement(writer)); /* references */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "id"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%p", task));
+ TRY0(xmlTextWriterEndElement(writer)); /* id */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "state"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%s",
+ statenames[task->state]));
+ TRY0(xmlTextWriterEndElement(writer)); /* state */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "quantum"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%d",
+ task->quantum));
+ TRY0(xmlTextWriterEndElement(writer)); /* quantum */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "events"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%d",
+ task->nevents));
+ TRY0(xmlTextWriterEndElement(writer)); /* events */
+
+ TRY0(xmlTextWriterEndElement(writer));
+
+ UNLOCK(&task->lock);
+ task = ISC_LIST_NEXT(task, link);
+ }
+ TRY0(xmlTextWriterEndElement(writer)); /* tasks */
+
+error:
+ if (task != NULL) {
+ UNLOCK(&task->lock);
+ }
+ UNLOCK(&mgr->lock);
+
+ return (xmlrc);
+}
+#endif /* HAVE_LIBXML2 */
+
+#ifdef HAVE_JSON_C
+#define CHECKMEM(m) \
+ do { \
+ if (m == NULL) { \
+ result = ISC_R_NOMEMORY; \
+ goto error; \
+ } \
+ } while (0)
+
+isc_result_t
+isc_taskmgr_renderjson(isc_taskmgr_t *mgr, void *tasks0) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_task_t *task = NULL;
+ json_object *obj = NULL, *array = NULL, *taskobj = NULL;
+ json_object *tasks = (json_object *)tasks0;
+
+ LOCK(&mgr->lock);
+
+ /*
+ * Write out the thread-model, and some details about each depending
+ * on which type is enabled.
+ */
+ obj = json_object_new_string("threaded");
+ CHECKMEM(obj);
+ json_object_object_add(tasks, "thread-model", obj);
+
+ obj = json_object_new_int(mgr->default_quantum);
+ CHECKMEM(obj);
+ json_object_object_add(tasks, "default-quantum", obj);
+
+ array = json_object_new_array();
+ CHECKMEM(array);
+
+ for (task = ISC_LIST_HEAD(mgr->tasks); task != NULL;
+ task = ISC_LIST_NEXT(task, link))
+ {
+ char buf[255];
+
+ LOCK(&task->lock);
+
+ taskobj = json_object_new_object();
+ CHECKMEM(taskobj);
+ json_object_array_add(array, taskobj);
+
+ snprintf(buf, sizeof(buf), "%p", task);
+ obj = json_object_new_string(buf);
+ CHECKMEM(obj);
+ json_object_object_add(taskobj, "id", obj);
+
+ if (task->name[0] != 0) {
+ obj = json_object_new_string(task->name);
+ CHECKMEM(obj);
+ json_object_object_add(taskobj, "name", obj);
+ }
+
+ obj = json_object_new_int(
+ isc_refcount_current(&task->references));
+ CHECKMEM(obj);
+ json_object_object_add(taskobj, "references", obj);
+
+ obj = json_object_new_string(statenames[task->state]);
+ CHECKMEM(obj);
+ json_object_object_add(taskobj, "state", obj);
+
+ obj = json_object_new_int(task->quantum);
+ CHECKMEM(obj);
+ json_object_object_add(taskobj, "quantum", obj);
+
+ obj = json_object_new_int(task->nevents);
+ CHECKMEM(obj);
+ json_object_object_add(taskobj, "events", obj);
+
+ UNLOCK(&task->lock);
+ }
+
+ json_object_object_add(tasks, "tasks", array);
+ array = NULL;
+ result = ISC_R_SUCCESS;
+
+error:
+ if (array != NULL) {
+ json_object_put(array);
+ }
+
+ if (task != NULL) {
+ UNLOCK(&task->lock);
+ }
+ UNLOCK(&mgr->lock);
+
+ return (result);
+}
+#endif /* ifdef HAVE_JSON_C */
diff --git a/lib/isc/task_p.h b/lib/isc/task_p.h
new file mode 100644
index 0000000..5fc50b0
--- /dev/null
+++ b/lib/isc/task_p.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <isc/mem.h>
+#include <isc/result.h>
+#include <isc/task.h>
+
+isc_result_t
+isc__taskmgr_create(isc_mem_t *mctx, unsigned int default_quantum, isc_nm_t *nm,
+ isc_taskmgr_t **managerp);
+/*%<
+ * Create a new task manager.
+ *
+ * Notes:
+ *
+ *\li If 'default_quantum' is non-zero, then it will be used as the default
+ * quantum value when tasks are created. If zero, then an implementation
+ * defined default quantum will be used.
+ *
+ *\li If 'nm' is set then netmgr is paused when an exclusive task mode
+ * is requested.
+ *
+ * Requires:
+ *
+ *\li 'mctx' is a valid memory context.
+ *
+ *\li managerp != NULL && *managerp == NULL
+ *
+ * Ensures:
+ *
+ *\li On success, '*managerp' will be attached to the newly created task
+ * manager.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *\li #ISC_R_NOTHREADS No threads could be created.
+ *\li #ISC_R_UNEXPECTED An unexpected error occurred.
+ *\li #ISC_R_SHUTTINGDOWN The non-threaded, shared, task
+ * manager shutting down.
+ */
+
+void
+isc__taskmgr_destroy(isc_taskmgr_t **managerp);
+/*%<
+ * Destroy '*managerp'.
+ *
+ * Notes:
+ *
+ *\li Calling isc_taskmgr_destroy() will shutdown all tasks managed by
+ * *managerp that haven't already been shutdown. The call will block
+ * until all tasks have entered the done state.
+ *
+ *\li isc_taskmgr_destroy() must not be called by a task event action,
+ * because it would block forever waiting for the event action to
+ * complete. An event action that wants to cause task manager shutdown
+ * should request some non-event action thread of execution to do the
+ * shutdown, e.g. by signaling a condition variable or using
+ * isc_app_shutdown().
+ *
+ *\li Task manager references are not reference counted, so the caller
+ * must ensure that no attempt will be made to use the manager after
+ * isc_taskmgr_destroy() returns.
+ *
+ * Requires:
+ *
+ *\li '*managerp' is a valid task manager.
+ *
+ *\li 'isc__taskmgr_shutdown()' and isc__netmgr_shutdown() have been
+ * called.
+ */
+
+void
+isc__taskmgr_shutdown(isc_taskmgr_t *manager);
+/*%>
+ * Shutdown 'manager'.
+ *
+ * Notes:
+ *
+ *\li Calling isc__taskmgr_shutdown() will shut down all tasks managed by
+ * *managerp that haven't already been shut down.
+ *
+ * Requires:
+ *
+ *\li 'manager' is a valid task manager.
+ *
+ *\li isc_taskmgr_destroy() has not be called previously on '*managerp'.
+ *
+ * Ensures:
+ *
+ *\li All resources used by the task manager, and any tasks it managed,
+ * have been freed.
+ */
diff --git a/lib/isc/taskpool.c b/lib/isc/taskpool.c
new file mode 100644
index 0000000..3099532
--- /dev/null
+++ b/lib/isc/taskpool.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <stdbool.h>
+
+#include <isc/mem.h>
+#include <isc/random.h>
+#include <isc/taskpool.h>
+#include <isc/util.h>
+
+/***
+ *** Types.
+ ***/
+
+struct isc_taskpool {
+ isc_mem_t *mctx;
+ isc_taskmgr_t *tmgr;
+ unsigned int ntasks;
+ unsigned int quantum;
+ isc_task_t **tasks;
+};
+
+/***
+ *** Functions.
+ ***/
+
+static void
+alloc_pool(isc_taskmgr_t *tmgr, isc_mem_t *mctx, unsigned int ntasks,
+ unsigned int quantum, isc_taskpool_t **poolp) {
+ isc_taskpool_t *pool;
+ unsigned int i;
+
+ pool = isc_mem_get(mctx, sizeof(*pool));
+
+ pool->mctx = NULL;
+ isc_mem_attach(mctx, &pool->mctx);
+ pool->ntasks = ntasks;
+ pool->quantum = quantum;
+ pool->tmgr = tmgr;
+ pool->tasks = isc_mem_get(mctx, ntasks * sizeof(isc_task_t *));
+ for (i = 0; i < ntasks; i++) {
+ pool->tasks[i] = NULL;
+ }
+
+ *poolp = pool;
+}
+
+isc_result_t
+isc_taskpool_create(isc_taskmgr_t *tmgr, isc_mem_t *mctx, unsigned int ntasks,
+ unsigned int quantum, bool priv, isc_taskpool_t **poolp) {
+ unsigned int i;
+ isc_taskpool_t *pool = NULL;
+
+ INSIST(ntasks > 0);
+
+ /* Allocate the pool structure */
+ alloc_pool(tmgr, mctx, ntasks, quantum, &pool);
+
+ /* Create the tasks */
+ for (i = 0; i < ntasks; i++) {
+ isc_result_t result = isc_task_create_bound(tmgr, quantum,
+ &pool->tasks[i], i);
+ if (result != ISC_R_SUCCESS) {
+ isc_taskpool_destroy(&pool);
+ return (result);
+ }
+ isc_task_setprivilege(pool->tasks[i], priv);
+ isc_task_setname(pool->tasks[i], "taskpool", NULL);
+ }
+
+ *poolp = pool;
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_taskpool_gettask(isc_taskpool_t *pool, isc_task_t **targetp) {
+ isc_task_attach(pool->tasks[isc_random_uniform(pool->ntasks)], targetp);
+}
+
+int
+isc_taskpool_size(isc_taskpool_t *pool) {
+ REQUIRE(pool != NULL);
+ return (pool->ntasks);
+}
+
+isc_result_t
+isc_taskpool_expand(isc_taskpool_t **sourcep, unsigned int size, bool priv,
+ isc_taskpool_t **targetp) {
+ isc_taskpool_t *pool;
+
+ REQUIRE(sourcep != NULL && *sourcep != NULL);
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ pool = *sourcep;
+ *sourcep = NULL;
+ if (size > pool->ntasks) {
+ isc_taskpool_t *newpool = NULL;
+ unsigned int i;
+
+ /* Allocate a new pool structure */
+ alloc_pool(pool->tmgr, pool->mctx, size, pool->quantum,
+ &newpool);
+
+ /* Copy over the tasks from the old pool */
+ for (i = 0; i < pool->ntasks; i++) {
+ newpool->tasks[i] = pool->tasks[i];
+ pool->tasks[i] = NULL;
+ }
+
+ /* Create new tasks */
+ for (i = pool->ntasks; i < size; i++) {
+ isc_result_t result =
+ isc_task_create_bound(pool->tmgr, pool->quantum,
+ &newpool->tasks[i], i);
+ if (result != ISC_R_SUCCESS) {
+ *sourcep = pool;
+ isc_taskpool_destroy(&newpool);
+ return (result);
+ }
+ isc_task_setprivilege(newpool->tasks[i], priv);
+ isc_task_setname(newpool->tasks[i], "taskpool", NULL);
+ }
+
+ isc_taskpool_destroy(&pool);
+ pool = newpool;
+ }
+
+ *targetp = pool;
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_taskpool_destroy(isc_taskpool_t **poolp) {
+ unsigned int i;
+ isc_taskpool_t *pool = *poolp;
+ *poolp = NULL;
+ for (i = 0; i < pool->ntasks; i++) {
+ if (pool->tasks[i] != NULL) {
+ isc_task_detach(&pool->tasks[i]);
+ }
+ }
+ isc_mem_put(pool->mctx, pool->tasks,
+ pool->ntasks * sizeof(isc_task_t *));
+ isc_mem_putanddetach(&pool->mctx, pool, sizeof(*pool));
+}
diff --git a/lib/isc/thread.c b/lib/isc/thread.c
new file mode 100644
index 0000000..5b762ed
--- /dev/null
+++ b/lib/isc/thread.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#if defined(HAVE_SCHED_H)
+#include <sched.h>
+#endif /* if defined(HAVE_SCHED_H) */
+
+#if defined(HAVE_CPUSET_H)
+#include <sys/cpuset.h>
+#include <sys/param.h>
+#endif /* if defined(HAVE_CPUSET_H) */
+
+#if defined(HAVE_SYS_PROCSET_H)
+#include <sys/processor.h>
+#include <sys/procset.h>
+#include <sys/types.h>
+#endif /* if defined(HAVE_SYS_PROCSET_H) */
+
+#include <isc/thread.h>
+#include <isc/util.h>
+
+#include "trampoline_p.h"
+
+#ifndef THREAD_MINSTACKSIZE
+#define THREAD_MINSTACKSIZE (1024U * 1024)
+#endif /* ifndef THREAD_MINSTACKSIZE */
+
+void
+isc_thread_create(isc_threadfunc_t func, isc_threadarg_t arg,
+ isc_thread_t *thread) {
+ pthread_attr_t attr;
+ isc__trampoline_t *trampoline_arg;
+
+ trampoline_arg = isc__trampoline_get(func, arg);
+
+#if defined(HAVE_PTHREAD_ATTR_GETSTACKSIZE) && \
+ defined(HAVE_PTHREAD_ATTR_SETSTACKSIZE)
+ size_t stacksize;
+#endif /* if defined(HAVE_PTHREAD_ATTR_GETSTACKSIZE) && \
+ * defined(HAVE_PTHREAD_ATTR_SETSTACKSIZE) */
+ int ret;
+
+ pthread_attr_init(&attr);
+
+#if defined(HAVE_PTHREAD_ATTR_GETSTACKSIZE) && \
+ defined(HAVE_PTHREAD_ATTR_SETSTACKSIZE)
+ ret = pthread_attr_getstacksize(&attr, &stacksize);
+ if (ret != 0) {
+ FATAL_SYSERROR(ret, "pthread_attr_getstacksize()");
+ }
+
+ if (stacksize < THREAD_MINSTACKSIZE) {
+ ret = pthread_attr_setstacksize(&attr, THREAD_MINSTACKSIZE);
+ if (ret != 0) {
+ FATAL_SYSERROR(ret, "pthread_attr_setstacksize()");
+ }
+ }
+#endif /* if defined(HAVE_PTHREAD_ATTR_GETSTACKSIZE) && \
+ * defined(HAVE_PTHREAD_ATTR_SETSTACKSIZE) */
+
+ ret = pthread_create(thread, &attr, isc__trampoline_run,
+ trampoline_arg);
+ if (ret != 0) {
+ FATAL_SYSERROR(ret, "pthread_create()");
+ }
+
+ pthread_attr_destroy(&attr);
+
+ return;
+}
+
+void
+isc_thread_join(isc_thread_t thread, isc_threadresult_t *result) {
+ int ret = pthread_join(thread, result);
+ if (ret != 0) {
+ FATAL_SYSERROR(ret, "pthread_join()");
+ }
+}
+
+void
+isc_thread_setname(isc_thread_t thread, const char *name) {
+#if defined(HAVE_PTHREAD_SETNAME_NP) && !defined(__APPLE__)
+ /*
+ * macOS has pthread_setname_np but only works on the
+ * current thread so it's not used here
+ */
+#if defined(__NetBSD__)
+ (void)pthread_setname_np(thread, name, NULL);
+#else /* if defined(__NetBSD__) */
+ (void)pthread_setname_np(thread, name);
+#endif /* if defined(__NetBSD__) */
+#elif defined(HAVE_PTHREAD_SET_NAME_NP)
+ (void)pthread_set_name_np(thread, name);
+#else /* if defined(HAVE_PTHREAD_SETNAME_NP) && !defined(__APPLE__) */
+ UNUSED(thread);
+ UNUSED(name);
+#endif /* if defined(HAVE_PTHREAD_SETNAME_NP) && !defined(__APPLE__) */
+}
+
+void
+isc_thread_yield(void) {
+#if defined(HAVE_SCHED_YIELD)
+ sched_yield();
+#elif defined(HAVE_PTHREAD_YIELD)
+ pthread_yield();
+#elif defined(HAVE_PTHREAD_YIELD_NP)
+ pthread_yield_np();
+#endif /* if defined(HAVE_SCHED_YIELD) */
+}
diff --git a/lib/isc/time.c b/lib/isc/time.c
new file mode 100644
index 0000000..b03f377
--- /dev/null
+++ b/lib/isc/time.c
@@ -0,0 +1,563 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/time.h> /* Required for struct timeval on some platforms. */
+#include <syslog.h>
+#include <time.h>
+
+#include <isc/log.h>
+#include <isc/print.h>
+#include <isc/string.h>
+#include <isc/time.h>
+#include <isc/tm.h>
+#include <isc/util.h>
+
+#if defined(CLOCK_REALTIME)
+#define CLOCKSOURCE_HIRES CLOCK_REALTIME
+#endif /* #if defined(CLOCK_REALTIME) */
+
+#if defined(CLOCK_REALTIME_COARSE)
+#define CLOCKSOURCE CLOCK_REALTIME_COARSE
+#elif defined(CLOCK_REALTIME_FAST)
+#define CLOCKSOURCE CLOCK_REALTIME_FAST
+#else /* if defined(CLOCK_REALTIME_COARSE) */
+#define CLOCKSOURCE CLOCK_REALTIME
+#endif /* if defined(CLOCK_REALTIME_COARSE) */
+
+#if !defined(CLOCKSOURCE_HIRES)
+#define CLOCKSOURCE_HIRES CLOCKSOURCE
+#endif /* #ifndef CLOCKSOURCE_HIRES */
+
+/*%
+ *** Intervals
+ ***/
+
+#if !defined(UNIT_TESTING)
+static const isc_interval_t zero_interval = { 0, 0 };
+const isc_interval_t *const isc_interval_zero = &zero_interval;
+#endif
+
+void
+isc_interval_set(isc_interval_t *i, unsigned int seconds,
+ unsigned int nanoseconds) {
+ REQUIRE(i != NULL);
+ REQUIRE(nanoseconds < NS_PER_SEC);
+
+ i->seconds = seconds;
+ i->nanoseconds = nanoseconds;
+}
+
+bool
+isc_interval_iszero(const isc_interval_t *i) {
+ REQUIRE(i != NULL);
+ INSIST(i->nanoseconds < NS_PER_SEC);
+
+ if (i->seconds == 0 && i->nanoseconds == 0) {
+ return (true);
+ }
+
+ return (false);
+}
+
+unsigned int
+isc_interval_ms(const isc_interval_t *i) {
+ REQUIRE(i != NULL);
+ INSIST(i->nanoseconds < NS_PER_SEC);
+
+ return ((i->seconds * MS_PER_SEC) + (i->nanoseconds / NS_PER_MS));
+}
+
+/***
+ *** Absolute Times
+ ***/
+
+#if !defined(UNIT_TESTING)
+static const isc_time_t epoch = { 0, 0 };
+const isc_time_t *const isc_time_epoch = &epoch;
+#endif
+
+void
+isc_time_set(isc_time_t *t, unsigned int seconds, unsigned int nanoseconds) {
+ REQUIRE(t != NULL);
+ REQUIRE(nanoseconds < NS_PER_SEC);
+
+ t->seconds = seconds;
+ t->nanoseconds = nanoseconds;
+}
+
+void
+isc_time_settoepoch(isc_time_t *t) {
+ REQUIRE(t != NULL);
+
+ t->seconds = 0;
+ t->nanoseconds = 0;
+}
+
+bool
+isc_time_isepoch(const isc_time_t *t) {
+ REQUIRE(t != NULL);
+ INSIST(t->nanoseconds < NS_PER_SEC);
+
+ if (t->seconds == 0 && t->nanoseconds == 0) {
+ return (true);
+ }
+
+ return (false);
+}
+
+static isc_result_t
+time_now(isc_time_t *t, clockid_t clock) {
+ struct timespec ts;
+
+ REQUIRE(t != NULL);
+
+ if (clock_gettime(clock, &ts) == -1) {
+ UNEXPECTED_SYSERROR(errno, "clock_gettime()");
+ return (ISC_R_UNEXPECTED);
+ }
+
+ if (ts.tv_sec < 0 || ts.tv_nsec < 0 || ts.tv_nsec >= NS_PER_SEC) {
+ return (ISC_R_UNEXPECTED);
+ }
+
+ /*
+ * Ensure the tv_sec value fits in t->seconds.
+ */
+ if (sizeof(ts.tv_sec) > sizeof(t->seconds) &&
+ ((ts.tv_sec | (unsigned int)-1) ^ (unsigned int)-1) != 0U)
+ {
+ return (ISC_R_RANGE);
+ }
+
+ t->seconds = ts.tv_sec;
+ t->nanoseconds = ts.tv_nsec;
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_time_now_hires(isc_time_t *t) {
+ return time_now(t, CLOCKSOURCE_HIRES);
+}
+
+isc_result_t
+isc_time_now(isc_time_t *t) {
+ return time_now(t, CLOCKSOURCE);
+}
+
+isc_result_t
+isc_time_nowplusinterval(isc_time_t *t, const isc_interval_t *i) {
+ struct timespec ts;
+
+ REQUIRE(t != NULL);
+ REQUIRE(i != NULL);
+ INSIST(i->nanoseconds < NS_PER_SEC);
+
+ if (clock_gettime(CLOCKSOURCE, &ts) == -1) {
+ UNEXPECTED_SYSERROR(errno, "clock_gettime()");
+ return (ISC_R_UNEXPECTED);
+ }
+
+ if (ts.tv_sec < 0 || ts.tv_nsec < 0 || ts.tv_nsec >= NS_PER_SEC) {
+ return (ISC_R_UNEXPECTED);
+ }
+
+ /*
+ * Ensure the resulting seconds value fits in the size of an
+ * unsigned int. (It is written this way as a slight optimization;
+ * note that even if both values == INT_MAX, then when added
+ * and getting another 1 added below the result is UINT_MAX.)
+ */
+ if ((ts.tv_sec > INT_MAX || i->seconds > INT_MAX) &&
+ ((long long)ts.tv_sec + i->seconds > UINT_MAX))
+ {
+ return (ISC_R_RANGE);
+ }
+
+ t->seconds = ts.tv_sec + i->seconds;
+ t->nanoseconds = ts.tv_nsec + i->nanoseconds;
+ if (t->nanoseconds >= NS_PER_SEC) {
+ t->seconds++;
+ t->nanoseconds -= NS_PER_SEC;
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+int
+isc_time_compare(const isc_time_t *t1, const isc_time_t *t2) {
+ REQUIRE(t1 != NULL && t2 != NULL);
+ INSIST(t1->nanoseconds < NS_PER_SEC && t2->nanoseconds < NS_PER_SEC);
+
+ if (t1->seconds < t2->seconds) {
+ return (-1);
+ }
+ if (t1->seconds > t2->seconds) {
+ return (1);
+ }
+ if (t1->nanoseconds < t2->nanoseconds) {
+ return (-1);
+ }
+ if (t1->nanoseconds > t2->nanoseconds) {
+ return (1);
+ }
+ return (0);
+}
+
+isc_result_t
+isc_time_add(const isc_time_t *t, const isc_interval_t *i, isc_time_t *result) {
+ REQUIRE(t != NULL && i != NULL && result != NULL);
+ REQUIRE(t->nanoseconds < NS_PER_SEC && i->nanoseconds < NS_PER_SEC);
+
+ /* Seconds */
+#if HAVE_BUILTIN_OVERFLOW
+ if (__builtin_uadd_overflow(t->seconds, i->seconds, &result->seconds)) {
+ return (ISC_R_RANGE);
+ }
+#else
+ if (t->seconds > UINT_MAX - i->seconds) {
+ return (ISC_R_RANGE);
+ }
+ result->seconds = t->seconds + i->seconds;
+#endif
+
+ /* Nanoseconds */
+ result->nanoseconds = t->nanoseconds + i->nanoseconds;
+ if (result->nanoseconds >= NS_PER_SEC) {
+ if (result->seconds == UINT_MAX) {
+ return (ISC_R_RANGE);
+ }
+ result->nanoseconds -= NS_PER_SEC;
+ result->seconds++;
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_time_subtract(const isc_time_t *t, const isc_interval_t *i,
+ isc_time_t *result) {
+ REQUIRE(t != NULL && i != NULL && result != NULL);
+ REQUIRE(t->nanoseconds < NS_PER_SEC && i->nanoseconds < NS_PER_SEC);
+
+ /* Seconds */
+#if HAVE_BUILTIN_OVERFLOW
+ if (__builtin_usub_overflow(t->seconds, i->seconds, &result->seconds)) {
+ return (ISC_R_RANGE);
+ }
+#else
+ if (t->seconds < i->seconds) {
+ return (ISC_R_RANGE);
+ }
+ result->seconds = t->seconds - i->seconds;
+#endif
+
+ /* Nanoseconds */
+ if (t->nanoseconds >= i->nanoseconds) {
+ result->nanoseconds = t->nanoseconds - i->nanoseconds;
+ } else {
+ if (result->seconds == 0) {
+ return (ISC_R_RANGE);
+ }
+ result->seconds--;
+ result->nanoseconds = NS_PER_SEC + t->nanoseconds -
+ i->nanoseconds;
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+uint64_t
+isc_time_microdiff(const isc_time_t *t1, const isc_time_t *t2) {
+ uint64_t i1, i2, i3;
+
+ REQUIRE(t1 != NULL && t2 != NULL);
+ INSIST(t1->nanoseconds < NS_PER_SEC && t2->nanoseconds < NS_PER_SEC);
+
+ i1 = (uint64_t)t1->seconds * NS_PER_SEC + t1->nanoseconds;
+ i2 = (uint64_t)t2->seconds * NS_PER_SEC + t2->nanoseconds;
+
+ if (i1 <= i2) {
+ return (0);
+ }
+
+ i3 = i1 - i2;
+
+ /*
+ * Convert to microseconds.
+ */
+ i3 /= NS_PER_US;
+
+ return (i3);
+}
+
+uint32_t
+isc_time_seconds(const isc_time_t *t) {
+ REQUIRE(t != NULL);
+ INSIST(t->nanoseconds < NS_PER_SEC);
+
+ return ((uint32_t)t->seconds);
+}
+
+isc_result_t
+isc_time_secondsastimet(const isc_time_t *t, time_t *secondsp) {
+ time_t seconds;
+
+ REQUIRE(t != NULL);
+ INSIST(t->nanoseconds < NS_PER_SEC);
+
+ /*
+ * Ensure that the number of seconds represented by t->seconds
+ * can be represented by a time_t. Since t->seconds is an
+ * unsigned int and since time_t is mostly opaque, this is
+ * trickier than it seems. (This standardized opaqueness of
+ * time_t is *very* frustrating; time_t is not even limited to
+ * being an integral type.)
+ *
+ * The mission, then, is to avoid generating any kind of warning
+ * about "signed versus unsigned" while trying to determine if
+ * the unsigned int t->seconds is out range for tv_sec,
+ * which is pretty much only true if time_t is a signed integer
+ * of the same size as the return value of isc_time_seconds.
+ *
+ * If the paradox in the if clause below is true, t->seconds is
+ * out of range for time_t.
+ */
+ seconds = (time_t)t->seconds;
+
+ INSIST(sizeof(unsigned int) == sizeof(uint32_t));
+ INSIST(sizeof(time_t) >= sizeof(uint32_t));
+
+ if (t->seconds > (~0U >> 1) && seconds <= (time_t)(~0U >> 1)) {
+ return (ISC_R_RANGE);
+ }
+
+ *secondsp = seconds;
+
+ return (ISC_R_SUCCESS);
+}
+
+uint32_t
+isc_time_nanoseconds(const isc_time_t *t) {
+ REQUIRE(t != NULL);
+
+ ENSURE(t->nanoseconds < NS_PER_SEC);
+
+ return ((uint32_t)t->nanoseconds);
+}
+
+void
+isc_time_formattimestamp(const isc_time_t *t, char *buf, unsigned int len) {
+ time_t now;
+ unsigned int flen;
+ struct tm tm;
+
+ REQUIRE(t != NULL);
+ INSIST(t->nanoseconds < NS_PER_SEC);
+ REQUIRE(buf != NULL);
+ REQUIRE(len > 0);
+
+ now = (time_t)t->seconds;
+ flen = strftime(buf, len, "%d-%b-%Y %X", localtime_r(&now, &tm));
+ INSIST(flen < len);
+ if (flen != 0) {
+ snprintf(buf + flen, len - flen, ".%03u",
+ t->nanoseconds / NS_PER_MS);
+ } else {
+ strlcpy(buf, "99-Bad-9999 99:99:99.999", len);
+ }
+}
+
+void
+isc_time_formathttptimestamp(const isc_time_t *t, char *buf, unsigned int len) {
+ time_t now;
+ unsigned int flen;
+ struct tm tm;
+
+ REQUIRE(t != NULL);
+ INSIST(t->nanoseconds < NS_PER_SEC);
+ REQUIRE(buf != NULL);
+ REQUIRE(len > 0);
+
+ /*
+ * 5 spaces, 1 comma, 3 GMT, 2 %d, 4 %Y, 8 %H:%M:%S, 3+ %a, 3+
+ * %b (29+)
+ */
+ now = (time_t)t->seconds;
+ flen = strftime(buf, len, "%a, %d %b %Y %H:%M:%S GMT",
+ gmtime_r(&now, &tm));
+ INSIST(flen < len);
+}
+
+isc_result_t
+isc_time_parsehttptimestamp(char *buf, isc_time_t *t) {
+ struct tm t_tm;
+ time_t when;
+ char *p;
+
+ REQUIRE(buf != NULL);
+ REQUIRE(t != NULL);
+
+ p = isc_tm_strptime(buf, "%a, %d %b %Y %H:%M:%S", &t_tm);
+ if (p == NULL) {
+ return (ISC_R_UNEXPECTED);
+ }
+ when = isc_tm_timegm(&t_tm);
+ if (when == -1) {
+ return (ISC_R_UNEXPECTED);
+ }
+ isc_time_set(t, when, 0);
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_time_formatISO8601L(const isc_time_t *t, char *buf, unsigned int len) {
+ time_t now;
+ unsigned int flen;
+ struct tm tm;
+
+ REQUIRE(t != NULL);
+ INSIST(t->nanoseconds < NS_PER_SEC);
+ REQUIRE(buf != NULL);
+ REQUIRE(len > 0);
+
+ now = (time_t)t->seconds;
+ flen = strftime(buf, len, "%Y-%m-%dT%H:%M:%S", localtime_r(&now, &tm));
+ INSIST(flen < len);
+}
+
+void
+isc_time_formatISO8601Lms(const isc_time_t *t, char *buf, unsigned int len) {
+ time_t now;
+ unsigned int flen;
+ struct tm tm;
+
+ REQUIRE(t != NULL);
+ INSIST(t->nanoseconds < NS_PER_SEC);
+ REQUIRE(buf != NULL);
+ REQUIRE(len > 0);
+
+ now = (time_t)t->seconds;
+ flen = strftime(buf, len, "%Y-%m-%dT%H:%M:%S", localtime_r(&now, &tm));
+ INSIST(flen < len);
+ if (flen > 0U && len - flen >= 6) {
+ snprintf(buf + flen, len - flen, ".%03u",
+ t->nanoseconds / NS_PER_MS);
+ }
+}
+
+void
+isc_time_formatISO8601Lus(const isc_time_t *t, char *buf, unsigned int len) {
+ time_t now;
+ unsigned int flen;
+ struct tm tm;
+
+ REQUIRE(t != NULL);
+ INSIST(t->nanoseconds < NS_PER_SEC);
+ REQUIRE(buf != NULL);
+ REQUIRE(len > 0);
+
+ now = (time_t)t->seconds;
+ flen = strftime(buf, len, "%Y-%m-%dT%H:%M:%S", localtime_r(&now, &tm));
+ INSIST(flen < len);
+ if (flen > 0U && len - flen >= 6) {
+ snprintf(buf + flen, len - flen, ".%06u",
+ t->nanoseconds / NS_PER_US);
+ }
+}
+
+void
+isc_time_formatISO8601(const isc_time_t *t, char *buf, unsigned int len) {
+ time_t now;
+ unsigned int flen;
+ struct tm tm;
+
+ REQUIRE(t != NULL);
+ INSIST(t->nanoseconds < NS_PER_SEC);
+ REQUIRE(buf != NULL);
+ REQUIRE(len > 0);
+
+ now = (time_t)t->seconds;
+ flen = strftime(buf, len, "%Y-%m-%dT%H:%M:%SZ", gmtime_r(&now, &tm));
+ INSIST(flen < len);
+}
+
+void
+isc_time_formatISO8601ms(const isc_time_t *t, char *buf, unsigned int len) {
+ time_t now;
+ unsigned int flen;
+ struct tm tm;
+
+ REQUIRE(t != NULL);
+ INSIST(t->nanoseconds < NS_PER_SEC);
+ REQUIRE(buf != NULL);
+ REQUIRE(len > 0);
+
+ now = (time_t)t->seconds;
+ flen = strftime(buf, len, "%Y-%m-%dT%H:%M:%SZ", gmtime_r(&now, &tm));
+ INSIST(flen < len);
+ if (flen > 0U && len - flen >= 5) {
+ flen -= 1; /* rewind one character (Z) */
+ snprintf(buf + flen, len - flen, ".%03uZ",
+ t->nanoseconds / NS_PER_MS);
+ }
+}
+
+void
+isc_time_formatISO8601us(const isc_time_t *t, char *buf, unsigned int len) {
+ time_t now;
+ unsigned int flen;
+ struct tm tm;
+
+ REQUIRE(t != NULL);
+ INSIST(t->nanoseconds < NS_PER_SEC);
+ REQUIRE(buf != NULL);
+ REQUIRE(len > 0);
+
+ now = (time_t)t->seconds;
+ flen = strftime(buf, len, "%Y-%m-%dT%H:%M:%SZ", gmtime_r(&now, &tm));
+ INSIST(flen < len);
+ if (flen > 0U && len - flen >= 5) {
+ flen -= 1; /* rewind one character (Z) */
+ snprintf(buf + flen, len - flen, ".%06uZ",
+ t->nanoseconds / NS_PER_US);
+ }
+}
+
+void
+isc_time_formatshorttimestamp(const isc_time_t *t, char *buf,
+ unsigned int len) {
+ time_t now;
+ unsigned int flen;
+ struct tm tm;
+
+ REQUIRE(t != NULL);
+ INSIST(t->nanoseconds < NS_PER_SEC);
+ REQUIRE(buf != NULL);
+ REQUIRE(len > 0);
+
+ now = (time_t)t->seconds;
+ flen = strftime(buf, len, "%Y%m%d%H%M%S", gmtime_r(&now, &tm));
+ INSIST(flen < len);
+ if (flen > 0U && len - flen >= 5) {
+ snprintf(buf + flen, len - flen, "%03u",
+ t->nanoseconds / NS_PER_MS);
+ }
+}
diff --git a/lib/isc/timer.c b/lib/isc/timer.c
new file mode 100644
index 0000000..40ae929
--- /dev/null
+++ b/lib/isc/timer.c
@@ -0,0 +1,741 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <stdbool.h>
+
+#include <isc/app.h>
+#include <isc/condition.h>
+#include <isc/heap.h>
+#include <isc/log.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/once.h>
+#include <isc/print.h>
+#include <isc/refcount.h>
+#include <isc/result.h>
+#include <isc/task.h>
+#include <isc/thread.h>
+#include <isc/time.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+#include "timer_p.h"
+
+#ifdef ISC_TIMER_TRACE
+#define XTRACE(s) fprintf(stderr, "%s\n", (s))
+#define XTRACEID(s, t) fprintf(stderr, "%s %p\n", (s), (t))
+#define XTRACETIME(s, d) \
+ fprintf(stderr, "%s %u.%09u\n", (s), (d).seconds, (d).nanoseconds)
+#define XTRACETIME2(s, d, n) \
+ fprintf(stderr, "%s %u.%09u %u.%09u\n", (s), (d).seconds, \
+ (d).nanoseconds, (n).seconds, (n).nanoseconds)
+#define XTRACETIMER(s, t, d) \
+ fprintf(stderr, "%s %p %u.%09u\n", (s), (t), (d).seconds, \
+ (d).nanoseconds)
+#else /* ifdef ISC_TIMER_TRACE */
+#define XTRACE(s)
+#define XTRACEID(s, t)
+#define XTRACETIME(s, d)
+#define XTRACETIME2(s, d, n)
+#define XTRACETIMER(s, t, d)
+#endif /* ISC_TIMER_TRACE */
+
+#define TIMER_MAGIC ISC_MAGIC('T', 'I', 'M', 'R')
+#define VALID_TIMER(t) ISC_MAGIC_VALID(t, TIMER_MAGIC)
+
+struct isc_timer {
+ /*! Not locked. */
+ unsigned int magic;
+ isc_timermgr_t *manager;
+ isc_mutex_t lock;
+ /*! Locked by timer lock. */
+ isc_time_t idle;
+ ISC_LIST(isc_timerevent_t) active;
+ /*! Locked by manager lock. */
+ isc_timertype_t type;
+ isc_time_t expires;
+ isc_interval_t interval;
+ isc_task_t *task;
+ isc_taskaction_t action;
+ void *arg;
+ unsigned int index;
+ isc_time_t due;
+ LINK(isc_timer_t) link;
+};
+
+#define TIMER_MANAGER_MAGIC ISC_MAGIC('T', 'I', 'M', 'M')
+#define VALID_MANAGER(m) ISC_MAGIC_VALID(m, TIMER_MANAGER_MAGIC)
+
+struct isc_timermgr {
+ /* Not locked. */
+ unsigned int magic;
+ isc_mem_t *mctx;
+ isc_mutex_t lock;
+ /* Locked by manager lock. */
+ bool done;
+ LIST(isc_timer_t) timers;
+ unsigned int nscheduled;
+ isc_time_t due;
+ isc_condition_t wakeup;
+ isc_thread_t thread;
+ isc_heap_t *heap;
+};
+
+void
+isc_timermgr_poke(isc_timermgr_t *manager);
+
+static inline isc_result_t
+schedule(isc_timer_t *timer, isc_time_t *now, bool signal_ok) {
+ isc_timermgr_t *manager;
+ isc_time_t due;
+
+ /*!
+ * Note: the caller must ensure locking.
+ */
+
+ REQUIRE(timer->type != isc_timertype_inactive);
+
+ manager = timer->manager;
+
+ /*
+ * Compute the new due time.
+ */
+ if (timer->type != isc_timertype_once) {
+ isc_result_t result = isc_time_add(now, &timer->interval, &due);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ if (timer->type == isc_timertype_limited &&
+ isc_time_compare(&timer->expires, &due) < 0)
+ {
+ due = timer->expires;
+ }
+ } else {
+ if (isc_time_isepoch(&timer->idle)) {
+ due = timer->expires;
+ } else if (isc_time_isepoch(&timer->expires)) {
+ due = timer->idle;
+ } else if (isc_time_compare(&timer->idle, &timer->expires) < 0)
+ {
+ due = timer->idle;
+ } else {
+ due = timer->expires;
+ }
+ }
+
+ /*
+ * Schedule the timer.
+ */
+
+ if (timer->index > 0) {
+ /*
+ * Already scheduled.
+ */
+ int cmp = isc_time_compare(&due, &timer->due);
+ timer->due = due;
+ switch (cmp) {
+ case -1:
+ isc_heap_increased(manager->heap, timer->index);
+ break;
+ case 1:
+ isc_heap_decreased(manager->heap, timer->index);
+ break;
+ case 0:
+ /* Nothing to do. */
+ break;
+ }
+ } else {
+ timer->due = due;
+ isc_heap_insert(manager->heap, timer);
+ manager->nscheduled++;
+ }
+
+ XTRACETIMER("schedule", timer, due);
+
+ /*
+ * If this timer is at the head of the queue, we need to ensure
+ * that we won't miss it if it has a more recent due time than
+ * the current "next" timer. We do this either by waking up the
+ * run thread, or explicitly setting the value in the manager.
+ */
+
+ if (timer->index == 1 && signal_ok) {
+ XTRACE("signal (schedule)");
+ SIGNAL(&manager->wakeup);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+deschedule(isc_timer_t *timer) {
+ isc_timermgr_t *manager;
+
+ /*
+ * The caller must ensure locking.
+ */
+
+ manager = timer->manager;
+ if (timer->index > 0) {
+ bool need_wakeup = false;
+ if (timer->index == 1) {
+ need_wakeup = true;
+ }
+ isc_heap_delete(manager->heap, timer->index);
+ timer->index = 0;
+ INSIST(manager->nscheduled > 0);
+ manager->nscheduled--;
+ if (need_wakeup) {
+ XTRACE("signal (deschedule)");
+ SIGNAL(&manager->wakeup);
+ }
+ }
+}
+
+static void
+timerevent_unlink(isc_timer_t *timer, isc_timerevent_t *event) {
+ REQUIRE(ISC_LINK_LINKED(event, ev_timerlink));
+ ISC_LIST_UNLINK(timer->active, event, ev_timerlink);
+}
+
+static void
+timerevent_destroy(isc_event_t *event0) {
+ isc_timer_t *timer = event0->ev_destroy_arg;
+ isc_timerevent_t *event = (isc_timerevent_t *)event0;
+
+ LOCK(&timer->lock);
+ if (ISC_LINK_LINKED(event, ev_timerlink)) {
+ /* The event was unlinked via timer_purge() */
+ timerevent_unlink(timer, event);
+ }
+ UNLOCK(&timer->lock);
+
+ isc_mem_put(timer->manager->mctx, event, event0->ev_size);
+}
+
+static void
+timer_purge(isc_timer_t *timer) {
+ isc_timerevent_t *event = NULL;
+
+ while ((event = ISC_LIST_HEAD(timer->active)) != NULL) {
+ timerevent_unlink(timer, event);
+ UNLOCK(&timer->lock);
+ (void)isc_task_purgeevent(timer->task, (isc_event_t *)event);
+ LOCK(&timer->lock);
+ }
+}
+
+isc_result_t
+isc_timer_create(isc_timermgr_t *manager, isc_timertype_t type,
+ const isc_time_t *expires, const isc_interval_t *interval,
+ isc_task_t *task, isc_taskaction_t action, void *arg,
+ isc_timer_t **timerp) {
+ REQUIRE(VALID_MANAGER(manager));
+ REQUIRE(task != NULL);
+ REQUIRE(action != NULL);
+
+ isc_timer_t *timer;
+ isc_result_t result;
+ isc_time_t now;
+
+ /*
+ * Create a new 'type' timer managed by 'manager'. The timers
+ * parameters are specified by 'expires' and 'interval'. Events
+ * will be posted to 'task' and when dispatched 'action' will be
+ * called with 'arg' as the arg value. The new timer is returned
+ * in 'timerp'.
+ */
+ if (expires == NULL) {
+ expires = isc_time_epoch;
+ }
+ if (interval == NULL) {
+ interval = isc_interval_zero;
+ }
+ REQUIRE(type == isc_timertype_inactive ||
+ !(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
+ REQUIRE(timerp != NULL && *timerp == NULL);
+ REQUIRE(type != isc_timertype_limited ||
+ !(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
+
+ /*
+ * Get current time.
+ */
+ if (type != isc_timertype_inactive) {
+ TIME_NOW(&now);
+ } else {
+ /*
+ * We don't have to do this, but it keeps the compiler from
+ * complaining about "now" possibly being used without being
+ * set, even though it will never actually happen.
+ */
+ isc_time_settoepoch(&now);
+ }
+
+ timer = isc_mem_get(manager->mctx, sizeof(*timer));
+
+ timer->manager = manager;
+
+ if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
+ result = isc_time_add(&now, interval, &timer->idle);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(manager->mctx, timer, sizeof(*timer));
+ return (result);
+ }
+ } else {
+ isc_time_settoepoch(&timer->idle);
+ }
+
+ timer->type = type;
+ timer->expires = *expires;
+ timer->interval = *interval;
+ timer->task = NULL;
+ isc_task_attach(task, &timer->task);
+ timer->action = action;
+ /*
+ * Removing the const attribute from "arg" is the best of two
+ * evils here. If the timer->arg member is made const, then
+ * it affects a great many recipients of the timer event
+ * which did not pass in an "arg" that was truly const.
+ * Changing isc_timer_create() to not have "arg" prototyped as const,
+ * though, can cause compilers warnings for calls that *do*
+ * have a truly const arg. The caller will have to carefully
+ * keep track of whether arg started as a true const.
+ */
+ DE_CONST(arg, timer->arg);
+ timer->index = 0;
+ isc_mutex_init(&timer->lock);
+ ISC_LINK_INIT(timer, link);
+
+ ISC_LIST_INIT(timer->active);
+
+ timer->magic = TIMER_MAGIC;
+
+ LOCK(&manager->lock);
+
+ /*
+ * Note we don't have to lock the timer like we normally would because
+ * there are no external references to it yet.
+ */
+
+ if (type != isc_timertype_inactive) {
+ result = schedule(timer, &now, true);
+ } else {
+ result = ISC_R_SUCCESS;
+ }
+ if (result == ISC_R_SUCCESS) {
+ *timerp = timer;
+ APPEND(manager->timers, timer, link);
+ }
+
+ UNLOCK(&manager->lock);
+
+ if (result != ISC_R_SUCCESS) {
+ timer->magic = 0;
+ isc_mutex_destroy(&timer->lock);
+ isc_task_detach(&timer->task);
+ isc_mem_put(manager->mctx, timer, sizeof(*timer));
+ return (result);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_timer_reset(isc_timer_t *timer, isc_timertype_t type,
+ const isc_time_t *expires, const isc_interval_t *interval,
+ bool purge) {
+ isc_time_t now;
+ isc_timermgr_t *manager;
+ isc_result_t result;
+
+ /*
+ * Change the timer's type, expires, and interval values to the given
+ * values. If 'purge' is true, any pending events from this timer
+ * are purged from its task's event queue.
+ */
+
+ REQUIRE(VALID_TIMER(timer));
+ manager = timer->manager;
+ REQUIRE(VALID_MANAGER(manager));
+
+ if (expires == NULL) {
+ expires = isc_time_epoch;
+ }
+ if (interval == NULL) {
+ interval = isc_interval_zero;
+ }
+ REQUIRE(type == isc_timertype_inactive ||
+ !(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
+ REQUIRE(type != isc_timertype_limited ||
+ !(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
+
+ /*
+ * Get current time.
+ */
+ if (type != isc_timertype_inactive) {
+ TIME_NOW(&now);
+ } else {
+ /*
+ * We don't have to do this, but it keeps the compiler from
+ * complaining about "now" possibly being used without being
+ * set, even though it will never actually happen.
+ */
+ isc_time_settoepoch(&now);
+ }
+
+ LOCK(&manager->lock);
+ LOCK(&timer->lock);
+
+ if (purge) {
+ timer_purge(timer);
+ }
+ timer->type = type;
+ timer->expires = *expires;
+ timer->interval = *interval;
+ if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
+ result = isc_time_add(&now, interval, &timer->idle);
+ } else {
+ isc_time_settoepoch(&timer->idle);
+ result = ISC_R_SUCCESS;
+ }
+
+ if (result == ISC_R_SUCCESS) {
+ if (type == isc_timertype_inactive) {
+ deschedule(timer);
+ result = ISC_R_SUCCESS;
+ } else {
+ result = schedule(timer, &now, true);
+ }
+ }
+
+ UNLOCK(&timer->lock);
+ UNLOCK(&manager->lock);
+
+ return (result);
+}
+
+isc_timertype_t
+isc_timer_gettype(isc_timer_t *timer) {
+ isc_timertype_t t;
+
+ REQUIRE(VALID_TIMER(timer));
+
+ LOCK(&timer->lock);
+ t = timer->type;
+ UNLOCK(&timer->lock);
+
+ return (t);
+}
+
+isc_result_t
+isc_timer_touch(isc_timer_t *timer) {
+ isc_result_t result;
+ isc_time_t now;
+
+ /*
+ * Set the last-touched time of 'timer' to the current time.
+ */
+
+ REQUIRE(VALID_TIMER(timer));
+
+ LOCK(&timer->lock);
+
+ /*
+ * We'd like to
+ *
+ * REQUIRE(timer->type == isc_timertype_once);
+ *
+ * but we cannot without locking the manager lock too, which we
+ * don't want to do.
+ */
+
+ TIME_NOW(&now);
+ result = isc_time_add(&now, &timer->interval, &timer->idle);
+
+ UNLOCK(&timer->lock);
+
+ return (result);
+}
+
+void
+isc_timer_destroy(isc_timer_t **timerp) {
+ isc_timer_t *timer = NULL;
+ isc_timermgr_t *manager = NULL;
+
+ REQUIRE(timerp != NULL && VALID_TIMER(*timerp));
+
+ timer = *timerp;
+ *timerp = NULL;
+
+ manager = timer->manager;
+
+ LOCK(&manager->lock);
+
+ LOCK(&timer->lock);
+ timer_purge(timer);
+ deschedule(timer);
+ UNLOCK(&timer->lock);
+
+ UNLINK(manager->timers, timer, link);
+
+ UNLOCK(&manager->lock);
+
+ isc_task_detach(&timer->task);
+ isc_mutex_destroy(&timer->lock);
+ timer->magic = 0;
+ isc_mem_put(manager->mctx, timer, sizeof(*timer));
+}
+
+static void
+timer_post_event(isc_timermgr_t *manager, isc_timer_t *timer,
+ isc_eventtype_t type) {
+ isc_timerevent_t *event;
+ XTRACEID("posting", timer);
+
+ event = (isc_timerevent_t *)isc_event_allocate(
+ manager->mctx, timer, type, timer->action, timer->arg,
+ sizeof(*event));
+
+ ISC_LINK_INIT(event, ev_timerlink);
+ ((isc_event_t *)event)->ev_destroy = timerevent_destroy;
+ ((isc_event_t *)event)->ev_destroy_arg = timer;
+
+ event->due = timer->due;
+
+ LOCK(&timer->lock);
+ ISC_LIST_APPEND(timer->active, event, ev_timerlink);
+ UNLOCK(&timer->lock);
+
+ isc_task_send(timer->task, ISC_EVENT_PTR(&event));
+}
+
+static void
+dispatch(isc_timermgr_t *manager, isc_time_t *now) {
+ bool done = false, post_event, need_schedule;
+ isc_eventtype_t type = 0;
+ isc_timer_t *timer;
+ isc_result_t result;
+ bool idle;
+
+ /*!
+ * The caller must be holding the manager lock.
+ */
+
+ while (manager->nscheduled > 0 && !done) {
+ timer = isc_heap_element(manager->heap, 1);
+ INSIST(timer != NULL && timer->type != isc_timertype_inactive);
+ if (isc_time_compare(now, &timer->due) >= 0) {
+ if (timer->type == isc_timertype_ticker) {
+ type = ISC_TIMEREVENT_TICK;
+ post_event = true;
+ need_schedule = true;
+ } else if (timer->type == isc_timertype_limited) {
+ int cmp;
+ cmp = isc_time_compare(now, &timer->expires);
+ if (cmp >= 0) {
+ type = ISC_TIMEREVENT_LIFE;
+ post_event = true;
+ need_schedule = false;
+ } else {
+ type = ISC_TIMEREVENT_TICK;
+ post_event = true;
+ need_schedule = true;
+ }
+ } else if (!isc_time_isepoch(&timer->expires) &&
+ isc_time_compare(now, &timer->expires) >= 0)
+ {
+ type = ISC_TIMEREVENT_LIFE;
+ post_event = true;
+ need_schedule = false;
+ } else {
+ idle = false;
+
+ LOCK(&timer->lock);
+ if (!isc_time_isepoch(&timer->idle) &&
+ isc_time_compare(now, &timer->idle) >= 0)
+ {
+ idle = true;
+ }
+ UNLOCK(&timer->lock);
+ if (idle) {
+ type = ISC_TIMEREVENT_IDLE;
+ post_event = true;
+ need_schedule = false;
+ } else {
+ /*
+ * Idle timer has been touched;
+ * reschedule.
+ */
+ XTRACEID("idle reschedule", timer);
+ post_event = false;
+ need_schedule = true;
+ }
+ }
+
+ if (post_event) {
+ timer_post_event(manager, timer, type);
+ }
+
+ timer->index = 0;
+ isc_heap_delete(manager->heap, 1);
+ manager->nscheduled--;
+
+ if (need_schedule) {
+ result = schedule(timer, now, false);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(
+ "couldn't schedule timer: %s",
+ isc_result_totext(result));
+ }
+ }
+ } else {
+ manager->due = timer->due;
+ done = true;
+ }
+ }
+}
+
+static isc_threadresult_t
+run(void *uap) {
+ isc_timermgr_t *manager = uap;
+ isc_time_t now;
+ isc_result_t result;
+
+ LOCK(&manager->lock);
+ while (!manager->done) {
+ TIME_NOW(&now);
+
+ XTRACETIME("running", now);
+
+ dispatch(manager, &now);
+
+ if (manager->nscheduled > 0) {
+ XTRACETIME2("waituntil", manager->due, now);
+ result = WAITUNTIL(&manager->wakeup, &manager->lock,
+ &manager->due);
+ INSIST(result == ISC_R_SUCCESS ||
+ result == ISC_R_TIMEDOUT);
+ } else {
+ XTRACETIME("wait", now);
+ WAIT(&manager->wakeup, &manager->lock);
+ }
+ XTRACE("wakeup");
+ }
+ UNLOCK(&manager->lock);
+
+ return ((isc_threadresult_t)0);
+}
+
+static bool
+sooner(void *v1, void *v2) {
+ isc_timer_t *t1, *t2;
+
+ t1 = v1;
+ t2 = v2;
+ REQUIRE(VALID_TIMER(t1));
+ REQUIRE(VALID_TIMER(t2));
+
+ if (isc_time_compare(&t1->due, &t2->due) < 0) {
+ return (true);
+ }
+ return (false);
+}
+
+static void
+set_index(void *what, unsigned int index) {
+ isc_timer_t *timer;
+
+ REQUIRE(VALID_TIMER(what));
+ timer = what;
+
+ timer->index = index;
+}
+
+isc_result_t
+isc__timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp) {
+ isc_timermgr_t *manager;
+
+ /*
+ * Create a timer manager.
+ */
+
+ REQUIRE(managerp != NULL && *managerp == NULL);
+
+ manager = isc_mem_get(mctx, sizeof(*manager));
+
+ manager->magic = TIMER_MANAGER_MAGIC;
+ manager->mctx = NULL;
+ manager->done = false;
+ INIT_LIST(manager->timers);
+ manager->nscheduled = 0;
+ isc_time_settoepoch(&manager->due);
+ manager->heap = NULL;
+ isc_heap_create(mctx, sooner, set_index, 0, &manager->heap);
+ isc_mutex_init(&manager->lock);
+ isc_mem_attach(mctx, &manager->mctx);
+ isc_condition_init(&manager->wakeup);
+ isc_thread_create(run, manager, &manager->thread);
+ isc_thread_setname(manager->thread, "isc-timer");
+
+ *managerp = manager;
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc_timermgr_poke(isc_timermgr_t *manager) {
+ REQUIRE(VALID_MANAGER(manager));
+
+ SIGNAL(&manager->wakeup);
+}
+
+void
+isc__timermgr_destroy(isc_timermgr_t **managerp) {
+ isc_timermgr_t *manager;
+
+ /*
+ * Destroy a timer manager.
+ */
+
+ REQUIRE(managerp != NULL);
+ manager = *managerp;
+ REQUIRE(VALID_MANAGER(manager));
+
+ LOCK(&manager->lock);
+
+ REQUIRE(EMPTY(manager->timers));
+ manager->done = true;
+
+ XTRACE("signal (destroy)");
+ SIGNAL(&manager->wakeup);
+
+ UNLOCK(&manager->lock);
+
+ /*
+ * Wait for thread to exit.
+ */
+ isc_thread_join(manager->thread, NULL);
+
+ /*
+ * Clean up.
+ */
+ (void)isc_condition_destroy(&manager->wakeup);
+ isc_mutex_destroy(&manager->lock);
+ isc_heap_destroy(&manager->heap);
+ manager->magic = 0;
+ isc_mem_putanddetach(&manager->mctx, manager, sizeof(*manager));
+
+ *managerp = NULL;
+}
diff --git a/lib/isc/timer_p.h b/lib/isc/timer_p.h
new file mode 100644
index 0000000..ab9bf9c
--- /dev/null
+++ b/lib/isc/timer_p.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <isc/mem.h>
+#include <isc/result.h>
+#include <isc/timer.h>
+
+isc_result_t
+isc__timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp);
+/*%<
+ * Create a timer manager.
+ *
+ * Notes:
+ *
+ *\li All memory will be allocated in memory context 'mctx'.
+ *
+ * Requires:
+ *
+ *\li 'mctx' is a valid memory context.
+ *
+ *\li 'managerp' points to a NULL isc_timermgr_t.
+ *
+ * Ensures:
+ *
+ *\li '*managerp' is a valid isc_timermgr_t.
+ *
+ * Returns:
+ *
+ *\li Success
+ *\li No memory
+ *\li Unexpected error
+ */
+
+void
+isc__timermgr_destroy(isc_timermgr_t **managerp);
+/*%<
+ * Destroy a timer manager.
+ *
+ * Notes:
+ *
+ *\li This routine blocks until there are no timers left in the manager,
+ * so if the caller holds any timer references using the manager, it
+ * must detach them before calling isc_timermgr_destroy() or it will
+ * block forever.
+ *
+ * Requires:
+ *
+ *\li '*managerp' is a valid isc_timermgr_t.
+ *
+ * Ensures:
+ *
+ *\li *managerp == NULL
+ *
+ *\li All resources used by the manager have been freed.
+ */
diff --git a/lib/isc/tls.c b/lib/isc/tls.c
new file mode 100644
index 0000000..e2cd63e
--- /dev/null
+++ b/lib/isc/tls.c
@@ -0,0 +1,1678 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <inttypes.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#if HAVE_LIBNGHTTP2
+#include <nghttp2/nghttp2.h>
+#endif /* HAVE_LIBNGHTTP2 */
+#include <arpa/inet.h>
+
+#include <openssl/bn.h>
+#include <openssl/conf.h>
+#include <openssl/crypto.h>
+#include <openssl/dh.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/opensslv.h>
+#include <openssl/rand.h>
+#include <openssl/rsa.h>
+#include <openssl/x509_vfy.h>
+#include <openssl/x509v3.h>
+
+#include <isc/atomic.h>
+#include <isc/ht.h>
+#include <isc/log.h>
+#include <isc/magic.h>
+#include <isc/mutex.h>
+#include <isc/mutexblock.h>
+#include <isc/once.h>
+#include <isc/random.h>
+#include <isc/refcount.h>
+#include <isc/rwlock.h>
+#include <isc/sockaddr.h>
+#include <isc/thread.h>
+#include <isc/tls.h>
+#include <isc/util.h>
+
+#include "openssl_shim.h"
+#include "tls_p.h"
+
+#define COMMON_SSL_OPTIONS \
+ (SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION)
+
+static isc_once_t init_once = ISC_ONCE_INIT;
+static isc_once_t shut_once = ISC_ONCE_INIT;
+static atomic_bool init_done = false;
+static atomic_bool shut_done = false;
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+static isc_mutex_t *locks = NULL;
+static int nlocks;
+
+static void
+isc__tls_lock_callback(int mode, int type, const char *file, int line) {
+ UNUSED(file);
+ UNUSED(line);
+ if ((mode & CRYPTO_LOCK) != 0) {
+ LOCK(&locks[type]);
+ } else {
+ UNLOCK(&locks[type]);
+ }
+}
+
+static void
+isc__tls_set_thread_id(CRYPTO_THREADID *id) {
+ CRYPTO_THREADID_set_numeric(id, (unsigned long)isc_thread_self());
+}
+#endif
+
+static void
+tls_initialize(void) {
+ REQUIRE(!atomic_load(&init_done));
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ RUNTIME_CHECK(OPENSSL_init_ssl(OPENSSL_INIT_ENGINE_ALL_BUILTIN |
+ OPENSSL_INIT_LOAD_CONFIG,
+ NULL) == 1);
+#else
+ nlocks = CRYPTO_num_locks();
+ /*
+ * We can't use isc_mem API here, because it's called too
+ * early and when the isc_mem_debugging flags are changed
+ * later.
+ *
+ * Actually, since this is a single allocation at library load
+ * and deallocation at library unload, using the standard
+ * allocator without the tracking is fine for this purpose.
+ */
+ locks = calloc(nlocks, sizeof(locks[0]));
+ isc_mutexblock_init(locks, nlocks);
+ CRYPTO_set_locking_callback(isc__tls_lock_callback);
+ CRYPTO_THREADID_set_callback(isc__tls_set_thread_id);
+
+ CRYPTO_malloc_init();
+ ERR_load_crypto_strings();
+ SSL_load_error_strings();
+ SSL_library_init();
+
+#if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000
+ ENGINE_load_builtin_engines();
+#endif
+ OpenSSL_add_all_algorithms();
+ OPENSSL_load_builtin_modules();
+
+ CONF_modules_load_file(NULL, NULL,
+ CONF_MFLAGS_DEFAULT_SECTION |
+ CONF_MFLAGS_IGNORE_MISSING_FILE);
+#endif
+
+ /* Protect ourselves against unseeded PRNG */
+ if (RAND_status() != 1) {
+ FATAL_ERROR("OpenSSL pseudorandom number generator "
+ "cannot be initialized (see the `PRNG not "
+ "seeded' message in the OpenSSL FAQ)");
+ }
+
+ atomic_compare_exchange_enforced(&init_done, &(bool){ false }, true);
+}
+
+void
+isc__tls_initialize(void) {
+ isc_result_t result = isc_once_do(&init_once, tls_initialize);
+ REQUIRE(result == ISC_R_SUCCESS);
+ REQUIRE(atomic_load(&init_done));
+}
+
+static void
+tls_shutdown(void) {
+ REQUIRE(atomic_load(&init_done));
+ REQUIRE(!atomic_load(&shut_done));
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ OPENSSL_cleanup();
+#else
+ CONF_modules_unload(1);
+ OBJ_cleanup();
+ EVP_cleanup();
+#if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000
+ ENGINE_cleanup();
+#endif
+ CRYPTO_cleanup_all_ex_data();
+ ERR_remove_thread_state(NULL);
+ RAND_cleanup();
+ ERR_free_strings();
+
+ CRYPTO_set_locking_callback(NULL);
+
+ if (locks != NULL) {
+ isc_mutexblock_destroy(locks, nlocks);
+ free(locks);
+ locks = NULL;
+ }
+#endif
+
+ atomic_compare_exchange_enforced(&shut_done, &(bool){ false }, true);
+}
+
+void
+isc__tls_shutdown(void) {
+ isc_result_t result = isc_once_do(&shut_once, tls_shutdown);
+ REQUIRE(result == ISC_R_SUCCESS);
+ REQUIRE(atomic_load(&shut_done));
+}
+
+void
+isc_tlsctx_free(isc_tlsctx_t **ctxp) {
+ SSL_CTX *ctx = NULL;
+ REQUIRE(ctxp != NULL && *ctxp != NULL);
+
+ ctx = *ctxp;
+ *ctxp = NULL;
+
+ SSL_CTX_free(ctx);
+}
+
+void
+isc_tlsctx_attach(isc_tlsctx_t *src, isc_tlsctx_t **ptarget) {
+ REQUIRE(src != NULL);
+ REQUIRE(ptarget != NULL && *ptarget == NULL);
+
+ RUNTIME_CHECK(SSL_CTX_up_ref(src) == 1);
+
+ *ptarget = src;
+}
+
+#if HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
+/*
+ * Callback invoked by the SSL library whenever a new TLS pre-master secret
+ * needs to be logged.
+ */
+static void
+sslkeylogfile_append(const SSL *ssl, const char *line) {
+ UNUSED(ssl);
+
+ isc_log_write(isc_lctx, ISC_LOGCATEGORY_SSLKEYLOG, ISC_LOGMODULE_NETMGR,
+ ISC_LOG_INFO, "%s", line);
+}
+
+/*
+ * Enable TLS pre-master secret logging if the SSLKEYLOGFILE environment
+ * variable is set. This needs to be done on a per-context basis as that is
+ * how SSL_CTX_set_keylog_callback() works.
+ */
+static void
+sslkeylogfile_init(isc_tlsctx_t *ctx) {
+ if (getenv("SSLKEYLOGFILE") != NULL) {
+ SSL_CTX_set_keylog_callback(ctx, sslkeylogfile_append);
+ }
+}
+#else /* HAVE_SSL_CTX_SET_KEYLOG_CALLBACK */
+#define sslkeylogfile_init(ctx)
+#endif /* HAVE_SSL_CTX_SET_KEYLOG_CALLBACK */
+
+isc_result_t
+isc_tlsctx_createclient(isc_tlsctx_t **ctxp) {
+ unsigned long err;
+ char errbuf[256];
+ SSL_CTX *ctx = NULL;
+ const SSL_METHOD *method = NULL;
+
+ REQUIRE(ctxp != NULL && *ctxp == NULL);
+
+ method = TLS_client_method();
+ if (method == NULL) {
+ goto ssl_error;
+ }
+ ctx = SSL_CTX_new(method);
+ if (ctx == NULL) {
+ goto ssl_error;
+ }
+
+ SSL_CTX_set_options(ctx, COMMON_SSL_OPTIONS);
+
+#if HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
+ SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
+#else
+ SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
+ SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);
+#endif
+
+ sslkeylogfile_init(ctx);
+
+ *ctxp = ctx;
+
+ return (ISC_R_SUCCESS);
+
+ssl_error:
+ err = ERR_get_error();
+ ERR_error_string_n(err, errbuf, sizeof(errbuf));
+ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_NETMGR,
+ ISC_LOG_ERROR, "Error initializing TLS context: %s",
+ errbuf);
+
+ return (ISC_R_TLSERROR);
+}
+
+isc_result_t
+isc_tlsctx_load_certificate(isc_tlsctx_t *ctx, const char *keyfile,
+ const char *certfile) {
+ int rv;
+ REQUIRE(ctx != NULL);
+ REQUIRE(keyfile != NULL);
+ REQUIRE(certfile != NULL);
+
+ rv = SSL_CTX_use_certificate_chain_file(ctx, certfile);
+ if (rv != 1) {
+ return (ISC_R_TLSERROR);
+ }
+ rv = SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM);
+ if (rv != 1) {
+ return (ISC_R_TLSERROR);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_tlsctx_createserver(const char *keyfile, const char *certfile,
+ isc_tlsctx_t **ctxp) {
+ int rv;
+ unsigned long err;
+ bool ephemeral = (keyfile == NULL && certfile == NULL);
+ X509 *cert = NULL;
+ EVP_PKEY *pkey = NULL;
+ SSL_CTX *ctx = NULL;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ EC_KEY *eckey = NULL;
+#else
+ EVP_PKEY_CTX *pkey_ctx = NULL;
+ EVP_PKEY *params_pkey = NULL;
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
+ char errbuf[256];
+ const SSL_METHOD *method = NULL;
+
+ REQUIRE(ctxp != NULL && *ctxp == NULL);
+ REQUIRE((keyfile == NULL) == (certfile == NULL));
+
+ method = TLS_server_method();
+ if (method == NULL) {
+ goto ssl_error;
+ }
+ ctx = SSL_CTX_new(method);
+ if (ctx == NULL) {
+ goto ssl_error;
+ }
+ RUNTIME_CHECK(ctx != NULL);
+
+ SSL_CTX_set_options(ctx, COMMON_SSL_OPTIONS);
+
+#if HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
+ SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
+#else
+ SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
+ SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);
+#endif
+
+ if (ephemeral) {
+ const int group_nid = NID_X9_62_prime256v1;
+
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ eckey = EC_KEY_new_by_curve_name(group_nid);
+ if (eckey == NULL) {
+ goto ssl_error;
+ }
+
+ /* Generate the key. */
+ rv = EC_KEY_generate_key(eckey);
+ if (rv != 1) {
+ goto ssl_error;
+ }
+ pkey = EVP_PKEY_new();
+ if (pkey == NULL) {
+ goto ssl_error;
+ }
+ rv = EVP_PKEY_set1_EC_KEY(pkey, eckey);
+ if (rv != 1) {
+ goto ssl_error;
+ }
+
+ /* Use a named curve and uncompressed point conversion form. */
+#if HAVE_EVP_PKEY_GET0_EC_KEY
+ EC_KEY_set_asn1_flag(EVP_PKEY_get0_EC_KEY(pkey),
+ OPENSSL_EC_NAMED_CURVE);
+ EC_KEY_set_conv_form(EVP_PKEY_get0_EC_KEY(pkey),
+ POINT_CONVERSION_UNCOMPRESSED);
+#else
+ EC_KEY_set_asn1_flag(pkey->pkey.ec, OPENSSL_EC_NAMED_CURVE);
+ EC_KEY_set_conv_form(pkey->pkey.ec,
+ POINT_CONVERSION_UNCOMPRESSED);
+#endif /* HAVE_EVP_PKEY_GET0_EC_KEY */
+
+#if defined(SSL_CTX_set_ecdh_auto)
+ /*
+ * Using this macro is required for older versions of OpenSSL to
+ * automatically enable ECDH support.
+ *
+ * On later versions this function is no longer needed and is
+ * deprecated.
+ */
+ (void)SSL_CTX_set_ecdh_auto(ctx, 1);
+#endif /* defined(SSL_CTX_set_ecdh_auto) */
+
+ /* Cleanup */
+ EC_KEY_free(eckey);
+ eckey = NULL;
+#else
+ /* Generate the key's parameters. */
+ pkey_ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
+ if (pkey_ctx == NULL) {
+ goto ssl_error;
+ }
+ rv = EVP_PKEY_paramgen_init(pkey_ctx);
+ if (rv != 1) {
+ goto ssl_error;
+ }
+ rv = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pkey_ctx,
+ group_nid);
+ if (rv != 1) {
+ goto ssl_error;
+ }
+ rv = EVP_PKEY_paramgen(pkey_ctx, &params_pkey);
+ if (rv != 1 || params_pkey == NULL) {
+ goto ssl_error;
+ }
+ EVP_PKEY_CTX_free(pkey_ctx);
+
+ /* Generate the key. */
+ pkey_ctx = EVP_PKEY_CTX_new(params_pkey, NULL);
+ if (pkey_ctx == NULL) {
+ goto ssl_error;
+ }
+ rv = EVP_PKEY_keygen_init(pkey_ctx);
+ if (rv != 1) {
+ goto ssl_error;
+ }
+ rv = EVP_PKEY_keygen(pkey_ctx, &pkey);
+ if (rv != 1 || pkey == NULL) {
+ goto ssl_error;
+ }
+
+ /* Cleanup */
+ EVP_PKEY_free(params_pkey);
+ params_pkey = NULL;
+ EVP_PKEY_CTX_free(pkey_ctx);
+ pkey_ctx = NULL;
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
+
+ cert = X509_new();
+ if (cert == NULL) {
+ goto ssl_error;
+ }
+
+ ASN1_INTEGER_set(X509_get_serialNumber(cert),
+ (long)isc_random32());
+
+ /*
+ * Set the "not before" property 5 minutes into the past to
+ * accommodate with some possible clock skew across systems.
+ */
+#if OPENSSL_VERSION_NUMBER < 0x10101000L
+ X509_gmtime_adj(X509_get_notBefore(cert), -300);
+#else
+ X509_gmtime_adj(X509_getm_notBefore(cert), -300);
+#endif
+
+ /*
+ * We set the vailidy for 10 years.
+ */
+#if OPENSSL_VERSION_NUMBER < 0x10101000L
+ X509_gmtime_adj(X509_get_notAfter(cert), 3650 * 24 * 3600);
+#else
+ X509_gmtime_adj(X509_getm_notAfter(cert), 3650 * 24 * 3600);
+#endif
+
+ X509_set_pubkey(cert, pkey);
+
+ X509_NAME *name = X509_get_subject_name(cert);
+
+ X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
+ (const unsigned char *)"AQ", -1, -1,
+ 0);
+ X509_NAME_add_entry_by_txt(
+ name, "O", MBSTRING_ASC,
+ (const unsigned char *)"BIND9 ephemeral "
+ "certificate",
+ -1, -1, 0);
+ X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
+ (const unsigned char *)"bind9.local",
+ -1, -1, 0);
+
+ X509_set_issuer_name(cert, name);
+ X509_sign(cert, pkey, EVP_sha256());
+ rv = SSL_CTX_use_certificate(ctx, cert);
+ if (rv != 1) {
+ goto ssl_error;
+ }
+ rv = SSL_CTX_use_PrivateKey(ctx, pkey);
+ if (rv != 1) {
+ goto ssl_error;
+ }
+
+ X509_free(cert);
+ EVP_PKEY_free(pkey);
+ } else {
+ isc_result_t result;
+ result = isc_tlsctx_load_certificate(ctx, keyfile, certfile);
+ if (result != ISC_R_SUCCESS) {
+ goto ssl_error;
+ }
+ }
+
+ sslkeylogfile_init(ctx);
+
+ *ctxp = ctx;
+ return (ISC_R_SUCCESS);
+
+ssl_error:
+ err = ERR_get_error();
+ ERR_error_string_n(err, errbuf, sizeof(errbuf));
+ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_NETMGR,
+ ISC_LOG_ERROR, "Error initializing TLS context: %s",
+ errbuf);
+
+ if (ctx != NULL) {
+ SSL_CTX_free(ctx);
+ }
+ if (cert != NULL) {
+ X509_free(cert);
+ }
+ if (pkey != NULL) {
+ EVP_PKEY_free(pkey);
+ }
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ if (eckey != NULL) {
+ EC_KEY_free(eckey);
+ }
+#else
+ if (params_pkey != NULL) {
+ EVP_PKEY_free(params_pkey);
+ }
+ if (pkey_ctx != NULL) {
+ EVP_PKEY_CTX_free(pkey_ctx);
+ }
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
+
+ return (ISC_R_TLSERROR);
+}
+
+static long
+get_tls_version_disable_bit(const isc_tls_protocol_version_t tls_ver) {
+ long bit = 0;
+
+ switch (tls_ver) {
+ case ISC_TLS_PROTO_VER_1_2:
+#ifdef SSL_OP_NO_TLSv1_2
+ bit = SSL_OP_NO_TLSv1_2;
+#else
+ bit = 0;
+#endif
+ break;
+ case ISC_TLS_PROTO_VER_1_3:
+#ifdef SSL_OP_NO_TLSv1_3
+ bit = SSL_OP_NO_TLSv1_3;
+#else
+ bit = 0;
+#endif
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ };
+
+ return (bit);
+}
+
+bool
+isc_tls_protocol_supported(const isc_tls_protocol_version_t tls_ver) {
+ return (get_tls_version_disable_bit(tls_ver) != 0);
+}
+
+isc_tls_protocol_version_t
+isc_tls_protocol_name_to_version(const char *name) {
+ REQUIRE(name != NULL);
+
+ if (strcasecmp(name, "TLSv1.2") == 0) {
+ return (ISC_TLS_PROTO_VER_1_2);
+ } else if (strcasecmp(name, "TLSv1.3") == 0) {
+ return (ISC_TLS_PROTO_VER_1_3);
+ }
+
+ return (ISC_TLS_PROTO_VER_UNDEFINED);
+}
+
+void
+isc_tlsctx_set_protocols(isc_tlsctx_t *ctx, const uint32_t tls_versions) {
+ REQUIRE(ctx != NULL);
+ REQUIRE(tls_versions != 0);
+ long set_options = 0;
+ long clear_options = 0;
+ uint32_t versions = tls_versions;
+
+ /*
+ * The code below might be initially hard to follow because of the
+ * double negation that OpenSSL enforces.
+ *
+ * Taking into account that OpenSSL provides bits to *disable*
+ * specific protocol versions, like SSL_OP_NO_TLSv1_2,
+ * SSL_OP_NO_TLSv1_3, etc., the code has the following logic:
+ *
+ * If a protocol version is not specified in the bitmask, get the
+ * bit that disables it and add it to the set of TLS options to
+ * set ('set_options'). Otherwise, if a protocol version is set,
+ * add the bit to the set of options to clear ('clear_options').
+ */
+
+ /* TLS protocol versions are defined as powers of two. */
+ for (uint32_t tls_ver = ISC_TLS_PROTO_VER_1_2;
+ tls_ver < ISC_TLS_PROTO_VER_UNDEFINED; tls_ver <<= 1)
+ {
+ if ((tls_versions & tls_ver) == 0) {
+ set_options |= get_tls_version_disable_bit(tls_ver);
+ } else {
+ /*
+ * Only supported versions should ever be passed to the
+ * function SSL_CTX_clear_options. For example, in order
+ * to enable TLS v1.2, we have to clear
+ * SSL_OP_NO_TLSv1_2. Insist that the configuration file
+ * was verified properly, so we are not trying to enable
+ * an unsupported TLS version.
+ */
+ INSIST(isc_tls_protocol_supported(tls_ver));
+ clear_options |= get_tls_version_disable_bit(tls_ver);
+ }
+ versions &= ~(tls_ver);
+ }
+
+ /* All versions should be processed at this point, thus the value
+ * must equal zero. If it is not, then some garbage has been
+ * passed to the function; this situation is worth
+ * investigation. */
+ INSIST(versions == 0);
+
+ (void)SSL_CTX_set_options(ctx, set_options);
+ (void)SSL_CTX_clear_options(ctx, clear_options);
+}
+
+bool
+isc_tlsctx_load_dhparams(isc_tlsctx_t *ctx, const char *dhparams_file) {
+ REQUIRE(ctx != NULL);
+ REQUIRE(dhparams_file != NULL);
+ REQUIRE(*dhparams_file != '\0');
+
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ /* OpenSSL < 3.0 */
+ DH *dh = NULL;
+ FILE *paramfile;
+
+ paramfile = fopen(dhparams_file, "r");
+
+ if (paramfile) {
+ int check = 0;
+ dh = PEM_read_DHparams(paramfile, NULL, NULL, NULL);
+ fclose(paramfile);
+
+ if (dh == NULL) {
+ return (false);
+ } else if (DH_check(dh, &check) != 1 || check != 0) {
+ DH_free(dh);
+ return (false);
+ }
+ } else {
+ return (false);
+ }
+
+ if (SSL_CTX_set_tmp_dh(ctx, dh) != 1) {
+ DH_free(dh);
+ return (false);
+ }
+
+ DH_free(dh);
+#else
+ /* OpenSSL >= 3.0: low level DH APIs are deprecated in OpenSSL 3.0 */
+ EVP_PKEY *dh = NULL;
+ BIO *bio = NULL;
+
+ bio = BIO_new_file(dhparams_file, "r");
+ if (bio == NULL) {
+ return (false);
+ }
+
+ dh = PEM_read_bio_Parameters(bio, NULL);
+ if (dh == NULL) {
+ BIO_free(bio);
+ return (false);
+ }
+
+ if (SSL_CTX_set0_tmp_dh_pkey(ctx, dh) != 1) {
+ BIO_free(bio);
+ EVP_PKEY_free(dh);
+ return (false);
+ }
+
+ /* No need to call EVP_PKEY_free(dh) as the "dh" is owned by the
+ * SSL context at this point. */
+
+ BIO_free(bio);
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
+
+ return (true);
+}
+
+bool
+isc_tls_cipherlist_valid(const char *cipherlist) {
+ isc_tlsctx_t *tmp_ctx = NULL;
+ const SSL_METHOD *method = NULL;
+ bool result;
+ REQUIRE(cipherlist != NULL);
+
+ if (*cipherlist == '\0') {
+ return (false);
+ }
+
+ method = TLS_server_method();
+ if (method == NULL) {
+ return (false);
+ }
+ tmp_ctx = SSL_CTX_new(method);
+ if (tmp_ctx == NULL) {
+ return (false);
+ }
+
+ result = SSL_CTX_set_cipher_list(tmp_ctx, cipherlist) == 1;
+
+ isc_tlsctx_free(&tmp_ctx);
+
+ return (result);
+}
+
+void
+isc_tlsctx_set_cipherlist(isc_tlsctx_t *ctx, const char *cipherlist) {
+ REQUIRE(ctx != NULL);
+ REQUIRE(cipherlist != NULL);
+ REQUIRE(*cipherlist != '\0');
+
+ RUNTIME_CHECK(SSL_CTX_set_cipher_list(ctx, cipherlist) == 1);
+}
+
+void
+isc_tlsctx_prefer_server_ciphers(isc_tlsctx_t *ctx, const bool prefer) {
+ REQUIRE(ctx != NULL);
+
+ if (prefer) {
+ (void)SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
+ } else {
+ (void)SSL_CTX_clear_options(ctx,
+ SSL_OP_CIPHER_SERVER_PREFERENCE);
+ }
+}
+
+void
+isc_tlsctx_session_tickets(isc_tlsctx_t *ctx, const bool use) {
+ REQUIRE(ctx != NULL);
+
+ if (!use) {
+ (void)SSL_CTX_set_options(ctx, SSL_OP_NO_TICKET);
+ } else {
+ (void)SSL_CTX_clear_options(ctx, SSL_OP_NO_TICKET);
+ }
+}
+
+isc_tls_t *
+isc_tls_create(isc_tlsctx_t *ctx) {
+ isc_tls_t *newctx = NULL;
+
+ REQUIRE(ctx != NULL);
+
+ newctx = SSL_new(ctx);
+ if (newctx == NULL) {
+ char errbuf[256];
+ unsigned long err = ERR_get_error();
+
+ ERR_error_string_n(err, errbuf, sizeof(errbuf));
+ fprintf(stderr, "%s:SSL_new(%p) -> %s\n", __func__, ctx,
+ errbuf);
+ }
+
+ return (newctx);
+}
+
+void
+isc_tls_free(isc_tls_t **tlsp) {
+ isc_tls_t *tls = NULL;
+ REQUIRE(tlsp != NULL && *tlsp != NULL);
+
+ tls = *tlsp;
+ *tlsp = NULL;
+ SSL_free(tls);
+}
+
+const char *
+isc_tls_verify_peer_result_string(isc_tls_t *tls) {
+ REQUIRE(tls != NULL);
+
+ return (X509_verify_cert_error_string(SSL_get_verify_result(tls)));
+}
+
+#if HAVE_LIBNGHTTP2
+#ifndef OPENSSL_NO_NEXTPROTONEG
+/*
+ * NPN TLS extension client callback.
+ */
+static int
+select_next_proto_cb(SSL *ssl, unsigned char **out, unsigned char *outlen,
+ const unsigned char *in, unsigned int inlen, void *arg) {
+ UNUSED(ssl);
+ UNUSED(arg);
+
+ if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
+ return (SSL_TLSEXT_ERR_NOACK);
+ }
+ return (SSL_TLSEXT_ERR_OK);
+}
+#endif /* !OPENSSL_NO_NEXTPROTONEG */
+
+void
+isc_tlsctx_enable_http2client_alpn(isc_tlsctx_t *ctx) {
+ REQUIRE(ctx != NULL);
+
+#ifndef OPENSSL_NO_NEXTPROTONEG
+ SSL_CTX_set_next_proto_select_cb(ctx, select_next_proto_cb, NULL);
+#endif /* !OPENSSL_NO_NEXTPROTONEG */
+
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ SSL_CTX_set_alpn_protos(ctx, (const unsigned char *)NGHTTP2_PROTO_ALPN,
+ NGHTTP2_PROTO_ALPN_LEN);
+#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
+}
+
+#ifndef OPENSSL_NO_NEXTPROTONEG
+static int
+next_proto_cb(isc_tls_t *ssl, const unsigned char **data, unsigned int *len,
+ void *arg) {
+ UNUSED(ssl);
+ UNUSED(arg);
+
+ *data = (const unsigned char *)NGHTTP2_PROTO_ALPN;
+ *len = (unsigned int)NGHTTP2_PROTO_ALPN_LEN;
+ return (SSL_TLSEXT_ERR_OK);
+}
+#endif /* !OPENSSL_NO_NEXTPROTONEG */
+
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+static int
+alpn_select_proto_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen,
+ const unsigned char *in, unsigned int inlen, void *arg) {
+ int ret;
+
+ UNUSED(ssl);
+ UNUSED(arg);
+
+ ret = nghttp2_select_next_protocol((unsigned char **)(uintptr_t)out,
+ outlen, in, inlen);
+
+ if (ret != 1) {
+ return (SSL_TLSEXT_ERR_NOACK);
+ }
+
+ return (SSL_TLSEXT_ERR_OK);
+}
+#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
+
+void
+isc_tlsctx_enable_http2server_alpn(isc_tlsctx_t *tls) {
+ REQUIRE(tls != NULL);
+
+#ifndef OPENSSL_NO_NEXTPROTONEG
+ SSL_CTX_set_next_protos_advertised_cb(tls, next_proto_cb, NULL);
+#endif // OPENSSL_NO_NEXTPROTONEG
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ SSL_CTX_set_alpn_select_cb(tls, alpn_select_proto_cb, NULL);
+#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
+}
+#endif /* HAVE_LIBNGHTTP2 */
+
+void
+isc_tls_get_selected_alpn(isc_tls_t *tls, const unsigned char **alpn,
+ unsigned int *alpnlen) {
+ REQUIRE(tls != NULL);
+ REQUIRE(alpn != NULL);
+ REQUIRE(alpnlen != NULL);
+
+#ifndef OPENSSL_NO_NEXTPROTONEG
+ SSL_get0_next_proto_negotiated(tls, alpn, alpnlen);
+#endif
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ if (*alpn == NULL) {
+ SSL_get0_alpn_selected(tls, alpn, alpnlen);
+ }
+#endif
+}
+
+static bool
+protoneg_check_protocol(const uint8_t **pout, uint8_t *pout_len,
+ const uint8_t *in, size_t in_len, const uint8_t *key,
+ size_t key_len) {
+ for (size_t i = 0; i + key_len <= in_len; i += (size_t)(in[i] + 1)) {
+ if (memcmp(&in[i], key, key_len) == 0) {
+ *pout = (const uint8_t *)(&in[i + 1]);
+ *pout_len = in[i];
+ return (true);
+ }
+ }
+ return (false);
+}
+
+/* dot prepended by its length (3 bytes) */
+#define DOT_PROTO_ALPN "\x3" ISC_TLS_DOT_PROTO_ALPN_ID
+#define DOT_PROTO_ALPN_LEN (sizeof(DOT_PROTO_ALPN) - 1)
+
+static bool
+dot_select_next_protocol(const uint8_t **pout, uint8_t *pout_len,
+ const uint8_t *in, size_t in_len) {
+ return (protoneg_check_protocol(pout, pout_len, in, in_len,
+ (const uint8_t *)DOT_PROTO_ALPN,
+ DOT_PROTO_ALPN_LEN));
+}
+
+void
+isc_tlsctx_enable_dot_client_alpn(isc_tlsctx_t *ctx) {
+ REQUIRE(ctx != NULL);
+
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ SSL_CTX_set_alpn_protos(ctx, (const uint8_t *)DOT_PROTO_ALPN,
+ DOT_PROTO_ALPN_LEN);
+#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
+}
+
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+static int
+dot_alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
+ unsigned char *outlen, const unsigned char *in,
+ unsigned int inlen, void *arg) {
+ bool ret;
+
+ UNUSED(ssl);
+ UNUSED(arg);
+
+ ret = dot_select_next_protocol(out, outlen, in, inlen);
+
+ if (!ret) {
+ return (SSL_TLSEXT_ERR_NOACK);
+ }
+
+ return (SSL_TLSEXT_ERR_OK);
+}
+#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
+
+void
+isc_tlsctx_enable_dot_server_alpn(isc_tlsctx_t *tls) {
+ REQUIRE(tls != NULL);
+
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ SSL_CTX_set_alpn_select_cb(tls, dot_alpn_select_proto_cb, NULL);
+#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
+}
+
+isc_result_t
+isc_tlsctx_enable_peer_verification(isc_tlsctx_t *tlsctx, const bool is_server,
+ isc_tls_cert_store_t *store,
+ const char *hostname,
+ bool hostname_ignore_subject) {
+ int ret = 0;
+ REQUIRE(tlsctx != NULL);
+ REQUIRE(store != NULL);
+
+ /* Set the hostname/IP address. */
+ if (!is_server && hostname != NULL && *hostname != '\0') {
+ struct in6_addr sa6;
+ struct in_addr sa;
+ X509_VERIFY_PARAM *param = SSL_CTX_get0_param(tlsctx);
+ unsigned int hostflags = X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS;
+
+ /* It might be an IP address. */
+ if (inet_pton(AF_INET6, hostname, &sa6) == 1 ||
+ inet_pton(AF_INET, hostname, &sa) == 1)
+ {
+ ret = X509_VERIFY_PARAM_set1_ip_asc(param, hostname);
+ } else {
+ /* It seems that it is a host name. Let's set it. */
+ ret = X509_VERIFY_PARAM_set1_host(param, hostname, 0);
+ }
+ if (ret != 1) {
+ ERR_clear_error();
+ return (ISC_R_FAILURE);
+ }
+
+#ifdef X509_CHECK_FLAG_NEVER_CHECK_SUBJECT
+ /*
+ * According to the RFC 8310, Section 8.1, Subject field MUST
+ * NOT be inspected when verifying a hostname when using
+ * DoT. Only SubjectAltName must be checked instead. That is
+ * not the case for HTTPS, though.
+ *
+ * Unfortunately, some quite old versions of OpenSSL (< 1.1.1)
+ * might lack the functionality to implement that. It should
+ * have very little real-world consequences, as most of the
+ * production-ready certificates issued by real CAs will have
+ * SubjectAltName set. In such a case, the Subject field is
+ * ignored.
+ */
+ if (hostname_ignore_subject) {
+ hostflags |= X509_CHECK_FLAG_NEVER_CHECK_SUBJECT;
+ }
+#else
+ UNUSED(hostname_ignore_subject);
+#endif
+ X509_VERIFY_PARAM_set_hostflags(param, hostflags);
+ }
+
+ /* "Attach" the cert store to the context */
+ SSL_CTX_set1_cert_store(tlsctx, store);
+
+ /* enable verification */
+ if (is_server) {
+ SSL_CTX_set_verify(tlsctx,
+ SSL_VERIFY_PEER |
+ SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+ NULL);
+ } else {
+ SSL_CTX_set_verify(tlsctx, SSL_VERIFY_PEER, NULL);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_tlsctx_load_client_ca_names(isc_tlsctx_t *ctx, const char *ca_bundle_file) {
+ STACK_OF(X509_NAME) * cert_names;
+ REQUIRE(ctx != NULL);
+ REQUIRE(ca_bundle_file != NULL);
+
+ cert_names = SSL_load_client_CA_file(ca_bundle_file);
+ if (cert_names == NULL) {
+ ERR_clear_error();
+ return (ISC_R_FAILURE);
+ }
+
+ SSL_CTX_set_client_CA_list(ctx, cert_names);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_tls_cert_store_create(const char *ca_bundle_filename,
+ isc_tls_cert_store_t **pstore) {
+ int ret = 0;
+ isc_tls_cert_store_t *store = NULL;
+ REQUIRE(pstore != NULL && *pstore == NULL);
+
+ store = X509_STORE_new();
+ if (store == NULL) {
+ goto error;
+ }
+
+ /* Let's treat empty string as the default (system wide) store */
+ if (ca_bundle_filename != NULL && *ca_bundle_filename == '\0') {
+ ca_bundle_filename = NULL;
+ }
+
+ if (ca_bundle_filename == NULL) {
+ ret = X509_STORE_set_default_paths(store);
+ } else {
+ ret = X509_STORE_load_locations(store, ca_bundle_filename,
+ NULL);
+ }
+
+ if (ret == 0) {
+ goto error;
+ }
+
+ *pstore = store;
+ return (ISC_R_SUCCESS);
+
+error:
+ ERR_clear_error();
+ if (store != NULL) {
+ X509_STORE_free(store);
+ }
+ return (ISC_R_FAILURE);
+}
+
+void
+isc_tls_cert_store_free(isc_tls_cert_store_t **pstore) {
+ isc_tls_cert_store_t *store;
+ REQUIRE(pstore != NULL && *pstore != NULL);
+
+ store = *pstore;
+
+ X509_STORE_free(store);
+
+ *pstore = NULL;
+}
+
+#define TLSCTX_CACHE_MAGIC ISC_MAGIC('T', 'l', 'S', 'c')
+#define VALID_TLSCTX_CACHE(t) ISC_MAGIC_VALID(t, TLSCTX_CACHE_MAGIC)
+
+#define TLSCTX_CLIENT_SESSION_CACHE_MAGIC ISC_MAGIC('T', 'l', 'C', 'c')
+#define VALID_TLSCTX_CLIENT_SESSION_CACHE(t) \
+ ISC_MAGIC_VALID(t, TLSCTX_CLIENT_SESSION_CACHE_MAGIC)
+
+typedef struct isc_tlsctx_cache_entry {
+ /*
+ * We need a TLS context entry for each transport on both IPv4 and
+ * IPv6 in order to avoid cluttering a context-specific
+ * session-resumption cache.
+ */
+ isc_tlsctx_t *ctx[isc_tlsctx_cache_count - 1][2];
+ isc_tlsctx_client_session_cache_t
+ *client_sess_cache[isc_tlsctx_cache_count - 1][2];
+ /*
+ * One certificate store is enough for all the contexts defined
+ * above. We need that for peer validation.
+ */
+ isc_tls_cert_store_t *ca_store;
+} isc_tlsctx_cache_entry_t;
+
+struct isc_tlsctx_cache {
+ uint32_t magic;
+ isc_refcount_t references;
+ isc_mem_t *mctx;
+
+ isc_rwlock_t rwlock;
+ isc_ht_t *data;
+};
+
+void
+isc_tlsctx_cache_create(isc_mem_t *mctx, isc_tlsctx_cache_t **cachep) {
+ isc_tlsctx_cache_t *nc;
+
+ REQUIRE(cachep != NULL && *cachep == NULL);
+ nc = isc_mem_get(mctx, sizeof(*nc));
+
+ *nc = (isc_tlsctx_cache_t){ .magic = TLSCTX_CACHE_MAGIC };
+ isc_refcount_init(&nc->references, 1);
+ isc_mem_attach(mctx, &nc->mctx);
+
+ isc_ht_init(&nc->data, mctx, 5, ISC_HT_CASE_SENSITIVE);
+ isc_rwlock_init(&nc->rwlock, 0, 0);
+
+ *cachep = nc;
+}
+
+void
+isc_tlsctx_cache_attach(isc_tlsctx_cache_t *source,
+ isc_tlsctx_cache_t **targetp) {
+ REQUIRE(VALID_TLSCTX_CACHE(source));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ isc_refcount_increment(&source->references);
+
+ *targetp = source;
+}
+
+static void
+tlsctx_cache_entry_destroy(isc_mem_t *mctx, isc_tlsctx_cache_entry_t *entry) {
+ size_t i, k;
+
+ for (i = 0; i < (isc_tlsctx_cache_count - 1); i++) {
+ for (k = 0; k < 2; k++) {
+ if (entry->ctx[i][k] != NULL) {
+ isc_tlsctx_free(&entry->ctx[i][k]);
+ }
+
+ if (entry->client_sess_cache[i][k] != NULL) {
+ isc_tlsctx_client_session_cache_detach(
+ &entry->client_sess_cache[i][k]);
+ }
+ }
+ }
+ if (entry->ca_store != NULL) {
+ isc_tls_cert_store_free(&entry->ca_store);
+ }
+ isc_mem_put(mctx, entry, sizeof(*entry));
+}
+
+static void
+tlsctx_cache_destroy(isc_tlsctx_cache_t *cache) {
+ isc_ht_iter_t *it = NULL;
+ isc_result_t result;
+
+ cache->magic = 0;
+
+ isc_refcount_destroy(&cache->references);
+
+ isc_ht_iter_create(cache->data, &it);
+ for (result = isc_ht_iter_first(it); result == ISC_R_SUCCESS;
+ result = isc_ht_iter_delcurrent_next(it))
+ {
+ isc_tlsctx_cache_entry_t *entry = NULL;
+ isc_ht_iter_current(it, (void **)&entry);
+ tlsctx_cache_entry_destroy(cache->mctx, entry);
+ }
+
+ isc_ht_iter_destroy(&it);
+ isc_ht_destroy(&cache->data);
+ isc_rwlock_destroy(&cache->rwlock);
+ isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache));
+}
+
+void
+isc_tlsctx_cache_detach(isc_tlsctx_cache_t **cachep) {
+ isc_tlsctx_cache_t *cache = NULL;
+
+ REQUIRE(cachep != NULL);
+
+ cache = *cachep;
+ *cachep = NULL;
+
+ REQUIRE(VALID_TLSCTX_CACHE(cache));
+
+ if (isc_refcount_decrement(&cache->references) == 1) {
+ tlsctx_cache_destroy(cache);
+ }
+}
+
+isc_result_t
+isc_tlsctx_cache_add(
+ isc_tlsctx_cache_t *cache, const char *name,
+ const isc_tlsctx_cache_transport_t transport, const uint16_t family,
+ isc_tlsctx_t *ctx, isc_tls_cert_store_t *store,
+ isc_tlsctx_client_session_cache_t *client_sess_cache,
+ isc_tlsctx_t **pfound, isc_tls_cert_store_t **pfound_store,
+ isc_tlsctx_client_session_cache_t **pfound_client_sess_cache) {
+ isc_result_t result = ISC_R_FAILURE;
+ size_t name_len, tr_offset;
+ isc_tlsctx_cache_entry_t *entry = NULL;
+ bool ipv6;
+
+ REQUIRE(VALID_TLSCTX_CACHE(cache));
+ REQUIRE(client_sess_cache == NULL ||
+ VALID_TLSCTX_CLIENT_SESSION_CACHE(client_sess_cache));
+ REQUIRE(name != NULL && *name != '\0');
+ REQUIRE(transport > isc_tlsctx_cache_none &&
+ transport < isc_tlsctx_cache_count);
+ REQUIRE(family == AF_INET || family == AF_INET6);
+ REQUIRE(ctx != NULL);
+
+ tr_offset = (transport - 1);
+ ipv6 = (family == AF_INET6);
+
+ RWLOCK(&cache->rwlock, isc_rwlocktype_write);
+
+ name_len = strlen(name);
+ result = isc_ht_find(cache->data, (const uint8_t *)name, name_len,
+ (void **)&entry);
+ if (result == ISC_R_SUCCESS && entry->ctx[tr_offset][ipv6] != NULL) {
+ isc_tlsctx_client_session_cache_t *found_client_sess_cache;
+ /* The entry exists. */
+ if (pfound != NULL) {
+ INSIST(*pfound == NULL);
+ *pfound = entry->ctx[tr_offset][ipv6];
+ }
+
+ if (pfound_store != NULL && entry->ca_store != NULL) {
+ INSIST(*pfound_store == NULL);
+ *pfound_store = entry->ca_store;
+ }
+
+ found_client_sess_cache =
+ entry->client_sess_cache[tr_offset][ipv6];
+ if (pfound_client_sess_cache != NULL &&
+ found_client_sess_cache != NULL)
+ {
+ INSIST(*pfound_client_sess_cache == NULL);
+ *pfound_client_sess_cache = found_client_sess_cache;
+ }
+ result = ISC_R_EXISTS;
+ } else if (result == ISC_R_SUCCESS &&
+ entry->ctx[tr_offset][ipv6] == NULL)
+ {
+ /*
+ * The hash table entry exists, but is not filled for this
+ * particular transport/IP type combination.
+ */
+ entry->ctx[tr_offset][ipv6] = ctx;
+ entry->client_sess_cache[tr_offset][ipv6] = client_sess_cache;
+ /*
+ * As the passed certificates store object is supposed
+ * to be internally managed by the cache object anyway,
+ * we might destroy the unneeded store object right now.
+ */
+ if (store != NULL && store != entry->ca_store) {
+ isc_tls_cert_store_free(&store);
+ }
+ result = ISC_R_SUCCESS;
+ } else {
+ /*
+ * The hash table entry does not exist, let's create one.
+ */
+ INSIST(result != ISC_R_SUCCESS);
+ entry = isc_mem_get(cache->mctx, sizeof(*entry));
+ /* Oracle/Red Hat Linux, GCC bug #53119 */
+ memset(entry, 0, sizeof(*entry));
+ entry->ctx[tr_offset][ipv6] = ctx;
+ entry->client_sess_cache[tr_offset][ipv6] = client_sess_cache;
+ entry->ca_store = store;
+ RUNTIME_CHECK(isc_ht_add(cache->data, (const uint8_t *)name,
+ name_len,
+ (void *)entry) == ISC_R_SUCCESS);
+ result = ISC_R_SUCCESS;
+ }
+
+ RWUNLOCK(&cache->rwlock, isc_rwlocktype_write);
+
+ return (result);
+}
+
+isc_result_t
+isc_tlsctx_cache_find(
+ isc_tlsctx_cache_t *cache, const char *name,
+ const isc_tlsctx_cache_transport_t transport, const uint16_t family,
+ isc_tlsctx_t **pctx, isc_tls_cert_store_t **pstore,
+ isc_tlsctx_client_session_cache_t **pfound_client_sess_cache) {
+ isc_result_t result = ISC_R_FAILURE;
+ size_t tr_offset;
+ isc_tlsctx_cache_entry_t *entry = NULL;
+ bool ipv6;
+
+ REQUIRE(VALID_TLSCTX_CACHE(cache));
+ REQUIRE(name != NULL && *name != '\0');
+ REQUIRE(transport > isc_tlsctx_cache_none &&
+ transport < isc_tlsctx_cache_count);
+ REQUIRE(family == AF_INET || family == AF_INET6);
+ REQUIRE(pctx != NULL && *pctx == NULL);
+
+ tr_offset = (transport - 1);
+ ipv6 = (family == AF_INET6);
+
+ RWLOCK(&cache->rwlock, isc_rwlocktype_read);
+
+ result = isc_ht_find(cache->data, (const uint8_t *)name, strlen(name),
+ (void **)&entry);
+
+ if (result == ISC_R_SUCCESS && pstore != NULL &&
+ entry->ca_store != NULL)
+ {
+ *pstore = entry->ca_store;
+ }
+
+ if (result == ISC_R_SUCCESS && entry->ctx[tr_offset][ipv6] != NULL) {
+ isc_tlsctx_client_session_cache_t *found_client_sess_cache =
+ entry->client_sess_cache[tr_offset][ipv6];
+
+ *pctx = entry->ctx[tr_offset][ipv6];
+
+ if (pfound_client_sess_cache != NULL &&
+ found_client_sess_cache != NULL)
+ {
+ INSIST(*pfound_client_sess_cache == NULL);
+ *pfound_client_sess_cache = found_client_sess_cache;
+ }
+ } else if (result == ISC_R_SUCCESS &&
+ entry->ctx[tr_offset][ipv6] == NULL)
+ {
+ result = ISC_R_NOTFOUND;
+ } else {
+ INSIST(result != ISC_R_SUCCESS);
+ }
+
+ RWUNLOCK(&cache->rwlock, isc_rwlocktype_read);
+
+ return (result);
+}
+
+typedef struct client_session_cache_entry client_session_cache_entry_t;
+
+typedef struct client_session_cache_bucket {
+ char *bucket_key;
+ size_t bucket_key_len;
+ /* Cache entries within the bucket (from the oldest to the newest). */
+ ISC_LIST(client_session_cache_entry_t) entries;
+} client_session_cache_bucket_t;
+
+struct client_session_cache_entry {
+ SSL_SESSION *session;
+ client_session_cache_bucket_t *bucket; /* "Parent" bucket pointer. */
+ ISC_LINK(client_session_cache_entry_t) bucket_link;
+ ISC_LINK(client_session_cache_entry_t) cache_link;
+};
+
+struct isc_tlsctx_client_session_cache {
+ uint32_t magic;
+ isc_refcount_t references;
+ isc_mem_t *mctx;
+
+ /*
+ * We need to keep a reference to the related TLS context in order
+ * to ensure that it remains valid while the TLS client sessions
+ * cache object is valid, as every TLS session object
+ * (SSL_SESSION) is "tied" to a particular context.
+ */
+ isc_tlsctx_t *ctx;
+
+ /*
+ * The idea is to have one bucket per remote server. Each bucket,
+ * can maintain multiple TLS sessions to that server, as BIND
+ * might want to establish multiple TLS connections to the remote
+ * server at once.
+ */
+ isc_ht_t *buckets;
+
+ /*
+ * The list of all current entries within the cache maintained in
+ * LRU-manner, so that the oldest entry might be efficiently
+ * removed.
+ */
+ ISC_LIST(client_session_cache_entry_t) lru_entries;
+ /* Number of the entries within the cache. */
+ size_t nentries;
+ /* Maximum number of the entries within the cache. */
+ size_t max_entries;
+
+ isc_mutex_t lock;
+};
+
+void
+isc_tlsctx_client_session_cache_create(
+ isc_mem_t *mctx, isc_tlsctx_t *ctx, const size_t max_entries,
+ isc_tlsctx_client_session_cache_t **cachep) {
+ isc_tlsctx_client_session_cache_t *nc;
+
+ REQUIRE(ctx != NULL);
+ REQUIRE(max_entries > 0);
+ REQUIRE(cachep != NULL && *cachep == NULL);
+
+ nc = isc_mem_get(mctx, sizeof(*nc));
+
+ *nc = (isc_tlsctx_client_session_cache_t){ .max_entries = max_entries };
+ isc_refcount_init(&nc->references, 1);
+ isc_mem_attach(mctx, &nc->mctx);
+ isc_tlsctx_attach(ctx, &nc->ctx);
+
+ isc_ht_init(&nc->buckets, mctx, 5, ISC_HT_CASE_SENSITIVE);
+ ISC_LIST_INIT(nc->lru_entries);
+ isc_mutex_init(&nc->lock);
+
+ nc->magic = TLSCTX_CLIENT_SESSION_CACHE_MAGIC;
+
+ *cachep = nc;
+}
+
+void
+isc_tlsctx_client_session_cache_attach(
+ isc_tlsctx_client_session_cache_t *source,
+ isc_tlsctx_client_session_cache_t **targetp) {
+ REQUIRE(VALID_TLSCTX_CLIENT_SESSION_CACHE(source));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ isc_refcount_increment(&source->references);
+
+ *targetp = source;
+}
+
+static void
+client_cache_entry_delete(isc_tlsctx_client_session_cache_t *restrict cache,
+ client_session_cache_entry_t *restrict entry) {
+ client_session_cache_bucket_t *restrict bucket = entry->bucket;
+
+ /* Unlink and free the cache entry */
+ ISC_LIST_UNLINK(bucket->entries, entry, bucket_link);
+ ISC_LIST_UNLINK(cache->lru_entries, entry, cache_link);
+ cache->nentries--;
+ (void)SSL_SESSION_free(entry->session);
+ isc_mem_put(cache->mctx, entry, sizeof(*entry));
+
+ /* The bucket is empty - let's remove it */
+ if (ISC_LIST_EMPTY(bucket->entries)) {
+ RUNTIME_CHECK(isc_ht_delete(cache->buckets,
+ (const uint8_t *)bucket->bucket_key,
+ bucket->bucket_key_len) ==
+ ISC_R_SUCCESS);
+
+ isc_mem_free(cache->mctx, bucket->bucket_key);
+ isc_mem_put(cache->mctx, bucket, sizeof(*bucket));
+ }
+}
+
+void
+isc_tlsctx_client_session_cache_detach(
+ isc_tlsctx_client_session_cache_t **cachep) {
+ isc_tlsctx_client_session_cache_t *cache = NULL;
+ client_session_cache_entry_t *entry = NULL, *next = NULL;
+
+ REQUIRE(cachep != NULL);
+
+ cache = *cachep;
+ *cachep = NULL;
+
+ REQUIRE(VALID_TLSCTX_CLIENT_SESSION_CACHE(cache));
+
+ if (isc_refcount_decrement(&cache->references) != 1) {
+ return;
+ }
+
+ cache->magic = 0;
+
+ isc_refcount_destroy(&cache->references);
+
+ entry = ISC_LIST_HEAD(cache->lru_entries);
+ while (entry != NULL) {
+ next = ISC_LIST_NEXT(entry, cache_link);
+ client_cache_entry_delete(cache, entry);
+ entry = next;
+ }
+
+ RUNTIME_CHECK(isc_ht_count(cache->buckets) == 0);
+ isc_ht_destroy(&cache->buckets);
+
+ isc_mutex_destroy(&cache->lock);
+ isc_tlsctx_free(&cache->ctx);
+ isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache));
+}
+
+static bool
+ssl_session_seems_resumable(const SSL_SESSION *sess) {
+#ifdef HAVE_SSL_SESSION_IS_RESUMABLE
+ /*
+ * If SSL_SESSION_is_resumable() is available, let's use that. It
+ * is expected to be available on OpenSSL >= 1.1.1 and its modern
+ * siblings.
+ */
+ return (SSL_SESSION_is_resumable(sess) != 0);
+#elif (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+ /*
+ * Taking into consideration that OpenSSL 1.1.0 uses opaque
+ * pointers for SSL_SESSION, we cannot implement a replacement for
+ * SSL_SESSION_is_resumable() manually. Let's use a sensible
+ * approximation for that, then: if there is an associated session
+ * ticket or session ID, then, most likely, the session is
+ * resumable.
+ */
+ unsigned int session_id_len = 0;
+ (void)SSL_SESSION_get_id(sess, &session_id_len);
+ return (SSL_SESSION_has_ticket(sess) || session_id_len > 0);
+#else
+ return (!sess->not_resumable &&
+ (sess->session_id_length > 0 || sess->tlsext_ticklen > 0));
+#endif
+}
+
+void
+isc_tlsctx_client_session_cache_keep(isc_tlsctx_client_session_cache_t *cache,
+ char *remote_peer_name, isc_tls_t *tls) {
+ size_t name_len;
+ isc_result_t result;
+ SSL_SESSION *sess;
+ client_session_cache_bucket_t *restrict bucket = NULL;
+ client_session_cache_entry_t *restrict entry = NULL;
+
+ REQUIRE(VALID_TLSCTX_CLIENT_SESSION_CACHE(cache));
+ REQUIRE(remote_peer_name != NULL && *remote_peer_name != '\0');
+ REQUIRE(tls != NULL);
+
+ sess = SSL_get1_session(tls);
+ if (sess == NULL) {
+ ERR_clear_error();
+ return;
+ } else if (!ssl_session_seems_resumable(sess)) {
+ SSL_SESSION_free(sess);
+ return;
+ }
+
+ isc_mutex_lock(&cache->lock);
+
+ name_len = strlen(remote_peer_name);
+ result = isc_ht_find(cache->buckets, (const uint8_t *)remote_peer_name,
+ name_len, (void **)&bucket);
+
+ if (result != ISC_R_SUCCESS) {
+ /* Let's create a new bucket */
+ INSIST(bucket == NULL);
+ bucket = isc_mem_get(cache->mctx, sizeof(*bucket));
+ *bucket = (client_session_cache_bucket_t){
+ .bucket_key = isc_mem_strdup(cache->mctx,
+ remote_peer_name),
+ .bucket_key_len = name_len
+ };
+ ISC_LIST_INIT(bucket->entries);
+ RUNTIME_CHECK(isc_ht_add(cache->buckets,
+ (const uint8_t *)remote_peer_name,
+ name_len,
+ (void *)bucket) == ISC_R_SUCCESS);
+ }
+
+ /* Let's add a new cache entry to the new/found bucket */
+ entry = isc_mem_get(cache->mctx, sizeof(*entry));
+ *entry = (client_session_cache_entry_t){ .session = sess,
+ .bucket = bucket };
+ ISC_LINK_INIT(entry, bucket_link);
+ ISC_LINK_INIT(entry, cache_link);
+
+ ISC_LIST_APPEND(bucket->entries, entry, bucket_link);
+
+ ISC_LIST_APPEND(cache->lru_entries, entry, cache_link);
+ cache->nentries++;
+
+ if (cache->nentries > cache->max_entries) {
+ /*
+ * Cache overrun. We need to remove the oldest entry from the
+ * cache
+ */
+ client_session_cache_entry_t *restrict oldest;
+ INSIST((cache->nentries - 1) == cache->max_entries);
+
+ oldest = ISC_LIST_HEAD(cache->lru_entries);
+ client_cache_entry_delete(cache, oldest);
+ }
+
+ isc_mutex_unlock(&cache->lock);
+}
+
+void
+isc_tlsctx_client_session_cache_reuse(isc_tlsctx_client_session_cache_t *cache,
+ char *remote_peer_name, isc_tls_t *tls) {
+ client_session_cache_bucket_t *restrict bucket = NULL;
+ client_session_cache_entry_t *restrict entry;
+ size_t name_len;
+ isc_result_t result;
+
+ REQUIRE(VALID_TLSCTX_CLIENT_SESSION_CACHE(cache));
+ REQUIRE(remote_peer_name != NULL && *remote_peer_name != '\0');
+ REQUIRE(tls != NULL);
+
+ isc_mutex_lock(&cache->lock);
+
+ /* Let's find the bucket */
+ name_len = strlen(remote_peer_name);
+ result = isc_ht_find(cache->buckets, (const uint8_t *)remote_peer_name,
+ name_len, (void **)&bucket);
+
+ if (result != ISC_R_SUCCESS) {
+ goto exit;
+ }
+
+ INSIST(bucket != NULL);
+
+ /*
+ * If the bucket has been found, let's use the newest session from
+ * the bucket, as it has the highest chance to be successfully
+ * resumed.
+ */
+ INSIST(!ISC_LIST_EMPTY(bucket->entries));
+ entry = ISC_LIST_TAIL(bucket->entries);
+ RUNTIME_CHECK(SSL_set_session(tls, entry->session) == 1);
+ client_cache_entry_delete(cache, entry);
+
+exit:
+ isc_mutex_unlock(&cache->lock);
+}
+
+void
+isc_tlsctx_client_session_cache_keep_sockaddr(
+ isc_tlsctx_client_session_cache_t *cache, isc_sockaddr_t *remote_peer,
+ isc_tls_t *tls) {
+ char peername[ISC_SOCKADDR_FORMATSIZE] = { 0 };
+
+ REQUIRE(remote_peer != NULL);
+
+ isc_sockaddr_format(remote_peer, peername, sizeof(peername));
+
+ isc_tlsctx_client_session_cache_keep(cache, peername, tls);
+}
+
+void
+isc_tlsctx_client_session_cache_reuse_sockaddr(
+ isc_tlsctx_client_session_cache_t *cache, isc_sockaddr_t *remote_peer,
+ isc_tls_t *tls) {
+ char peername[ISC_SOCKADDR_FORMATSIZE] = { 0 };
+
+ REQUIRE(remote_peer != NULL);
+
+ isc_sockaddr_format(remote_peer, peername, sizeof(peername));
+
+ isc_tlsctx_client_session_cache_reuse(cache, peername, tls);
+}
+
+const isc_tlsctx_t *
+isc_tlsctx_client_session_cache_getctx(
+ isc_tlsctx_client_session_cache_t *cache) {
+ REQUIRE(VALID_TLSCTX_CLIENT_SESSION_CACHE(cache));
+ return (cache->ctx);
+}
+
+void
+isc_tlsctx_set_random_session_id_context(isc_tlsctx_t *ctx) {
+ uint8_t session_id_ctx[SSL_MAX_SID_CTX_LENGTH] = { 0 };
+ const size_t len = ISC_MIN(20, sizeof(session_id_ctx));
+
+ REQUIRE(ctx != NULL);
+
+ RUNTIME_CHECK(RAND_bytes(session_id_ctx, len) == 1);
+
+ RUNTIME_CHECK(
+ SSL_CTX_set_session_id_context(ctx, session_id_ctx, len) == 1);
+}
diff --git a/lib/isc/tls_p.h b/lib/isc/tls_p.h
new file mode 100644
index 0000000..c0bd693
--- /dev/null
+++ b/lib/isc/tls_p.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+void
+isc__tls_initialize(void);
+
+void
+isc__tls_shutdown(void);
diff --git a/lib/isc/tm.c b/lib/isc/tm.c
new file mode 100644
index 0000000..19a7bee
--- /dev/null
+++ b/lib/isc/tm.c
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND BSD-2-Clause
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*-
+ * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code was contributed to The NetBSD Foundation by Klaus Klein.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <isc/tm.h>
+#include <isc/util.h>
+
+/*
+ * Portable conversion routines for struct tm, replacing
+ * timegm() and strptime(), which are not available on all
+ * platforms and don't always behave the same way when they
+ * are.
+ */
+
+/*
+ * We do not implement alternate representations. However, we always
+ * check whether a given modifier is allowed for a certain conversion.
+ */
+#define ALT_E 0x01
+#define ALT_O 0x02
+#define LEGAL_ALT(x) \
+ { \
+ if ((alt_format & ~(x)) != 0) \
+ return ((0)); \
+ }
+
+#ifndef TM_YEAR_BASE
+#define TM_YEAR_BASE 1900
+#endif /* ifndef TM_YEAR_BASE */
+
+static const char *day[7] = { "Sunday", "Monday", "Tuesday", "Wednesday",
+ "Thursday", "Friday", "Saturday" };
+static const char *abday[7] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+static const char *mon[12] = {
+ "January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December"
+};
+static const char *abmon[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+static const char *am_pm[2] = { "AM", "PM" };
+
+static int
+conv_num(const char **buf, int *dest, int llim, int ulim) {
+ int result = 0;
+
+ /* The limit also determines the number of valid digits. */
+ int rulim = ulim;
+
+ if (!isdigit((unsigned char)**buf)) {
+ return (0);
+ }
+
+ do {
+ result *= 10;
+ result += *(*buf)++ - '0';
+ rulim /= 10;
+ } while ((result * 10 <= ulim) && rulim && **buf >= '0' &&
+ **buf <= '9');
+
+ if (result < llim || result > ulim) {
+ return (0);
+ }
+
+ *dest = result;
+ return (1);
+}
+
+time_t
+isc_tm_timegm(struct tm *tm) {
+ time_t ret;
+ int i, yday = 0, leapday;
+ int mdays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30 };
+
+ leapday = ((((tm->tm_year + 1900) % 4) == 0 &&
+ ((tm->tm_year + 1900) % 100) != 0) ||
+ ((tm->tm_year + 1900) % 400) == 0)
+ ? 1
+ : 0;
+ mdays[1] += leapday;
+
+ yday = tm->tm_mday - 1;
+ for (i = 1; i <= tm->tm_mon; i++) {
+ yday += mdays[i - 1];
+ }
+ ret = tm->tm_sec + (60 * tm->tm_min) + (3600 * tm->tm_hour) +
+ (86400 *
+ (yday + ((tm->tm_year - 70) * 365) + ((tm->tm_year - 69) / 4) -
+ ((tm->tm_year - 1) / 100) + ((tm->tm_year + 299) / 400)));
+ return (ret);
+}
+
+char *
+isc_tm_strptime(const char *buf, const char *fmt, struct tm *tm) {
+ char c, *ret;
+ const char *bp;
+ size_t len = 0;
+ int alt_format, i, split_year = 0;
+
+ REQUIRE(buf != NULL);
+ REQUIRE(fmt != NULL);
+ REQUIRE(tm != NULL);
+
+ memset(tm, 0, sizeof(struct tm));
+
+ bp = buf;
+
+ while ((c = *fmt) != '\0') {
+ /* Clear `alternate' modifier prior to new conversion. */
+ alt_format = 0;
+
+ /* Eat up white-space. */
+ if (isspace((unsigned char)c)) {
+ while (isspace((unsigned char)*bp)) {
+ bp++;
+ }
+
+ fmt++;
+ continue;
+ }
+
+ if ((c = *fmt++) != '%') {
+ goto literal;
+ }
+
+ again:
+ switch (c = *fmt++) {
+ case '%': /* "%%" is converted to "%". */
+ literal:
+ if (c != *bp++) {
+ return (0);
+ }
+ break;
+
+ /*
+ * "Alternative" modifiers. Just set the appropriate flag
+ * and start over again.
+ */
+ case 'E': /* "%E?" alternative conversion modifier. */
+ LEGAL_ALT(0);
+ alt_format |= ALT_E;
+ goto again;
+
+ case 'O': /* "%O?" alternative conversion modifier. */
+ LEGAL_ALT(0);
+ alt_format |= ALT_O;
+ goto again;
+
+ /*
+ * "Complex" conversion rules, implemented through recursion.
+ */
+ case 'c': /* Date and time, using the locale's format. */
+ LEGAL_ALT(ALT_E);
+ if (!(bp = isc_tm_strptime(bp, "%x %X", tm))) {
+ return (0);
+ }
+ break;
+
+ case 'D': /* The date as "%m/%d/%y". */
+ LEGAL_ALT(0);
+ if (!(bp = isc_tm_strptime(bp, "%m/%d/%y", tm))) {
+ return (0);
+ }
+ break;
+
+ case 'R': /* The time as "%H:%M". */
+ LEGAL_ALT(0);
+ if (!(bp = isc_tm_strptime(bp, "%H:%M", tm))) {
+ return (0);
+ }
+ break;
+
+ case 'r': /* The time in 12-hour clock representation. */
+ LEGAL_ALT(0);
+ if (!(bp = isc_tm_strptime(bp, "%I:%M:%S %p", tm))) {
+ return (0);
+ }
+ break;
+
+ case 'T': /* The time as "%H:%M:%S". */
+ LEGAL_ALT(0);
+ if (!(bp = isc_tm_strptime(bp, "%H:%M:%S", tm))) {
+ return (0);
+ }
+ break;
+
+ case 'X': /* The time, using the locale's format. */
+ LEGAL_ALT(ALT_E);
+ if (!(bp = isc_tm_strptime(bp, "%H:%M:%S", tm))) {
+ return (0);
+ }
+ break;
+
+ case 'x': /* The date, using the locale's format. */
+ LEGAL_ALT(ALT_E);
+ if (!(bp = isc_tm_strptime(bp, "%m/%d/%y", tm))) {
+ return (0);
+ }
+ break;
+
+ /*
+ * "Elementary" conversion rules.
+ */
+ case 'A': /* The day of week, using the locale's form. */
+ case 'a':
+ LEGAL_ALT(0);
+ for (i = 0; i < 7; i++) {
+ /* Full name. */
+ len = strlen(day[i]);
+ if (strncasecmp(day[i], bp, len) == 0) {
+ break;
+ }
+
+ /* Abbreviated name. */
+ len = strlen(abday[i]);
+ if (strncasecmp(abday[i], bp, len) == 0) {
+ break;
+ }
+ }
+
+ /* Nothing matched. */
+ if (i == 7) {
+ return (0);
+ }
+
+ tm->tm_wday = i;
+ bp += len;
+ break;
+
+ case 'B': /* The month, using the locale's form. */
+ case 'b':
+ case 'h':
+ LEGAL_ALT(0);
+ for (i = 0; i < 12; i++) {
+ /* Full name. */
+ len = strlen(mon[i]);
+ if (strncasecmp(mon[i], bp, len) == 0) {
+ break;
+ }
+
+ /* Abbreviated name. */
+ len = strlen(abmon[i]);
+ if (strncasecmp(abmon[i], bp, len) == 0) {
+ break;
+ }
+ }
+
+ /* Nothing matched. */
+ if (i == 12) {
+ return (0);
+ }
+
+ tm->tm_mon = i;
+ bp += len;
+ break;
+
+ case 'C': /* The century number. */
+ LEGAL_ALT(ALT_E);
+ if (!(conv_num(&bp, &i, 0, 99))) {
+ return (0);
+ }
+
+ if (split_year) {
+ tm->tm_year = (tm->tm_year % 100) + (i * 100);
+ } else {
+ tm->tm_year = i * 100;
+ split_year = 1;
+ }
+ break;
+
+ case 'd': /* The day of month. */
+ case 'e':
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &tm->tm_mday, 1, 31))) {
+ return (0);
+ }
+ break;
+
+ case 'k': /* The hour (24-hour clock representation). */
+ LEGAL_ALT(0);
+ FALLTHROUGH;
+ case 'H':
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &tm->tm_hour, 0, 23))) {
+ return (0);
+ }
+ break;
+
+ case 'l': /* The hour (12-hour clock representation). */
+ LEGAL_ALT(0);
+ FALLTHROUGH;
+ case 'I':
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &tm->tm_hour, 1, 12))) {
+ return (0);
+ }
+ if (tm->tm_hour == 12) {
+ tm->tm_hour = 0;
+ }
+ break;
+
+ case 'j': /* The day of year. */
+ LEGAL_ALT(0);
+ if (!(conv_num(&bp, &i, 1, 366))) {
+ return (0);
+ }
+ tm->tm_yday = i - 1;
+ break;
+
+ case 'M': /* The minute. */
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &tm->tm_min, 0, 59))) {
+ return (0);
+ }
+ break;
+
+ case 'm': /* The month. */
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &i, 1, 12))) {
+ return (0);
+ }
+ tm->tm_mon = i - 1;
+ break;
+
+ case 'p': /* The locale's equivalent of AM/PM. */
+ LEGAL_ALT(0);
+ /* AM? */
+ if (strcasecmp(am_pm[0], bp) == 0) {
+ if (tm->tm_hour > 11) {
+ return (0);
+ }
+
+ bp += strlen(am_pm[0]);
+ break;
+ }
+ /* PM? */
+ else if (strcasecmp(am_pm[1], bp) == 0)
+ {
+ if (tm->tm_hour > 11) {
+ return (0);
+ }
+
+ tm->tm_hour += 12;
+ bp += strlen(am_pm[1]);
+ break;
+ }
+
+ /* Nothing matched. */
+ return (0);
+
+ case 'S': /* The seconds. */
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &tm->tm_sec, 0, 61))) {
+ return (0);
+ }
+ break;
+
+ case 'U': /* The week of year, beginning on sunday. */
+ case 'W': /* The week of year, beginning on monday. */
+ LEGAL_ALT(ALT_O);
+ /*
+ * XXX This is bogus, as we can not assume any valid
+ * information present in the tm structure at this
+ * point to calculate a real value, so just check the
+ * range for now.
+ */
+ if (!(conv_num(&bp, &i, 0, 53))) {
+ return (0);
+ }
+ break;
+
+ case 'w': /* The day of week, beginning on sunday. */
+ LEGAL_ALT(ALT_O);
+ if (!(conv_num(&bp, &tm->tm_wday, 0, 6))) {
+ return (0);
+ }
+ break;
+
+ case 'Y': /* The year. */
+ LEGAL_ALT(ALT_E);
+ if (!(conv_num(&bp, &i, 0, 9999))) {
+ return (0);
+ }
+
+ tm->tm_year = i - TM_YEAR_BASE;
+ break;
+
+ case 'y': /* The year within 100 years of the epoch. */
+ LEGAL_ALT(ALT_E | ALT_O);
+ if (!(conv_num(&bp, &i, 0, 99))) {
+ return (0);
+ }
+
+ if (split_year) {
+ tm->tm_year = ((tm->tm_year / 100) * 100) + i;
+ break;
+ }
+ split_year = 1;
+ if (i <= 68) {
+ tm->tm_year = i + 2000 - TM_YEAR_BASE;
+ } else {
+ tm->tm_year = i + 1900 - TM_YEAR_BASE;
+ }
+ break;
+
+ /*
+ * Miscellaneous conversions.
+ */
+ case 'n': /* Any kind of white-space. */
+ case 't':
+ LEGAL_ALT(0);
+ while (isspace((unsigned char)*bp)) {
+ bp++;
+ }
+ break;
+
+ default: /* Unknown/unsupported conversion. */
+ return (0);
+ }
+ }
+
+ /* LINTED functional specification */
+ DE_CONST(bp, ret);
+ return (ret);
+}
diff --git a/lib/isc/trampoline.c b/lib/isc/trampoline.c
new file mode 100644
index 0000000..58171d9
--- /dev/null
+++ b/lib/isc/trampoline.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <uv.h>
+
+#include <isc/mem.h>
+#include <isc/once.h>
+#include <isc/thread.h>
+#include <isc/util.h>
+
+#include "trampoline_p.h"
+
+#define ISC__TRAMPOLINE_UNUSED 0
+
+struct isc__trampoline {
+ int tid; /* const */
+ uintptr_t self;
+ isc_threadfunc_t start;
+ isc_threadarg_t arg;
+ void *jemalloc_enforce_init;
+};
+
+/*
+ * We can't use isc_mem API here, because it's called too early and the
+ * isc_mem_debugging flags can be changed later causing mismatch between flags
+ * used for isc_mem_get() and isc_mem_put().
+ *
+ * Since this is a single allocation at library load and deallocation at library
+ * unload, using the standard allocator without the tracking is fine for this
+ * single purpose.
+ *
+ * We can't use isc_mutex API either, because we track whether the mutexes get
+ * properly destroyed, and we intentionally leak the static mutex here without
+ * destroying it to prevent data race between library destructor running while
+ * thread is being still created.
+ */
+
+static uv_mutex_t isc__trampoline_lock;
+static isc__trampoline_t **trampolines;
+thread_local size_t isc_tid_v = SIZE_MAX;
+static size_t isc__trampoline_min = 1;
+static size_t isc__trampoline_max = 65;
+
+static isc__trampoline_t *
+isc__trampoline_new(int tid, isc_threadfunc_t start, isc_threadarg_t arg) {
+ isc__trampoline_t *trampoline = calloc(1, sizeof(*trampoline));
+ RUNTIME_CHECK(trampoline != NULL);
+
+ *trampoline = (isc__trampoline_t){
+ .tid = tid,
+ .start = start,
+ .arg = arg,
+ .self = ISC__TRAMPOLINE_UNUSED,
+ };
+
+ return (trampoline);
+}
+
+void
+isc__trampoline_initialize(void) {
+ uv_mutex_init(&isc__trampoline_lock);
+
+ trampolines = calloc(isc__trampoline_max, sizeof(trampolines[0]));
+ RUNTIME_CHECK(trampolines != NULL);
+
+ /* Get the trampoline slot 0 for the main thread */
+ trampolines[0] = isc__trampoline_new(0, NULL, NULL);
+ isc_tid_v = trampolines[0]->tid;
+ trampolines[0]->self = isc_thread_self();
+
+ /* Initialize the other trampolines */
+ for (size_t i = 1; i < isc__trampoline_max; i++) {
+ trampolines[i] = NULL;
+ }
+ isc__trampoline_min = 1;
+}
+
+void
+isc__trampoline_shutdown(void) {
+ /*
+ * When the program using the library exits abruptly and the library
+ * gets unloaded, there might be some existing trampolines from unjoined
+ * threads. We intentionally ignore those and don't check whether all
+ * trampolines have been cleared before exiting, so we leak a little bit
+ * of resources here, including the lock.
+ */
+ free(trampolines[0]);
+}
+
+isc__trampoline_t *
+isc__trampoline_get(isc_threadfunc_t start, isc_threadarg_t arg) {
+ isc__trampoline_t **tmp = NULL;
+ isc__trampoline_t *trampoline = NULL;
+ uv_mutex_lock(&isc__trampoline_lock);
+again:
+ for (size_t i = isc__trampoline_min; i < isc__trampoline_max; i++) {
+ if (trampolines[i] == NULL) {
+ trampoline = isc__trampoline_new(i, start, arg);
+ trampolines[i] = trampoline;
+ isc__trampoline_min = i + 1;
+ goto done;
+ }
+ }
+ tmp = calloc(2 * isc__trampoline_max, sizeof(trampolines[0]));
+ RUNTIME_CHECK(tmp != NULL);
+ for (size_t i = 0; i < isc__trampoline_max; i++) {
+ tmp[i] = trampolines[i];
+ }
+ for (size_t i = isc__trampoline_max; i < 2 * isc__trampoline_max; i++) {
+ tmp[i] = NULL;
+ }
+ free(trampolines);
+ trampolines = tmp;
+ isc__trampoline_max = isc__trampoline_max * 2;
+ goto again;
+done:
+ INSIST(trampoline != NULL);
+ uv_mutex_unlock(&isc__trampoline_lock);
+
+ return (trampoline);
+}
+
+void
+isc__trampoline_detach(isc__trampoline_t *trampoline) {
+ uv_mutex_lock(&isc__trampoline_lock);
+ REQUIRE(trampoline->self == isc_thread_self());
+ REQUIRE(trampoline->tid > 0);
+ REQUIRE((size_t)trampoline->tid < isc__trampoline_max);
+ REQUIRE(trampolines[trampoline->tid] == trampoline);
+
+ trampolines[trampoline->tid] = NULL;
+
+ if (isc__trampoline_min > (size_t)trampoline->tid) {
+ isc__trampoline_min = trampoline->tid;
+ }
+
+ free(trampoline->jemalloc_enforce_init);
+ free(trampoline);
+
+ uv_mutex_unlock(&isc__trampoline_lock);
+ return;
+}
+
+void
+isc__trampoline_attach(isc__trampoline_t *trampoline) {
+ uv_mutex_lock(&isc__trampoline_lock);
+ REQUIRE(trampoline->self == ISC__TRAMPOLINE_UNUSED);
+ REQUIRE(trampoline->tid > 0);
+ REQUIRE((size_t)trampoline->tid < isc__trampoline_max);
+ REQUIRE(trampolines[trampoline->tid] == trampoline);
+
+ /* Initialize the trampoline */
+ isc_tid_v = trampoline->tid;
+ trampoline->self = isc_thread_self();
+
+ /*
+ * Ensure every thread starts with a malloc() call to prevent memory
+ * bloat caused by a jemalloc quirk. While this dummy allocation is
+ * not used for anything, free() must not be immediately called for it
+ * so that an optimizing compiler does not strip away such a pair of
+ * malloc() + free() calls altogether, as it would foil the fix.
+ */
+ trampoline->jemalloc_enforce_init = malloc(8);
+ uv_mutex_unlock(&isc__trampoline_lock);
+}
+
+isc_threadresult_t
+isc__trampoline_run(isc_threadarg_t arg) {
+ isc__trampoline_t *trampoline = (isc__trampoline_t *)arg;
+ isc_threadresult_t result;
+
+ isc__trampoline_attach(trampoline);
+
+ /* Run the main function */
+ result = (trampoline->start)(trampoline->arg);
+
+ isc__trampoline_detach(trampoline);
+
+ return (result);
+}
diff --git a/lib/isc/trampoline_p.h b/lib/isc/trampoline_p.h
new file mode 100644
index 0000000..616a3dd
--- /dev/null
+++ b/lib/isc/trampoline_p.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <isc/thread.h>
+
+/*! \file isc/trampoline_p.h
+ * \brief isc__trampoline: allows safe reuse of thread ID numbers.
+ *
+ * The 'isc_hp' hazard pointer API uses an internal thread ID
+ * variable ('tid_v') that is incremented for each new thread that uses
+ * hazard pointers. This thread ID is then used as an index into a global
+ * shared table of hazard pointer state.
+ *
+ * Since the thread ID is only incremented and never decremented, the
+ * table can overflow if threads are frequently created and destroyed.
+ *
+ * A trampoline is a thin wrapper around any function to be called from
+ * a newly launched thread. It maintains a table of thread IDs used by
+ * current and previous threads; when a thread is destroyed, its slot in
+ * the trampoline table becomes available, and the next thread to occupy
+ * that slot can use the same thread ID that its predecessor did.
+ *
+ * The trampoline table initially has space for 64 worker threads in
+ * addition to the main thread. If more threads than that are in
+ * concurrent use, the table is reallocated with twice as much space.
+ * (Note that the number of concurrent threads is currently capped at
+ * 128 by the queue and hazard pointer implementations.)
+ */
+
+typedef struct isc__trampoline isc__trampoline_t;
+
+void
+isc__trampoline_initialize(void);
+/*%<
+ * Initialize the thread trampoline internal structures, must be called only
+ * once as a library constructor (see lib/isc/lib.c).
+ */
+
+void
+isc__trampoline_shutdown(void);
+/*%<
+ * Destroy the thread trampoline internal structures, must be called only
+ * once as a library destructor (see lib/isc/lib.c).
+ */
+
+isc__trampoline_t *
+isc__trampoline_get(isc_threadfunc_t start_routine, isc_threadarg_t arg);
+/*%<
+ * Get a free thread trampoline structure and initialize it with
+ * start_routine and arg passed to start_routine.
+ *
+ * Requires:
+ *\li 'start_routine' is a valid non-NULL thread start_routine
+ */
+
+void
+isc__trampoline_attach(isc__trampoline_t *trampoline);
+void
+isc__trampoline_detach(isc__trampoline_t *trampoline);
+/*%<
+ * Attach/detach the trampoline to/from the current thread.
+ *
+ * Requires:
+ * \li 'trampoline' is a valid isc__trampoline_t
+ */
+
+isc_threadresult_t
+isc__trampoline_run(isc_threadarg_t arg);
+/*%<
+ * Run the thread trampoline, this will get passed to the actual
+ * pthread_create(), initialize the isc_tid_v.
+ *
+ * Requires:
+ *\li 'arg' is a valid isc_trampoline_t
+ *
+ * Returns:
+ *\li return value from start_routine (see isc__trampoline_get())
+ */
diff --git a/lib/isc/url.c b/lib/isc/url.c
new file mode 100644
index 0000000..cccb712
--- /dev/null
+++ b/lib/isc/url.c
@@ -0,0 +1,671 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 and MIT
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * 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.
+ */
+
+#include <ctype.h>
+#include <limits.h>
+#include <stddef.h>
+#include <string.h>
+
+#include <isc/url.h>
+#include <isc/util.h>
+
+#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,
+};
+
+#undef T
+
+typedef enum {
+ 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_http_major,
+ s_res_http_dot,
+ s_res_http_minor,
+ s_res_http_end,
+ 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_http_I,
+ s_req_http_IC,
+ s_req_http_major,
+ s_req_http_dot,
+ s_req_http_minor,
+ s_req_http_end,
+ 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
+} state_t;
+
+typedef enum {
+ 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
+} host_state_t;
+
+/* Macros for character classes; depends on strict-mode */
+#define IS_MARK(c) \
+ ((c) == '-' || (c) == '_' || (c) == '.' || (c) == '!' || (c) == '~' || \
+ (c) == '*' || (c) == '\'' || (c) == '(' || (c) == ')')
+#define IS_USERINFO_CHAR(c) \
+ (isalnum((unsigned char)c) || IS_MARK(c) || (c) == '%' || \
+ (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
+ (c) == '$' || (c) == ',')
+
+#if HTTP_PARSER_STRICT
+#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c))
+#define IS_HOST_CHAR(c) (isalnum((unsigned char)c) || (c) == '.' || (c) == '-')
+#else
+#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c) || ((c)&0x80))
+#define IS_HOST_CHAR(c) \
+ (isalnum((unsigned char)c) || (c) == '.' || (c) == '-' || (c) == '_')
+#endif
+
+/*
+ * 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 state_t
+parse_url_char(state_t 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 (isalpha((unsigned char)ch)) {
+ return (s_req_schema);
+ }
+
+ break;
+
+ case s_req_schema:
+ if (isalpha((unsigned char)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 host_state_t
+http_parse_host_char(host_state_t 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 (isxdigit((unsigned char)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 (isalnum((unsigned char)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 (isdigit((unsigned char)ch)) {
+ return (s_http_host_port);
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ return (s_http_host_dead);
+}
+
+static isc_result_t
+http_parse_host(const char *buf, isc_url_parser_t *up, int found_at) {
+ host_state_t s;
+ const char *p = NULL;
+ size_t buflen = up->field_data[ISC_UF_HOST].off +
+ up->field_data[ISC_UF_HOST].len;
+
+ REQUIRE((up->field_set & (1 << ISC_UF_HOST)) != 0);
+
+ up->field_data[ISC_UF_HOST].len = 0;
+
+ s = found_at ? s_http_userinfo_start : s_http_host_start;
+
+ for (p = buf + up->field_data[ISC_UF_HOST].off; p < buf + buflen; p++) {
+ host_state_t new_s = http_parse_host_char(s, *p);
+
+ if (new_s == s_http_host_dead) {
+ return (ISC_R_FAILURE);
+ }
+
+ switch (new_s) {
+ case s_http_host:
+ if (s != s_http_host) {
+ up->field_data[ISC_UF_HOST].off =
+ (uint16_t)(p - buf);
+ }
+ up->field_data[ISC_UF_HOST].len++;
+ break;
+
+ case s_http_host_v6:
+ if (s != s_http_host_v6) {
+ up->field_data[ISC_UF_HOST].off =
+ (uint16_t)(p - buf);
+ }
+ up->field_data[ISC_UF_HOST].len++;
+ break;
+
+ case s_http_host_v6_zone_start:
+ case s_http_host_v6_zone:
+ up->field_data[ISC_UF_HOST].len++;
+ break;
+
+ case s_http_host_port:
+ if (s != s_http_host_port) {
+ up->field_data[ISC_UF_PORT].off =
+ (uint16_t)(p - buf);
+ up->field_data[ISC_UF_PORT].len = 0;
+ up->field_set |= (1 << ISC_UF_PORT);
+ }
+ up->field_data[ISC_UF_PORT].len++;
+ break;
+
+ case s_http_userinfo:
+ if (s != s_http_userinfo) {
+ up->field_data[ISC_UF_USERINFO].off =
+ (uint16_t)(p - buf);
+ up->field_data[ISC_UF_USERINFO].len = 0;
+ up->field_set |= (1 << ISC_UF_USERINFO);
+ }
+ up->field_data[ISC_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 (ISC_R_FAILURE);
+ default:
+ break;
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_url_parse(const char *buf, size_t buflen, bool is_connect,
+ isc_url_parser_t *up) {
+ state_t s;
+ isc_url_field_t uf, old_uf;
+ int found_at = 0;
+ const char *p = NULL;
+
+ if (buflen == 0) {
+ return (ISC_R_FAILURE);
+ }
+
+ up->port = up->field_set = 0;
+ s = is_connect ? s_req_server_start : s_req_spaces_before_url;
+ old_uf = ISC_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 (ISC_R_FAILURE);
+
+ /* 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 = ISC_UF_SCHEMA;
+ break;
+
+ case s_req_server_with_at:
+ found_at = 1;
+ FALLTHROUGH;
+ case s_req_server:
+ uf = ISC_UF_HOST;
+ break;
+
+ case s_req_path:
+ uf = ISC_UF_PATH;
+ break;
+
+ case s_req_query_string:
+ uf = ISC_UF_QUERY;
+ break;
+
+ case s_req_fragment:
+ uf = ISC_UF_FRAGMENT;
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+
+ /* Nothing's changed; soldier on */
+ if (uf == old_uf) {
+ up->field_data[uf].len++;
+ continue;
+ }
+
+ up->field_data[uf].off = (uint16_t)(p - buf);
+ up->field_data[uf].len = 1;
+
+ up->field_set |= (1 << uf);
+ old_uf = uf;
+ }
+
+ /* host must be present if there is a schema */
+ /* parsing http:///toto will fail */
+ if ((up->field_set & (1 << ISC_UF_SCHEMA)) &&
+ (up->field_set & (1 << ISC_UF_HOST)) == 0)
+ {
+ return (ISC_R_FAILURE);
+ }
+
+ if (up->field_set & (1 << ISC_UF_HOST)) {
+ isc_result_t result;
+
+ result = http_parse_host(buf, up, found_at);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ }
+
+ /* CONNECT requests can only contain "hostname:port" */
+ if (is_connect &&
+ up->field_set != ((1 << ISC_UF_HOST) | (1 << ISC_UF_PORT)))
+ {
+ return (ISC_R_FAILURE);
+ }
+
+ if (up->field_set & (1 << ISC_UF_PORT)) {
+ uint16_t off;
+ uint16_t len;
+ const char *pp = NULL;
+ const char *end = NULL;
+ unsigned long v;
+
+ off = up->field_data[ISC_UF_PORT].off;
+ len = up->field_data[ISC_UF_PORT].len;
+ end = buf + off + len;
+
+ /*
+ * NOTE: The characters are already validated and are in the
+ * [0-9] range
+ */
+ INSIST(off + len <= buflen);
+
+ v = 0;
+ for (pp = buf + off; pp < end; pp++) {
+ v *= 10;
+ v += *pp - '0';
+
+ /* Ports have a max value of 2^16 */
+ if (v > 0xffff) {
+ return (ISC_R_RANGE);
+ }
+ }
+
+ up->port = (uint16_t)v;
+ }
+
+ return (ISC_R_SUCCESS);
+}
diff --git a/lib/isc/utf8.c b/lib/isc/utf8.c
new file mode 100644
index 0000000..a348c5d
--- /dev/null
+++ b/lib/isc/utf8.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <string.h>
+
+#include <isc/utf8.h>
+#include <isc/util.h>
+
+/*
+ * UTF-8 is defined in "The Unicode Standard -- Version 4.0"
+ * Also see RFC 3629.
+ *
+ * Char. number range | UTF-8 octet sequence
+ * (hexadecimal) | (binary)
+ * --------------------+---------------------------------------------
+ * 0000 0000-0000 007F | 0xxxxxxx
+ * 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
+ * 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
+ * 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ */
+bool
+isc_utf8_valid(const unsigned char *buf, size_t len) {
+ REQUIRE(buf != NULL);
+
+ for (size_t i = 0; i < len; i++) {
+ if (buf[i] <= 0x7f) {
+ continue;
+ }
+ if ((i + 1) < len && (buf[i] & 0xe0) == 0xc0 &&
+ (buf[i + 1] & 0xc0) == 0x80)
+ {
+ unsigned int w;
+ w = (buf[i] & 0x1f) << 6;
+ w |= (buf[++i] & 0x3f);
+ if (w < 0x80) {
+ return (false);
+ }
+ continue;
+ }
+ if ((i + 2) < len && (buf[i] & 0xf0) == 0xe0 &&
+ (buf[i + 1] & 0xc0) == 0x80 && (buf[i + 2] & 0xc0) == 0x80)
+ {
+ unsigned int w;
+ w = (buf[i] & 0x0f) << 12;
+ w |= (buf[++i] & 0x3f) << 6;
+ w |= (buf[++i] & 0x3f);
+ if (w < 0x0800) {
+ return (false);
+ }
+ continue;
+ }
+ if ((i + 3) < len && (buf[i] & 0xf8) == 0xf0 &&
+ (buf[i + 1] & 0xc0) == 0x80 &&
+ (buf[i + 2] & 0xc0) == 0x80 && (buf[i + 3] & 0xc0) == 0x80)
+ {
+ unsigned int w;
+ w = (buf[i] & 0x07) << 18;
+ w |= (buf[++i] & 0x3f) << 12;
+ w |= (buf[++i] & 0x3f) << 6;
+ w |= (buf[++i] & 0x3f);
+ if (w < 0x10000 || w > 0x10FFFF) {
+ return (false);
+ }
+ continue;
+ }
+ return (false);
+ }
+ return (true);
+}
+
+bool
+isc_utf8_bom(const unsigned char *buf, size_t len) {
+ REQUIRE(buf != NULL);
+
+ if (len >= 3U && !memcmp(buf, "\xef\xbb\xbf", 3)) {
+ return (true);
+ }
+ return (false);
+}
diff --git a/lib/isccc/Makefile.am b/lib/isccc/Makefile.am
new file mode 100644
index 0000000..7877bfb
--- /dev/null
+++ b/lib/isccc/Makefile.am
@@ -0,0 +1,38 @@
+include $(top_srcdir)/Makefile.top
+
+lib_LTLIBRARIES = libisccc.la
+
+libisccc_ladir = $(includedir)/isccc
+libisccc_la_HEADERS = \
+ include/isccc/alist.h \
+ include/isccc/base64.h \
+ include/isccc/cc.h \
+ include/isccc/ccmsg.h \
+ include/isccc/events.h \
+ include/isccc/sexpr.h \
+ include/isccc/symtab.h \
+ include/isccc/symtype.h \
+ include/isccc/types.h \
+ include/isccc/util.h
+
+libisccc_la_SOURCES = \
+ $(libisccc_la_HEADERS) \
+ alist.c \
+ base64.c \
+ cc.c \
+ ccmsg.c \
+ sexpr.c \
+ symtab.c
+
+libisccc_la_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ $(LIBISC_CFLAGS) \
+ $(LIBDNS_CFLAGS) \
+ $(LIBISCCC_CFLAGS)
+
+libisccc_la_LIBADD = \
+ $(LIBISC_LIBS)
+
+libisccc_la_LDFLAGS = \
+ $(AM_LDFLAGS) \
+ -release "$(PACKAGE_VERSION)"
diff --git a/lib/isccc/Makefile.in b/lib/isccc/Makefile.in
new file mode 100644
index 0000000..3fee7ca
--- /dev/null
+++ b/lib/isccc/Makefile.in
@@ -0,0 +1,956 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 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@
+
+# Hey Emacs, this is -*- makefile-automake -*- file!
+# vim: filetype=automake
+
+
+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@
+target_triplet = @target@
+@HOST_MACOS_TRUE@am__append_1 = \
+@HOST_MACOS_TRUE@ -Wl,-flat_namespace
+
+subdir = lib/isccc
+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/ax_check_openssl.m4 \
+ $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4/ax_jemalloc.m4 \
+ $(top_srcdir)/m4/ax_lib_lmdb.m4 \
+ $(top_srcdir)/m4/ax_perl_module.m4 \
+ $(top_srcdir)/m4/ax_posix_shell.m4 \
+ $(top_srcdir)/m4/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4/ax_pthread.m4 \
+ $(top_srcdir)/m4/ax_python_module.m4 \
+ $(top_srcdir)/m4/ax_restore_flags.m4 \
+ $(top_srcdir)/m4/ax_save_flags.m4 $(top_srcdir)/m4/ax_tls.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(libisccc_la_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libisccc_ladir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+libisccc_la_DEPENDENCIES = $(LIBISC_LIBS)
+am__objects_1 =
+am_libisccc_la_OBJECTS = $(am__objects_1) libisccc_la-alist.lo \
+ libisccc_la-base64.lo libisccc_la-cc.lo libisccc_la-ccmsg.lo \
+ libisccc_la-sexpr.lo libisccc_la-symtab.lo
+libisccc_la_OBJECTS = $(am_libisccc_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 =
+libisccc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libisccc_la_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@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/libisccc_la-alist.Plo \
+ ./$(DEPDIR)/libisccc_la-base64.Plo \
+ ./$(DEPDIR)/libisccc_la-cc.Plo \
+ ./$(DEPDIR)/libisccc_la-ccmsg.Plo \
+ ./$(DEPDIR)/libisccc_la-sexpr.Plo \
+ ./$(DEPDIR)/libisccc_la-symtab.Plo
+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 = $(libisccc_la_SOURCES)
+DIST_SOURCES = $(libisccc_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(libisccc_la_HEADERS)
+am__extra_recursive_targets = test-recursive unit-recursive \
+ doc-recursive
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/Makefile.top \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CURL = @CURL@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DEVELOPER_MODE = @DEVELOPER_MODE@
+DLLTOOL = @DLLTOOL@
+DNSTAP_CFLAGS = @DNSTAP_CFLAGS@
+DNSTAP_LIBS = @DNSTAP_LIBS@
+DOXYGEN = @DOXYGEN@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILECMD = @FILECMD@
+FSTRM_CAPTURE = @FSTRM_CAPTURE@
+FUZZ_LDFLAGS = @FUZZ_LDFLAGS@
+FUZZ_LOG_COMPILER = @FUZZ_LOG_COMPILER@
+GREP = @GREP@
+GSSAPI_CFLAGS = @GSSAPI_CFLAGS@
+GSSAPI_LIBS = @GSSAPI_LIBS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@
+JEMALLOC_LIBS = @JEMALLOC_LIBS@
+JSON_C_CFLAGS = @JSON_C_CFLAGS@
+JSON_C_LIBS = @JSON_C_LIBS@
+KRB5_CFLAGS = @KRB5_CFLAGS@
+KRB5_CONFIG = @KRB5_CONFIG@
+KRB5_LIBS = @KRB5_LIBS@
+LATEXMK = @LATEXMK@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBCAP_LIBS = @LIBCAP_LIBS@
+LIBIDN2_CFLAGS = @LIBIDN2_CFLAGS@
+LIBIDN2_LIBS = @LIBIDN2_LIBS@
+LIBNGHTTP2_CFLAGS = @LIBNGHTTP2_CFLAGS@
+LIBNGHTTP2_LIBS = @LIBNGHTTP2_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUV_CFLAGS = @LIBUV_CFLAGS@
+LIBUV_LIBS = @LIBUV_LIBS@
+LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
+LIBXML2_LIBS = @LIBXML2_LIBS@
+LIPO = @LIPO@
+LMDB_CFLAGS = @LMDB_CFLAGS@
+LMDB_LIBS = @LMDB_LIBS@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MAXMINDDB_CFLAGS = @MAXMINDDB_CFLAGS@
+MAXMINDDB_LIBS = @MAXMINDDB_LIBS@
+MAXMINDDB_PREFIX = @MAXMINDDB_PREFIX@
+MKDIR_P = @MKDIR_P@
+NC = @NC@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENSSL_CFLAGS = @OPENSSL_CFLAGS@
+OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@
+OPENSSL_LIBS = @OPENSSL_LIBS@
+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@
+PERL = @PERL@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PROTOC_C = @PROTOC_C@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_CXX = @PTHREAD_CXX@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+PYTEST = @PYTEST@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+READLINE_CFLAGS = @READLINE_CFLAGS@
+READLINE_LIBS = @READLINE_LIBS@
+RELEASE_DATE = @RELEASE_DATE@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINX_BUILD = @SPHINX_BUILD@
+STD_CFLAGS = @STD_CFLAGS@
+STD_CPPFLAGS = @STD_CPPFLAGS@
+STD_LDFLAGS = @STD_LDFLAGS@
+STRIP = @STRIP@
+TEST_CFLAGS = @TEST_CFLAGS@
+VERSION = @VERSION@
+XELATEX = @XELATEX@
+XSLTPROC = @XSLTPROC@
+ZLIB_CFLAGS = @ZLIB_CFLAGS@
+ZLIB_LIBS = @ZLIB_LIBS@
+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_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+ax_pthread_config = @ax_pthread_config@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+ACLOCAL_AMFLAGS = -I $(top_srcdir)/m4
+AM_CFLAGS = \
+ $(STD_CFLAGS)
+
+AM_CPPFLAGS = \
+ $(STD_CPPFLAGS) \
+ -include $(top_builddir)/config.h \
+ -I$(srcdir)/include
+
+AM_LDFLAGS = $(STD_LDFLAGS) $(am__append_1)
+LDADD =
+LIBISC_CFLAGS = \
+ -I$(top_srcdir)/include \
+ -I$(top_srcdir)/lib/isc/include \
+ -I$(top_builddir)/lib/isc/include
+
+LIBISC_LIBS = $(top_builddir)/lib/isc/libisc.la
+LIBDNS_CFLAGS = \
+ -I$(top_srcdir)/lib/dns/include \
+ -I$(top_builddir)/lib/dns/include
+
+LIBDNS_LIBS = \
+ $(top_builddir)/lib/dns/libdns.la
+
+LIBNS_CFLAGS = \
+ -I$(top_srcdir)/lib/ns/include
+
+LIBNS_LIBS = \
+ $(top_builddir)/lib/ns/libns.la
+
+LIBIRS_CFLAGS = \
+ -I$(top_srcdir)/lib/irs/include
+
+LIBIRS_LIBS = \
+ $(top_builddir)/lib/irs/libirs.la
+
+LIBISCCFG_CFLAGS = \
+ -I$(top_srcdir)/lib/isccfg/include
+
+LIBISCCFG_LIBS = \
+ $(top_builddir)/lib/isccfg/libisccfg.la
+
+LIBISCCC_CFLAGS = \
+ -I$(top_srcdir)/lib/isccc/include/
+
+LIBISCCC_LIBS = \
+ $(top_builddir)/lib/isccc/libisccc.la
+
+LIBBIND9_CFLAGS = \
+ -I$(top_srcdir)/lib/bind9/include
+
+LIBBIND9_LIBS = \
+ $(top_builddir)/lib/bind9/libbind9.la
+
+lib_LTLIBRARIES = libisccc.la
+libisccc_ladir = $(includedir)/isccc
+libisccc_la_HEADERS = \
+ include/isccc/alist.h \
+ include/isccc/base64.h \
+ include/isccc/cc.h \
+ include/isccc/ccmsg.h \
+ include/isccc/events.h \
+ include/isccc/sexpr.h \
+ include/isccc/symtab.h \
+ include/isccc/symtype.h \
+ include/isccc/types.h \
+ include/isccc/util.h
+
+libisccc_la_SOURCES = \
+ $(libisccc_la_HEADERS) \
+ alist.c \
+ base64.c \
+ cc.c \
+ ccmsg.c \
+ sexpr.c \
+ symtab.c
+
+libisccc_la_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ $(LIBISC_CFLAGS) \
+ $(LIBDNS_CFLAGS) \
+ $(LIBISCCC_CFLAGS)
+
+libisccc_la_LIBADD = \
+ $(LIBISC_LIBS)
+
+libisccc_la_LDFLAGS = \
+ $(AM_LDFLAGS) \
+ -release "$(PACKAGE_VERSION)"
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.top $(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 lib/isccc/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign lib/isccc/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_srcdir)/Makefile.top $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+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}; \
+ }
+
+libisccc.la: $(libisccc_la_OBJECTS) $(libisccc_la_DEPENDENCIES) $(EXTRA_libisccc_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libisccc_la_LINK) -rpath $(libdir) $(libisccc_la_OBJECTS) $(libisccc_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisccc_la-alist.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisccc_la-base64.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisccc_la-cc.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisccc_la-ccmsg.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisccc_la-sexpr.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisccc_la-symtab.Plo@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 $@ $<
+
+libisccc_la-alist.lo: alist.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisccc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisccc_la-alist.lo -MD -MP -MF $(DEPDIR)/libisccc_la-alist.Tpo -c -o libisccc_la-alist.lo `test -f 'alist.c' || echo '$(srcdir)/'`alist.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisccc_la-alist.Tpo $(DEPDIR)/libisccc_la-alist.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='alist.c' object='libisccc_la-alist.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisccc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisccc_la-alist.lo `test -f 'alist.c' || echo '$(srcdir)/'`alist.c
+
+libisccc_la-base64.lo: base64.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisccc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisccc_la-base64.lo -MD -MP -MF $(DEPDIR)/libisccc_la-base64.Tpo -c -o libisccc_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisccc_la-base64.Tpo $(DEPDIR)/libisccc_la-base64.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='base64.c' object='libisccc_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) $(libisccc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisccc_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c
+
+libisccc_la-cc.lo: cc.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisccc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisccc_la-cc.lo -MD -MP -MF $(DEPDIR)/libisccc_la-cc.Tpo -c -o libisccc_la-cc.lo `test -f 'cc.c' || echo '$(srcdir)/'`cc.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisccc_la-cc.Tpo $(DEPDIR)/libisccc_la-cc.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cc.c' object='libisccc_la-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) $(libisccc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisccc_la-cc.lo `test -f 'cc.c' || echo '$(srcdir)/'`cc.c
+
+libisccc_la-ccmsg.lo: ccmsg.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisccc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisccc_la-ccmsg.lo -MD -MP -MF $(DEPDIR)/libisccc_la-ccmsg.Tpo -c -o libisccc_la-ccmsg.lo `test -f 'ccmsg.c' || echo '$(srcdir)/'`ccmsg.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisccc_la-ccmsg.Tpo $(DEPDIR)/libisccc_la-ccmsg.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ccmsg.c' object='libisccc_la-ccmsg.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisccc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisccc_la-ccmsg.lo `test -f 'ccmsg.c' || echo '$(srcdir)/'`ccmsg.c
+
+libisccc_la-sexpr.lo: sexpr.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisccc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisccc_la-sexpr.lo -MD -MP -MF $(DEPDIR)/libisccc_la-sexpr.Tpo -c -o libisccc_la-sexpr.lo `test -f 'sexpr.c' || echo '$(srcdir)/'`sexpr.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisccc_la-sexpr.Tpo $(DEPDIR)/libisccc_la-sexpr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sexpr.c' object='libisccc_la-sexpr.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisccc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisccc_la-sexpr.lo `test -f 'sexpr.c' || echo '$(srcdir)/'`sexpr.c
+
+libisccc_la-symtab.lo: symtab.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisccc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisccc_la-symtab.lo -MD -MP -MF $(DEPDIR)/libisccc_la-symtab.Tpo -c -o libisccc_la-symtab.lo `test -f 'symtab.c' || echo '$(srcdir)/'`symtab.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisccc_la-symtab.Tpo $(DEPDIR)/libisccc_la-symtab.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='symtab.c' object='libisccc_la-symtab.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisccc_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisccc_la-symtab.lo `test -f 'symtab.c' || echo '$(srcdir)/'`symtab.c
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-libisccc_laHEADERS: $(libisccc_la_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(libisccc_la_HEADERS)'; test -n "$(libisccc_ladir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libisccc_ladir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libisccc_ladir)" || 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)$(libisccc_ladir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(libisccc_ladir)" || exit $$?; \
+ done
+
+uninstall-libisccc_laHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libisccc_la_HEADERS)'; test -n "$(libisccc_ladir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(libisccc_ladir)'; $(am__uninstall_files_from_dir)
+test-local:
+unit-local:
+doc-local:
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libisccc_ladir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/libisccc_la-alist.Plo
+ -rm -f ./$(DEPDIR)/libisccc_la-base64.Plo
+ -rm -f ./$(DEPDIR)/libisccc_la-cc.Plo
+ -rm -f ./$(DEPDIR)/libisccc_la-ccmsg.Plo
+ -rm -f ./$(DEPDIR)/libisccc_la-sexpr.Plo
+ -rm -f ./$(DEPDIR)/libisccc_la-symtab.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+doc: doc-am
+
+doc-am: doc-local
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-libisccc_laHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/libisccc_la-alist.Plo
+ -rm -f ./$(DEPDIR)/libisccc_la-base64.Plo
+ -rm -f ./$(DEPDIR)/libisccc_la-cc.Plo
+ -rm -f ./$(DEPDIR)/libisccc_la-ccmsg.Plo
+ -rm -f ./$(DEPDIR)/libisccc_la-sexpr.Plo
+ -rm -f ./$(DEPDIR)/libisccc_la-symtab.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+test: test-am
+
+test-am: test-local
+
+uninstall-am: uninstall-libLTLIBRARIES uninstall-libisccc_laHEADERS
+
+unit: unit-am
+
+unit-am: unit-local
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libLTLIBRARIES clean-libtool cscopelist-am \
+ ctags ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir doc-am doc-local 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-libLTLIBRARIES \
+ install-libisccc_laHEADERS install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am test-am test-local uninstall uninstall-am \
+ uninstall-libLTLIBRARIES uninstall-libisccc_laHEADERS unit-am \
+ unit-local
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/lib/isccc/alist.c b/lib/isccc/alist.c
new file mode 100644
index 0000000..182ada0
--- /dev/null
+++ b/lib/isccc/alist.c
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) 2001 Nominum, Inc.
+ *
+ * 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 ISC AND NOMINUM DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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.
+ */
+
+/*! \file */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/assertions.h>
+#include <isc/print.h>
+#include <isc/result.h>
+
+#include <isccc/alist.h>
+#include <isccc/sexpr.h>
+#include <isccc/util.h>
+
+#define CAR(s) (s)->value.as_dottedpair.car
+#define CDR(s) (s)->value.as_dottedpair.cdr
+
+#define ALIST_TAG "*alist*"
+#define MAX_INDENT 64
+
+static char spaces[MAX_INDENT + 1] = " "
+ " ";
+
+isccc_sexpr_t *
+isccc_alist_create(void) {
+ isccc_sexpr_t *alist, *tag;
+
+ tag = isccc_sexpr_fromstring(ALIST_TAG);
+ if (tag == NULL) {
+ return (NULL);
+ }
+ alist = isccc_sexpr_cons(tag, NULL);
+ if (alist == NULL) {
+ isccc_sexpr_free(&tag);
+ return (NULL);
+ }
+
+ return (alist);
+}
+
+bool
+isccc_alist_alistp(isccc_sexpr_t *alist) {
+ isccc_sexpr_t *car;
+
+ if (alist == NULL || alist->type != ISCCC_SEXPRTYPE_DOTTEDPAIR) {
+ return (false);
+ }
+ car = CAR(alist);
+ if (car == NULL || car->type != ISCCC_SEXPRTYPE_STRING) {
+ return (false);
+ }
+ if (strcmp(car->value.as_string, ALIST_TAG) != 0) {
+ return (false);
+ }
+ return (true);
+}
+
+bool
+isccc_alist_emptyp(isccc_sexpr_t *alist) {
+ REQUIRE(isccc_alist_alistp(alist));
+
+ if (CDR(alist) == NULL) {
+ return (true);
+ }
+ return (false);
+}
+
+isccc_sexpr_t *
+isccc_alist_first(isccc_sexpr_t *alist) {
+ REQUIRE(isccc_alist_alistp(alist));
+
+ return (CDR(alist));
+}
+
+isccc_sexpr_t *
+isccc_alist_assq(isccc_sexpr_t *alist, const char *key) {
+ isccc_sexpr_t *car, *caar;
+
+ REQUIRE(isccc_alist_alistp(alist));
+
+ /*
+ * Skip alist type tag.
+ */
+ alist = CDR(alist);
+
+ while (alist != NULL) {
+ INSIST(alist->type == ISCCC_SEXPRTYPE_DOTTEDPAIR);
+ car = CAR(alist);
+ INSIST(car->type == ISCCC_SEXPRTYPE_DOTTEDPAIR);
+ caar = CAR(car);
+ if (caar->type == ISCCC_SEXPRTYPE_STRING &&
+ strcmp(caar->value.as_string, key) == 0)
+ {
+ return (car);
+ }
+ alist = CDR(alist);
+ }
+
+ return (NULL);
+}
+
+void
+isccc_alist_delete(isccc_sexpr_t *alist, const char *key) {
+ isccc_sexpr_t *car, *caar, *rest, *prev;
+
+ REQUIRE(isccc_alist_alistp(alist));
+
+ prev = alist;
+ rest = CDR(alist);
+ while (rest != NULL) {
+ INSIST(rest->type == ISCCC_SEXPRTYPE_DOTTEDPAIR);
+ car = CAR(rest);
+ INSIST(car != NULL && car->type == ISCCC_SEXPRTYPE_DOTTEDPAIR);
+ caar = CAR(car);
+ if (caar->type == ISCCC_SEXPRTYPE_STRING &&
+ strcmp(caar->value.as_string, key) == 0)
+ {
+ CDR(prev) = CDR(rest);
+ CDR(rest) = NULL;
+ isccc_sexpr_free(&rest);
+ break;
+ }
+ prev = rest;
+ rest = CDR(rest);
+ }
+}
+
+isccc_sexpr_t *
+isccc_alist_define(isccc_sexpr_t *alist, const char *key,
+ isccc_sexpr_t *value) {
+ isccc_sexpr_t *kv, *k, *elt;
+
+ kv = isccc_alist_assq(alist, key);
+ if (kv == NULL) {
+ /*
+ * New association.
+ */
+ k = isccc_sexpr_fromstring(key);
+ if (k == NULL) {
+ return (NULL);
+ }
+ kv = isccc_sexpr_cons(k, value);
+ if (kv == NULL) {
+ isccc_sexpr_free(&kv);
+ return (NULL);
+ }
+ elt = isccc_sexpr_addtolist(&alist, kv);
+ if (elt == NULL) {
+ isccc_sexpr_free(&kv);
+ return (NULL);
+ }
+ } else {
+ /*
+ * We've already got an entry for this key. Replace it.
+ */
+ isccc_sexpr_free(&CDR(kv));
+ CDR(kv) = value;
+ }
+
+ return (kv);
+}
+
+isccc_sexpr_t *
+isccc_alist_definestring(isccc_sexpr_t *alist, const char *key,
+ const char *str) {
+ isccc_sexpr_t *v, *kv;
+
+ v = isccc_sexpr_fromstring(str);
+ if (v == NULL) {
+ return (NULL);
+ }
+ kv = isccc_alist_define(alist, key, v);
+ if (kv == NULL) {
+ isccc_sexpr_free(&v);
+ }
+
+ return (kv);
+}
+
+isccc_sexpr_t *
+isccc_alist_definebinary(isccc_sexpr_t *alist, const char *key,
+ isccc_region_t *r) {
+ isccc_sexpr_t *v, *kv;
+
+ v = isccc_sexpr_frombinary(r);
+ if (v == NULL) {
+ return (NULL);
+ }
+ kv = isccc_alist_define(alist, key, v);
+ if (kv == NULL) {
+ isccc_sexpr_free(&v);
+ }
+
+ return (kv);
+}
+
+isccc_sexpr_t *
+isccc_alist_lookup(isccc_sexpr_t *alist, const char *key) {
+ isccc_sexpr_t *kv;
+
+ kv = isccc_alist_assq(alist, key);
+ if (kv != NULL) {
+ return (CDR(kv));
+ }
+ return (NULL);
+}
+
+isc_result_t
+isccc_alist_lookupstring(isccc_sexpr_t *alist, const char *key, char **strp) {
+ isccc_sexpr_t *kv, *v;
+
+ kv = isccc_alist_assq(alist, key);
+ if (kv != NULL) {
+ v = CDR(kv);
+ if (isccc_sexpr_stringp(v)) {
+ if (strp != NULL) {
+ *strp = isccc_sexpr_tostring(v);
+ }
+ return (ISC_R_SUCCESS);
+ } else {
+ return (ISC_R_EXISTS);
+ }
+ }
+
+ return (ISC_R_NOTFOUND);
+}
+
+isc_result_t
+isccc_alist_lookupbinary(isccc_sexpr_t *alist, const char *key,
+ isccc_region_t **r) {
+ isccc_sexpr_t *kv, *v;
+
+ kv = isccc_alist_assq(alist, key);
+ if (kv != NULL) {
+ v = CDR(kv);
+ if (isccc_sexpr_binaryp(v)) {
+ if (r != NULL) {
+ *r = isccc_sexpr_tobinary(v);
+ }
+ return (ISC_R_SUCCESS);
+ } else {
+ return (ISC_R_EXISTS);
+ }
+ }
+
+ return (ISC_R_NOTFOUND);
+}
+
+void
+isccc_alist_prettyprint(isccc_sexpr_t *sexpr, unsigned int indent,
+ FILE *stream) {
+ isccc_sexpr_t *elt, *kv, *k, *v;
+
+ if (isccc_alist_alistp(sexpr)) {
+ fprintf(stream, "{\n");
+ indent += 4;
+ for (elt = isccc_alist_first(sexpr); elt != NULL;
+ elt = CDR(elt))
+ {
+ kv = CAR(elt);
+ INSIST(isccc_sexpr_listp(kv));
+ k = CAR(kv);
+ v = CDR(kv);
+ INSIST(isccc_sexpr_stringp(k));
+ fprintf(stream, "%.*s%s => ", (int)indent, spaces,
+ isccc_sexpr_tostring(k));
+ isccc_alist_prettyprint(v, indent, stream);
+ if (CDR(elt) != NULL) {
+ fprintf(stream, ",");
+ }
+ fprintf(stream, "\n");
+ }
+ indent -= 4;
+ fprintf(stream, "%.*s}", (int)indent, spaces);
+ } else if (isccc_sexpr_listp(sexpr)) {
+ fprintf(stream, "(\n");
+ indent += 4;
+ for (elt = sexpr; elt != NULL; elt = CDR(elt)) {
+ fprintf(stream, "%.*s", (int)indent, spaces);
+ isccc_alist_prettyprint(CAR(elt), indent, stream);
+ if (CDR(elt) != NULL) {
+ fprintf(stream, ",");
+ }
+ fprintf(stream, "\n");
+ }
+ indent -= 4;
+ fprintf(stream, "%.*s)", (int)indent, spaces);
+ } else {
+ isccc_sexpr_print(sexpr, stream);
+ }
+}
diff --git a/lib/isccc/base64.c b/lib/isccc/base64.c
new file mode 100644
index 0000000..fd0f381
--- /dev/null
+++ b/lib/isccc/base64.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) 2001 Nominum, Inc.
+ *
+ * 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 ISC AND NOMINUM DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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.
+ */
+
+/*! \file */
+
+#include <isc/base64.h>
+#include <isc/buffer.h>
+#include <isc/region.h>
+#include <isc/result.h>
+
+#include <isccc/base64.h>
+#include <isccc/util.h>
+
+isc_result_t
+isccc_base64_encode(isccc_region_t *source, int wordlength,
+ const char *wordbreak, isccc_region_t *target) {
+ isc_region_t sr;
+ isc_buffer_t tb;
+ isc_result_t result;
+
+ sr.base = source->rstart;
+ sr.length = (unsigned int)(source->rend - source->rstart);
+ isc_buffer_init(&tb, target->rstart,
+ (unsigned int)(target->rend - target->rstart));
+
+ result = isc_base64_totext(&sr, wordlength, wordbreak, &tb);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ source->rstart = source->rend;
+ target->rstart = isc_buffer_used(&tb);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isccc_base64_decode(const char *cstr, isccc_region_t *target) {
+ isc_buffer_t b;
+ isc_result_t result;
+
+ isc_buffer_init(&b, target->rstart,
+ (unsigned int)(target->rend - target->rstart));
+ result = isc_base64_decodestring(cstr, &b);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ target->rstart = isc_buffer_used(&b);
+ return (ISC_R_SUCCESS);
+}
diff --git a/lib/isccc/cc.c b/lib/isccc/cc.c
new file mode 100644
index 0000000..cbd9bad
--- /dev/null
+++ b/lib/isccc/cc.c
@@ -0,0 +1,1057 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) 2001 Nominum, Inc.
+ *
+ * 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 ISC AND NOMINUM DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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.
+ */
+
+/*! \file */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/assertions.h>
+#include <isc/hmac.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/safe.h>
+
+#include <isccc/alist.h>
+#include <isccc/base64.h>
+#include <isccc/cc.h>
+#include <isccc/sexpr.h>
+#include <isccc/symtab.h>
+#include <isccc/symtype.h>
+#include <isccc/util.h>
+
+#define MAX_TAGS 256
+#define DUP_LIFETIME 900
+#ifndef ISCCC_MAXDEPTH
+#define ISCCC_MAXDEPTH \
+ 10 /* Big enough for rndc which just sends a string each way. */
+#endif
+
+typedef isccc_sexpr_t *sexpr_ptr;
+
+static unsigned char auth_hmd5[] = {
+ 0x05, 0x5f, 0x61, 0x75, 0x74, 0x68, /*%< len + _auth */
+ ISCCC_CCMSGTYPE_TABLE, /*%< message type */
+ 0x00, 0x00, 0x00, 0x20, /*%< length == 32 */
+ 0x04, 0x68, 0x6d, 0x64, 0x35, /*%< len + hmd5 */
+ ISCCC_CCMSGTYPE_BINARYDATA, /*%< message type */
+ 0x00, 0x00, 0x00, 0x16, /*%< length == 22 */
+ /*
+ * The base64 encoding of one of our HMAC-MD5 signatures is
+ * 22 bytes.
+ */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+#define HMD5_OFFSET 21 /*%< 21 = 6 + 1 + 4 + 5 + 1 + 4 */
+#define HMD5_LENGTH 22
+
+static unsigned char auth_hsha[] = {
+ 0x05, 0x5f, 0x61, 0x75, 0x74, 0x68, /*%< len + _auth */
+ ISCCC_CCMSGTYPE_TABLE, /*%< message type */
+ 0x00, 0x00, 0x00, 0x63, /*%< length == 99 */
+ 0x04, 0x68, 0x73, 0x68, 0x61, /*%< len + hsha */
+ ISCCC_CCMSGTYPE_BINARYDATA, /*%< message type */
+ 0x00, 0x00, 0x00, 0x59, /*%< length == 89 */
+ 0x00, /*%< algorithm */
+ /*
+ * The base64 encoding of one of our HMAC-SHA* signatures is
+ * 88 bytes.
+ */
+ 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, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
+
+#define HSHA_OFFSET 22 /*%< 21 = 6 + 1 + 4 + 5 + 1 + 4 + 1 */
+#define HSHA_LENGTH 88
+
+static isc_result_t
+table_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer);
+
+static isc_result_t
+list_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer);
+
+static isc_result_t
+value_towire(isccc_sexpr_t *elt, isc_buffer_t **buffer) {
+ unsigned int len;
+ isccc_region_t *vr;
+ isc_result_t result;
+
+ if (isccc_sexpr_binaryp(elt)) {
+ vr = isccc_sexpr_tobinary(elt);
+ len = REGION_SIZE(*vr);
+ result = isc_buffer_reserve(buffer, 1 + 4);
+ if (result != ISC_R_SUCCESS) {
+ return (ISC_R_NOSPACE);
+ }
+ isc_buffer_putuint8(*buffer, ISCCC_CCMSGTYPE_BINARYDATA);
+ isc_buffer_putuint32(*buffer, len);
+
+ result = isc_buffer_reserve(buffer, len);
+ if (result != ISC_R_SUCCESS) {
+ return (ISC_R_NOSPACE);
+ }
+ isc_buffer_putmem(*buffer, vr->rstart, len);
+ } else if (isccc_alist_alistp(elt)) {
+ unsigned int used;
+ isc_buffer_t b;
+
+ result = isc_buffer_reserve(buffer, 1 + 4);
+ if (result != ISC_R_SUCCESS) {
+ return (ISC_R_NOSPACE);
+ }
+ isc_buffer_putuint8(*buffer, ISCCC_CCMSGTYPE_TABLE);
+ /*
+ * Emit a placeholder length.
+ */
+ used = (*buffer)->used;
+ isc_buffer_putuint32(*buffer, 0);
+
+ /*
+ * Emit the table.
+ */
+ result = table_towire(elt, buffer);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ len = (*buffer)->used - used;
+ /*
+ * 'len' is 4 bytes too big, since it counts
+ * the placeholder length too. Adjust and
+ * emit.
+ */
+ INSIST(len >= 4U);
+ len -= 4;
+
+ isc_buffer_init(&b, (unsigned char *)(*buffer)->base + used, 4);
+ isc_buffer_putuint32(&b, len);
+ } else if (isccc_sexpr_listp(elt)) {
+ unsigned int used;
+ isc_buffer_t b;
+
+ result = isc_buffer_reserve(buffer, 1 + 4);
+ if (result != ISC_R_SUCCESS) {
+ return (ISC_R_NOSPACE);
+ }
+ isc_buffer_putuint8(*buffer, ISCCC_CCMSGTYPE_LIST);
+ /*
+ * Emit a placeholder length.
+ */
+ used = (*buffer)->used;
+ isc_buffer_putuint32(*buffer, 0);
+
+ /*
+ * Emit the list.
+ */
+ result = list_towire(elt, buffer);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ len = (*buffer)->used - used;
+ /*
+ * 'len' is 4 bytes too big, since it counts
+ * the placeholder length too. Adjust and
+ * emit.
+ */
+ INSIST(len >= 4U);
+ len -= 4;
+
+ isc_buffer_init(&b, (unsigned char *)(*buffer)->base + used, 4);
+ isc_buffer_putuint32(&b, len);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+table_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer) {
+ isccc_sexpr_t *kv, *elt, *k, *v;
+ char *ks;
+ isc_result_t result;
+ unsigned int len;
+
+ for (elt = isccc_alist_first(alist); elt != NULL;
+ elt = ISCCC_SEXPR_CDR(elt))
+ {
+ kv = ISCCC_SEXPR_CAR(elt);
+ k = ISCCC_SEXPR_CAR(kv);
+ ks = isccc_sexpr_tostring(k);
+ v = ISCCC_SEXPR_CDR(kv);
+ len = (unsigned int)strlen(ks);
+ INSIST(len <= 255U);
+ /*
+ * Emit the key name.
+ */
+ result = isc_buffer_reserve(buffer, 1 + len);
+ if (result != ISC_R_SUCCESS) {
+ return (ISC_R_NOSPACE);
+ }
+ isc_buffer_putuint8(*buffer, (uint8_t)len);
+ isc_buffer_putmem(*buffer, (const unsigned char *)ks, len);
+ /*
+ * Emit the value.
+ */
+ result = value_towire(v, buffer);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+list_towire(isccc_sexpr_t *list, isc_buffer_t **buffer) {
+ isc_result_t result;
+
+ while (list != NULL) {
+ result = value_towire(ISCCC_SEXPR_CAR(list), buffer);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ list = ISCCC_SEXPR_CDR(list);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+sign(unsigned char *data, unsigned int length, unsigned char *hmac,
+ uint32_t algorithm, isccc_region_t *secret) {
+ const isc_md_type_t *md_type;
+ isc_result_t result;
+ isccc_region_t source, target;
+ unsigned char digest[ISC_MAX_MD_SIZE];
+ unsigned int digestlen = sizeof(digest);
+ unsigned char digestb64[HSHA_LENGTH + 4];
+
+ source.rstart = digest;
+
+ switch (algorithm) {
+ case ISCCC_ALG_HMACMD5:
+ md_type = ISC_MD_MD5;
+ break;
+ case ISCCC_ALG_HMACSHA1:
+ md_type = ISC_MD_SHA1;
+ break;
+ case ISCCC_ALG_HMACSHA224:
+ md_type = ISC_MD_SHA224;
+ break;
+ case ISCCC_ALG_HMACSHA256:
+ md_type = ISC_MD_SHA256;
+ break;
+ case ISCCC_ALG_HMACSHA384:
+ md_type = ISC_MD_SHA384;
+ break;
+ case ISCCC_ALG_HMACSHA512:
+ md_type = ISC_MD_SHA512;
+ break;
+ default:
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+
+ result = isc_hmac(md_type, secret->rstart, REGION_SIZE(*secret), data,
+ length, digest, &digestlen);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ source.rend = digest + digestlen;
+
+ memset(digestb64, 0, sizeof(digestb64));
+ target.rstart = digestb64;
+ target.rend = digestb64 + sizeof(digestb64);
+ result = isccc_base64_encode(&source, 64, "", &target);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ if (algorithm == ISCCC_ALG_HMACMD5) {
+ PUT_MEM(digestb64, HMD5_LENGTH, hmac);
+ } else {
+ PUT_MEM(digestb64, HSHA_LENGTH, hmac);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isccc_cc_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer, uint32_t algorithm,
+ isccc_region_t *secret) {
+ unsigned int hmac_base, signed_base;
+ isc_result_t result;
+
+ result = isc_buffer_reserve(buffer,
+ 4 + ((algorithm == ISCCC_ALG_HMACMD5)
+ ? sizeof(auth_hmd5)
+ : sizeof(auth_hsha)));
+ if (result != ISC_R_SUCCESS) {
+ return (ISC_R_NOSPACE);
+ }
+
+ /*
+ * Emit protocol version.
+ */
+ isc_buffer_putuint32(*buffer, 1);
+
+ if (secret != NULL) {
+ /*
+ * Emit _auth section with zeroed HMAC signature.
+ * We'll replace the zeros with the real signature once
+ * we know what it is.
+ */
+ if (algorithm == ISCCC_ALG_HMACMD5) {
+ hmac_base = (*buffer)->used + HMD5_OFFSET;
+ isc_buffer_putmem(*buffer, auth_hmd5,
+ sizeof(auth_hmd5));
+ } else {
+ unsigned char *hmac_alg;
+
+ hmac_base = (*buffer)->used + HSHA_OFFSET;
+ hmac_alg = (unsigned char *)isc_buffer_used(*buffer) +
+ HSHA_OFFSET - 1;
+ isc_buffer_putmem(*buffer, auth_hsha,
+ sizeof(auth_hsha));
+ *hmac_alg = algorithm;
+ }
+ } else {
+ hmac_base = 0;
+ }
+ signed_base = (*buffer)->used;
+ /*
+ * Delete any existing _auth section so that we don't try
+ * to encode it.
+ */
+ isccc_alist_delete(alist, "_auth");
+ /*
+ * Emit the message.
+ */
+ result = table_towire(alist, buffer);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ if (secret != NULL) {
+ return (sign((unsigned char *)(*buffer)->base + signed_base,
+ (*buffer)->used - signed_base,
+ (unsigned char *)(*buffer)->base + hmac_base,
+ algorithm, secret));
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+verify(isccc_sexpr_t *alist, unsigned char *data, unsigned int length,
+ uint32_t algorithm, isccc_region_t *secret) {
+ const isc_md_type_t *md_type;
+ isccc_region_t source;
+ isccc_region_t target;
+ isc_result_t result;
+ isccc_sexpr_t *_auth, *hmac;
+ unsigned char digest[ISC_MAX_MD_SIZE];
+ unsigned int digestlen = sizeof(digest);
+ unsigned char digestb64[HSHA_LENGTH * 4];
+
+ /*
+ * Extract digest.
+ */
+ _auth = isccc_alist_lookup(alist, "_auth");
+ if (!isccc_alist_alistp(_auth)) {
+ return (ISC_R_FAILURE);
+ }
+ if (algorithm == ISCCC_ALG_HMACMD5) {
+ hmac = isccc_alist_lookup(_auth, "hmd5");
+ } else {
+ hmac = isccc_alist_lookup(_auth, "hsha");
+ }
+ if (!isccc_sexpr_binaryp(hmac)) {
+ return (ISC_R_FAILURE);
+ }
+ /*
+ * Compute digest.
+ */
+ source.rstart = digest;
+
+ switch (algorithm) {
+ case ISCCC_ALG_HMACMD5:
+ md_type = ISC_MD_MD5;
+ break;
+ case ISCCC_ALG_HMACSHA1:
+ md_type = ISC_MD_SHA1;
+ break;
+ case ISCCC_ALG_HMACSHA224:
+ md_type = ISC_MD_SHA224;
+ break;
+ case ISCCC_ALG_HMACSHA256:
+ md_type = ISC_MD_SHA256;
+ break;
+ case ISCCC_ALG_HMACSHA384:
+ md_type = ISC_MD_SHA384;
+ break;
+ case ISCCC_ALG_HMACSHA512:
+ md_type = ISC_MD_SHA512;
+ break;
+ default:
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+
+ result = isc_hmac(md_type, secret->rstart, REGION_SIZE(*secret), data,
+ length, digest, &digestlen);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ source.rend = digest + digestlen;
+
+ target.rstart = digestb64;
+ target.rend = digestb64 + sizeof(digestb64);
+ memset(digestb64, 0, sizeof(digestb64));
+ result = isccc_base64_encode(&source, 64, "", &target);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ /*
+ * Verify.
+ */
+ if (algorithm == ISCCC_ALG_HMACMD5) {
+ isccc_region_t *region;
+ unsigned char *value;
+
+ region = isccc_sexpr_tobinary(hmac);
+ if ((region->rend - region->rstart) != HMD5_LENGTH) {
+ return (ISCCC_R_BADAUTH);
+ }
+ value = region->rstart;
+ if (!isc_safe_memequal(value, digestb64, HMD5_LENGTH)) {
+ return (ISCCC_R_BADAUTH);
+ }
+ } else {
+ isccc_region_t *region;
+ unsigned char *value;
+ uint32_t valalg;
+
+ region = isccc_sexpr_tobinary(hmac);
+
+ /*
+ * Note: with non-MD5 algorithms, there's an extra octet
+ * to identify which algorithm is in use.
+ */
+ if ((region->rend - region->rstart) != HSHA_LENGTH + 1) {
+ return (ISCCC_R_BADAUTH);
+ }
+ value = region->rstart;
+ GET8(valalg, value);
+ if ((valalg != algorithm) ||
+ !isc_safe_memequal(value, digestb64, HSHA_LENGTH))
+ {
+ return (ISCCC_R_BADAUTH);
+ }
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+table_fromwire(isccc_region_t *source, isccc_region_t *secret,
+ uint32_t algorithm, unsigned int depth, isccc_sexpr_t **alistp);
+
+static isc_result_t
+list_fromwire(isccc_region_t *source, unsigned int depth,
+ isccc_sexpr_t **listp);
+
+static isc_result_t
+value_fromwire(isccc_region_t *source, unsigned int depth,
+ isccc_sexpr_t **valuep) {
+ unsigned int msgtype;
+ uint32_t len;
+ isccc_sexpr_t *value;
+ isccc_region_t active;
+ isc_result_t result;
+
+ if (depth > ISCCC_MAXDEPTH) {
+ return (ISCCC_R_MAXDEPTH);
+ }
+
+ if (REGION_SIZE(*source) < 1 + 4) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ GET8(msgtype, source->rstart);
+ GET32(len, source->rstart);
+ if (REGION_SIZE(*source) < len) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ active.rstart = source->rstart;
+ active.rend = active.rstart + len;
+ source->rstart = active.rend;
+ if (msgtype == ISCCC_CCMSGTYPE_BINARYDATA) {
+ value = isccc_sexpr_frombinary(&active);
+ if (value != NULL) {
+ *valuep = value;
+ result = ISC_R_SUCCESS;
+ } else {
+ result = ISC_R_NOMEMORY;
+ }
+ } else if (msgtype == ISCCC_CCMSGTYPE_TABLE) {
+ result = table_fromwire(&active, NULL, 0, depth + 1, valuep);
+ } else if (msgtype == ISCCC_CCMSGTYPE_LIST) {
+ result = list_fromwire(&active, depth + 1, valuep);
+ } else {
+ result = ISCCC_R_SYNTAX;
+ }
+
+ return (result);
+}
+
+static isc_result_t
+table_fromwire(isccc_region_t *source, isccc_region_t *secret,
+ uint32_t algorithm, unsigned int depth, isccc_sexpr_t **alistp) {
+ char key[256];
+ uint32_t len;
+ isc_result_t result;
+ isccc_sexpr_t *alist, *value;
+ bool first_tag;
+ unsigned char *checksum_rstart;
+
+ REQUIRE(alistp != NULL && *alistp == NULL);
+
+ if (depth > ISCCC_MAXDEPTH) {
+ return (ISCCC_R_MAXDEPTH);
+ }
+
+ checksum_rstart = NULL;
+ first_tag = true;
+ alist = isccc_alist_create();
+ if (alist == NULL) {
+ return (ISC_R_NOMEMORY);
+ }
+
+ while (!REGION_EMPTY(*source)) {
+ GET8(len, source->rstart);
+ if (REGION_SIZE(*source) < len) {
+ result = ISC_R_UNEXPECTEDEND;
+ goto bad;
+ }
+ GET_MEM(key, len, source->rstart);
+ key[len] = '\0'; /* Ensure NUL termination. */
+ value = NULL;
+ result = value_fromwire(source, depth + 1, &value);
+ if (result != ISC_R_SUCCESS) {
+ goto bad;
+ }
+ if (isccc_alist_define(alist, key, value) == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto bad;
+ }
+ if (first_tag && secret != NULL && strcmp(key, "_auth") == 0) {
+ checksum_rstart = source->rstart;
+ }
+ first_tag = false;
+ }
+
+ if (secret != NULL) {
+ if (checksum_rstart != NULL) {
+ result = verify(
+ alist, checksum_rstart,
+ (unsigned int)(source->rend - checksum_rstart),
+ algorithm, secret);
+ } else {
+ result = ISCCC_R_BADAUTH;
+ }
+ } else {
+ result = ISC_R_SUCCESS;
+ }
+
+bad:
+ if (result == ISC_R_SUCCESS) {
+ *alistp = alist;
+ } else {
+ isccc_sexpr_free(&alist);
+ }
+
+ return (result);
+}
+
+static isc_result_t
+list_fromwire(isccc_region_t *source, unsigned int depth,
+ isccc_sexpr_t **listp) {
+ isccc_sexpr_t *list, *value;
+ isc_result_t result;
+
+ if (depth > ISCCC_MAXDEPTH) {
+ return (ISCCC_R_MAXDEPTH);
+ }
+
+ list = NULL;
+ while (!REGION_EMPTY(*source)) {
+ value = NULL;
+ result = value_fromwire(source, depth + 1, &value);
+ if (result != ISC_R_SUCCESS) {
+ isccc_sexpr_free(&list);
+ return (result);
+ }
+ if (isccc_sexpr_addtolist(&list, value) == NULL) {
+ isccc_sexpr_free(&value);
+ isccc_sexpr_free(&list);
+ return (ISC_R_NOMEMORY);
+ }
+ }
+
+ *listp = list;
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isccc_cc_fromwire(isccc_region_t *source, isccc_sexpr_t **alistp,
+ uint32_t algorithm, isccc_region_t *secret) {
+ unsigned int size;
+ uint32_t version;
+
+ size = REGION_SIZE(*source);
+ if (size < 4) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ GET32(version, source->rstart);
+ if (version != 1) {
+ return (ISCCC_R_UNKNOWNVERSION);
+ }
+
+ return (table_fromwire(source, secret, algorithm, 0, alistp));
+}
+
+static isc_result_t
+createmessage(uint32_t version, const char *from, const char *to,
+ uint32_t serial, isccc_time_t now, isccc_time_t expires,
+ isccc_sexpr_t **alistp, bool want_expires) {
+ isccc_sexpr_t *alist, *_ctrl, *_data;
+ isc_result_t result;
+
+ REQUIRE(alistp != NULL && *alistp == NULL);
+
+ if (version != 1) {
+ return (ISCCC_R_UNKNOWNVERSION);
+ }
+
+ alist = isccc_alist_create();
+ if (alist == NULL) {
+ return (ISC_R_NOMEMORY);
+ }
+
+ result = ISC_R_NOMEMORY;
+
+ _ctrl = isccc_alist_create();
+ if (_ctrl == NULL) {
+ goto bad;
+ }
+ if (isccc_alist_define(alist, "_ctrl", _ctrl) == NULL) {
+ isccc_sexpr_free(&_ctrl);
+ goto bad;
+ }
+
+ _data = isccc_alist_create();
+ if (_data == NULL) {
+ goto bad;
+ }
+ if (isccc_alist_define(alist, "_data", _data) == NULL) {
+ isccc_sexpr_free(&_data);
+ goto bad;
+ }
+
+ if (isccc_cc_defineuint32(_ctrl, "_ser", serial) == NULL ||
+ isccc_cc_defineuint32(_ctrl, "_tim", now) == NULL ||
+ (want_expires &&
+ isccc_cc_defineuint32(_ctrl, "_exp", expires) == NULL))
+ {
+ goto bad;
+ }
+ if (from != NULL && isccc_cc_definestring(_ctrl, "_frm", from) == NULL)
+ {
+ goto bad;
+ }
+ if (to != NULL && isccc_cc_definestring(_ctrl, "_to", to) == NULL) {
+ goto bad;
+ }
+
+ *alistp = alist;
+
+ return (ISC_R_SUCCESS);
+
+bad:
+ isccc_sexpr_free(&alist);
+
+ return (result);
+}
+
+isc_result_t
+isccc_cc_createmessage(uint32_t version, const char *from, const char *to,
+ uint32_t serial, isccc_time_t now, isccc_time_t expires,
+ isccc_sexpr_t **alistp) {
+ return (createmessage(version, from, to, serial, now, expires, alistp,
+ true));
+}
+
+isc_result_t
+isccc_cc_createack(isccc_sexpr_t *message, bool ok, isccc_sexpr_t **ackp) {
+ char *_frm, *_to;
+ uint32_t serial;
+ isccc_sexpr_t *ack, *_ctrl;
+ isc_result_t result;
+ isccc_time_t t;
+
+ REQUIRE(ackp != NULL && *ackp == NULL);
+
+ _ctrl = isccc_alist_lookup(message, "_ctrl");
+ if (!isccc_alist_alistp(_ctrl) ||
+ isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
+ isccc_cc_lookupuint32(_ctrl, "_tim", &t) != ISC_R_SUCCESS)
+ {
+ return (ISC_R_FAILURE);
+ }
+ /*
+ * _frm and _to are optional.
+ */
+ _frm = NULL;
+ (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
+ _to = NULL;
+ (void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
+ /*
+ * Create the ack.
+ */
+ ack = NULL;
+ result = createmessage(1, _to, _frm, serial, t, 0, &ack, false);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ _ctrl = isccc_alist_lookup(ack, "_ctrl");
+ if (_ctrl == NULL) {
+ result = ISC_R_FAILURE;
+ goto bad;
+ }
+ if (isccc_cc_definestring(ack, "_ack", (ok) ? "1" : "0") == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto bad;
+ }
+
+ *ackp = ack;
+
+ return (ISC_R_SUCCESS);
+
+bad:
+ isccc_sexpr_free(&ack);
+
+ return (result);
+}
+
+bool
+isccc_cc_isack(isccc_sexpr_t *message) {
+ isccc_sexpr_t *_ctrl;
+
+ _ctrl = isccc_alist_lookup(message, "_ctrl");
+ if (!isccc_alist_alistp(_ctrl)) {
+ return (false);
+ }
+ if (isccc_cc_lookupstring(_ctrl, "_ack", NULL) == ISC_R_SUCCESS) {
+ return (true);
+ }
+ return (false);
+}
+
+bool
+isccc_cc_isreply(isccc_sexpr_t *message) {
+ isccc_sexpr_t *_ctrl;
+
+ _ctrl = isccc_alist_lookup(message, "_ctrl");
+ if (!isccc_alist_alistp(_ctrl)) {
+ return (false);
+ }
+ if (isccc_cc_lookupstring(_ctrl, "_rpl", NULL) == ISC_R_SUCCESS) {
+ return (true);
+ }
+ return (false);
+}
+
+isc_result_t
+isccc_cc_createresponse(isccc_sexpr_t *message, isccc_time_t now,
+ isccc_time_t expires, isccc_sexpr_t **alistp) {
+ char *_frm, *_to, *type = NULL;
+ uint32_t serial;
+ isccc_sexpr_t *alist, *_ctrl, *_data;
+ isc_result_t result;
+
+ REQUIRE(alistp != NULL && *alistp == NULL);
+
+ _ctrl = isccc_alist_lookup(message, "_ctrl");
+ _data = isccc_alist_lookup(message, "_data");
+ if (!isccc_alist_alistp(_ctrl) || !isccc_alist_alistp(_data) ||
+ isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
+ isccc_cc_lookupstring(_data, "type", &type) != ISC_R_SUCCESS)
+ {
+ return (ISC_R_FAILURE);
+ }
+ /*
+ * _frm and _to are optional.
+ */
+ _frm = NULL;
+ (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
+ _to = NULL;
+ (void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
+ /*
+ * Create the response.
+ */
+ alist = NULL;
+ result = isccc_cc_createmessage(1, _to, _frm, serial, now, expires,
+ &alist);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ _ctrl = isccc_alist_lookup(alist, "_ctrl");
+ if (_ctrl == NULL) {
+ result = ISC_R_FAILURE;
+ goto bad;
+ }
+
+ _data = isccc_alist_lookup(alist, "_data");
+ if (_data == NULL) {
+ result = ISC_R_FAILURE;
+ goto bad;
+ }
+
+ if (isccc_cc_definestring(_ctrl, "_rpl", "1") == NULL ||
+ isccc_cc_definestring(_data, "type", type) == NULL)
+ {
+ result = ISC_R_NOMEMORY;
+ goto bad;
+ }
+
+ *alistp = alist;
+
+ return (ISC_R_SUCCESS);
+
+bad:
+ isccc_sexpr_free(&alist);
+ return (result);
+}
+
+isccc_sexpr_t *
+isccc_cc_definestring(isccc_sexpr_t *alist, const char *key, const char *str) {
+ size_t len;
+ isccc_region_t r;
+
+ len = strlen(str);
+ DE_CONST(str, r.rstart);
+ r.rend = r.rstart + len;
+
+ return (isccc_alist_definebinary(alist, key, &r));
+}
+
+isccc_sexpr_t *
+isccc_cc_defineuint32(isccc_sexpr_t *alist, const char *key, uint32_t i) {
+ char b[100];
+ size_t len;
+ isccc_region_t r;
+
+ snprintf(b, sizeof(b), "%u", i);
+ len = strlen(b);
+ r.rstart = (unsigned char *)b;
+ r.rend = (unsigned char *)b + len;
+
+ return (isccc_alist_definebinary(alist, key, &r));
+}
+
+isc_result_t
+isccc_cc_lookupstring(isccc_sexpr_t *alist, const char *key, char **strp) {
+ isccc_sexpr_t *kv, *v;
+
+ REQUIRE(strp == NULL || *strp == NULL);
+
+ kv = isccc_alist_assq(alist, key);
+ if (kv != NULL) {
+ v = ISCCC_SEXPR_CDR(kv);
+ if (isccc_sexpr_binaryp(v)) {
+ if (strp != NULL) {
+ *strp = isccc_sexpr_tostring(v);
+ }
+ return (ISC_R_SUCCESS);
+ } else {
+ return (ISC_R_EXISTS);
+ }
+ }
+
+ return (ISC_R_NOTFOUND);
+}
+
+isc_result_t
+isccc_cc_lookupuint32(isccc_sexpr_t *alist, const char *key, uint32_t *uintp) {
+ isccc_sexpr_t *kv, *v;
+
+ kv = isccc_alist_assq(alist, key);
+ if (kv != NULL) {
+ v = ISCCC_SEXPR_CDR(kv);
+ if (isccc_sexpr_binaryp(v)) {
+ if (uintp != NULL) {
+ *uintp = (uint32_t)strtoul(
+ isccc_sexpr_tostring(v), NULL, 10);
+ }
+ return (ISC_R_SUCCESS);
+ } else {
+ return (ISC_R_EXISTS);
+ }
+ }
+
+ return (ISC_R_NOTFOUND);
+}
+
+static void
+symtab_undefine(char *key, unsigned int type, isccc_symvalue_t value,
+ void *arg) {
+ UNUSED(type);
+ UNUSED(value);
+ UNUSED(arg);
+
+ free(key);
+}
+
+static bool
+symtab_clean(char *key, unsigned int type, isccc_symvalue_t value, void *arg) {
+ isccc_time_t *now;
+
+ UNUSED(key);
+ UNUSED(type);
+
+ now = arg;
+
+ if (*now < value.as_uinteger) {
+ return (false);
+ }
+ if ((*now - value.as_uinteger) < DUP_LIFETIME) {
+ return (false);
+ }
+ return (true);
+}
+
+isc_result_t
+isccc_cc_createsymtab(isccc_symtab_t **symtabp) {
+ return (isccc_symtab_create(11897, symtab_undefine, NULL, false,
+ symtabp));
+}
+
+void
+isccc_cc_cleansymtab(isccc_symtab_t *symtab, isccc_time_t now) {
+ isccc_symtab_foreach(symtab, symtab_clean, &now);
+}
+
+static bool
+has_whitespace(const char *str) {
+ char c;
+
+ if (str == NULL) {
+ return (false);
+ }
+ while ((c = *str++) != '\0') {
+ if (c == ' ' || c == '\t' || c == '\n') {
+ return (true);
+ }
+ }
+ return (false);
+}
+
+isc_result_t
+isccc_cc_checkdup(isccc_symtab_t *symtab, isccc_sexpr_t *message,
+ isccc_time_t now) {
+ const char *_frm;
+ const char *_to;
+ char *_ser = NULL, *_tim = NULL, *tmp;
+ isc_result_t result;
+ char *key;
+ size_t len;
+ isccc_symvalue_t value;
+ isccc_sexpr_t *_ctrl;
+
+ _ctrl = isccc_alist_lookup(message, "_ctrl");
+ if (!isccc_alist_alistp(_ctrl) ||
+ isccc_cc_lookupstring(_ctrl, "_ser", &_ser) != ISC_R_SUCCESS ||
+ isccc_cc_lookupstring(_ctrl, "_tim", &_tim) != ISC_R_SUCCESS)
+ {
+ return (ISC_R_FAILURE);
+ }
+
+ INSIST(_ser != NULL);
+ INSIST(_tim != NULL);
+
+ /*
+ * _frm and _to are optional.
+ */
+ tmp = NULL;
+ if (isccc_cc_lookupstring(_ctrl, "_frm", &tmp) != ISC_R_SUCCESS) {
+ _frm = "";
+ } else {
+ _frm = tmp;
+ }
+ tmp = NULL;
+ if (isccc_cc_lookupstring(_ctrl, "_to", &tmp) != ISC_R_SUCCESS) {
+ _to = "";
+ } else {
+ _to = tmp;
+ }
+ /*
+ * Ensure there is no newline in any of the strings. This is so
+ * we can write them to a file later.
+ */
+ if (has_whitespace(_frm) || has_whitespace(_to) ||
+ has_whitespace(_ser) || has_whitespace(_tim))
+ {
+ return (ISC_R_FAILURE);
+ }
+ len = strlen(_frm) + strlen(_to) + strlen(_ser) + strlen(_tim) + 4;
+ key = malloc(len);
+ if (key == NULL) {
+ return (ISC_R_NOMEMORY);
+ }
+ snprintf(key, len, "%s;%s;%s;%s", _frm, _to, _ser, _tim);
+ value.as_uinteger = now;
+ result = isccc_symtab_define(symtab, key, ISCCC_SYMTYPE_CCDUP, value,
+ isccc_symexists_reject);
+ if (result != ISC_R_SUCCESS) {
+ free(key);
+ return (result);
+ }
+
+ return (ISC_R_SUCCESS);
+}
diff --git a/lib/isccc/ccmsg.c b/lib/isccc/ccmsg.c
new file mode 100644
index 0000000..9ee48ab
--- /dev/null
+++ b/lib/isccc/ccmsg.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) 2001 Nominum, Inc.
+ *
+ * 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 ISC AND NOMINUM DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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.
+ */
+
+/*! \file */
+
+#include <inttypes.h>
+
+#include <isc/mem.h>
+#include <isc/netmgr.h>
+#include <isc/result.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <isccc/ccmsg.h>
+#include <isccc/events.h>
+
+#define CCMSG_MAGIC ISC_MAGIC('C', 'C', 'm', 's')
+#define VALID_CCMSG(foo) ISC_MAGIC_VALID(foo, CCMSG_MAGIC)
+
+static void
+recv_data(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
+ void *arg) {
+ isccc_ccmsg_t *ccmsg = arg;
+ size_t size;
+
+ INSIST(VALID_CCMSG(ccmsg));
+
+ switch (eresult) {
+ case ISC_R_SHUTTINGDOWN:
+ case ISC_R_CANCELED:
+ case ISC_R_EOF:
+ ccmsg->result = eresult;
+ goto done;
+ case ISC_R_SUCCESS:
+ if (region == NULL) {
+ ccmsg->result = ISC_R_EOF;
+ goto done;
+ }
+ ccmsg->result = ISC_R_SUCCESS;
+ break;
+ default:
+ ccmsg->result = eresult;
+ goto done;
+ }
+
+ if (!ccmsg->length_received) {
+ if (region->length < sizeof(uint32_t)) {
+ ccmsg->result = ISC_R_UNEXPECTEDEND;
+ goto done;
+ }
+
+ ccmsg->size = ntohl(*(uint32_t *)region->base);
+
+ if (ccmsg->size == 0) {
+ ccmsg->result = ISC_R_UNEXPECTEDEND;
+ goto done;
+ }
+ if (ccmsg->size > ccmsg->maxsize) {
+ ccmsg->result = ISC_R_RANGE;
+ goto done;
+ }
+
+ isc_region_consume(region, sizeof(uint32_t));
+ isc_buffer_allocate(ccmsg->mctx, &ccmsg->buffer, ccmsg->size);
+
+ ccmsg->length_received = true;
+ }
+
+ /*
+ * If there's no more data, wait for more
+ */
+ if (region->length == 0) {
+ return;
+ }
+
+ /* We have some data in the buffer, read it */
+
+ size = ISC_MIN(isc_buffer_availablelength(ccmsg->buffer),
+ region->length);
+ isc_buffer_putmem(ccmsg->buffer, region->base, size);
+ isc_region_consume(region, size);
+
+ if (isc_buffer_usedlength(ccmsg->buffer) == ccmsg->size) {
+ ccmsg->result = ISC_R_SUCCESS;
+ goto done;
+ }
+
+ /* Wait for more data to come */
+ return;
+
+done:
+ isc_nm_pauseread(handle);
+ ccmsg->cb(handle, ccmsg->result, ccmsg->cbarg);
+}
+
+void
+isccc_ccmsg_init(isc_mem_t *mctx, isc_nmhandle_t *handle,
+ isccc_ccmsg_t *ccmsg) {
+ REQUIRE(mctx != NULL);
+ REQUIRE(handle != NULL);
+ REQUIRE(ccmsg != NULL);
+
+ *ccmsg = (isccc_ccmsg_t){
+ .magic = CCMSG_MAGIC,
+ .maxsize = 0xffffffffU, /* Largest message possible. */
+ .mctx = mctx,
+ .handle = handle,
+ .result = ISC_R_UNEXPECTED /* None yet. */
+ };
+}
+
+void
+isccc_ccmsg_setmaxsize(isccc_ccmsg_t *ccmsg, unsigned int maxsize) {
+ REQUIRE(VALID_CCMSG(ccmsg));
+
+ ccmsg->maxsize = maxsize;
+}
+
+void
+isccc_ccmsg_readmessage(isccc_ccmsg_t *ccmsg, isc_nm_cb_t cb, void *cbarg) {
+ REQUIRE(VALID_CCMSG(ccmsg));
+
+ if (ccmsg->buffer != NULL) {
+ isc_buffer_free(&ccmsg->buffer);
+ }
+
+ ccmsg->cb = cb;
+ ccmsg->cbarg = cbarg;
+ ccmsg->result = ISC_R_UNEXPECTED; /* unknown right now */
+ ccmsg->length_received = false;
+
+ if (ccmsg->reading) {
+ isc_nm_resumeread(ccmsg->handle);
+ } else {
+ isc_nm_read(ccmsg->handle, recv_data, ccmsg);
+ ccmsg->reading = true;
+ }
+}
+
+void
+isccc_ccmsg_cancelread(isccc_ccmsg_t *ccmsg) {
+ REQUIRE(VALID_CCMSG(ccmsg));
+
+ if (ccmsg->reading) {
+ isc_nm_cancelread(ccmsg->handle);
+ ccmsg->reading = false;
+ }
+}
+
+void
+isccc_ccmsg_invalidate(isccc_ccmsg_t *ccmsg) {
+ REQUIRE(VALID_CCMSG(ccmsg));
+
+ ccmsg->magic = 0;
+
+ if (ccmsg->buffer != NULL) {
+ isc_buffer_free(&ccmsg->buffer);
+ }
+}
diff --git a/lib/isccc/include/isccc/alist.h b/lib/isccc/include/isccc/alist.h
new file mode 100644
index 0000000..dc70044
--- /dev/null
+++ b/lib/isccc/include/isccc/alist.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) 2001 Nominum, Inc.
+ *
+ * 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 ISC AND NOMINUM DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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
+
+/*! \file isccc/alist.h */
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <isc/lang.h>
+
+#include <isccc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+isccc_sexpr_t *
+isccc_alist_create(void);
+
+bool
+isccc_alist_alistp(isccc_sexpr_t *alist);
+
+bool
+isccc_alist_emptyp(isccc_sexpr_t *alist);
+
+isccc_sexpr_t *
+isccc_alist_first(isccc_sexpr_t *alist);
+
+isccc_sexpr_t *
+isccc_alist_assq(isccc_sexpr_t *alist, const char *key);
+
+void
+isccc_alist_delete(isccc_sexpr_t *alist, const char *key);
+
+isccc_sexpr_t *
+isccc_alist_define(isccc_sexpr_t *alist, const char *key, isccc_sexpr_t *value);
+
+isccc_sexpr_t *
+isccc_alist_definestring(isccc_sexpr_t *alist, const char *key,
+ const char *str);
+
+isccc_sexpr_t *
+isccc_alist_definebinary(isccc_sexpr_t *alist, const char *key,
+ isccc_region_t *r);
+
+isccc_sexpr_t *
+isccc_alist_lookup(isccc_sexpr_t *alist, const char *key);
+
+isc_result_t
+isccc_alist_lookupstring(isccc_sexpr_t *alist, const char *key, char **strp);
+
+isc_result_t
+isccc_alist_lookupbinary(isccc_sexpr_t *alist, const char *key,
+ isccc_region_t **r);
+
+void
+isccc_alist_prettyprint(isccc_sexpr_t *sexpr, unsigned int indent,
+ FILE *stream);
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isccc/include/isccc/base64.h b/lib/isccc/include/isccc/base64.h
new file mode 100644
index 0000000..641dd4e
--- /dev/null
+++ b/lib/isccc/include/isccc/base64.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) 2001 Nominum, Inc.
+ *
+ * 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 ISC AND NOMINUM DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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
+
+/*! \file isccc/base64.h */
+
+#include <isc/lang.h>
+
+#include <isccc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Functions
+ ***/
+
+isc_result_t
+isccc_base64_encode(isccc_region_t *source, int wordlength,
+ const char *wordbreak, isccc_region_t *target);
+/*%<
+ * Convert data into base64 encoded text.
+ *
+ * Notes:
+ *\li The base64 encoded text in 'target' will be divided into
+ * words of at most 'wordlength' characters, separated by
+ * the 'wordbreak' string. No parentheses will surround
+ * the text.
+ *
+ * Requires:
+ *\li 'source' is a region containing binary data.
+ *\li 'target' is a text region containing available space.
+ *\li 'wordbreak' points to a null-terminated string of
+ * zero or more whitespace characters.
+ */
+
+isc_result_t
+isccc_base64_decode(const char *cstr, isccc_region_t *target);
+/*%<
+ * Decode a null-terminated base64 string.
+ *
+ * Requires:
+ *\li 'cstr' is non-null.
+ *\li 'target' is a valid region.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS -- the entire decoded representation of 'cstring'
+ * fit in 'target'.
+ *\li #ISC_R_BADBASE64 -- 'cstr' is not a valid base64 encoding.
+ *\li #ISC_R_NOSPACE -- 'target' is not big enough.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isccc/include/isccc/cc.h b/lib/isccc/include/isccc/cc.h
new file mode 100644
index 0000000..f597276
--- /dev/null
+++ b/lib/isccc/include/isccc/cc.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) 2001 Nominum, Inc.
+ *
+ * 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 ISC AND NOMINUM DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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
+
+/*! \file isccc/cc.h */
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/buffer.h>
+#include <isc/lang.h>
+
+#include <dst/dst.h>
+#include <isccc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/*%
+ * The HMAC algorithms supported by isccc_cc_fromwire and
+ * isccc_cc_towire as implemented in DST.
+ */
+#define ISCCC_ALG_UNKNOWN 0
+#define ISCCC_ALG_HMACMD5 DST_ALG_HMACMD5
+#define ISCCC_ALG_HMACSHA1 DST_ALG_HMACSHA1
+#define ISCCC_ALG_HMACSHA224 DST_ALG_HMACSHA224
+#define ISCCC_ALG_HMACSHA256 DST_ALG_HMACSHA256
+#define ISCCC_ALG_HMACSHA384 DST_ALG_HMACSHA384
+#define ISCCC_ALG_HMACSHA512 DST_ALG_HMACSHA512
+
+/*% Maximum Datagram Package */
+#define ISCCC_CC_MAXDGRAMPACKET 4096
+
+/*% Message Type String */
+#define ISCCC_CCMSGTYPE_STRING 0x00
+/*% Message Type Binary Data */
+#define ISCCC_CCMSGTYPE_BINARYDATA 0x01
+/*% Message Type Table */
+#define ISCCC_CCMSGTYPE_TABLE 0x02
+/*% Message Type List */
+#define ISCCC_CCMSGTYPE_LIST 0x03
+
+/*% Send to Wire */
+isc_result_t
+isccc_cc_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer, uint32_t algorithm,
+ isccc_region_t *secret);
+
+/*% Get From Wire */
+isc_result_t
+isccc_cc_fromwire(isccc_region_t *source, isccc_sexpr_t **alistp,
+ uint32_t algorithm, isccc_region_t *secret);
+
+/*% Create Message */
+isc_result_t
+isccc_cc_createmessage(uint32_t version, const char *from, const char *to,
+ uint32_t serial, isccc_time_t now, isccc_time_t expires,
+ isccc_sexpr_t **alistp);
+
+/*% Create Acknowledgment */
+isc_result_t
+isccc_cc_createack(isccc_sexpr_t *message, bool ok, isccc_sexpr_t **ackp);
+
+/*% Is Ack? */
+bool
+isccc_cc_isack(isccc_sexpr_t *message);
+
+/*% Is Reply? */
+bool
+isccc_cc_isreply(isccc_sexpr_t *message);
+
+/*% Create Response */
+isc_result_t
+isccc_cc_createresponse(isccc_sexpr_t *message, isccc_time_t now,
+ isccc_time_t expires, isccc_sexpr_t **alistp);
+
+/*% Define String */
+isccc_sexpr_t *
+isccc_cc_definestring(isccc_sexpr_t *alist, const char *key, const char *str);
+
+/*% Define uint 32 */
+isccc_sexpr_t *
+isccc_cc_defineuint32(isccc_sexpr_t *alist, const char *key, uint32_t i);
+
+/*% Lookup String */
+isc_result_t
+isccc_cc_lookupstring(isccc_sexpr_t *alist, const char *key, char **strp);
+
+/*% Lookup uint 32 */
+isc_result_t
+isccc_cc_lookupuint32(isccc_sexpr_t *alist, const char *key, uint32_t *uintp);
+
+/*% Create Symbol Table */
+isc_result_t
+isccc_cc_createsymtab(isccc_symtab_t **symtabp);
+
+/*% Clean up Symbol Table */
+void
+isccc_cc_cleansymtab(isccc_symtab_t *symtab, isccc_time_t now);
+
+/*% Check for Duplicates */
+isc_result_t
+isccc_cc_checkdup(isccc_symtab_t *symtab, isccc_sexpr_t *message,
+ isccc_time_t now);
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isccc/include/isccc/ccmsg.h b/lib/isccc/include/isccc/ccmsg.h
new file mode 100644
index 0000000..a648226
--- /dev/null
+++ b/lib/isccc/include/isccc/ccmsg.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) 2001 Nominum, Inc.
+ *
+ * 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 ISC AND NOMINUM DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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
+
+/*! \file isccc/ccmsg.h */
+
+#include <inttypes.h>
+
+#include <isc/buffer.h>
+#include <isc/lang.h>
+#include <isc/netmgr.h>
+#include <isc/sockaddr.h>
+
+/*% ISCCC Message Structure */
+typedef struct isccc_ccmsg {
+ /* private (don't touch!) */
+ unsigned int magic;
+ uint32_t size;
+ bool length_received;
+ isc_buffer_t *buffer;
+ unsigned int maxsize;
+ isc_mem_t *mctx;
+ isc_nmhandle_t *handle;
+ isc_nm_cb_t cb;
+ void *cbarg;
+ bool reading;
+ /* public (read-only) */
+ isc_result_t result;
+} isccc_ccmsg_t;
+
+ISC_LANG_BEGINDECLS
+
+void
+isccc_ccmsg_init(isc_mem_t *mctx, isc_nmhandle_t *handle, isccc_ccmsg_t *ccmsg);
+/*%
+ * Associate a cc message state with a given memory context and
+ * netmgr handle. (Note that the caller must hold a reference to
+ * the handle during asynchronous ccmsg operations; the ccmsg code
+ * does not hold the reference itself.)
+ *
+ * Requires:
+ *
+ *\li "mctx" be a valid memory context.
+ *
+ *\li "handle" be a netmgr handle for a stream socket.
+ *
+ *\li "ccmsg" be non-NULL and an uninitialized or invalidated structure.
+ *
+ * Ensures:
+ *
+ *\li "ccmsg" is a valid structure.
+ */
+
+void
+isccc_ccmsg_setmaxsize(isccc_ccmsg_t *ccmsg, unsigned int maxsize);
+/*%
+ * Set the maximum packet size to "maxsize"
+ *
+ * Requires:
+ *
+ *\li "ccmsg" be valid.
+ *
+ *\li 512 <= "maxsize" <= 4294967296
+ */
+
+void
+isccc_ccmsg_readmessage(isccc_ccmsg_t *ccmsg, isc_nm_cb_t cb, void *cbarg);
+/*%
+ * Schedule an event to be delivered when a command channel message is
+ * readable, or when an error occurs on the socket.
+ *
+ * Requires:
+ *
+ *\li "ccmsg" be valid.
+ *
+ * Notes:
+ *
+ *\li The event delivered is a fully generic event. It will contain no
+ * actual data. The sender will be a pointer to the isccc_ccmsg_t.
+ * The result code inside that structure should be checked to see
+ * what the final result was.
+ */
+
+void
+isccc_ccmsg_cancelread(isccc_ccmsg_t *ccmsg);
+/*%
+ * Cancel a readmessage() call. The event will still be posted with a
+ * CANCELED result code.
+ *
+ * Requires:
+ *
+ *\li "ccmsg" be valid.
+ */
+
+void
+isccc_ccmsg_invalidate(isccc_ccmsg_t *ccmsg);
+/*%
+ * Clean up all allocated state, and invalidate the structure.
+ *
+ * Requires:
+ *
+ *\li "ccmsg" be valid.
+ *
+ * Ensures:
+ *
+ *\li "ccmsg" is invalidated and disassociated with all memory contexts,
+ * sockets, etc.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isccc/include/isccc/events.h b/lib/isccc/include/isccc/events.h
new file mode 100644
index 0000000..0461a8a
--- /dev/null
+++ b/lib/isccc/include/isccc/events.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) 2001 Nominum, Inc.
+ *
+ * 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 ISC AND NOMINUM DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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
+
+/*! \file isccc/events.h */
+
+#include <isc/eventclass.h>
+
+/*%
+ * Registry of ISCCC event numbers.
+ */
+
+#define ISCCC_EVENT_CCMSG (ISC_EVENTCLASS_ISCCC + 0)
+
+#define ISCCC_EVENT_FIRSTEVENT (ISC_EVENTCLASS_ISCCC + 0)
+#define ISCCC_EVENT_LASTEVENT (ISC_EVENTCLASS_ISCCC + 65535)
diff --git a/lib/isccc/include/isccc/sexpr.h b/lib/isccc/include/isccc/sexpr.h
new file mode 100644
index 0000000..133d06d
--- /dev/null
+++ b/lib/isccc/include/isccc/sexpr.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) 2001 Nominum, Inc.
+ *
+ * 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 ISC AND NOMINUM DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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
+
+/*! \file isccc/sexpr.h */
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <isc/lang.h>
+
+#include <isccc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/*% dotted pair structure */
+struct isccc_dottedpair {
+ isccc_sexpr_t *car;
+ isccc_sexpr_t *cdr;
+};
+
+/*% iscc_sexpr structure */
+struct isccc_sexpr {
+ unsigned int type;
+ union {
+ char *as_string;
+ isccc_dottedpair_t as_dottedpair;
+ isccc_region_t as_region;
+ } value;
+};
+
+#define ISCCC_SEXPRTYPE_NONE 0x00 /*%< Illegal. */
+#define ISCCC_SEXPRTYPE_T 0x01
+#define ISCCC_SEXPRTYPE_STRING 0x02
+#define ISCCC_SEXPRTYPE_DOTTEDPAIR 0x03
+#define ISCCC_SEXPRTYPE_BINARY 0x04
+
+#define ISCCC_SEXPR_CAR(s) (s)->value.as_dottedpair.car
+#define ISCCC_SEXPR_CDR(s) (s)->value.as_dottedpair.cdr
+
+isccc_sexpr_t *
+isccc_sexpr_cons(isccc_sexpr_t *car, isccc_sexpr_t *cdr);
+
+isccc_sexpr_t *
+isccc_sexpr_tconst(void);
+
+isccc_sexpr_t *
+isccc_sexpr_fromstring(const char *str);
+
+isccc_sexpr_t *
+isccc_sexpr_frombinary(const isccc_region_t *region);
+
+void
+isccc_sexpr_free(isccc_sexpr_t **sexprp);
+
+void
+isccc_sexpr_print(isccc_sexpr_t *sexpr, FILE *stream);
+
+isccc_sexpr_t *
+isccc_sexpr_car(isccc_sexpr_t *list);
+
+isccc_sexpr_t *
+isccc_sexpr_cdr(isccc_sexpr_t *list);
+
+void
+isccc_sexpr_setcar(isccc_sexpr_t *pair, isccc_sexpr_t *car);
+
+void
+isccc_sexpr_setcdr(isccc_sexpr_t *pair, isccc_sexpr_t *cdr);
+
+isccc_sexpr_t *
+isccc_sexpr_addtolist(isccc_sexpr_t **l1p, isccc_sexpr_t *l2);
+
+bool
+isccc_sexpr_listp(isccc_sexpr_t *sexpr);
+
+bool
+isccc_sexpr_emptyp(isccc_sexpr_t *sexpr);
+
+bool
+isccc_sexpr_stringp(isccc_sexpr_t *sexpr);
+
+bool
+isccc_sexpr_binaryp(isccc_sexpr_t *sexpr);
+
+char *
+isccc_sexpr_tostring(isccc_sexpr_t *sexpr);
+
+isccc_region_t *
+isccc_sexpr_tobinary(isccc_sexpr_t *sexpr);
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isccc/include/isccc/symtab.h b/lib/isccc/include/isccc/symtab.h
new file mode 100644
index 0000000..a156c06
--- /dev/null
+++ b/lib/isccc/include/isccc/symtab.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) 2001 Nominum, Inc.
+ *
+ * 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 ISC AND NOMINUM DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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
+
+/*****
+***** Module Info
+*****/
+
+/*! \file isccc/symtab.h
+ * \brief
+ * Provides a simple memory-based symbol table.
+ *
+ * Keys are C strings. A type may be specified when looking up,
+ * defining, or undefining. A type value of 0 means "match any type";
+ * any other value will only match the given type.
+ *
+ * It's possible that a client will attempt to define a <key, type,
+ * value> tuple when a tuple with the given key and type already
+ * exists in the table. What to do in this case is specified by the
+ * client. Possible policies are:
+ *
+ *\li isccc_symexists_reject Disallow the define, returning #ISC_R_EXISTS
+ *\li isccc_symexists_replace Replace the old value with the new. The
+ * undefine action (if provided) will be called
+ * with the old <key, type, value> tuple.
+ *\li isccc_symexists_add Add the new tuple, leaving the old tuple in
+ * the table. Subsequent lookups will retrieve
+ * the most-recently-defined tuple.
+ *
+ * A lookup of a key using type 0 will return the most-recently
+ * defined symbol with that key. An undefine of a key using type 0
+ * will undefine the most-recently defined symbol with that key.
+ * Trying to define a key with type 0 is illegal.
+ *
+ * The symbol table library does not make a copy the key field, so the
+ * caller must ensure that any key it passes to isccc_symtab_define()
+ * will not change until it calls isccc_symtab_undefine() or
+ * isccc_symtab_destroy().
+ *
+ * A user-specified action will be called (if provided) when a symbol
+ * is undefined. It can be used to free memory associated with keys
+ * and/or values.
+ */
+
+/***
+ *** Imports.
+ ***/
+
+#include <stdbool.h>
+
+#include <isc/lang.h>
+
+#include <isccc/types.h>
+
+/***
+ *** Symbol Tables.
+ ***/
+
+typedef union isccc_symvalue {
+ void *as_pointer;
+ int as_integer;
+ unsigned int as_uinteger;
+} isccc_symvalue_t;
+
+typedef void (*isccc_symtabundefaction_t)(char *key, unsigned int type,
+ isccc_symvalue_t value,
+ void *userarg);
+
+typedef bool (*isccc_symtabforeachaction_t)(char *key, unsigned int type,
+ isccc_symvalue_t value,
+ void *userarg);
+
+typedef enum {
+ isccc_symexists_reject = 0,
+ isccc_symexists_replace = 1,
+ isccc_symexists_add = 2
+} isccc_symexists_t;
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+isccc_symtab_create(unsigned int size,
+ isccc_symtabundefaction_t undefine_action,
+ void *undefine_arg, bool case_sensitive,
+ isccc_symtab_t **symtabp);
+
+void
+isccc_symtab_destroy(isccc_symtab_t **symtabp);
+
+isc_result_t
+isccc_symtab_lookup(isccc_symtab_t *symtab, const char *key, unsigned int type,
+ isccc_symvalue_t *value);
+
+isc_result_t
+isccc_symtab_define(isccc_symtab_t *symtab, char *key, unsigned int type,
+ isccc_symvalue_t value, isccc_symexists_t exists_policy);
+
+isc_result_t
+isccc_symtab_undefine(isccc_symtab_t *symtab, const char *key,
+ unsigned int type);
+
+void
+isccc_symtab_foreach(isccc_symtab_t *symtab, isccc_symtabforeachaction_t action,
+ void *arg);
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isccc/include/isccc/symtype.h b/lib/isccc/include/isccc/symtype.h
new file mode 100644
index 0000000..8e67eda
--- /dev/null
+++ b/lib/isccc/include/isccc/symtype.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) 2001 Nominum, Inc.
+ *
+ * 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 ISC AND NOMINUM DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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
+
+/*! \file isccc/symtype.h */
+
+#define ISCCC_SYMTYPE_ZONESTATS 0x0001
+#define ISCCC_SYMTYPE_CCDUP 0x0002
+#define ISCCC_SYMTYPE_TELLSERVICE 0x0003
+#define ISCCC_SYMTYPE_TELLRESPONSE 0x0004
diff --git a/lib/isccc/include/isccc/types.h b/lib/isccc/include/isccc/types.h
new file mode 100644
index 0000000..f2032a7
--- /dev/null
+++ b/lib/isccc/include/isccc/types.h
@@ -0,0 +1,52 @@
+/*
+ * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) 2001 Nominum, Inc.
+ *
+ * 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 ISC AND NOMINUM DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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
+
+/*! \file isccc/types.h */
+
+#include <inttypes.h>
+
+#include <isc/result.h>
+
+/*% isccc_time_t typedef */
+typedef uint32_t isccc_time_t;
+
+/*% isccc_sexpr_t typedef */
+typedef struct isccc_sexpr isccc_sexpr_t;
+/*% isccc_dottedpair_t typedef */
+typedef struct isccc_dottedpair isccc_dottedpair_t;
+/*% isccc_symtab_t typedef */
+typedef struct isccc_symtab isccc_symtab_t;
+
+/*% iscc region structure */
+typedef struct isccc_region {
+ unsigned char *rstart;
+ unsigned char *rend;
+} isccc_region_t;
diff --git a/lib/isccc/include/isccc/util.h b/lib/isccc/include/isccc/util.h
new file mode 100644
index 0000000..546b633
--- /dev/null
+++ b/lib/isccc/include/isccc/util.h
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) 2001 Nominum, Inc.
+ *
+ * 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 ISC AND NOMINUM DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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
+
+#include <inttypes.h>
+
+#include <isc/util.h>
+
+/*! \file isccc/util.h
+ * \brief
+ * Macros for dealing with unaligned numbers.
+ *
+ * \note no side effects are allowed when invoking these macros!
+ */
+
+#define GET8(v, w) \
+ do { \
+ v = *w; \
+ w++; \
+ } while (0)
+
+#define GET16(v, w) \
+ do { \
+ v = (unsigned int)w[0] << 8; \
+ v |= (unsigned int)w[1]; \
+ w += 2; \
+ } while (0)
+
+#define GET24(v, w) \
+ do { \
+ v = (unsigned int)w[0] << 16; \
+ v |= (unsigned int)w[1] << 8; \
+ v |= (unsigned int)w[2]; \
+ w += 3; \
+ } while (0)
+
+#define GET32(v, w) \
+ do { \
+ v = (unsigned int)w[0] << 24; \
+ v |= (unsigned int)w[1] << 16; \
+ v |= (unsigned int)w[2] << 8; \
+ v |= (unsigned int)w[3]; \
+ w += 4; \
+ } while (0)
+
+#define GET64(v, w) \
+ do { \
+ v = (uint64_t)w[0] << 56; \
+ v |= (uint64_t)w[1] << 48; \
+ v |= (uint64_t)w[2] << 40; \
+ v |= (uint64_t)w[3] << 32; \
+ v |= (uint64_t)w[4] << 24; \
+ v |= (uint64_t)w[5] << 16; \
+ v |= (uint64_t)w[6] << 8; \
+ v |= (uint64_t)w[7]; \
+ w += 8; \
+ } while (0)
+
+#define GETC16(v, w, d) \
+ do { \
+ GET8(v, w); \
+ if (v == 0) { \
+ d = ISCCC_TRUE; \
+ } else { \
+ d = ISCCC_FALSE; \
+ if (v == 255) \
+ GET16(v, w); \
+ } \
+ } while (0)
+
+#define GETC32(v, w) \
+ do { \
+ GET24(v, w); \
+ if (v == 0xffffffu) { \
+ GET32(v, w); \
+ } \
+ } while (0)
+
+#define GET_OFFSET(v, w) GET32(v, w)
+
+#define GET_MEM(v, c, w) \
+ do { \
+ memmove(v, w, c); \
+ w += c; \
+ } while (0)
+
+#define GET_TYPE(v, w) \
+ do { \
+ GET8(v, w); \
+ if (v > 127) { \
+ if (v < 255) { \
+ v = ((v & 0x7f) << 16) | ISCCC_RDATATYPE_SIG; \
+ } else { \
+ GET32(v, w); \
+ } \
+ } \
+ } while (0)
+
+#define PUT8(v, w) \
+ do { \
+ *w = (v & 0x000000ffU); \
+ w++; \
+ } while (0)
+
+#define PUT16(v, w) \
+ do { \
+ w[0] = (v & 0x0000ff00U) >> 8; \
+ w[1] = (v & 0x000000ffU); \
+ w += 2; \
+ } while (0)
+
+#define PUT24(v, w) \
+ do { \
+ w[0] = (v & 0x00ff0000U) >> 16; \
+ w[1] = (v & 0x0000ff00U) >> 8; \
+ w[2] = (v & 0x000000ffU); \
+ w += 3; \
+ } while (0)
+
+#define PUT32(v, w) \
+ do { \
+ w[0] = (v & 0xff000000U) >> 24; \
+ w[1] = (v & 0x00ff0000U) >> 16; \
+ w[2] = (v & 0x0000ff00U) >> 8; \
+ w[3] = (v & 0x000000ffU); \
+ w += 4; \
+ } while (0)
+
+#define PUT64(v, w) \
+ do { \
+ w[0] = (v & 0xff00000000000000ULL) >> 56; \
+ w[1] = (v & 0x00ff000000000000ULL) >> 48; \
+ w[2] = (v & 0x0000ff0000000000ULL) >> 40; \
+ w[3] = (v & 0x000000ff00000000ULL) >> 32; \
+ w[4] = (v & 0x00000000ff000000ULL) >> 24; \
+ w[5] = (v & 0x0000000000ff0000ULL) >> 16; \
+ w[6] = (v & 0x000000000000ff00ULL) >> 8; \
+ w[7] = (v & 0x00000000000000ffULL); \
+ w += 8; \
+ } while (0)
+
+#define PUTC16(v, w) \
+ do { \
+ if (v > 0 && v < 255) { \
+ PUT8(v, w); \
+ } else { \
+ PUT8(255, w); \
+ PUT16(v, w); \
+ } \
+ } while (0)
+
+#define PUTC32(v, w) \
+ do { \
+ if (v < 0xffffffU) { \
+ PUT24(v, w); \
+ } else { \
+ PUT24(0xffffffU, w); \
+ PUT32(v, w); \
+ } \
+ } while (0)
+
+#define PUT_OFFSET(v, w) PUT32(v, w)
+
+#include <string.h>
+
+#define PUT_MEM(s, c, w) \
+ do { \
+ memmove(w, s, c); \
+ w += c; \
+ } while (0)
+
+/*
+ * Regions.
+ */
+#define REGION_SIZE(r) ((unsigned int)((r).rend - (r).rstart))
+#define REGION_EMPTY(r) ((r).rstart == (r).rend)
+#define REGION_FROMSTRING(r, s) \
+ do { \
+ (r).rstart = (unsigned char *)s; \
+ (r).rend = (r).rstart + strlen(s); \
+ } while (0)
diff --git a/lib/isccc/sexpr.c b/lib/isccc/sexpr.c
new file mode 100644
index 0000000..62c80c4
--- /dev/null
+++ b/lib/isccc/sexpr.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) 2001 Nominum, Inc.
+ *
+ * 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 ISC AND NOMINUM DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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.
+ */
+
+/*! \file */
+
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/assertions.h>
+#include <isc/print.h>
+
+#include <isccc/sexpr.h>
+#include <isccc/util.h>
+
+static isccc_sexpr_t sexpr_t = { ISCCC_SEXPRTYPE_T, { NULL } };
+
+#define CAR(s) (s)->value.as_dottedpair.car
+#define CDR(s) (s)->value.as_dottedpair.cdr
+
+isccc_sexpr_t *
+isccc_sexpr_cons(isccc_sexpr_t *car, isccc_sexpr_t *cdr) {
+ isccc_sexpr_t *sexpr;
+
+ sexpr = malloc(sizeof(*sexpr));
+ if (sexpr == NULL) {
+ return (NULL);
+ }
+ sexpr->type = ISCCC_SEXPRTYPE_DOTTEDPAIR;
+ CAR(sexpr) = car;
+ CDR(sexpr) = cdr;
+
+ return (sexpr);
+}
+
+isccc_sexpr_t *
+isccc_sexpr_tconst(void) {
+ return (&sexpr_t);
+}
+
+isccc_sexpr_t *
+isccc_sexpr_fromstring(const char *str) {
+ isccc_sexpr_t *sexpr;
+
+ sexpr = malloc(sizeof(*sexpr));
+ if (sexpr == NULL) {
+ return (NULL);
+ }
+ sexpr->type = ISCCC_SEXPRTYPE_STRING;
+ sexpr->value.as_string = strdup(str);
+ if (sexpr->value.as_string == NULL) {
+ free(sexpr);
+ return (NULL);
+ }
+
+ return (sexpr);
+}
+
+isccc_sexpr_t *
+isccc_sexpr_frombinary(const isccc_region_t *region) {
+ isccc_sexpr_t *sexpr;
+ unsigned int region_size;
+
+ sexpr = malloc(sizeof(*sexpr));
+ if (sexpr == NULL) {
+ return (NULL);
+ }
+ sexpr->type = ISCCC_SEXPRTYPE_BINARY;
+ region_size = REGION_SIZE(*region);
+ /*
+ * We add an extra byte when we malloc so we can NUL terminate
+ * the binary data. This allows the caller to use it as a C
+ * string. It's up to the caller to ensure this is safe. We don't
+ * add 1 to the length of the binary region, because the NUL is
+ * not part of the binary data.
+ */
+ sexpr->value.as_region.rstart = malloc(region_size + 1);
+ if (sexpr->value.as_region.rstart == NULL) {
+ free(sexpr);
+ return (NULL);
+ }
+ sexpr->value.as_region.rend = sexpr->value.as_region.rstart +
+ region_size;
+ memmove(sexpr->value.as_region.rstart, region->rstart, region_size);
+ /*
+ * NUL terminate.
+ */
+ sexpr->value.as_region.rstart[region_size] = '\0';
+
+ return (sexpr);
+}
+
+void
+isccc_sexpr_free(isccc_sexpr_t **sexprp) {
+ isccc_sexpr_t *sexpr;
+ isccc_sexpr_t *item;
+
+ sexpr = *sexprp;
+ *sexprp = NULL;
+ if (sexpr == NULL) {
+ return;
+ }
+ switch (sexpr->type) {
+ case ISCCC_SEXPRTYPE_STRING:
+ free(sexpr->value.as_string);
+ break;
+ case ISCCC_SEXPRTYPE_DOTTEDPAIR:
+ item = CAR(sexpr);
+ if (item != NULL) {
+ isccc_sexpr_free(&item);
+ }
+ item = CDR(sexpr);
+ if (item != NULL) {
+ isccc_sexpr_free(&item);
+ }
+ break;
+ case ISCCC_SEXPRTYPE_BINARY:
+ free(sexpr->value.as_region.rstart);
+ break;
+ }
+ free(sexpr);
+}
+
+static bool
+printable(isccc_region_t *r) {
+ unsigned char *curr;
+
+ curr = r->rstart;
+ while (curr != r->rend) {
+ if (!isprint(*curr)) {
+ return (false);
+ }
+ curr++;
+ }
+
+ return (true);
+}
+
+void
+isccc_sexpr_print(isccc_sexpr_t *sexpr, FILE *stream) {
+ isccc_sexpr_t *cdr;
+ unsigned int size, i;
+ unsigned char *curr;
+
+ if (sexpr == NULL) {
+ fprintf(stream, "nil");
+ return;
+ }
+
+ switch (sexpr->type) {
+ case ISCCC_SEXPRTYPE_T:
+ fprintf(stream, "t");
+ break;
+ case ISCCC_SEXPRTYPE_STRING:
+ fprintf(stream, "\"%s\"", sexpr->value.as_string);
+ break;
+ case ISCCC_SEXPRTYPE_DOTTEDPAIR:
+ fprintf(stream, "(");
+ do {
+ isccc_sexpr_print(CAR(sexpr), stream);
+ cdr = CDR(sexpr);
+ if (cdr != NULL) {
+ fprintf(stream, " ");
+ if (cdr->type != ISCCC_SEXPRTYPE_DOTTEDPAIR) {
+ fprintf(stream, ". ");
+ isccc_sexpr_print(cdr, stream);
+ cdr = NULL;
+ }
+ }
+ sexpr = cdr;
+ } while (sexpr != NULL);
+ fprintf(stream, ")");
+ break;
+ case ISCCC_SEXPRTYPE_BINARY:
+ size = REGION_SIZE(sexpr->value.as_region);
+ curr = sexpr->value.as_region.rstart;
+ if (printable(&sexpr->value.as_region)) {
+ fprintf(stream, "'%.*s'", (int)size, curr);
+ } else {
+ fprintf(stream, "0x");
+ for (i = 0; i < size; i++) {
+ fprintf(stream, "%02x", *curr++);
+ }
+ }
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+isccc_sexpr_t *
+isccc_sexpr_car(isccc_sexpr_t *list) {
+ REQUIRE(list->type == ISCCC_SEXPRTYPE_DOTTEDPAIR);
+
+ return (CAR(list));
+}
+
+isccc_sexpr_t *
+isccc_sexpr_cdr(isccc_sexpr_t *list) {
+ REQUIRE(list->type == ISCCC_SEXPRTYPE_DOTTEDPAIR);
+
+ return (CDR(list));
+}
+
+void
+isccc_sexpr_setcar(isccc_sexpr_t *pair, isccc_sexpr_t *car) {
+ REQUIRE(pair->type == ISCCC_SEXPRTYPE_DOTTEDPAIR);
+
+ CAR(pair) = car;
+}
+
+void
+isccc_sexpr_setcdr(isccc_sexpr_t *pair, isccc_sexpr_t *cdr) {
+ REQUIRE(pair->type == ISCCC_SEXPRTYPE_DOTTEDPAIR);
+
+ CDR(pair) = cdr;
+}
+
+isccc_sexpr_t *
+isccc_sexpr_addtolist(isccc_sexpr_t **l1p, isccc_sexpr_t *l2) {
+ isccc_sexpr_t *last, *elt, *l1;
+
+ REQUIRE(l1p != NULL);
+ l1 = *l1p;
+ REQUIRE(l1 == NULL || l1->type == ISCCC_SEXPRTYPE_DOTTEDPAIR);
+
+ elt = isccc_sexpr_cons(l2, NULL);
+ if (elt == NULL) {
+ return (NULL);
+ }
+ if (l1 == NULL) {
+ *l1p = elt;
+ return (elt);
+ }
+ for (last = l1; CDR(last) != NULL; last = CDR(last)) {
+ /* Nothing */
+ }
+ CDR(last) = elt;
+
+ return (elt);
+}
+
+bool
+isccc_sexpr_listp(isccc_sexpr_t *sexpr) {
+ if (sexpr == NULL || sexpr->type == ISCCC_SEXPRTYPE_DOTTEDPAIR) {
+ return (true);
+ }
+ return (false);
+}
+
+bool
+isccc_sexpr_emptyp(isccc_sexpr_t *sexpr) {
+ if (sexpr == NULL) {
+ return (true);
+ }
+ return (false);
+}
+
+bool
+isccc_sexpr_stringp(isccc_sexpr_t *sexpr) {
+ if (sexpr != NULL && sexpr->type == ISCCC_SEXPRTYPE_STRING) {
+ return (true);
+ }
+ return (false);
+}
+
+bool
+isccc_sexpr_binaryp(isccc_sexpr_t *sexpr) {
+ if (sexpr != NULL && sexpr->type == ISCCC_SEXPRTYPE_BINARY) {
+ return (true);
+ }
+ return (false);
+}
+
+char *
+isccc_sexpr_tostring(isccc_sexpr_t *sexpr) {
+ REQUIRE(sexpr != NULL && (sexpr->type == ISCCC_SEXPRTYPE_STRING ||
+ sexpr->type == ISCCC_SEXPRTYPE_BINARY));
+
+ if (sexpr->type == ISCCC_SEXPRTYPE_BINARY) {
+ return ((char *)sexpr->value.as_region.rstart);
+ }
+ return (sexpr->value.as_string);
+}
+
+isccc_region_t *
+isccc_sexpr_tobinary(isccc_sexpr_t *sexpr) {
+ REQUIRE(sexpr != NULL && sexpr->type == ISCCC_SEXPRTYPE_BINARY);
+ return (&sexpr->value.as_region);
+}
diff --git a/lib/isccc/symtab.c b/lib/isccc/symtab.c
new file mode 100644
index 0000000..62e4614
--- /dev/null
+++ b/lib/isccc/symtab.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND ISC
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (C) 2001 Nominum, Inc.
+ *
+ * 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 ISC AND NOMINUM DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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.
+ */
+
+/*! \file */
+
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <isc/assertions.h>
+#include <isc/magic.h>
+#include <isc/result.h>
+#include <isc/string.h>
+
+#include <isccc/symtab.h>
+#include <isccc/util.h>
+
+typedef struct elt {
+ char *key;
+ unsigned int type;
+ isccc_symvalue_t value;
+ ISC_LINK(struct elt) link;
+} elt_t;
+
+typedef ISC_LIST(elt_t) eltlist_t;
+
+#define SYMTAB_MAGIC ISC_MAGIC('S', 'y', 'm', 'T')
+#define VALID_SYMTAB(st) ISC_MAGIC_VALID(st, SYMTAB_MAGIC)
+
+struct isccc_symtab {
+ unsigned int magic;
+ unsigned int size;
+ eltlist_t *table;
+ isccc_symtabundefaction_t undefine_action;
+ void *undefine_arg;
+ bool case_sensitive;
+};
+
+isc_result_t
+isccc_symtab_create(unsigned int size,
+ isccc_symtabundefaction_t undefine_action,
+ void *undefine_arg, bool case_sensitive,
+ isccc_symtab_t **symtabp) {
+ isccc_symtab_t *symtab;
+ unsigned int i;
+
+ REQUIRE(symtabp != NULL && *symtabp == NULL);
+ REQUIRE(size > 0); /* Should be prime. */
+
+ symtab = malloc(sizeof(*symtab));
+ if (symtab == NULL) {
+ return (ISC_R_NOMEMORY);
+ }
+ symtab->table = malloc(size * sizeof(eltlist_t));
+ if (symtab->table == NULL) {
+ free(symtab);
+ return (ISC_R_NOMEMORY);
+ }
+ for (i = 0; i < size; i++) {
+ ISC_LIST_INIT(symtab->table[i]);
+ }
+ symtab->size = size;
+ symtab->undefine_action = undefine_action;
+ symtab->undefine_arg = undefine_arg;
+ symtab->case_sensitive = case_sensitive;
+ symtab->magic = SYMTAB_MAGIC;
+
+ *symtabp = symtab;
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+free_elt(isccc_symtab_t *symtab, unsigned int bucket, elt_t *elt) {
+ ISC_LIST_UNLINK(symtab->table[bucket], elt, link);
+ if (symtab->undefine_action != NULL) {
+ (symtab->undefine_action)(elt->key, elt->type, elt->value,
+ symtab->undefine_arg);
+ }
+ free(elt);
+}
+
+void
+isccc_symtab_destroy(isccc_symtab_t **symtabp) {
+ isccc_symtab_t *symtab;
+ unsigned int i;
+ elt_t *elt, *nelt;
+
+ REQUIRE(symtabp != NULL);
+ symtab = *symtabp;
+ *symtabp = NULL;
+ REQUIRE(VALID_SYMTAB(symtab));
+
+ for (i = 0; i < symtab->size; i++) {
+ for (elt = ISC_LIST_HEAD(symtab->table[i]); elt != NULL;
+ elt = nelt)
+ {
+ nelt = ISC_LIST_NEXT(elt, link);
+ free_elt(symtab, i, elt);
+ }
+ }
+ free(symtab->table);
+ symtab->magic = 0;
+ free(symtab);
+}
+
+static unsigned int
+hash(const char *key, bool case_sensitive) {
+ const char *s;
+ unsigned int h = 0;
+ unsigned int g;
+ int c;
+
+ /*
+ * P. J. Weinberger's hash function, adapted from p. 436 of
+ * _Compilers: Principles, Techniques, and Tools_, Aho, Sethi
+ * and Ullman, Addison-Wesley, 1986, ISBN 0-201-10088-6.
+ */
+
+ if (case_sensitive) {
+ for (s = key; *s != '\0'; s++) {
+ h = (h << 4) + *s;
+ if ((g = (h & 0xf0000000)) != 0) {
+ h = h ^ (g >> 24);
+ h = h ^ g;
+ }
+ }
+ } else {
+ for (s = key; *s != '\0'; s++) {
+ c = *s;
+ c = tolower((unsigned char)c);
+ h = (h << 4) + c;
+ if ((g = (h & 0xf0000000)) != 0) {
+ h = h ^ (g >> 24);
+ h = h ^ g;
+ }
+ }
+ }
+
+ return (h);
+}
+
+#define FIND(s, k, t, b, e) \
+ b = hash((k), (s)->case_sensitive) % (s)->size; \
+ if ((s)->case_sensitive) { \
+ for (e = ISC_LIST_HEAD((s)->table[b]); e != NULL; \
+ e = ISC_LIST_NEXT(e, link)) \
+ { \
+ if (((t) == 0 || e->type == (t)) && \
+ strcmp(e->key, (k)) == 0) \
+ break; \
+ } \
+ } else { \
+ for (e = ISC_LIST_HEAD((s)->table[b]); e != NULL; \
+ e = ISC_LIST_NEXT(e, link)) \
+ { \
+ if (((t) == 0 || e->type == (t)) && \
+ strcasecmp(e->key, (k)) == 0) \
+ break; \
+ } \
+ }
+
+isc_result_t
+isccc_symtab_lookup(isccc_symtab_t *symtab, const char *key, unsigned int type,
+ isccc_symvalue_t *value) {
+ unsigned int bucket;
+ elt_t *elt;
+
+ REQUIRE(VALID_SYMTAB(symtab));
+ REQUIRE(key != NULL);
+
+ FIND(symtab, key, type, bucket, elt);
+
+ if (elt == NULL) {
+ return (ISC_R_NOTFOUND);
+ }
+
+ if (value != NULL) {
+ *value = elt->value;
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isccc_symtab_define(isccc_symtab_t *symtab, char *key, unsigned int type,
+ isccc_symvalue_t value, isccc_symexists_t exists_policy) {
+ unsigned int bucket;
+ elt_t *elt;
+
+ REQUIRE(VALID_SYMTAB(symtab));
+ REQUIRE(key != NULL);
+ REQUIRE(type != 0);
+
+ FIND(symtab, key, type, bucket, elt);
+
+ if (exists_policy != isccc_symexists_add && elt != NULL) {
+ if (exists_policy == isccc_symexists_reject) {
+ return (ISC_R_EXISTS);
+ }
+ INSIST(exists_policy == isccc_symexists_replace);
+ ISC_LIST_UNLINK(symtab->table[bucket], elt, link);
+ if (symtab->undefine_action != NULL) {
+ (symtab->undefine_action)(elt->key, elt->type,
+ elt->value,
+ symtab->undefine_arg);
+ }
+ } else {
+ elt = malloc(sizeof(*elt));
+ if (elt == NULL) {
+ return (ISC_R_NOMEMORY);
+ }
+ ISC_LINK_INIT(elt, link);
+ }
+
+ elt->key = key;
+ elt->type = type;
+ elt->value = value;
+
+ /*
+ * We prepend so that the most recent definition will be found.
+ */
+ ISC_LIST_PREPEND(symtab->table[bucket], elt, link);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isccc_symtab_undefine(isccc_symtab_t *symtab, const char *key,
+ unsigned int type) {
+ unsigned int bucket;
+ elt_t *elt;
+
+ REQUIRE(VALID_SYMTAB(symtab));
+ REQUIRE(key != NULL);
+
+ FIND(symtab, key, type, bucket, elt);
+
+ if (elt == NULL) {
+ return (ISC_R_NOTFOUND);
+ }
+
+ free_elt(symtab, bucket, elt);
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+isccc_symtab_foreach(isccc_symtab_t *symtab, isccc_symtabforeachaction_t action,
+ void *arg) {
+ unsigned int i;
+ elt_t *elt, *nelt;
+
+ REQUIRE(VALID_SYMTAB(symtab));
+ REQUIRE(action != NULL);
+
+ for (i = 0; i < symtab->size; i++) {
+ for (elt = ISC_LIST_HEAD(symtab->table[i]); elt != NULL;
+ elt = nelt)
+ {
+ nelt = ISC_LIST_NEXT(elt, link);
+ if ((action)(elt->key, elt->type, elt->value, arg)) {
+ free_elt(symtab, i, elt);
+ }
+ }
+ }
+}
diff --git a/lib/isccfg/Makefile.am b/lib/isccfg/Makefile.am
new file mode 100644
index 0000000..0c95c4f
--- /dev/null
+++ b/lib/isccfg/Makefile.am
@@ -0,0 +1,37 @@
+include $(top_srcdir)/Makefile.top
+
+lib_LTLIBRARIES = libisccfg.la
+
+libisccfg_ladir = $(includedir)/isccfg
+libisccfg_la_HEADERS = \
+ include/isccfg/aclconf.h \
+ include/isccfg/cfg.h \
+ include/isccfg/duration.h \
+ include/isccfg/grammar.h \
+ include/isccfg/kaspconf.h \
+ include/isccfg/log.h \
+ include/isccfg/namedconf.h
+
+libisccfg_la_SOURCES = \
+ $(libisccfg_la_HEADERS) \
+ aclconf.c \
+ dnsconf.c \
+ duration.c \
+ kaspconf.c \
+ log.c \
+ namedconf.c \
+ parser.c
+
+libisccfg_la_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ $(LIBISCCFG_CFLAGS) \
+ $(LIBISC_CFLAGS) \
+ $(LIBDNS_CFLAGS)
+
+libisccfg_la_LIBADD = \
+ $(LIBDNS_LIBS) \
+ $(LIBISC_LIBS)
+
+libisccfg_la_LDFLAGS = \
+ $(AM_LDFLAGS) \
+ -release "$(PACKAGE_VERSION)"
diff --git a/lib/isccfg/Makefile.in b/lib/isccfg/Makefile.in
new file mode 100644
index 0000000..38f6b93
--- /dev/null
+++ b/lib/isccfg/Makefile.in
@@ -0,0 +1,967 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 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@
+
+# Hey Emacs, this is -*- makefile-automake -*- file!
+# vim: filetype=automake
+
+
+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@
+target_triplet = @target@
+@HOST_MACOS_TRUE@am__append_1 = \
+@HOST_MACOS_TRUE@ -Wl,-flat_namespace
+
+subdir = lib/isccfg
+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/ax_check_openssl.m4 \
+ $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4/ax_jemalloc.m4 \
+ $(top_srcdir)/m4/ax_lib_lmdb.m4 \
+ $(top_srcdir)/m4/ax_perl_module.m4 \
+ $(top_srcdir)/m4/ax_posix_shell.m4 \
+ $(top_srcdir)/m4/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4/ax_pthread.m4 \
+ $(top_srcdir)/m4/ax_python_module.m4 \
+ $(top_srcdir)/m4/ax_restore_flags.m4 \
+ $(top_srcdir)/m4/ax_save_flags.m4 $(top_srcdir)/m4/ax_tls.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(libisccfg_la_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libisccfg_ladir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+libisccfg_la_DEPENDENCIES = $(LIBDNS_LIBS) $(LIBISC_LIBS)
+am__objects_1 =
+am_libisccfg_la_OBJECTS = $(am__objects_1) libisccfg_la-aclconf.lo \
+ libisccfg_la-dnsconf.lo libisccfg_la-duration.lo \
+ libisccfg_la-kaspconf.lo libisccfg_la-log.lo \
+ libisccfg_la-namedconf.lo libisccfg_la-parser.lo
+libisccfg_la_OBJECTS = $(am_libisccfg_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 =
+libisccfg_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libisccfg_la_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@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/libisccfg_la-aclconf.Plo \
+ ./$(DEPDIR)/libisccfg_la-dnsconf.Plo \
+ ./$(DEPDIR)/libisccfg_la-duration.Plo \
+ ./$(DEPDIR)/libisccfg_la-kaspconf.Plo \
+ ./$(DEPDIR)/libisccfg_la-log.Plo \
+ ./$(DEPDIR)/libisccfg_la-namedconf.Plo \
+ ./$(DEPDIR)/libisccfg_la-parser.Plo
+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 = $(libisccfg_la_SOURCES)
+DIST_SOURCES = $(libisccfg_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(libisccfg_la_HEADERS)
+am__extra_recursive_targets = test-recursive unit-recursive \
+ doc-recursive
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/Makefile.top \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CURL = @CURL@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DEVELOPER_MODE = @DEVELOPER_MODE@
+DLLTOOL = @DLLTOOL@
+DNSTAP_CFLAGS = @DNSTAP_CFLAGS@
+DNSTAP_LIBS = @DNSTAP_LIBS@
+DOXYGEN = @DOXYGEN@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILECMD = @FILECMD@
+FSTRM_CAPTURE = @FSTRM_CAPTURE@
+FUZZ_LDFLAGS = @FUZZ_LDFLAGS@
+FUZZ_LOG_COMPILER = @FUZZ_LOG_COMPILER@
+GREP = @GREP@
+GSSAPI_CFLAGS = @GSSAPI_CFLAGS@
+GSSAPI_LIBS = @GSSAPI_LIBS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@
+JEMALLOC_LIBS = @JEMALLOC_LIBS@
+JSON_C_CFLAGS = @JSON_C_CFLAGS@
+JSON_C_LIBS = @JSON_C_LIBS@
+KRB5_CFLAGS = @KRB5_CFLAGS@
+KRB5_CONFIG = @KRB5_CONFIG@
+KRB5_LIBS = @KRB5_LIBS@
+LATEXMK = @LATEXMK@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBCAP_LIBS = @LIBCAP_LIBS@
+LIBIDN2_CFLAGS = @LIBIDN2_CFLAGS@
+LIBIDN2_LIBS = @LIBIDN2_LIBS@
+LIBNGHTTP2_CFLAGS = @LIBNGHTTP2_CFLAGS@
+LIBNGHTTP2_LIBS = @LIBNGHTTP2_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUV_CFLAGS = @LIBUV_CFLAGS@
+LIBUV_LIBS = @LIBUV_LIBS@
+LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
+LIBXML2_LIBS = @LIBXML2_LIBS@
+LIPO = @LIPO@
+LMDB_CFLAGS = @LMDB_CFLAGS@
+LMDB_LIBS = @LMDB_LIBS@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MAXMINDDB_CFLAGS = @MAXMINDDB_CFLAGS@
+MAXMINDDB_LIBS = @MAXMINDDB_LIBS@
+MAXMINDDB_PREFIX = @MAXMINDDB_PREFIX@
+MKDIR_P = @MKDIR_P@
+NC = @NC@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENSSL_CFLAGS = @OPENSSL_CFLAGS@
+OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@
+OPENSSL_LIBS = @OPENSSL_LIBS@
+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@
+PERL = @PERL@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PROTOC_C = @PROTOC_C@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_CXX = @PTHREAD_CXX@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+PYTEST = @PYTEST@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+READLINE_CFLAGS = @READLINE_CFLAGS@
+READLINE_LIBS = @READLINE_LIBS@
+RELEASE_DATE = @RELEASE_DATE@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINX_BUILD = @SPHINX_BUILD@
+STD_CFLAGS = @STD_CFLAGS@
+STD_CPPFLAGS = @STD_CPPFLAGS@
+STD_LDFLAGS = @STD_LDFLAGS@
+STRIP = @STRIP@
+TEST_CFLAGS = @TEST_CFLAGS@
+VERSION = @VERSION@
+XELATEX = @XELATEX@
+XSLTPROC = @XSLTPROC@
+ZLIB_CFLAGS = @ZLIB_CFLAGS@
+ZLIB_LIBS = @ZLIB_LIBS@
+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_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+ax_pthread_config = @ax_pthread_config@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+ACLOCAL_AMFLAGS = -I $(top_srcdir)/m4
+AM_CFLAGS = \
+ $(STD_CFLAGS)
+
+AM_CPPFLAGS = \
+ $(STD_CPPFLAGS) \
+ -include $(top_builddir)/config.h \
+ -I$(srcdir)/include
+
+AM_LDFLAGS = $(STD_LDFLAGS) $(am__append_1)
+LDADD =
+LIBISC_CFLAGS = \
+ -I$(top_srcdir)/include \
+ -I$(top_srcdir)/lib/isc/include \
+ -I$(top_builddir)/lib/isc/include
+
+LIBISC_LIBS = $(top_builddir)/lib/isc/libisc.la
+LIBDNS_CFLAGS = \
+ -I$(top_srcdir)/lib/dns/include \
+ -I$(top_builddir)/lib/dns/include
+
+LIBDNS_LIBS = \
+ $(top_builddir)/lib/dns/libdns.la
+
+LIBNS_CFLAGS = \
+ -I$(top_srcdir)/lib/ns/include
+
+LIBNS_LIBS = \
+ $(top_builddir)/lib/ns/libns.la
+
+LIBIRS_CFLAGS = \
+ -I$(top_srcdir)/lib/irs/include
+
+LIBIRS_LIBS = \
+ $(top_builddir)/lib/irs/libirs.la
+
+LIBISCCFG_CFLAGS = \
+ -I$(top_srcdir)/lib/isccfg/include
+
+LIBISCCFG_LIBS = \
+ $(top_builddir)/lib/isccfg/libisccfg.la
+
+LIBISCCC_CFLAGS = \
+ -I$(top_srcdir)/lib/isccc/include/
+
+LIBISCCC_LIBS = \
+ $(top_builddir)/lib/isccc/libisccc.la
+
+LIBBIND9_CFLAGS = \
+ -I$(top_srcdir)/lib/bind9/include
+
+LIBBIND9_LIBS = \
+ $(top_builddir)/lib/bind9/libbind9.la
+
+lib_LTLIBRARIES = libisccfg.la
+libisccfg_ladir = $(includedir)/isccfg
+libisccfg_la_HEADERS = \
+ include/isccfg/aclconf.h \
+ include/isccfg/cfg.h \
+ include/isccfg/duration.h \
+ include/isccfg/grammar.h \
+ include/isccfg/kaspconf.h \
+ include/isccfg/log.h \
+ include/isccfg/namedconf.h
+
+libisccfg_la_SOURCES = \
+ $(libisccfg_la_HEADERS) \
+ aclconf.c \
+ dnsconf.c \
+ duration.c \
+ kaspconf.c \
+ log.c \
+ namedconf.c \
+ parser.c
+
+libisccfg_la_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ $(LIBISCCFG_CFLAGS) \
+ $(LIBISC_CFLAGS) \
+ $(LIBDNS_CFLAGS)
+
+libisccfg_la_LIBADD = \
+ $(LIBDNS_LIBS) \
+ $(LIBISC_LIBS)
+
+libisccfg_la_LDFLAGS = \
+ $(AM_LDFLAGS) \
+ -release "$(PACKAGE_VERSION)"
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.top $(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 lib/isccfg/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign lib/isccfg/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_srcdir)/Makefile.top $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+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}; \
+ }
+
+libisccfg.la: $(libisccfg_la_OBJECTS) $(libisccfg_la_DEPENDENCIES) $(EXTRA_libisccfg_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libisccfg_la_LINK) -rpath $(libdir) $(libisccfg_la_OBJECTS) $(libisccfg_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisccfg_la-aclconf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisccfg_la-dnsconf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisccfg_la-duration.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisccfg_la-kaspconf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisccfg_la-log.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisccfg_la-namedconf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libisccfg_la-parser.Plo@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 $@ $<
+
+libisccfg_la-aclconf.lo: aclconf.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisccfg_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisccfg_la-aclconf.lo -MD -MP -MF $(DEPDIR)/libisccfg_la-aclconf.Tpo -c -o libisccfg_la-aclconf.lo `test -f 'aclconf.c' || echo '$(srcdir)/'`aclconf.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisccfg_la-aclconf.Tpo $(DEPDIR)/libisccfg_la-aclconf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='aclconf.c' object='libisccfg_la-aclconf.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisccfg_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisccfg_la-aclconf.lo `test -f 'aclconf.c' || echo '$(srcdir)/'`aclconf.c
+
+libisccfg_la-dnsconf.lo: dnsconf.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisccfg_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisccfg_la-dnsconf.lo -MD -MP -MF $(DEPDIR)/libisccfg_la-dnsconf.Tpo -c -o libisccfg_la-dnsconf.lo `test -f 'dnsconf.c' || echo '$(srcdir)/'`dnsconf.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisccfg_la-dnsconf.Tpo $(DEPDIR)/libisccfg_la-dnsconf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dnsconf.c' object='libisccfg_la-dnsconf.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisccfg_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisccfg_la-dnsconf.lo `test -f 'dnsconf.c' || echo '$(srcdir)/'`dnsconf.c
+
+libisccfg_la-duration.lo: duration.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisccfg_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisccfg_la-duration.lo -MD -MP -MF $(DEPDIR)/libisccfg_la-duration.Tpo -c -o libisccfg_la-duration.lo `test -f 'duration.c' || echo '$(srcdir)/'`duration.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisccfg_la-duration.Tpo $(DEPDIR)/libisccfg_la-duration.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='duration.c' object='libisccfg_la-duration.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisccfg_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisccfg_la-duration.lo `test -f 'duration.c' || echo '$(srcdir)/'`duration.c
+
+libisccfg_la-kaspconf.lo: kaspconf.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisccfg_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisccfg_la-kaspconf.lo -MD -MP -MF $(DEPDIR)/libisccfg_la-kaspconf.Tpo -c -o libisccfg_la-kaspconf.lo `test -f 'kaspconf.c' || echo '$(srcdir)/'`kaspconf.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisccfg_la-kaspconf.Tpo $(DEPDIR)/libisccfg_la-kaspconf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='kaspconf.c' object='libisccfg_la-kaspconf.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisccfg_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisccfg_la-kaspconf.lo `test -f 'kaspconf.c' || echo '$(srcdir)/'`kaspconf.c
+
+libisccfg_la-log.lo: log.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisccfg_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisccfg_la-log.lo -MD -MP -MF $(DEPDIR)/libisccfg_la-log.Tpo -c -o libisccfg_la-log.lo `test -f 'log.c' || echo '$(srcdir)/'`log.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisccfg_la-log.Tpo $(DEPDIR)/libisccfg_la-log.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='log.c' object='libisccfg_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) $(libisccfg_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisccfg_la-log.lo `test -f 'log.c' || echo '$(srcdir)/'`log.c
+
+libisccfg_la-namedconf.lo: namedconf.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisccfg_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisccfg_la-namedconf.lo -MD -MP -MF $(DEPDIR)/libisccfg_la-namedconf.Tpo -c -o libisccfg_la-namedconf.lo `test -f 'namedconf.c' || echo '$(srcdir)/'`namedconf.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisccfg_la-namedconf.Tpo $(DEPDIR)/libisccfg_la-namedconf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='namedconf.c' object='libisccfg_la-namedconf.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisccfg_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisccfg_la-namedconf.lo `test -f 'namedconf.c' || echo '$(srcdir)/'`namedconf.c
+
+libisccfg_la-parser.lo: parser.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libisccfg_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libisccfg_la-parser.lo -MD -MP -MF $(DEPDIR)/libisccfg_la-parser.Tpo -c -o libisccfg_la-parser.lo `test -f 'parser.c' || echo '$(srcdir)/'`parser.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libisccfg_la-parser.Tpo $(DEPDIR)/libisccfg_la-parser.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='parser.c' object='libisccfg_la-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) $(libisccfg_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libisccfg_la-parser.lo `test -f 'parser.c' || echo '$(srcdir)/'`parser.c
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-libisccfg_laHEADERS: $(libisccfg_la_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(libisccfg_la_HEADERS)'; test -n "$(libisccfg_ladir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libisccfg_ladir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libisccfg_ladir)" || 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)$(libisccfg_ladir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(libisccfg_ladir)" || exit $$?; \
+ done
+
+uninstall-libisccfg_laHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libisccfg_la_HEADERS)'; test -n "$(libisccfg_ladir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(libisccfg_ladir)'; $(am__uninstall_files_from_dir)
+test-local:
+unit-local:
+doc-local:
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libisccfg_ladir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/libisccfg_la-aclconf.Plo
+ -rm -f ./$(DEPDIR)/libisccfg_la-dnsconf.Plo
+ -rm -f ./$(DEPDIR)/libisccfg_la-duration.Plo
+ -rm -f ./$(DEPDIR)/libisccfg_la-kaspconf.Plo
+ -rm -f ./$(DEPDIR)/libisccfg_la-log.Plo
+ -rm -f ./$(DEPDIR)/libisccfg_la-namedconf.Plo
+ -rm -f ./$(DEPDIR)/libisccfg_la-parser.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+doc: doc-am
+
+doc-am: doc-local
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-libisccfg_laHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/libisccfg_la-aclconf.Plo
+ -rm -f ./$(DEPDIR)/libisccfg_la-dnsconf.Plo
+ -rm -f ./$(DEPDIR)/libisccfg_la-duration.Plo
+ -rm -f ./$(DEPDIR)/libisccfg_la-kaspconf.Plo
+ -rm -f ./$(DEPDIR)/libisccfg_la-log.Plo
+ -rm -f ./$(DEPDIR)/libisccfg_la-namedconf.Plo
+ -rm -f ./$(DEPDIR)/libisccfg_la-parser.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+test: test-am
+
+test-am: test-local
+
+uninstall-am: uninstall-libLTLIBRARIES uninstall-libisccfg_laHEADERS
+
+unit: unit-am
+
+unit-am: unit-local
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libLTLIBRARIES clean-libtool cscopelist-am \
+ ctags ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir doc-am doc-local 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-libLTLIBRARIES \
+ install-libisccfg_laHEADERS install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am test-am test-local uninstall uninstall-am \
+ uninstall-libLTLIBRARIES uninstall-libisccfg_laHEADERS unit-am \
+ unit-local
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/lib/isccfg/aclconf.c b/lib/isccfg/aclconf.c
new file mode 100644
index 0000000..1e3566a
--- /dev/null
+++ b/lib/isccfg/aclconf.c
@@ -0,0 +1,1010 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/string.h> /* Required for HP/UX (and others?) */
+#include <isc/util.h>
+
+#include <dns/acl.h>
+#include <dns/fixedname.h>
+#include <dns/iptable.h>
+#include <dns/log.h>
+
+#include <isccfg/aclconf.h>
+#include <isccfg/namedconf.h>
+
+#define LOOP_MAGIC ISC_MAGIC('L', 'O', 'O', 'P')
+
+#if defined(HAVE_GEOIP2)
+static const char *geoip_dbnames[] = {
+ "country", "city", "asnum", "isp", "domain", NULL,
+};
+#endif /* if defined(HAVE_GEOIP2) */
+
+isc_result_t
+cfg_aclconfctx_create(isc_mem_t *mctx, cfg_aclconfctx_t **ret) {
+ cfg_aclconfctx_t *actx;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(ret != NULL && *ret == NULL);
+
+ actx = isc_mem_get(mctx, sizeof(*actx));
+
+ isc_refcount_init(&actx->references, 1);
+
+ actx->mctx = NULL;
+ isc_mem_attach(mctx, &actx->mctx);
+ ISC_LIST_INIT(actx->named_acl_cache);
+
+#if defined(HAVE_GEOIP2)
+ actx->geoip = NULL;
+#endif /* if defined(HAVE_GEOIP2) */
+
+ *ret = actx;
+ return (ISC_R_SUCCESS);
+}
+
+void
+cfg_aclconfctx_attach(cfg_aclconfctx_t *src, cfg_aclconfctx_t **dest) {
+ REQUIRE(src != NULL);
+ REQUIRE(dest != NULL && *dest == NULL);
+
+ isc_refcount_increment(&src->references);
+ *dest = src;
+}
+
+void
+cfg_aclconfctx_detach(cfg_aclconfctx_t **actxp) {
+ REQUIRE(actxp != NULL && *actxp != NULL);
+ cfg_aclconfctx_t *actx = *actxp;
+ *actxp = NULL;
+
+ if (isc_refcount_decrement(&actx->references) == 1) {
+ dns_acl_t *dacl, *next;
+ isc_refcount_destroy(&actx->references);
+ for (dacl = ISC_LIST_HEAD(actx->named_acl_cache); dacl != NULL;
+ dacl = next)
+ {
+ next = ISC_LIST_NEXT(dacl, nextincache);
+ ISC_LIST_UNLINK(actx->named_acl_cache, dacl,
+ nextincache);
+ dns_acl_detach(&dacl);
+ }
+ isc_mem_putanddetach(&actx->mctx, actx, sizeof(*actx));
+ }
+}
+
+/*
+ * Find the definition of the named acl whose name is "name".
+ */
+static isc_result_t
+get_acl_def(const cfg_obj_t *cctx, const char *name, const cfg_obj_t **ret) {
+ isc_result_t result;
+ const cfg_obj_t *acls = NULL;
+ const cfg_listelt_t *elt;
+
+ result = cfg_map_get(cctx, "acl", &acls);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ for (elt = cfg_list_first(acls); elt != NULL; elt = cfg_list_next(elt))
+ {
+ const cfg_obj_t *acl = cfg_listelt_value(elt);
+ const char *aclname =
+ cfg_obj_asstring(cfg_tuple_get(acl, "name"));
+ if (strcasecmp(aclname, name) == 0) {
+ if (ret != NULL) {
+ *ret = cfg_tuple_get(acl, "value");
+ }
+ return (ISC_R_SUCCESS);
+ }
+ }
+ return (ISC_R_NOTFOUND);
+}
+
+static isc_result_t
+convert_named_acl(const cfg_obj_t *nameobj, const cfg_obj_t *cctx,
+ isc_log_t *lctx, cfg_aclconfctx_t *ctx, isc_mem_t *mctx,
+ unsigned int nest_level, dns_acl_t **target) {
+ isc_result_t result;
+ const cfg_obj_t *cacl = NULL;
+ dns_acl_t *dacl;
+ dns_acl_t loop;
+ const char *aclname = cfg_obj_asstring(nameobj);
+
+ /* Look for an already-converted version. */
+ for (dacl = ISC_LIST_HEAD(ctx->named_acl_cache); dacl != NULL;
+ dacl = ISC_LIST_NEXT(dacl, nextincache))
+ {
+ if (strcasecmp(aclname, dacl->name) == 0) {
+ if (ISC_MAGIC_VALID(dacl, LOOP_MAGIC)) {
+ cfg_obj_log(nameobj, lctx, ISC_LOG_ERROR,
+ "acl loop detected: %s", aclname);
+ return (ISC_R_FAILURE);
+ }
+ dns_acl_attach(dacl, target);
+ return (ISC_R_SUCCESS);
+ }
+ }
+ /* Not yet converted. Convert now. */
+ result = get_acl_def(cctx, aclname, &cacl);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(nameobj, lctx, ISC_LOG_WARNING,
+ "undefined ACL '%s'", aclname);
+ return (result);
+ }
+ /*
+ * Add a loop detection element.
+ */
+ memset(&loop, 0, sizeof(loop));
+ ISC_LINK_INIT(&loop, nextincache);
+ DE_CONST(aclname, loop.name);
+ loop.magic = LOOP_MAGIC;
+ ISC_LIST_APPEND(ctx->named_acl_cache, &loop, nextincache);
+ result = cfg_acl_fromconfig(cacl, cctx, lctx, ctx, mctx, nest_level,
+ &dacl);
+ ISC_LIST_UNLINK(ctx->named_acl_cache, &loop, nextincache);
+ loop.magic = 0;
+ loop.name = NULL;
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ dacl->name = isc_mem_strdup(dacl->mctx, aclname);
+ ISC_LIST_APPEND(ctx->named_acl_cache, dacl, nextincache);
+ dns_acl_attach(dacl, target);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+convert_keyname(const cfg_obj_t *keyobj, isc_log_t *lctx, isc_mem_t *mctx,
+ dns_name_t *dnsname) {
+ isc_result_t result;
+ isc_buffer_t buf;
+ dns_fixedname_t fixname;
+ unsigned int keylen;
+ const char *txtname = cfg_obj_asstring(keyobj);
+
+ keylen = strlen(txtname);
+ isc_buffer_constinit(&buf, txtname, keylen);
+ isc_buffer_add(&buf, keylen);
+ dns_fixedname_init(&fixname);
+ result = dns_name_fromtext(dns_fixedname_name(&fixname), &buf,
+ dns_rootname, 0, NULL);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(keyobj, lctx, ISC_LOG_WARNING,
+ "key name '%s' is not a valid domain name",
+ txtname);
+ return (result);
+ }
+ dns_name_dup(dns_fixedname_name(&fixname), mctx, dnsname);
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Recursively pre-parse an ACL definition to find the total number
+ * of non-IP-prefix elements (localhost, localnets, key) in all nested
+ * ACLs, so that the parent will have enough space allocated for the
+ * elements table after all the nested ACLs have been merged in to the
+ * parent.
+ */
+static isc_result_t
+count_acl_elements(const cfg_obj_t *caml, const cfg_obj_t *cctx,
+ isc_log_t *lctx, cfg_aclconfctx_t *ctx, isc_mem_t *mctx,
+ uint32_t *count, bool *has_negative) {
+ const cfg_listelt_t *elt;
+ isc_result_t result;
+ uint32_t n = 0;
+
+ REQUIRE(count != NULL);
+
+ if (has_negative != NULL) {
+ *has_negative = false;
+ }
+
+ for (elt = cfg_list_first(caml); elt != NULL; elt = cfg_list_next(elt))
+ {
+ const cfg_obj_t *ce = cfg_listelt_value(elt);
+
+ /* might be a negated element, in which case get the value. */
+ if (cfg_obj_istuple(ce)) {
+ const cfg_obj_t *negated = cfg_tuple_get(ce, "negated");
+ if (!cfg_obj_isvoid(negated)) {
+ ce = negated;
+ if (has_negative != NULL) {
+ *has_negative = true;
+ }
+ }
+ }
+
+ if (cfg_obj_istype(ce, &cfg_type_keyref)) {
+ n++;
+ } else if (cfg_obj_islist(ce)) {
+ bool negative;
+ uint32_t sub;
+ result = count_acl_elements(ce, cctx, lctx, ctx, mctx,
+ &sub, &negative);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ n += sub;
+ if (negative) {
+ n++;
+ }
+#if defined(HAVE_GEOIP2)
+ } else if (cfg_obj_istuple(ce) &&
+ cfg_obj_isvoid(cfg_tuple_get(ce, "negated")))
+ {
+ n++;
+#endif /* HAVE_GEOIP2 */
+ } else if (cfg_obj_isstring(ce)) {
+ const char *name = cfg_obj_asstring(ce);
+ if (strcasecmp(name, "localhost") == 0 ||
+ strcasecmp(name, "localnets") == 0 ||
+ strcasecmp(name, "none") == 0)
+ {
+ n++;
+ } else if (strcasecmp(name, "any") != 0) {
+ dns_acl_t *inneracl = NULL;
+ /*
+ * Convert any named acls we reference now if
+ * they have not already been converted.
+ */
+ result = convert_named_acl(ce, cctx, lctx, ctx,
+ mctx, 0, &inneracl);
+ if (result == ISC_R_SUCCESS) {
+ if (inneracl->has_negatives) {
+ n++;
+ } else {
+ n += inneracl->length;
+ }
+ dns_acl_detach(&inneracl);
+ } else {
+ return (result);
+ }
+ }
+ }
+ }
+
+ *count = n;
+ return (ISC_R_SUCCESS);
+}
+
+#if defined(HAVE_GEOIP2)
+static dns_geoip_subtype_t
+get_subtype(const cfg_obj_t *obj, isc_log_t *lctx, dns_geoip_subtype_t subtype,
+ const char *dbname) {
+ if (dbname == NULL) {
+ return (subtype);
+ }
+
+ switch (subtype) {
+ case dns_geoip_countrycode:
+ if (strcasecmp(dbname, "city") == 0) {
+ return (dns_geoip_city_countrycode);
+ } else if (strcasecmp(dbname, "country") == 0) {
+ return (dns_geoip_country_code);
+ }
+ cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
+ "invalid database specified for "
+ "country search: ignored");
+ return (subtype);
+ case dns_geoip_countryname:
+ if (strcasecmp(dbname, "city") == 0) {
+ return (dns_geoip_city_countryname);
+ } else if (strcasecmp(dbname, "country") == 0) {
+ return (dns_geoip_country_name);
+ }
+ cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
+ "invalid database specified for "
+ "country search: ignored");
+ return (subtype);
+ case dns_geoip_continentcode:
+ if (strcasecmp(dbname, "city") == 0) {
+ return (dns_geoip_city_continentcode);
+ } else if (strcasecmp(dbname, "country") == 0) {
+ return (dns_geoip_country_continentcode);
+ }
+ cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
+ "invalid database specified for "
+ "continent search: ignored");
+ return (subtype);
+ case dns_geoip_continent:
+ if (strcasecmp(dbname, "city") == 0) {
+ return (dns_geoip_city_continent);
+ } else if (strcasecmp(dbname, "country") == 0) {
+ return (dns_geoip_country_continent);
+ }
+ cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
+ "invalid database specified for "
+ "continent search: ignored");
+ return (subtype);
+ case dns_geoip_region:
+ if (strcasecmp(dbname, "city") == 0) {
+ return (dns_geoip_city_region);
+ }
+ cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
+ "invalid database specified for "
+ "region/subdivision search: ignored");
+ return (subtype);
+ case dns_geoip_regionname:
+ if (strcasecmp(dbname, "city") == 0) {
+ return (dns_geoip_city_regionname);
+ }
+ cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
+ "invalid database specified for "
+ "region/subdivision search: ignored");
+ return (subtype);
+
+ /*
+ * Log a warning if the wrong database was specified
+ * on an unambiguous query
+ */
+ case dns_geoip_city_name:
+ case dns_geoip_city_postalcode:
+ case dns_geoip_city_metrocode:
+ case dns_geoip_city_areacode:
+ case dns_geoip_city_timezonecode:
+ if (strcasecmp(dbname, "city") != 0) {
+ cfg_obj_log(obj, lctx, ISC_LOG_WARNING,
+ "invalid database specified for "
+ "a 'city'-only search type: ignoring");
+ }
+ return (subtype);
+ case dns_geoip_isp_name:
+ if (strcasecmp(dbname, "isp") != 0) {
+ cfg_obj_log(obj, lctx, ISC_LOG_WARNING,
+ "invalid database specified for "
+ "an 'isp' search: ignoring");
+ }
+ return (subtype);
+ case dns_geoip_org_name:
+ if (strcasecmp(dbname, "org") != 0) {
+ cfg_obj_log(obj, lctx, ISC_LOG_WARNING,
+ "invalid database specified for "
+ "an 'org' search: ignoring");
+ }
+ return (subtype);
+ case dns_geoip_as_asnum:
+ if (strcasecmp(dbname, "asnum") != 0) {
+ cfg_obj_log(obj, lctx, ISC_LOG_WARNING,
+ "invalid database specified for "
+ "an 'asnum' search: ignoring");
+ }
+ return (subtype);
+ case dns_geoip_domain_name:
+ if (strcasecmp(dbname, "domain") != 0) {
+ cfg_obj_log(obj, lctx, ISC_LOG_WARNING,
+ "invalid database specified for "
+ "a 'domain' search: ignoring");
+ }
+ return (subtype);
+ case dns_geoip_netspeed_id:
+ if (strcasecmp(dbname, "netspeed") != 0) {
+ cfg_obj_log(obj, lctx, ISC_LOG_WARNING,
+ "invalid database specified for "
+ "a 'netspeed' search: ignoring");
+ }
+ return (subtype);
+ default:
+ UNREACHABLE();
+ }
+}
+
+static bool
+geoip_can_answer(dns_aclelement_t *elt, cfg_aclconfctx_t *ctx) {
+ if (ctx->geoip == NULL) {
+ return (true);
+ }
+
+ switch (elt->geoip_elem.subtype) {
+ case dns_geoip_countrycode:
+ case dns_geoip_countryname:
+ case dns_geoip_continentcode:
+ case dns_geoip_continent:
+ if (ctx->geoip->country != NULL || ctx->geoip->city != NULL) {
+ return (true);
+ }
+ break;
+ case dns_geoip_country_code:
+ case dns_geoip_country_name:
+ case dns_geoip_country_continentcode:
+ case dns_geoip_country_continent:
+ if (ctx->geoip->country != NULL) {
+ return (true);
+ }
+ /* city db can answer these too, so: */
+ FALLTHROUGH;
+ case dns_geoip_region:
+ case dns_geoip_regionname:
+ case dns_geoip_city_countrycode:
+ case dns_geoip_city_countryname:
+ case dns_geoip_city_region:
+ case dns_geoip_city_regionname:
+ case dns_geoip_city_name:
+ case dns_geoip_city_postalcode:
+ case dns_geoip_city_metrocode:
+ case dns_geoip_city_areacode:
+ case dns_geoip_city_continentcode:
+ case dns_geoip_city_continent:
+ case dns_geoip_city_timezonecode:
+ if (ctx->geoip->city != NULL) {
+ return (true);
+ }
+ break;
+ case dns_geoip_isp_name:
+ if (ctx->geoip->isp != NULL) {
+ return (true);
+ }
+ break;
+ case dns_geoip_as_asnum:
+ case dns_geoip_org_name:
+ if (ctx->geoip->as != NULL) {
+ return (true);
+ }
+ break;
+ case dns_geoip_domain_name:
+ if (ctx->geoip->domain != NULL) {
+ return (true);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return (false);
+}
+
+static isc_result_t
+parse_geoip_element(const cfg_obj_t *obj, isc_log_t *lctx,
+ cfg_aclconfctx_t *ctx, dns_aclelement_t *dep) {
+ const cfg_obj_t *ge;
+ const char *dbname = NULL;
+ const char *stype = NULL, *search = NULL;
+ dns_geoip_subtype_t subtype;
+ dns_aclelement_t de;
+ size_t len;
+
+ REQUIRE(dep != NULL);
+
+ de = *dep;
+
+ ge = cfg_tuple_get(obj, "db");
+ if (!cfg_obj_isvoid(ge)) {
+ int i;
+
+ dbname = cfg_obj_asstring(ge);
+
+ for (i = 0; geoip_dbnames[i] != NULL; i++) {
+ if (strcasecmp(dbname, geoip_dbnames[i]) == 0) {
+ break;
+ }
+ }
+ if (geoip_dbnames[i] == NULL) {
+ cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
+ "database '%s' is not defined for GeoIP2",
+ dbname);
+ return (ISC_R_UNEXPECTED);
+ }
+ }
+
+ stype = cfg_obj_asstring(cfg_tuple_get(obj, "subtype"));
+ search = cfg_obj_asstring(cfg_tuple_get(obj, "search"));
+ len = strlen(search);
+
+ if (len == 0) {
+ cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
+ "zero-length geoip search field");
+ return (ISC_R_FAILURE);
+ }
+
+ if (strcasecmp(stype, "country") == 0 && len == 2) {
+ /* Two-letter country code */
+ subtype = dns_geoip_countrycode;
+ strlcpy(de.geoip_elem.as_string, search,
+ sizeof(de.geoip_elem.as_string));
+ } else if (strcasecmp(stype, "country") == 0 && len == 3) {
+ /* Three-letter country code */
+ cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
+ "three-letter country codes are unavailable "
+ "in GeoIP2 databases");
+ return (ISC_R_FAILURE);
+ } else if (strcasecmp(stype, "country") == 0) {
+ /* Country name */
+ subtype = dns_geoip_countryname;
+ strlcpy(de.geoip_elem.as_string, search,
+ sizeof(de.geoip_elem.as_string));
+ } else if (strcasecmp(stype, "continent") == 0 && len == 2) {
+ /* Two-letter continent code */
+ subtype = dns_geoip_continentcode;
+ strlcpy(de.geoip_elem.as_string, search,
+ sizeof(de.geoip_elem.as_string));
+ } else if (strcasecmp(stype, "continent") == 0) {
+ subtype = dns_geoip_continent;
+ strlcpy(de.geoip_elem.as_string, search,
+ sizeof(de.geoip_elem.as_string));
+ } else if ((strcasecmp(stype, "region") == 0 ||
+ strcasecmp(stype, "subdivision") == 0) &&
+ len == 2)
+ {
+ /* Two-letter region code */
+ subtype = dns_geoip_region;
+ strlcpy(de.geoip_elem.as_string, search,
+ sizeof(de.geoip_elem.as_string));
+ } else if (strcasecmp(stype, "region") == 0 ||
+ strcasecmp(stype, "subdivision") == 0)
+ {
+ /* Region name */
+ subtype = dns_geoip_regionname;
+ strlcpy(de.geoip_elem.as_string, search,
+ sizeof(de.geoip_elem.as_string));
+ } else if (strcasecmp(stype, "city") == 0) {
+ /* City name */
+ subtype = dns_geoip_city_name;
+ strlcpy(de.geoip_elem.as_string, search,
+ sizeof(de.geoip_elem.as_string));
+ } else if (strcasecmp(stype, "postal") == 0 ||
+ strcasecmp(stype, "postalcode") == 0)
+ {
+ if (len < 7) {
+ subtype = dns_geoip_city_postalcode;
+ strlcpy(de.geoip_elem.as_string, search,
+ sizeof(de.geoip_elem.as_string));
+ } else {
+ cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
+ "geoiop postal code (%s) too long", search);
+ return (ISC_R_FAILURE);
+ }
+ } else if (strcasecmp(stype, "metro") == 0 ||
+ strcasecmp(stype, "metrocode") == 0)
+ {
+ subtype = dns_geoip_city_metrocode;
+ de.geoip_elem.as_int = atoi(search);
+ } else if (strcasecmp(stype, "tz") == 0 ||
+ strcasecmp(stype, "timezone") == 0)
+ {
+ subtype = dns_geoip_city_timezonecode;
+ strlcpy(de.geoip_elem.as_string, search,
+ sizeof(de.geoip_elem.as_string));
+ } else if (strcasecmp(stype, "isp") == 0) {
+ subtype = dns_geoip_isp_name;
+ strlcpy(de.geoip_elem.as_string, search,
+ sizeof(de.geoip_elem.as_string));
+ } else if (strcasecmp(stype, "asnum") == 0) {
+ subtype = dns_geoip_as_asnum;
+ strlcpy(de.geoip_elem.as_string, search,
+ sizeof(de.geoip_elem.as_string));
+ } else if (strcasecmp(stype, "org") == 0) {
+ subtype = dns_geoip_org_name;
+ strlcpy(de.geoip_elem.as_string, search,
+ sizeof(de.geoip_elem.as_string));
+ } else if (strcasecmp(stype, "domain") == 0) {
+ subtype = dns_geoip_domain_name;
+ strlcpy(de.geoip_elem.as_string, search,
+ sizeof(de.geoip_elem.as_string));
+ } else {
+ cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
+ "type '%s' is unavailable "
+ "in GeoIP2 databases",
+ stype);
+ return (ISC_R_FAILURE);
+ }
+
+ de.geoip_elem.subtype = get_subtype(obj, lctx, subtype, dbname);
+
+ if (!geoip_can_answer(&de, ctx)) {
+ cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
+ "no GeoIP2 database installed which can answer "
+ "queries of type '%s'",
+ stype);
+ return (ISC_R_FAILURE);
+ }
+
+ *dep = de;
+
+ return (ISC_R_SUCCESS);
+}
+#endif /* HAVE_GEOIP2 */
+
+isc_result_t
+cfg_acl_fromconfig(const cfg_obj_t *caml, const cfg_obj_t *cctx,
+ isc_log_t *lctx, cfg_aclconfctx_t *ctx, isc_mem_t *mctx,
+ unsigned int nest_level, dns_acl_t **target) {
+ return (cfg_acl_fromconfig2(caml, cctx, lctx, ctx, mctx, nest_level, 0,
+ target));
+}
+
+isc_result_t
+cfg_acl_fromconfig2(const cfg_obj_t *acl_data, const cfg_obj_t *cctx,
+ isc_log_t *lctx, cfg_aclconfctx_t *ctx, isc_mem_t *mctx,
+ unsigned int nest_level, uint16_t family,
+ dns_acl_t **target) {
+ isc_result_t result;
+ dns_acl_t *dacl = NULL, *inneracl = NULL;
+ dns_aclelement_t *de;
+ const cfg_listelt_t *elt;
+ dns_iptable_t *iptab;
+ int new_nest_level = 0;
+ bool setpos;
+ const cfg_obj_t *caml = NULL;
+ const cfg_obj_t *obj_acl_tuple = NULL;
+ const cfg_obj_t *obj_port = NULL, *obj_transport = NULL;
+ bool is_tuple = false;
+
+ if (nest_level != 0) {
+ new_nest_level = nest_level - 1;
+ }
+
+ REQUIRE(ctx != NULL);
+ REQUIRE(target != NULL);
+ REQUIRE(*target == NULL || DNS_ACL_VALID(*target));
+
+ REQUIRE(acl_data != NULL);
+ if (cfg_obj_islist(acl_data)) {
+ caml = acl_data;
+ } else {
+ INSIST(cfg_obj_istuple(acl_data));
+ caml = cfg_tuple_get(acl_data, "aml");
+ INSIST(caml != NULL);
+ obj_acl_tuple = cfg_tuple_get(acl_data, "port-transport");
+ INSIST(obj_acl_tuple != NULL);
+ obj_port = cfg_tuple_get(obj_acl_tuple, "port");
+ obj_transport = cfg_tuple_get(obj_acl_tuple, "transport");
+ is_tuple = true;
+ }
+
+ if (*target != NULL) {
+ /*
+ * If target already points to an ACL, then we're being
+ * called recursively to configure a nested ACL. The
+ * nested ACL's contents should just be absorbed into its
+ * parent ACL.
+ */
+ dns_acl_attach(*target, &dacl);
+ dns_acl_detach(target);
+ } else {
+ /*
+ * Need to allocate a new ACL structure. Count the items
+ * in the ACL definition that will require space in the
+ * elements table. (Note that if nest_level is nonzero,
+ * *everything* goes in the elements table.)
+ */
+ uint32_t nelem;
+
+ if (nest_level == 0) {
+ result = count_acl_elements(caml, cctx, lctx, ctx, mctx,
+ &nelem, NULL);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ } else {
+ nelem = cfg_list_length(caml, false);
+ }
+
+ result = dns_acl_create(mctx, nelem, &dacl);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ }
+
+ if (is_tuple) {
+ uint16_t port = 0;
+ uint32_t transports = 0;
+ bool encrypted = false;
+
+ if (obj_port != NULL && cfg_obj_isuint32(obj_port)) {
+ port = (uint16_t)cfg_obj_asuint32(obj_port);
+ }
+
+ if (obj_transport != NULL && cfg_obj_isstring(obj_transport)) {
+ if (strcasecmp(cfg_obj_asstring(obj_transport),
+ "udp") == 0)
+ {
+ transports = isc_nm_udpsocket;
+ encrypted = false;
+ } else if (strcasecmp(cfg_obj_asstring(obj_transport),
+ "tcp") == 0)
+ {
+ transports = isc_nm_tcpdnssocket;
+ encrypted = false;
+ } else if (strcasecmp(cfg_obj_asstring(obj_transport),
+ "udp-tcp") == 0)
+ {
+ /* Good ol' DNS over port 53 */
+ transports = isc_nm_tcpdnssocket |
+ isc_nm_udpsocket;
+ encrypted = false;
+ } else if (strcasecmp(cfg_obj_asstring(obj_transport),
+ "tls") == 0)
+ {
+ transports = isc_nm_tlsdnssocket;
+ encrypted = true;
+ } else if (strcasecmp(cfg_obj_asstring(obj_transport),
+ "http") == 0)
+ {
+ transports = isc_nm_httpsocket;
+ encrypted = true;
+ } else if (strcasecmp(cfg_obj_asstring(obj_transport),
+ "http-plain") == 0)
+ {
+ transports = isc_nm_httpsocket;
+ encrypted = false;
+ } else {
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ }
+
+ if (port != 0 || transports != 0) {
+ dns_acl_add_port_transports(dacl, port, transports,
+ encrypted, false);
+ }
+ }
+
+ de = dacl->elements;
+ for (elt = cfg_list_first(caml); elt != NULL; elt = cfg_list_next(elt))
+ {
+ const cfg_obj_t *ce = cfg_listelt_value(elt);
+ bool neg = false;
+
+ INSIST(dacl->length <= dacl->alloc);
+
+ if (cfg_obj_istuple(ce)) {
+ /* Might be a negated element */
+ const cfg_obj_t *negated = cfg_tuple_get(ce, "negated");
+ if (!cfg_obj_isvoid(negated)) {
+ neg = true;
+ dacl->has_negatives = true;
+ ce = negated;
+ }
+ }
+
+ /*
+ * If nest_level is nonzero, then every element is
+ * to be stored as a separate, nested ACL rather than
+ * merged into the main iptable.
+ */
+ iptab = dacl->iptable;
+
+ if (nest_level != 0) {
+ result = dns_acl_create(mctx,
+ cfg_list_length(ce, false),
+ &de->nestedacl);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ iptab = de->nestedacl->iptable;
+ }
+
+ if (cfg_obj_isnetprefix(ce)) {
+ /* Network prefix */
+ isc_netaddr_t addr;
+ unsigned int bitlen;
+
+ cfg_obj_asnetprefix(ce, &addr, &bitlen);
+ if (family != 0 && family != addr.family) {
+ char buf[ISC_NETADDR_FORMATSIZE + 1];
+ isc_netaddr_format(&addr, buf, sizeof(buf));
+ cfg_obj_log(ce, lctx, ISC_LOG_WARNING,
+ "'%s': incorrect address family; "
+ "ignoring",
+ buf);
+ if (nest_level != 0) {
+ dns_acl_detach(&de->nestedacl);
+ }
+ continue;
+ }
+ result = isc_netaddr_prefixok(&addr, bitlen);
+ if (result != ISC_R_SUCCESS) {
+ char buf[ISC_NETADDR_FORMATSIZE + 1];
+ isc_netaddr_format(&addr, buf, sizeof(buf));
+ cfg_obj_log(ce, lctx, ISC_LOG_ERROR,
+ "'%s/%u': address/prefix length "
+ "mismatch",
+ buf, bitlen);
+ goto cleanup;
+ }
+
+ /*
+ * If nesting ACLs (nest_level != 0), we negate
+ * the nestedacl element, not the iptable entry.
+ */
+ setpos = (nest_level != 0 || !neg);
+ result = dns_iptable_addprefix(iptab, &addr, bitlen,
+ setpos);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ if (nest_level > 0) {
+ INSIST(dacl->length < dacl->alloc);
+ de->type = dns_aclelementtype_nestedacl;
+ de->negative = neg;
+ } else {
+ continue;
+ }
+ } else if (cfg_obj_islist(ce)) {
+ /*
+ * If we're nesting ACLs, put the nested
+ * ACL onto the elements list; otherwise
+ * merge it into *this* ACL. We nest ACLs
+ * in two cases: 1) sortlist, 2) if the
+ * nested ACL contains negated members.
+ */
+ if (inneracl != NULL) {
+ dns_acl_detach(&inneracl);
+ }
+ result = cfg_acl_fromconfig(ce, cctx, lctx, ctx, mctx,
+ new_nest_level, &inneracl);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ nested_acl:
+ if (nest_level > 0 || inneracl->has_negatives) {
+ INSIST(dacl->length < dacl->alloc);
+ de->type = dns_aclelementtype_nestedacl;
+ de->negative = neg;
+ if (de->nestedacl != NULL) {
+ dns_acl_detach(&de->nestedacl);
+ }
+ /*
+ * Merge the port-transports entries from the
+ * nested ACL into its parent.
+ */
+ dns_acl_merge_ports_transports(dacl, inneracl,
+ !neg);
+ dns_acl_attach(inneracl, &de->nestedacl);
+ dns_acl_detach(&inneracl);
+ /* Fall through. */
+ } else {
+ INSIST(dacl->length + inneracl->length <=
+ dacl->alloc);
+ dns_acl_merge(dacl, inneracl, !neg);
+ de += inneracl->length; /* elements added */
+ dns_acl_detach(&inneracl);
+ INSIST(dacl->length <= dacl->alloc);
+ continue;
+ }
+ } else if (cfg_obj_istype(ce, &cfg_type_keyref)) {
+ /* Key name. */
+ INSIST(dacl->length < dacl->alloc);
+ de->type = dns_aclelementtype_keyname;
+ de->negative = neg;
+ dns_name_init(&de->keyname, NULL);
+ result = convert_keyname(ce, lctx, mctx, &de->keyname);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+#if defined(HAVE_GEOIP2)
+ } else if (cfg_obj_istuple(ce) &&
+ cfg_obj_isvoid(cfg_tuple_get(ce, "negated")))
+ {
+ INSIST(dacl->length < dacl->alloc);
+ result = parse_geoip_element(ce, lctx, ctx, de);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ de->type = dns_aclelementtype_geoip;
+ de->negative = neg;
+#endif /* HAVE_GEOIP2 */
+ } else if (cfg_obj_isstring(ce)) {
+ /* ACL name. */
+ const char *name = cfg_obj_asstring(ce);
+ if (strcasecmp(name, "any") == 0) {
+ /* Iptable entry with zero bit length. */
+ setpos = (nest_level != 0 || !neg);
+ result = dns_iptable_addprefix(iptab, NULL, 0,
+ setpos);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ if (nest_level != 0) {
+ INSIST(dacl->length < dacl->alloc);
+ de->type = dns_aclelementtype_nestedacl;
+ de->negative = neg;
+ } else {
+ continue;
+ }
+ } else if (strcasecmp(name, "none") == 0) {
+ /* none == !any */
+ /*
+ * We don't unconditional set
+ * dacl->has_negatives and
+ * de->negative to true so we can handle
+ * "!none;".
+ */
+ setpos = (nest_level != 0 || neg);
+ result = dns_iptable_addprefix(iptab, NULL, 0,
+ setpos);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ if (!neg) {
+ dacl->has_negatives = !neg;
+ }
+
+ if (nest_level != 0) {
+ INSIST(dacl->length < dacl->alloc);
+ de->type = dns_aclelementtype_nestedacl;
+ de->negative = !neg;
+ } else {
+ continue;
+ }
+ } else if (strcasecmp(name, "localhost") == 0) {
+ INSIST(dacl->length < dacl->alloc);
+ de->type = dns_aclelementtype_localhost;
+ de->negative = neg;
+ } else if (strcasecmp(name, "localnets") == 0) {
+ INSIST(dacl->length < dacl->alloc);
+ de->type = dns_aclelementtype_localnets;
+ de->negative = neg;
+ } else {
+ if (inneracl != NULL) {
+ dns_acl_detach(&inneracl);
+ }
+ /*
+ * This call should just find the cached
+ * of the named acl.
+ */
+ result = convert_named_acl(ce, cctx, lctx, ctx,
+ mctx, new_nest_level,
+ &inneracl);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ goto nested_acl;
+ }
+ } else {
+ cfg_obj_log(ce, lctx, ISC_LOG_WARNING,
+ "address match list contains "
+ "unsupported element type");
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ /*
+ * This should only be reached for localhost, localnets
+ * and keyname elements, and nested ACLs if nest_level is
+ * nonzero (i.e., in sortlists).
+ */
+ if (de->nestedacl != NULL &&
+ de->type != dns_aclelementtype_nestedacl)
+ {
+ dns_acl_detach(&de->nestedacl);
+ }
+
+ dns_acl_node_count(dacl)++;
+ de->node_num = dns_acl_node_count(dacl);
+
+ dacl->length++;
+ de++;
+ INSIST(dacl->length <= dacl->alloc);
+ }
+
+ dns_acl_attach(dacl, target);
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ if (inneracl != NULL) {
+ dns_acl_detach(&inneracl);
+ }
+ dns_acl_detach(&dacl);
+ return (result);
+}
diff --git a/lib/isccfg/dnsconf.c b/lib/isccfg/dnsconf.c
new file mode 100644
index 0000000..ccd7232
--- /dev/null
+++ b/lib/isccfg/dnsconf.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <isccfg/cfg.h>
+#include <isccfg/grammar.h>
+
+/*%
+ * A trusted key, as used in the "trusted-keys" statement.
+ */
+static cfg_tuplefielddef_t trustedkey_fields[] = {
+ { "name", &cfg_type_astring, 0 },
+ { "flags", &cfg_type_uint32, 0 },
+ { "protocol", &cfg_type_uint32, 0 },
+ { "algorithm", &cfg_type_uint32, 0 },
+ { "key", &cfg_type_qstring, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_trustedkey = { "trustedkey", cfg_parse_tuple,
+ cfg_print_tuple, cfg_doc_tuple,
+ &cfg_rep_tuple, trustedkey_fields };
+
+static cfg_type_t cfg_type_trustedkeys = { "trusted-keys",
+ cfg_parse_bracketed_list,
+ cfg_print_bracketed_list,
+ cfg_doc_bracketed_list,
+ &cfg_rep_list,
+ &cfg_type_trustedkey };
+
+/*%
+ * Clauses that can be found within the top level of the dns.conf
+ * file only.
+ */
+static cfg_clausedef_t dnsconf_clauses[] = {
+ { "trusted-keys", &cfg_type_trustedkeys, CFG_CLAUSEFLAG_MULTI },
+ { NULL, NULL, 0 }
+};
+
+/*% The top-level dns.conf syntax. */
+
+static cfg_clausedef_t *dnsconf_clausesets[] = { dnsconf_clauses, NULL };
+
+cfg_type_t cfg_type_dnsconf = { "dnsconf", cfg_parse_mapbody,
+ cfg_print_mapbody, cfg_doc_mapbody,
+ &cfg_rep_map, dnsconf_clausesets };
diff --git a/lib/isccfg/duration.c b/lib/isccfg/duration.c
new file mode 100644
index 0000000..9ed9d6f
--- /dev/null
+++ b/lib/isccfg/duration.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <ctype.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <isc/buffer.h>
+#include <isc/parseint.h>
+#include <isc/print.h>
+#include <isc/region.h>
+#include <isc/result.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/ttl.h>
+
+#include <isccfg/duration.h>
+
+/*
+ * isccfg_duration_fromtext initially taken from OpenDNSSEC code base.
+ * Modified to fit the BIND 9 code.
+ */
+isc_result_t
+isccfg_duration_fromtext(isc_textregion_t *source,
+ isccfg_duration_t *duration) {
+ char buf[CFG_DURATION_MAXLEN] = { 0 };
+ char *P, *X, *T, *W, *str;
+ bool not_weeks = false;
+ int i;
+ long long int lli;
+
+ /*
+ * Copy the buffer as it may not be NULL terminated.
+ */
+ if (source->length > sizeof(buf) - 1) {
+ return (ISC_R_BADNUMBER);
+ }
+ /* Copy source->length bytes and NULL terminate. */
+ snprintf(buf, sizeof(buf), "%.*s", (int)source->length, source->base);
+ str = buf;
+
+ /* Clear out duration. */
+ for (i = 0; i < 7; i++) {
+ duration->parts[i] = 0;
+ }
+ duration->iso8601 = false;
+ duration->unlimited = false;
+
+ /* Every duration starts with 'P' */
+ if (toupper((unsigned char)str[0]) != 'P') {
+ return (ISC_R_BADNUMBER);
+ }
+ P = str;
+
+ /* Record the time indicator. */
+ T = strpbrk(str, "Tt");
+
+ /* Record years. */
+ X = strpbrk(str, "Yy");
+ if (X != NULL) {
+ errno = 0;
+ lli = strtoll(str + 1, NULL, 10);
+ if (errno != 0 || lli < 0 || lli > UINT32_MAX) {
+ return (ISC_R_BADNUMBER);
+ }
+ duration->parts[0] = (uint32_t)lli;
+ str = X;
+ not_weeks = true;
+ }
+
+ /* Record months. */
+ X = strpbrk(str, "Mm");
+
+ /*
+ * M could be months or minutes. This is months if there is no time
+ * part, or this M indicator is before the time indicator.
+ */
+ if (X != NULL && (T == NULL || (size_t)(X - P) < (size_t)(T - P))) {
+ errno = 0;
+ lli = strtoll(str + 1, NULL, 10);
+ if (errno != 0 || lli < 0 || lli > UINT32_MAX) {
+ return (ISC_R_BADNUMBER);
+ }
+ duration->parts[1] = (uint32_t)lli;
+ str = X;
+ not_weeks = true;
+ }
+
+ /* Record days. */
+ X = strpbrk(str, "Dd");
+ if (X != NULL) {
+ errno = 0;
+ lli = strtoll(str + 1, NULL, 10);
+ if (errno != 0 || lli < 0 || lli > UINT32_MAX) {
+ return (ISC_R_BADNUMBER);
+ }
+ duration->parts[3] = (uint32_t)lli;
+ str = X;
+ not_weeks = true;
+ }
+
+ /* Time part? */
+ if (T != NULL) {
+ str = T;
+ not_weeks = true;
+ }
+
+ /* Record hours. */
+ X = strpbrk(str, "Hh");
+ if (X != NULL && T != NULL) {
+ errno = 0;
+ lli = strtoll(str + 1, NULL, 10);
+ if (errno != 0 || lli < 0 || lli > UINT32_MAX) {
+ return (ISC_R_BADNUMBER);
+ }
+ duration->parts[4] = (uint32_t)lli;
+ str = X;
+ not_weeks = true;
+ }
+
+ /* Record minutes. */
+ X = strpbrk(str, "Mm");
+
+ /*
+ * M could be months or minutes. This is minutes if there is a time
+ * part and the M indicator is behind the time indicator.
+ */
+ if (X != NULL && T != NULL && (size_t)(X - P) > (size_t)(T - P)) {
+ errno = 0;
+ lli = strtoll(str + 1, NULL, 10);
+ if (errno != 0 || lli < 0 || lli > UINT32_MAX) {
+ return (ISC_R_BADNUMBER);
+ }
+ duration->parts[5] = (uint32_t)lli;
+ str = X;
+ not_weeks = true;
+ }
+
+ /* Record seconds. */
+ X = strpbrk(str, "Ss");
+ if (X != NULL && T != NULL) {
+ errno = 0;
+ lli = strtoll(str + 1, NULL, 10);
+ if (errno != 0 || lli < 0 || lli > UINT32_MAX) {
+ return (ISC_R_BADNUMBER);
+ }
+ duration->parts[6] = (uint32_t)lli;
+ str = X;
+ not_weeks = true;
+ }
+
+ /* Or is the duration configured in weeks? */
+ W = strpbrk(buf, "Ww");
+ if (W != NULL) {
+ if (not_weeks) {
+ /* Mix of weeks and other indicators is not allowed */
+ return (ISC_R_BADNUMBER);
+ } else {
+ errno = 0;
+ lli = strtoll(str + 1, NULL, 10);
+ if (errno != 0 || lli < 0 || lli > UINT32_MAX) {
+ return (ISC_R_BADNUMBER);
+ }
+ duration->parts[2] = (uint32_t)lli;
+ str = W;
+ }
+ }
+
+ /* Deal with trailing garbage. */
+ if (str[1] != '\0') {
+ return (ISC_R_BADNUMBER);
+ }
+
+ duration->iso8601 = true;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isccfg_parse_duration(isc_textregion_t *source, isccfg_duration_t *duration) {
+ isc_result_t result;
+
+ REQUIRE(duration != NULL);
+
+ duration->unlimited = false;
+ result = isccfg_duration_fromtext(source, duration);
+ if (result == ISC_R_BADNUMBER) {
+ /* Fallback to dns_ttl_fromtext. */
+ uint32_t ttl;
+ result = dns_ttl_fromtext(source, &ttl);
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * With dns_ttl_fromtext() the information on optional
+ * units is lost, and is treated as seconds from now on.
+ */
+ duration->iso8601 = false;
+ duration->parts[6] = ttl;
+ }
+ }
+
+ return (result);
+}
+
+uint32_t
+isccfg_duration_toseconds(const isccfg_duration_t *duration) {
+ uint64_t seconds = 0;
+
+ REQUIRE(duration != NULL);
+
+ seconds += (uint64_t)duration->parts[6]; /* Seconds */
+ seconds += (uint64_t)duration->parts[5] * 60; /* Minutes */
+ seconds += (uint64_t)duration->parts[4] * 3600; /* Hours */
+ seconds += (uint64_t)duration->parts[3] * 86400; /* Days */
+ seconds += (uint64_t)duration->parts[2] * 86400 * 7; /* Weeks */
+ /*
+ * The below additions are not entirely correct
+ * because days may vary per month and per year.
+ */
+ seconds += (uint64_t)duration->parts[1] * 86400 * 31; /* Months */
+ seconds += (uint64_t)duration->parts[0] * 86400 * 365; /* Years */
+
+ return (seconds > UINT32_MAX ? UINT32_MAX : (uint32_t)seconds);
+}
diff --git a/lib/isccfg/include/isccfg/aclconf.h b/lib/isccfg/include/isccfg/aclconf.h
new file mode 100644
index 0000000..eb1b9ab
--- /dev/null
+++ b/lib/isccfg/include/isccfg/aclconf.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+
+#include <isc/lang.h>
+
+#include <dns/geoip.h>
+#include <dns/types.h>
+
+#include <isccfg/cfg.h>
+
+typedef struct cfg_aclconfctx {
+ ISC_LIST(dns_acl_t) named_acl_cache;
+ isc_mem_t *mctx;
+#if defined(HAVE_GEOIP2)
+ dns_geoip_databases_t *geoip;
+#endif /* if defined(HAVE_GEOIP2) */
+ isc_refcount_t references;
+} cfg_aclconfctx_t;
+
+/***
+ *** Functions
+ ***/
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+cfg_aclconfctx_create(isc_mem_t *mctx, cfg_aclconfctx_t **ret);
+/*
+ * Creates and initializes an ACL configuration context.
+ */
+
+void
+cfg_aclconfctx_detach(cfg_aclconfctx_t **actxp);
+/*
+ * Removes a reference to an ACL configuration context; when references
+ * reaches zero, clears the contents and deallocate the structure.
+ */
+
+void
+cfg_aclconfctx_attach(cfg_aclconfctx_t *src, cfg_aclconfctx_t **dest);
+/*
+ * Attaches a pointer to an existing ACL configuration context.
+ */
+
+isc_result_t
+cfg_acl_fromconfig(const cfg_obj_t *caml, const cfg_obj_t *cctx,
+ isc_log_t *lctx, cfg_aclconfctx_t *ctx, isc_mem_t *mctx,
+ unsigned int nest_level, dns_acl_t **target);
+
+isc_result_t
+cfg_acl_fromconfig2(const cfg_obj_t *caml, const cfg_obj_t *cctx,
+ isc_log_t *lctx, cfg_aclconfctx_t *ctx, isc_mem_t *mctx,
+ unsigned int nest_level, uint16_t family,
+ dns_acl_t **target);
+/*
+ * Construct a new dns_acl_t from configuration data in 'caml' and
+ * 'cctx'. Memory is allocated through 'mctx'.
+ *
+ * Any named ACLs referred to within 'caml' will be be converted
+ * into nested dns_acl_t objects. Multiple references to the same
+ * named ACLs will be converted into shared references to a single
+ * nested dns_acl_t object when the referring objects were created
+ * passing the same ACL configuration context 'ctx'.
+ *
+ * cfg_acl_fromconfig() is a backward-compatible version of
+ * cfg_acl_fromconfig2(), which allows an address family to be
+ * specified. If 'family' is not zero, then only addresses/prefixes
+ * of a matching family (AF_INET or AF_INET6) may be configured.
+ *
+ * On success, attach '*target' to the new dns_acl_t object.
+ *
+ * Require:
+ * 'ctx' to be non NULL.
+ * '*target' to be NULL or a valid dns_acl_t.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isccfg/include/isccfg/cfg.h b/lib/isccfg/include/isccfg/cfg.h
new file mode 100644
index 0000000..c4f2d86
--- /dev/null
+++ b/lib/isccfg/include/isccfg/cfg.h
@@ -0,0 +1,609 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*****
+***** Module Info
+*****/
+
+/*! \file isccfg/cfg.h
+ * \brief
+ * This is the new, table-driven, YACC-free configuration file parser.
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <time.h>
+
+#include <isc/formatcheck.h>
+#include <isc/lang.h>
+#include <isc/list.h>
+#include <isc/refcount.h>
+#include <isc/types.h>
+
+/***
+ *** Types
+ ***/
+
+/*%
+ * A configuration parser.
+ */
+typedef struct cfg_parser cfg_parser_t;
+
+/*%
+ * A configuration type definition object. There is a single
+ * static cfg_type_t object for each data type supported by
+ * the configuration parser.
+ */
+typedef struct cfg_type cfg_type_t;
+
+/*%
+ * A configuration object. This is the basic building block of the
+ * configuration parse tree. It contains a value (which may be
+ * of one of several types) and information identifying the file
+ * and line number the value came from, for printing error
+ * messages.
+ */
+typedef struct cfg_obj cfg_obj_t;
+
+/*%
+ * A configuration object list element.
+ */
+typedef struct cfg_listelt cfg_listelt_t;
+
+/*%
+ * A callback function to be called when parsing an option
+ * that needs to be interpreted at parsing time, like
+ * "directory".
+ */
+typedef isc_result_t (*cfg_parsecallback_t)(const char *clausename,
+ const cfg_obj_t *obj, void *arg);
+
+/***
+ *** Functions
+ ***/
+
+ISC_LANG_BEGINDECLS
+
+void
+cfg_parser_attach(cfg_parser_t *src, cfg_parser_t **dest);
+/*%<
+ * Reference a parser object.
+ */
+
+isc_result_t
+cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret);
+/*%<
+ * Create a configuration file parser. Any warning and error
+ * messages will be logged to 'lctx'.
+ *
+ * The parser object returned can be used for a single call
+ * to cfg_parse_file() or cfg_parse_buffer(). It must not
+ * be reused for parsing multiple files or buffers.
+ */
+
+void
+cfg_parser_setflags(cfg_parser_t *pctx, unsigned int flags, bool turn_on);
+/*%<
+ * Set parser context flags. The flags are not checked for sensibility.
+ * If 'turn_on' is 'true' the flags will be set, otherwise the flags will
+ * be cleared.
+ *
+ * Requires:
+ *\li "pctx" is not NULL.
+ */
+
+void
+cfg_parser_setcallback(cfg_parser_t *pctx, cfg_parsecallback_t callback,
+ void *arg);
+/*%<
+ * Make the parser call 'callback' whenever it encounters
+ * a configuration clause with the callback attribute,
+ * passing it the clause name, the clause value,
+ * and 'arg' as arguments.
+ *
+ * To restore the default of not invoking callbacks, pass
+ * callback==NULL and arg==NULL.
+ */
+
+isc_result_t
+cfg_parse_file(cfg_parser_t *pctx, const char *file, const cfg_type_t *type,
+ cfg_obj_t **ret);
+
+isc_result_t
+cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer, const char *file,
+ unsigned int line, const cfg_type_t *type, unsigned int flags,
+ cfg_obj_t **ret);
+/*%<
+ * Read a configuration containing data of type 'type'
+ * and make '*ret' point to its parse tree.
+ *
+ * The configuration is read from the file 'filename'
+ * (isc_parse_file()) or the buffer 'buffer'
+ * (isc_parse_buffer()).
+ *
+ * If 'file' is not NULL, it is the name of the file, or a name to use
+ * for the buffer in place of the filename, when logging errors.
+ *
+ * If 'line' is not 0, then it is the beginning line number to report
+ * when logging errors. This is useful when passing text that has been
+ * read from the middle of a file.
+ *
+ * Returns an error if the file or buffer does not parse correctly.
+ *
+ * Requires:
+ *\li "filename" is valid.
+ *\li "mem" is valid.
+ *\li "type" is valid.
+ *\li "cfg" is non-NULL and "*cfg" is NULL.
+ *\li "flags" be one or more of CFG_PCTX_NODEPRECATED or zero.
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS - success
+ *\li #ISC_R_NOMEMORY - no memory available
+ *\li #ISC_R_INVALIDFILE - file doesn't exist or is unreadable
+ *\li others - file contains errors
+ */
+
+isc_result_t
+cfg_parser_mapadd(cfg_parser_t *pctx, cfg_obj_t *mapobj, cfg_obj_t *obj,
+ const char *clause);
+/*%<
+ * Add the object 'obj' to the specified clause in mapbody 'mapobj'.
+ * Used for adding new zones.
+ *
+ * Require:
+ * \li 'obj' is a valid cfg_obj_t.
+ * \li 'mapobj' is a valid cfg_obj_t of type map.
+ * \li 'pctx' is a valid cfg_parser_t.
+ */
+
+void
+cfg_parser_reset(cfg_parser_t *pctx);
+/*%<
+ * Reset an existing parser so it can be re-used for a new file or
+ * buffer.
+ */
+
+void
+cfg_parser_destroy(cfg_parser_t **pctxp);
+/*%<
+ * Remove a reference to a configuration parser; destroy it if there are no
+ * more references.
+ */
+
+bool
+cfg_obj_isvoid(const cfg_obj_t *obj);
+/*%<
+ * Return true iff 'obj' is of void type (e.g., an optional
+ * value not specified).
+ */
+
+bool
+cfg_obj_ismap(const cfg_obj_t *obj);
+/*%<
+ * Return true iff 'obj' is of a map type.
+ */
+
+bool
+cfg_obj_isfixedpoint(const cfg_obj_t *obj);
+/*%<
+ * Return true iff 'obj' is of a fixedpoint type.
+ */
+
+bool
+cfg_obj_ispercentage(const cfg_obj_t *obj);
+/*%<
+ * Return true iff 'obj' is of a percentage type.
+ */
+
+isc_result_t
+cfg_map_get(const cfg_obj_t *mapobj, const char *name, const cfg_obj_t **obj);
+/*%<
+ * Extract an element from a configuration object, which
+ * must be of a map type.
+ *
+ * Requires:
+ * \li 'mapobj' points to a valid configuration object of a map type.
+ * \li 'name' points to a null-terminated string.
+ * \li 'obj' is non-NULL and '*obj' is NULL.
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS - success
+ * \li #ISC_R_NOTFOUND - name not found in map
+ */
+
+const cfg_obj_t *
+cfg_map_getname(const cfg_obj_t *mapobj);
+/*%<
+ * Get the name of a named map object, like a server "key" clause.
+ *
+ * Requires:
+ * \li 'mapobj' points to a valid configuration object of a map type.
+ *
+ * Returns:
+ * \li A pointer to a configuration object naming the map object,
+ * or NULL if the map object does not have a name.
+ */
+
+unsigned int
+cfg_map_count(const cfg_obj_t *mapobj);
+/*%<
+ * Get the number of elements defined in the symbol table of a map object.
+ *
+ * Requires:
+ * \li 'mapobj' points to a valid configuration object of a map type.
+ *
+ * Returns:
+ * \li The number of elements in the map object.
+ */
+
+bool
+cfg_obj_istuple(const cfg_obj_t *obj);
+/*%<
+ * Return true iff 'obj' is of a map type.
+ */
+
+const cfg_obj_t *
+cfg_tuple_get(const cfg_obj_t *tupleobj, const char *name);
+/*%<
+ * Extract an element from a configuration object, which
+ * must be of a tuple type.
+ *
+ * Requires:
+ * \li 'tupleobj' points to a valid configuration object of a tuple type.
+ * \li 'name' points to a null-terminated string naming one of the
+ *\li fields of said tuple type.
+ */
+
+bool
+cfg_obj_isuint32(const cfg_obj_t *obj);
+/*%<
+ * Return true iff 'obj' is of integer type.
+ */
+
+uint32_t
+cfg_obj_asuint32(const cfg_obj_t *obj);
+/*%<
+ * Returns the value of a configuration object of 32-bit integer type.
+ *
+ * Requires:
+ * \li 'obj' points to a valid configuration object of 32-bit integer type.
+ *
+ * Returns:
+ * \li A 32-bit unsigned integer.
+ */
+
+bool
+cfg_obj_isuint64(const cfg_obj_t *obj);
+/*%<
+ * Return true iff 'obj' is of integer type.
+ */
+
+uint64_t
+cfg_obj_asuint64(const cfg_obj_t *obj);
+/*%<
+ * Returns the value of a configuration object of 64-bit integer type.
+ *
+ * Requires:
+ * \li 'obj' points to a valid configuration object of 64-bit integer type.
+ *
+ * Returns:
+ * \li A 64-bit unsigned integer.
+ */
+
+uint32_t
+cfg_obj_asfixedpoint(const cfg_obj_t *obj);
+/*%<
+ * Returns the value of a configuration object of fixed point number.
+ *
+ * Requires:
+ * \li 'obj' points to a valid configuration object of fixed point type.
+ *
+ * Returns:
+ * \li A 32-bit unsigned integer.
+ */
+
+uint32_t
+cfg_obj_aspercentage(const cfg_obj_t *obj);
+/*%<
+ * Returns the value of a configuration object of percentage
+ *
+ * Requires:
+ * \li 'obj' points to a valid configuration object of percentage type.
+ *
+ * Returns:
+ * \li A 32-bit unsigned integer.
+ */
+
+bool
+cfg_obj_isduration(const cfg_obj_t *obj);
+/*%<
+ * Return true iff 'obj' is of duration type.
+ */
+
+uint32_t
+cfg_obj_asduration(const cfg_obj_t *obj);
+/*%<
+ * Returns the value of a configuration object of duration
+ *
+ * Requires:
+ * \li 'obj' points to a valid configuration object of duration type.
+ *
+ * Returns:
+ * \li A duration in seconds.
+ */
+
+bool
+cfg_obj_isstring(const cfg_obj_t *obj);
+/*%<
+ * Return true iff 'obj' is of string type.
+ */
+
+const char *
+cfg_obj_asstring(const cfg_obj_t *obj);
+/*%<
+ * Returns the value of a configuration object of a string type
+ * as a null-terminated string.
+ *
+ * Requires:
+ * \li 'obj' points to a valid configuration object of a string type.
+ *
+ * Returns:
+ * \li A pointer to a null terminated string.
+ */
+
+bool
+cfg_obj_isboolean(const cfg_obj_t *obj);
+/*%<
+ * Return true iff 'obj' is of a boolean type.
+ */
+
+bool
+cfg_obj_asboolean(const cfg_obj_t *obj);
+/*%<
+ * Returns the value of a configuration object of a boolean type.
+ *
+ * Requires:
+ * \li 'obj' points to a valid configuration object of a boolean type.
+ *
+ * Returns:
+ * \li A boolean value.
+ */
+
+bool
+cfg_obj_issockaddr(const cfg_obj_t *obj);
+/*%<
+ * Return true iff 'obj' is a socket address.
+ */
+
+const isc_sockaddr_t *
+cfg_obj_assockaddr(const cfg_obj_t *obj);
+/*%<
+ * Returns the value of a configuration object representing a socket address.
+ *
+ * Requires:
+ * \li 'obj' points to a valid configuration object of a socket address
+ * type.
+ *
+ * Returns:
+ * \li A pointer to a sockaddr. The sockaddr must be copied by the caller
+ * if necessary.
+ */
+
+bool
+cfg_obj_isnetprefix(const cfg_obj_t *obj);
+/*%<
+ * Return true iff 'obj' is a network prefix.
+ */
+
+void
+cfg_obj_asnetprefix(const cfg_obj_t *obj, isc_netaddr_t *netaddr,
+ unsigned int *prefixlen);
+/*%<
+ * Gets the value of a configuration object representing a network
+ * prefix. The network address is returned through 'netaddr' and the
+ * prefix length in bits through 'prefixlen'.
+ *
+ * Requires:
+ * \li 'obj' points to a valid configuration object of network prefix type.
+ *\li 'netaddr' and 'prefixlen' are non-NULL.
+ */
+
+bool
+cfg_obj_islist(const cfg_obj_t *obj);
+/*%<
+ * Return true iff 'obj' is of list type.
+ */
+
+const cfg_listelt_t *
+cfg_list_first(const cfg_obj_t *obj);
+/*%<
+ * Returns the first list element in a configuration object of a list type.
+ *
+ * Requires:
+ * \li 'obj' points to a valid configuration object of a list type or NULL.
+ *
+ * Returns:
+ * \li A pointer to a cfg_listelt_t representing the first list element,
+ * or NULL if the list is empty or nonexistent.
+ */
+
+const cfg_listelt_t *
+cfg_list_next(const cfg_listelt_t *elt);
+/*%<
+ * Returns the next element of a list of configuration objects.
+ *
+ * Requires:
+ * \li 'elt' points to cfg_listelt_t obtained from cfg_list_first() or
+ * a previous call to cfg_list_next().
+ *
+ * Returns:
+ * \li A pointer to a cfg_listelt_t representing the next element,
+ * or NULL if there are no more elements.
+ */
+
+unsigned int
+cfg_list_length(const cfg_obj_t *obj, bool recurse);
+/*%<
+ * Returns the length of a list of configure objects. If obj is
+ * not a list, returns 0. If recurse is true, add in the length of
+ * all contained lists.
+ */
+
+cfg_obj_t *
+cfg_listelt_value(const cfg_listelt_t *elt);
+/*%<
+ * Returns the configuration object associated with cfg_listelt_t.
+ *
+ * Requires:
+ * \li 'elt' points to cfg_listelt_t obtained from cfg_list_first() or
+ * cfg_list_next().
+ *
+ * Returns:
+ * \li A non-NULL pointer to a configuration object.
+ */
+
+void
+cfg_print(const cfg_obj_t *obj,
+ void (*f)(void *closure, const char *text, int textlen),
+ void *closure);
+void
+cfg_printx(const cfg_obj_t *obj, unsigned int flags,
+ void (*f)(void *closure, const char *text, int textlen),
+ void *closure);
+
+#define CFG_PRINTER_XKEY 0x1 /* '?' out shared keys. */
+#define CFG_PRINTER_ONELINE 0x2 /* print config as a single line */
+#define CFG_PRINTER_ACTIVEONLY \
+ 0x4 /* print only active configuration \
+ * options, omitting ancient, \
+ * obsolete, nonimplemented, \
+ * and test-only options. */
+
+/*%<
+ * Print the configuration object 'obj' by repeatedly calling the
+ * function 'f', passing 'closure' and a region of text starting
+ * at 'text' and comprising 'textlen' characters.
+ *
+ * If CFG_PRINTER_XKEY the contents of shared keys will be obscured
+ * by replacing them with question marks ('?')
+ */
+
+void
+cfg_print_grammar(const cfg_type_t *type, unsigned int flags,
+ void (*f)(void *closure, const char *text, int textlen),
+ void *closure);
+/*%<
+ * Print a summary of the grammar of the configuration type 'type'.
+ */
+
+bool
+cfg_obj_istype(const cfg_obj_t *obj, const cfg_type_t *type);
+/*%<
+ * Return true iff 'obj' is of type 'type'.
+ */
+
+void
+cfg_obj_attach(cfg_obj_t *src, cfg_obj_t **dest);
+/*%<
+ * Reference a configuration object.
+ */
+
+void
+cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **obj);
+/*%<
+ * Delete a reference to a configuration object; destroy the object if
+ * there are no more references.
+ *
+ * Require:
+ * \li '*obj' is a valid cfg_obj_t.
+ * \li 'pctx' is a valid cfg_parser_t.
+ */
+
+void
+cfg_obj_log(const cfg_obj_t *obj, isc_log_t *lctx, int level, const char *fmt,
+ ...) ISC_FORMAT_PRINTF(4, 5);
+/*%<
+ * Log a message concerning configuration object 'obj' to the logging
+ * channel of 'pctx', at log level 'level'. The message will be prefixed
+ * with the file name(s) and line number where 'obj' was defined.
+ */
+
+const char *
+cfg_obj_file(const cfg_obj_t *obj);
+/*%<
+ * Return the file that defined this object.
+ */
+
+unsigned int
+cfg_obj_line(const cfg_obj_t *obj);
+/*%<
+ * Return the line in file where this object was defined.
+ */
+
+const char *
+cfg_map_firstclause(const cfg_type_t *map, const void **clauses,
+ unsigned int *idx);
+const char *
+cfg_map_nextclause(const cfg_type_t *map, const void **clauses,
+ unsigned int *idx);
+
+typedef isc_result_t(pluginlist_cb_t)(const cfg_obj_t *config,
+ const cfg_obj_t *obj,
+ const char *plugin_path,
+ const char *parameters,
+ void *callback_data);
+/*%<
+ * Function prototype for the callback used with cfg_pluginlist_foreach().
+ * Called once for each element of the list passed to cfg_pluginlist_foreach().
+ * If this callback returns anything else than #ISC_R_SUCCESS, no further list
+ * elements will be processed.
+ *
+ * \li 'config' - the 'config' object passed to cfg_pluginlist_foreach()
+ * \li 'obj' - object representing the specific "plugin" stanza to be processed
+ * \li 'plugin_path' - path to the shared object with plugin code
+ * \li 'parameters' - configuration text for the plugin
+ * \li 'callback_data' - the pointer passed to cfg_pluginlist_foreach()
+ */
+
+isc_result_t
+cfg_pluginlist_foreach(const cfg_obj_t *config, const cfg_obj_t *list,
+ isc_log_t *lctx, pluginlist_cb_t *callback,
+ void *callback_data);
+/*%<
+ * For every "plugin" stanza present in 'list' (which in turn is a part of
+ * 'config'), invoke the given 'callback', passing 'callback_data' to it along
+ * with a fixed set of arguments (see the definition of the #pluginlist_cb_t
+ * type). Use logging context 'lctx' for logging error messages. Interrupt
+ * processing if 'callback' returns something else than #ISC_R_SUCCESS for any
+ * element of 'list'.
+ *
+ * Requires:
+ *
+ * \li 'config' is not NULL
+ * \li 'callback' is not NULL
+ *
+ * Returns:
+ *
+ * \li #ISC_R_SUCCESS if 'callback' returned #ISC_R_SUCCESS for all elements of
+ * 'list'
+ * \li first 'callback' return value which was not #ISC_R_SUCCESS otherwise
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isccfg/include/isccfg/duration.h b/lib/isccfg/include/isccfg/duration.h
new file mode 100644
index 0000000..bd0c35b
--- /dev/null
+++ b/lib/isccfg/include/isccfg/duration.h
@@ -0,0 +1,87 @@
+
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file */
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+#define CFG_DURATION_MAXLEN 80
+
+/*%
+ * A configuration object to store ISO 8601 durations.
+ */
+typedef struct isccfg_duration {
+ /*
+ * The duration is stored in multiple parts:
+ * [0] Years
+ * [1] Months
+ * [2] Weeks
+ * [3] Days
+ * [4] Hours
+ * [5] Minutes
+ * [6] Seconds
+ */
+ uint32_t parts[7];
+ bool iso8601;
+ bool unlimited;
+} isccfg_duration_t;
+
+isc_result_t
+isccfg_duration_fromtext(isc_textregion_t *source, isccfg_duration_t *duration);
+/*%<
+ * Converts an ISO 8601 duration style value.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ *\li DNS_R_BADNUMBER
+ */
+
+isc_result_t
+isccfg_parse_duration(isc_textregion_t *source, isccfg_duration_t *duration);
+/*%<
+ * Converts a duration string to a ISO 8601 duration.
+ * If the string does not start with a P (or p), fall back to TTL-style value.
+ * In that case the duration will be treated in seconds only.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ *\li DNS_R_BADNUMBER
+ *\li DNS_R_BADTTL
+ */
+
+uint32_t
+isccfg_duration_toseconds(const isccfg_duration_t *duration);
+/*%<
+ * Converts an ISO 8601 duration to seconds.
+ * The conversion is approximate:
+ * - Months will be treated as 31 days.
+ * - Years will be treated as 365 days.
+ *
+ * Notes:
+ *\li If the duration in seconds is greater than UINT32_MAX, the return value
+ * will be UINT32_MAX.
+ *
+ * Returns:
+ *\li The duration in seconds.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isccfg/include/isccfg/grammar.h b/lib/isccfg/include/isccfg/grammar.h
new file mode 100644
index 0000000..83482d9
--- /dev/null
+++ b/lib/isccfg/include/isccfg/grammar.h
@@ -0,0 +1,590 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isccfg/grammar.h */
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/lex.h>
+#include <isc/netaddr.h>
+#include <isc/region.h>
+#include <isc/sockaddr.h>
+#include <isc/types.h>
+
+#include <isccfg/cfg.h>
+#include <isccfg/duration.h>
+
+/*
+ * Definitions shared between the configuration parser
+ * and the grammars; not visible to users of the parser.
+ */
+
+/*% Clause may occur multiple times (e.g., "zone") */
+#define CFG_CLAUSEFLAG_MULTI 0x00000001
+/*% Clause is obsolete (logs a warning, but is not a fatal error) */
+#define CFG_CLAUSEFLAG_OBSOLETE 0x00000002
+/* obsolete: #define CFG_CLAUSEFLAG_NOTIMP 0x00000004 */
+/* obsolete: #define CFG_CLAUSEFLAG_NYI 0x00000008 */
+/* obsolete: #define CFG_CLAUSEFLAG_NEWDEFAULT 0x00000010 */
+/*%
+ * Clause needs to be interpreted during parsing
+ * by calling a callback function, like the
+ * "directory" option.
+ */
+#define CFG_CLAUSEFLAG_CALLBACK 0x00000020
+/*% An option that is only used in testing. */
+#define CFG_CLAUSEFLAG_TESTONLY 0x00000040
+/*% A configuration option that was not configured at compile time. */
+#define CFG_CLAUSEFLAG_NOTCONFIGURED 0x00000080
+/*% An option for an experimental feature. */
+#define CFG_CLAUSEFLAG_EXPERIMENTAL 0x00000100
+/*% An option that should be omited from the documentation */
+#define CFG_CLAUSEFLAG_NODOC 0x00000200
+/*% Clause will be obsolete in a future release (logs a warning) */
+#define CFG_CLAUSEFLAG_DEPRECATED 0x00000400
+/*% Clause has been obsolete so long that it's now a fatal error */
+#define CFG_CLAUSEFLAG_ANCIENT 0x00000800
+
+/*%
+ * Zone types for which a clause is valid:
+ * These share space with CFG_CLAUSEFLAG values, but count
+ * down from the top.
+ */
+#define CFG_ZONE_PRIMARY 0x80000000
+#define CFG_ZONE_SECONDARY 0x40000000
+#define CFG_ZONE_STUB 0x20000000
+#define CFG_ZONE_HINT 0x10000000
+#define CFG_ZONE_FORWARD 0x08000000
+#define CFG_ZONE_STATICSTUB 0x04000000
+#define CFG_ZONE_REDIRECT 0x02000000
+#define CFG_ZONE_DELEGATION 0x01000000
+#define CFG_ZONE_INVIEW 0x00800000
+#define CFG_ZONE_MIRROR 0x00400000
+
+typedef struct cfg_clausedef cfg_clausedef_t;
+typedef struct cfg_tuplefielddef cfg_tuplefielddef_t;
+typedef struct cfg_printer cfg_printer_t;
+typedef ISC_LIST(cfg_listelt_t) cfg_list_t;
+typedef struct cfg_map cfg_map_t;
+typedef struct cfg_rep cfg_rep_t;
+
+/*
+ * Function types for configuration object methods
+ */
+
+typedef isc_result_t (*cfg_parsefunc_t)(cfg_parser_t *, const cfg_type_t *type,
+ cfg_obj_t **);
+typedef void (*cfg_printfunc_t)(cfg_printer_t *, const cfg_obj_t *);
+typedef void (*cfg_docfunc_t)(cfg_printer_t *, const cfg_type_t *);
+typedef void (*cfg_freefunc_t)(cfg_parser_t *, cfg_obj_t *);
+
+/*
+ * Structure definitions
+ */
+
+/*%
+ * A configuration printer object. This is an abstract
+ * interface to a destination to which text can be printed
+ * by calling the function 'f'.
+ */
+struct cfg_printer {
+ void (*f)(void *closure, const char *text, int textlen);
+ void *closure;
+ int indent;
+ int flags;
+};
+
+/*% A clause definition. */
+struct cfg_clausedef {
+ const char *name;
+ cfg_type_t *type;
+ unsigned int flags;
+};
+
+/*% A tuple field definition. */
+struct cfg_tuplefielddef {
+ const char *name;
+ cfg_type_t *type;
+ unsigned int flags;
+};
+
+/*% A configuration object type definition. */
+struct cfg_type {
+ const char *name; /*%< For debugging purposes only */
+ cfg_parsefunc_t parse;
+ cfg_printfunc_t print;
+ cfg_docfunc_t doc; /*%< Print grammar description */
+ cfg_rep_t *rep; /*%< Data representation */
+ const void *of; /*%< Additional data for meta-types */
+};
+
+/*% A keyword-type definition, for things like "port <integer>". */
+typedef struct {
+ const char *name;
+ const cfg_type_t *type;
+} keyword_type_t;
+
+struct cfg_map {
+ cfg_obj_t *id; /*%< Used for 'named maps' like
+ * keys, zones, &c */
+ const cfg_clausedef_t *const *clausesets; /*%< The clauses that
+ * can occur in this map;
+ * used for printing */
+ isc_symtab_t *symtab;
+};
+
+typedef struct cfg_netprefix cfg_netprefix_t;
+
+struct cfg_netprefix {
+ isc_netaddr_t address; /* IP4/IP6 */
+ unsigned int prefixlen;
+};
+
+/*%
+ * A configuration data representation.
+ */
+struct cfg_rep {
+ const char *name; /*%< For debugging only */
+ cfg_freefunc_t free; /*%< How to free this kind of data. */
+};
+
+/*%
+ * A configuration object. This is the main building block
+ * of the configuration parse tree.
+ */
+
+struct cfg_obj {
+ const cfg_type_t *type;
+ union {
+ uint32_t uint32;
+ uint64_t uint64;
+ isc_textregion_t string; /*%< null terminated, too */
+ bool boolean;
+ cfg_map_t map;
+ cfg_list_t list;
+ cfg_obj_t **tuple;
+ isc_sockaddr_t sockaddr;
+ struct {
+ isc_sockaddr_t sockaddr;
+ int32_t dscp;
+ } sockaddrdscp;
+ cfg_netprefix_t netprefix;
+ isccfg_duration_t duration;
+ } value;
+ isc_refcount_t references; /*%< reference counter */
+ const char *file;
+ unsigned int line;
+ cfg_parser_t *pctx;
+};
+
+/*% A list element. */
+struct cfg_listelt {
+ cfg_obj_t *obj;
+ ISC_LINK(cfg_listelt_t) link;
+};
+
+/*% The parser object. */
+struct cfg_parser {
+ isc_mem_t *mctx;
+ isc_log_t *lctx;
+ isc_lex_t *lexer;
+ unsigned int errors;
+ unsigned int warnings;
+ isc_token_t token;
+
+ /*% We are at the end of all input. */
+ bool seen_eof;
+
+ /*% The current token has been pushed back. */
+ bool ungotten;
+
+ /*%
+ * The stack of currently active files, represented
+ * as a configuration list of configuration strings.
+ * The head is the top-level file, subsequent elements
+ * (if any) are the nested include files, and the
+ * last element is the file currently being parsed.
+ */
+ cfg_obj_t *open_files;
+
+ /*%
+ * Names of files that we have parsed and closed
+ * and were previously on the open_file list.
+ * We keep these objects around after closing
+ * the files because the file names may still be
+ * referenced from other configuration objects
+ * for use in reporting semantic errors after
+ * parsing is complete.
+ */
+ cfg_obj_t *closed_files;
+
+ /*%
+ * Name of a buffer being parsed; used only for
+ * logging.
+ */
+ char const *buf_name;
+
+ /*%
+ * Current line number. We maintain our own
+ * copy of this so that it is available even
+ * when a file has just been closed.
+ */
+ unsigned int line;
+
+ /*%
+ * Parser context flags, used for maintaining state
+ * from one token to the next.
+ */
+ unsigned int flags;
+
+ /*%< Reference counter */
+ isc_refcount_t references;
+
+ cfg_parsecallback_t callback;
+ void *callbackarg;
+};
+
+/* Parser context flags */
+#define CFG_PCTX_SKIP 0x1
+#define CFG_PCTX_NODEPRECATED 0x2
+
+/*@{*/
+/*%
+ * Flags defining whether to accept certain types of network addresses.
+ */
+#define CFG_ADDR_V4OK 0x00000001
+#define CFG_ADDR_V4PREFIXOK 0x00000002
+#define CFG_ADDR_V6OK 0x00000004
+#define CFG_ADDR_WILDOK 0x00000008
+#define CFG_ADDR_DSCPOK 0x00000010
+#define CFG_ADDR_PORTOK 0x00000020
+#define CFG_ADDR_MASK (CFG_ADDR_V6OK | CFG_ADDR_V4OK)
+/*@}*/
+
+/*@{*/
+/*%
+ * Predefined data representation types.
+ */
+extern cfg_rep_t cfg_rep_uint32;
+extern cfg_rep_t cfg_rep_uint64;
+extern cfg_rep_t cfg_rep_string;
+extern cfg_rep_t cfg_rep_boolean;
+extern cfg_rep_t cfg_rep_map;
+extern cfg_rep_t cfg_rep_list;
+extern cfg_rep_t cfg_rep_tuple;
+extern cfg_rep_t cfg_rep_sockaddr;
+extern cfg_rep_t cfg_rep_netprefix;
+extern cfg_rep_t cfg_rep_void;
+extern cfg_rep_t cfg_rep_fixedpoint;
+extern cfg_rep_t cfg_rep_percentage;
+extern cfg_rep_t cfg_rep_duration;
+/*@}*/
+
+/*@{*/
+/*%
+ * Predefined configuration object types.
+ */
+extern cfg_type_t cfg_type_boolean;
+extern cfg_type_t cfg_type_uint32;
+extern cfg_type_t cfg_type_uint64;
+extern cfg_type_t cfg_type_qstring;
+extern cfg_type_t cfg_type_astring;
+extern cfg_type_t cfg_type_ustring;
+extern cfg_type_t cfg_type_sstring;
+extern cfg_type_t cfg_type_bracketed_aml;
+extern cfg_type_t cfg_type_bracketed_text;
+extern cfg_type_t cfg_type_optional_bracketed_text;
+extern cfg_type_t cfg_type_keyref;
+extern cfg_type_t cfg_type_sockaddr;
+extern cfg_type_t cfg_type_sockaddrdscp;
+extern cfg_type_t cfg_type_netaddr;
+extern cfg_type_t cfg_type_netaddr4;
+extern cfg_type_t cfg_type_netaddr4wild;
+extern cfg_type_t cfg_type_netaddr6;
+extern cfg_type_t cfg_type_netaddr6wild;
+extern cfg_type_t cfg_type_netprefix;
+extern cfg_type_t cfg_type_void;
+extern cfg_type_t cfg_type_token;
+extern cfg_type_t cfg_type_unsupported;
+extern cfg_type_t cfg_type_fixedpoint;
+extern cfg_type_t cfg_type_percentage;
+extern cfg_type_t cfg_type_duration;
+extern cfg_type_t cfg_type_duration_or_unlimited;
+/*@}*/
+
+isc_result_t
+cfg_gettoken(cfg_parser_t *pctx, int options);
+
+isc_result_t
+cfg_peektoken(cfg_parser_t *pctx, int options);
+
+void
+cfg_ungettoken(cfg_parser_t *pctx);
+
+#define CFG_LEXOPT_QSTRING (ISC_LEXOPT_QSTRING | ISC_LEXOPT_QSTRINGMULTILINE)
+
+isc_result_t
+cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
+
+void
+cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u);
+
+isc_result_t
+cfg_parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
+
+void
+cfg_print_uint32(cfg_printer_t *pctx, const cfg_obj_t *obj);
+
+void
+cfg_print_uint64(cfg_printer_t *pctx, const cfg_obj_t *obj);
+
+isc_result_t
+cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
+
+void
+cfg_print_ustring(cfg_printer_t *pctx, const cfg_obj_t *obj);
+
+isc_result_t
+cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
+
+isc_result_t
+cfg_parse_sstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
+
+isc_result_t
+cfg_parse_rawaddr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na);
+
+void
+cfg_print_rawaddr(cfg_printer_t *pctx, const isc_netaddr_t *na);
+
+bool
+cfg_lookingat_netaddr(cfg_parser_t *pctx, unsigned int flags);
+
+isc_result_t
+cfg_parse_rawport(cfg_parser_t *pctx, unsigned int flags, in_port_t *port);
+
+isc_result_t
+cfg_parse_sockaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
+
+isc_result_t
+cfg_parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
+
+void
+cfg_print_sockaddr(cfg_printer_t *pctx, const cfg_obj_t *obj);
+
+void
+cfg_print_boolean(cfg_printer_t *pctx, const cfg_obj_t *obj);
+
+void
+cfg_doc_sockaddr(cfg_printer_t *pctx, const cfg_type_t *type);
+
+isc_result_t
+cfg_parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret);
+
+isc_result_t
+cfg_parse_special(cfg_parser_t *pctx, int special);
+/*%< Parse a required special character 'special'. */
+
+isc_result_t
+cfg_create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
+
+isc_result_t
+cfg_parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
+
+void
+cfg_print_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj);
+
+void
+cfg_doc_tuple(cfg_printer_t *pctx, const cfg_type_t *type);
+
+isc_result_t
+cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
+
+isc_result_t
+cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
+ cfg_listelt_t **ret);
+
+isc_result_t
+cfg_parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret);
+
+void
+cfg_print_bracketed_list(cfg_printer_t *pctx, const cfg_obj_t *obj);
+
+void
+cfg_doc_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type);
+
+isc_result_t
+cfg_parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret);
+
+void
+cfg_print_spacelist(cfg_printer_t *pctx, const cfg_obj_t *obj);
+
+isc_result_t
+cfg_parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
+
+void
+cfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type);
+
+isc_result_t
+cfg_parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype,
+ const cfg_type_t *othertype, cfg_obj_t **ret);
+
+void
+cfg_doc_enum_or_other(cfg_printer_t *pctx, const cfg_type_t *enumtype,
+ const cfg_type_t *othertype);
+
+void
+cfg_print_chars(cfg_printer_t *pctx, const char *text, int len);
+/*%< Print 'len' characters at 'text' */
+
+void
+cfg_print_cstr(cfg_printer_t *pctx, const char *s);
+/*%< Print the null-terminated string 's' */
+
+isc_result_t
+cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
+
+isc_result_t
+cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret);
+
+isc_result_t
+cfg_parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret);
+
+isc_result_t
+cfg_parse_netprefix_map(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret);
+
+void
+cfg_print_map(cfg_printer_t *pctx, const cfg_obj_t *obj);
+
+void
+cfg_doc_map(cfg_printer_t *pctx, const cfg_type_t *type);
+
+isc_result_t
+cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
+
+void
+cfg_print_mapbody(cfg_printer_t *pctx, const cfg_obj_t *obj);
+
+void
+cfg_doc_mapbody(cfg_printer_t *pctx, const cfg_type_t *type);
+
+isc_result_t
+cfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
+
+void
+cfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj);
+
+void
+cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type);
+
+isc_result_t
+cfg_parse_fixedpoint(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret);
+
+void
+cfg_print_fixedpoint(cfg_printer_t *pctx, const cfg_obj_t *obj);
+
+isc_result_t
+cfg_parse_percentage(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret);
+
+void
+cfg_print_percentage(cfg_printer_t *pctx, const cfg_obj_t *obj);
+
+isc_result_t
+cfg_parse_duration(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
+
+void
+cfg_print_duration(cfg_printer_t *pctx, const cfg_obj_t *obj);
+
+isc_result_t
+cfg_parse_duration_or_unlimited(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret);
+
+void
+cfg_print_duration_or_unlimited(cfg_printer_t *pctx, const cfg_obj_t *obj);
+
+isc_result_t
+cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
+
+void
+cfg_print_obj(cfg_printer_t *pctx, const cfg_obj_t *obj);
+
+void
+cfg_doc_obj(cfg_printer_t *pctx, const cfg_type_t *type);
+/*%<
+ * Print a description of the grammar of an arbitrary configuration
+ * type 'type'
+ */
+
+void
+cfg_doc_terminal(cfg_printer_t *pctx, const cfg_type_t *type);
+/*%<
+ * Document the type 'type' as a terminal by printing its
+ * name in angle brackets, e.g., &lt;uint32>.
+ */
+
+void
+cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...)
+ ISC_FORMAT_PRINTF(3, 4);
+/*!
+ * Pass one of these flags to cfg_parser_error() to include the
+ * token text in log message.
+ */
+#define CFG_LOG_NEAR 0x00000001 /*%< Say "near <token>" */
+#define CFG_LOG_BEFORE 0x00000002 /*%< Say "before <token>" */
+#define CFG_LOG_NOPREP 0x00000004 /*%< Say just "<token>" */
+
+void
+cfg_parser_warning(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...)
+ ISC_FORMAT_PRINTF(3, 4);
+
+bool
+cfg_is_enum(const char *s, const char *const *enums);
+/*%< Return true iff the string 's' is one of the strings in 'enums' */
+
+bool
+cfg_clause_validforzone(const char *name, unsigned int ztype);
+/*%<
+ * Check whether an option is legal for the specified zone type.
+ */
+
+void
+cfg_print_zonegrammar(const unsigned int zonetype, unsigned int flags,
+ void (*f)(void *closure, const char *text, int textlen),
+ void *closure);
+/*%<
+ * Print a summary of the grammar of the zone type represented by
+ * 'zonetype'.
+ */
+
+void
+cfg_print_clauseflags(cfg_printer_t *pctx, unsigned int flags);
+/*%<
+ * Print clause flags (e.g. "obsolete", "not implemented", etc) in
+ * human readable form
+ */
+
+void
+cfg_print_indent(cfg_printer_t *pctx);
+/*%<
+ * Print the necessary indent required by the current settings of 'pctx'.
+ */
diff --git a/lib/isccfg/include/isccfg/kaspconf.h b/lib/isccfg/include/isccfg/kaspconf.h
new file mode 100644
index 0000000..7b1e075
--- /dev/null
+++ b/lib/isccfg/include/isccfg/kaspconf.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <isc/lang.h>
+
+#include <isccfg/cfg.h>
+
+/***
+ *** Functions
+ ***/
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp,
+ isc_mem_t *mctx, isc_log_t *logctx,
+ dns_kasplist_t *kasplist, dns_kasp_t **kaspp);
+/*%<
+ * Create and configure a KASP. If 'default_kasp' is not NULL, the built-in
+ * default configuration is used to set values that are not explicitly set in
+ * the policy. If a 'kasplist' is provided, a lookup happens and if a KASP
+ * already exists with the same name, no new KASP is created, and no attach to
+ * 'kaspp' happens.
+ *
+ * Requires:
+ *
+ *\li 'name' is either NULL, or a valid C string.
+ *
+ *\li 'mctx' is a valid memory context.
+ *
+ *\li 'logctx' is a valid logging context.
+ *
+ *\li kaspp != NULL && *kaspp == NULL
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS If creating and configuring the KASP succeeds.
+ *\li #ISC_R_EXISTS If 'kasplist' already has a kasp structure with 'name'.
+ *\li #ISC_R_NOMEMORY
+ *
+ *\li Other errors are possible.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isccfg/include/isccfg/log.h b/lib/isccfg/include/isccfg/log.h
new file mode 100644
index 0000000..8af3095
--- /dev/null
+++ b/lib/isccfg/include/isccfg/log.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isccfg/log.h */
+
+#include <isc/lang.h>
+#include <isc/log.h>
+
+extern isc_logcategory_t cfg_categories[];
+extern isc_logmodule_t cfg_modules[];
+
+#define CFG_LOGCATEGORY_CONFIG (&cfg_categories[0])
+
+#define CFG_LOGMODULE_PARSER (&cfg_modules[0])
+
+ISC_LANG_BEGINDECLS
+
+void
+cfg_log_init(isc_log_t *lctx);
+/*%<
+ * Make the libisccfg categories and modules available for use with the
+ * ISC logging library.
+ *
+ * Requires:
+ *\li lctx is a valid logging context.
+ *
+ *\li cfg_log_init() is called only once.
+ *
+ * Ensures:
+ * \li The categories and modules defined above are available for
+ * use by isc_log_usechannnel() and isc_log_write().
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/isccfg/include/isccfg/namedconf.h b/lib/isccfg/include/isccfg/namedconf.h
new file mode 100644
index 0000000..f2d0145
--- /dev/null
+++ b/lib/isccfg/include/isccfg/namedconf.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+/*! \file isccfg/namedconf.h
+ * \brief
+ * This module defines the named.conf, rndc.conf, and rndc.key grammars.
+ */
+
+#include <isccfg/cfg.h>
+
+/*
+ * Configuration object types.
+ */
+extern cfg_type_t cfg_type_namedconf;
+/*%< A complete named.conf file. */
+
+extern cfg_type_t cfg_type_bindkeys;
+/*%< A bind.keys file. */
+
+extern cfg_type_t cfg_type_newzones;
+/*%< A new-zones file (for zones added by 'rndc addzone'). */
+
+extern cfg_type_t cfg_type_addzoneconf;
+/*%< A single zone passed via the addzone rndc command. */
+
+extern cfg_type_t cfg_type_rndcconf;
+/*%< A complete rndc.conf file. */
+
+extern cfg_type_t cfg_type_rndckey;
+/*%< A complete rndc.key file. */
+
+extern cfg_type_t cfg_type_sessionkey;
+/*%< A complete session.key file. */
+
+extern cfg_type_t cfg_type_keyref;
+/*%< A key reference, used as an ACL element */
+
+/*%< Zone options */
+extern cfg_type_t cfg_type_zoneopts;
+
+/*%< DNSSEC Key and Signing Policy options */
+extern cfg_type_t cfg_type_dnssecpolicyopts;
diff --git a/lib/isccfg/kaspconf.c b/lib/isccfg/kaspconf.c
new file mode 100644
index 0000000..ba36b0d
--- /dev/null
+++ b/lib/isccfg/kaspconf.c
@@ -0,0 +1,576 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/region.h>
+#include <isc/result.h>
+#include <isc/string.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+#include <dns/kasp.h>
+#include <dns/keyvalues.h>
+#include <dns/log.h>
+#include <dns/nsec3.h>
+#include <dns/secalg.h>
+#include <dns/ttl.h>
+
+#include <isccfg/cfg.h>
+#include <isccfg/duration.h>
+#include <isccfg/kaspconf.h>
+#include <isccfg/namedconf.h>
+
+#define DEFAULT_NSEC3PARAM_ITER 0
+#define DEFAULT_NSEC3PARAM_SALTLEN 0
+
+/*
+ * Utility function for getting a configuration option.
+ */
+static isc_result_t
+confget(cfg_obj_t const *const *maps, const char *name, const cfg_obj_t **obj) {
+ for (size_t i = 0;; i++) {
+ if (maps[i] == NULL) {
+ return (ISC_R_NOTFOUND);
+ }
+ if (cfg_map_get(maps[i], name, obj) == ISC_R_SUCCESS) {
+ return (ISC_R_SUCCESS);
+ }
+ }
+}
+
+/*
+ * Utility function for parsing durations from string.
+ */
+static uint32_t
+parse_duration(const char *str) {
+ uint32_t time = 0;
+ isccfg_duration_t duration;
+ isc_result_t result;
+ isc_textregion_t tr;
+
+ DE_CONST(str, tr.base);
+ tr.length = strlen(tr.base);
+ result = isccfg_parse_duration(&tr, &duration);
+ if (result == ISC_R_SUCCESS) {
+ time = isccfg_duration_toseconds(&duration);
+ }
+ return (time);
+}
+
+/*
+ * Utility function for configuring durations.
+ */
+static uint32_t
+get_duration(const cfg_obj_t **maps, const char *option, const char *dfl) {
+ const cfg_obj_t *obj;
+ isc_result_t result;
+ obj = NULL;
+
+ result = confget(maps, option, &obj);
+ if (result == ISC_R_NOTFOUND) {
+ return (parse_duration(dfl));
+ }
+ INSIST(result == ISC_R_SUCCESS);
+ return (cfg_obj_asduration(obj));
+}
+
+/*
+ * Create a new kasp key derived from configuration.
+ */
+static isc_result_t
+cfg_kaspkey_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp,
+ isc_log_t *logctx, uint32_t ksk_min_lifetime,
+ uint32_t zsk_min_lifetime) {
+ isc_result_t result;
+ dns_kasp_key_t *key = NULL;
+
+ /* Create a new key reference. */
+ result = dns_kasp_key_create(kasp, &key);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ if (config == NULL) {
+ /* We are creating a key reference for the default kasp. */
+ key->role |= DNS_KASP_KEY_ROLE_KSK | DNS_KASP_KEY_ROLE_ZSK;
+ key->lifetime = 0; /* unlimited */
+ key->algorithm = DNS_KEYALG_ECDSA256;
+ key->length = -1;
+ } else {
+ const char *rolestr = NULL;
+ const cfg_obj_t *obj = NULL;
+ isc_consttextregion_t alg;
+ bool error = false;
+
+ rolestr = cfg_obj_asstring(cfg_tuple_get(config, "role"));
+ if (strcmp(rolestr, "ksk") == 0) {
+ key->role |= DNS_KASP_KEY_ROLE_KSK;
+ } else if (strcmp(rolestr, "zsk") == 0) {
+ key->role |= DNS_KASP_KEY_ROLE_ZSK;
+ } else if (strcmp(rolestr, "csk") == 0) {
+ key->role |= DNS_KASP_KEY_ROLE_KSK;
+ key->role |= DNS_KASP_KEY_ROLE_ZSK;
+ }
+
+ key->lifetime = 0; /* unlimited */
+ obj = cfg_tuple_get(config, "lifetime");
+ if (cfg_obj_isduration(obj)) {
+ key->lifetime = cfg_obj_asduration(obj);
+ }
+ if (key->lifetime > 0) {
+ if (key->lifetime < 30 * (24 * 3600)) {
+ cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
+ "dnssec-policy: key lifetime is "
+ "shorter than 30 days");
+ }
+ if ((key->role & DNS_KASP_KEY_ROLE_KSK) != 0 &&
+ key->lifetime <= ksk_min_lifetime)
+ {
+ error = true;
+ }
+ if ((key->role & DNS_KASP_KEY_ROLE_ZSK) != 0 &&
+ key->lifetime <= zsk_min_lifetime)
+ {
+ error = true;
+ }
+ if (error) {
+ cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
+ "dnssec-policy: key lifetime is "
+ "shorter than the time it takes to "
+ "do a rollover");
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ }
+
+ obj = cfg_tuple_get(config, "algorithm");
+ alg.base = cfg_obj_asstring(obj);
+ alg.length = strlen(alg.base);
+ result = dns_secalg_fromtext(&key->algorithm,
+ (isc_textregion_t *)&alg);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
+ "dnssec-policy: bad algorithm %s",
+ alg.base);
+ result = DNS_R_BADALG;
+ goto cleanup;
+ }
+
+ obj = cfg_tuple_get(config, "length");
+ if (cfg_obj_isuint32(obj)) {
+ uint32_t min, size;
+ size = cfg_obj_asuint32(obj);
+
+ switch (key->algorithm) {
+ case DNS_KEYALG_RSASHA1:
+ case DNS_KEYALG_NSEC3RSASHA1:
+ case DNS_KEYALG_RSASHA256:
+ case DNS_KEYALG_RSASHA512:
+ min = DNS_KEYALG_RSASHA512 ? 1024 : 512;
+ if (size < min || size > 4096) {
+ cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
+ "dnssec-policy: key with "
+ "algorithm %s has invalid "
+ "key length %u",
+ alg.base, size);
+ result = ISC_R_RANGE;
+ goto cleanup;
+ }
+ break;
+ case DNS_KEYALG_ECDSA256:
+ case DNS_KEYALG_ECDSA384:
+ case DNS_KEYALG_ED25519:
+ case DNS_KEYALG_ED448:
+ cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
+ "dnssec-policy: key algorithm %s "
+ "has predefined length; ignoring "
+ "length value %u",
+ alg.base, size);
+ default:
+ break;
+ }
+
+ key->length = size;
+ }
+ }
+
+ dns_kasp_addkey(kasp, key);
+ return (ISC_R_SUCCESS);
+
+cleanup:
+
+ dns_kasp_key_destroy(key);
+ return (result);
+}
+
+static isc_result_t
+cfg_nsec3param_fromconfig(const cfg_obj_t *config, dns_kasp_t *kasp,
+ isc_log_t *logctx) {
+ dns_kasp_key_t *kkey;
+ unsigned int min_keysize = 4096;
+ const cfg_obj_t *obj = NULL;
+ uint32_t iter = DEFAULT_NSEC3PARAM_ITER;
+ uint32_t saltlen = DEFAULT_NSEC3PARAM_SALTLEN;
+ uint32_t badalg = 0;
+ bool optout = false;
+ isc_result_t ret = ISC_R_SUCCESS;
+
+ /* How many iterations. */
+ obj = cfg_tuple_get(config, "iterations");
+ if (cfg_obj_isuint32(obj)) {
+ iter = cfg_obj_asuint32(obj);
+ }
+ dns_kasp_freeze(kasp);
+ for (kkey = ISC_LIST_HEAD(dns_kasp_keys(kasp)); kkey != NULL;
+ kkey = ISC_LIST_NEXT(kkey, link))
+ {
+ unsigned int keysize = dns_kasp_key_size(kkey);
+ uint32_t keyalg = dns_kasp_key_algorithm(kkey);
+
+ if (keysize < min_keysize) {
+ min_keysize = keysize;
+ }
+
+ /* NSEC3 cannot be used with certain key algorithms. */
+ if (keyalg == DNS_KEYALG_RSAMD5 || keyalg == DNS_KEYALG_DH ||
+ keyalg == DNS_KEYALG_DSA || keyalg == DNS_KEYALG_RSASHA1)
+ {
+ badalg = keyalg;
+ }
+ }
+ dns_kasp_thaw(kasp);
+
+ if (badalg > 0) {
+ char algstr[DNS_SECALG_FORMATSIZE];
+ dns_secalg_format((dns_secalg_t)badalg, algstr, sizeof(algstr));
+ cfg_obj_log(
+ obj, logctx, ISC_LOG_ERROR,
+ "dnssec-policy: cannot use nsec3 with algorithm '%s'",
+ algstr);
+ return (DNS_R_NSEC3BADALG);
+ }
+
+ if (iter > dns_nsec3_maxiterations()) {
+ ret = DNS_R_NSEC3ITERRANGE;
+ }
+
+ if (ret == DNS_R_NSEC3ITERRANGE) {
+ cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
+ "dnssec-policy: nsec3 iterations value %u "
+ "out of range",
+ iter);
+ return (ret);
+ }
+
+ /* Opt-out? */
+ obj = cfg_tuple_get(config, "optout");
+ if (cfg_obj_isboolean(obj)) {
+ optout = cfg_obj_asboolean(obj);
+ }
+
+ /* Salt */
+ obj = cfg_tuple_get(config, "salt-length");
+ if (cfg_obj_isuint32(obj)) {
+ saltlen = cfg_obj_asuint32(obj);
+ }
+ if (saltlen > 0xff) {
+ cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
+ "dnssec-policy: nsec3 salt length %u too high",
+ saltlen);
+ return (DNS_R_NSEC3SALTRANGE);
+ }
+
+ dns_kasp_setnsec3param(kasp, iter, optout, saltlen);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp,
+ isc_mem_t *mctx, isc_log_t *logctx,
+ dns_kasplist_t *kasplist, dns_kasp_t **kaspp) {
+ isc_result_t result;
+ const cfg_obj_t *maps[2];
+ const cfg_obj_t *koptions = NULL;
+ const cfg_obj_t *keys = NULL;
+ const cfg_obj_t *nsec3 = NULL;
+ const cfg_listelt_t *element = NULL;
+ const char *kaspname = NULL;
+ dns_kasp_t *kasp = NULL;
+ size_t i = 0;
+ uint32_t sigrefresh = 0, sigvalidity = 0;
+ uint32_t dnskeyttl = 0, dsttl = 0, maxttl = 0;
+ uint32_t publishsafety = 0, retiresafety = 0;
+ uint32_t zonepropdelay = 0, parentpropdelay = 0;
+ uint32_t ipub = 0, iret = 0;
+ uint32_t ksk_min_lifetime = 0, zsk_min_lifetime = 0;
+
+ REQUIRE(config != NULL);
+ REQUIRE(kaspp != NULL && *kaspp == NULL);
+
+ kaspname = cfg_obj_asstring(cfg_tuple_get(config, "name"));
+ INSIST(kaspname != NULL);
+
+ cfg_obj_log(config, logctx, ISC_LOG_DEBUG(1),
+ "dnssec-policy: load policy '%s'", kaspname);
+
+ result = dns_kasplist_find(kasplist, kaspname, &kasp);
+
+ if (result == ISC_R_SUCCESS) {
+ cfg_obj_log(
+ config, logctx, ISC_LOG_ERROR,
+ "dnssec-policy: duplicately named policy found '%s'",
+ kaspname);
+ dns_kasp_detach(&kasp);
+ return (ISC_R_EXISTS);
+ }
+ if (result != ISC_R_NOTFOUND) {
+ return (result);
+ }
+
+ /* No kasp with configured name was found in list, create new one. */
+ INSIST(kasp == NULL);
+ result = dns_kasp_create(mctx, kaspname, &kasp);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ INSIST(kasp != NULL);
+
+ /* Now configure. */
+ INSIST(DNS_KASP_VALID(kasp));
+
+ if (config != NULL) {
+ koptions = cfg_tuple_get(config, "options");
+ maps[i++] = koptions;
+ }
+ maps[i] = NULL;
+
+ /* Configuration: Signatures */
+ sigrefresh = get_duration(maps, "signatures-refresh",
+ DNS_KASP_SIG_REFRESH);
+ dns_kasp_setsigrefresh(kasp, sigrefresh);
+
+ sigvalidity = get_duration(maps, "signatures-validity-dnskey",
+ DNS_KASP_SIG_VALIDITY_DNSKEY);
+ if (sigrefresh >= (sigvalidity * 0.9)) {
+ cfg_obj_log(
+ config, logctx, ISC_LOG_ERROR,
+ "dnssec-policy: policy '%s' signatures-refresh must be "
+ "at most 90%% of the signatures-validity-dnskey",
+ kaspname);
+ result = ISC_R_FAILURE;
+ }
+ dns_kasp_setsigvalidity_dnskey(kasp, sigvalidity);
+
+ sigvalidity = get_duration(maps, "signatures-validity",
+ DNS_KASP_SIG_VALIDITY);
+ if (sigrefresh >= (sigvalidity * 0.9)) {
+ cfg_obj_log(
+ config, logctx, ISC_LOG_ERROR,
+ "dnssec-policy: policy '%s' signatures-refresh must be "
+ "at most 90%% of the signatures-validity",
+ kaspname);
+ result = ISC_R_FAILURE;
+ }
+ dns_kasp_setsigvalidity(kasp, sigvalidity);
+
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ /* Configuration: Zone settings */
+ maxttl = get_duration(maps, "max-zone-ttl", DNS_KASP_ZONE_MAXTTL);
+ dns_kasp_setzonemaxttl(kasp, maxttl);
+
+ zonepropdelay = get_duration(maps, "zone-propagation-delay",
+ DNS_KASP_ZONE_PROPDELAY);
+ dns_kasp_setzonepropagationdelay(kasp, zonepropdelay);
+
+ /* Configuration: Parent settings */
+ dsttl = get_duration(maps, "parent-ds-ttl", DNS_KASP_DS_TTL);
+ dns_kasp_setdsttl(kasp, dsttl);
+
+ parentpropdelay = get_duration(maps, "parent-propagation-delay",
+ DNS_KASP_PARENT_PROPDELAY);
+ dns_kasp_setparentpropagationdelay(kasp, parentpropdelay);
+
+ /* Configuration: Keys */
+ dnskeyttl = get_duration(maps, "dnskey-ttl", DNS_KASP_KEY_TTL);
+ dns_kasp_setdnskeyttl(kasp, dnskeyttl);
+
+ publishsafety = get_duration(maps, "publish-safety",
+ DNS_KASP_PUBLISH_SAFETY);
+ dns_kasp_setpublishsafety(kasp, publishsafety);
+
+ retiresafety = get_duration(maps, "retire-safety",
+ DNS_KASP_RETIRE_SAFETY);
+ dns_kasp_setretiresafety(kasp, retiresafety);
+
+ dns_kasp_setpurgekeys(
+ kasp, get_duration(maps, "purge-keys", DNS_KASP_PURGE_KEYS));
+
+ ipub = dnskeyttl + publishsafety + zonepropdelay;
+ iret = dsttl + retiresafety + parentpropdelay;
+ ksk_min_lifetime = ISC_MAX(ipub, iret);
+
+ iret = (sigvalidity - sigrefresh) + maxttl + retiresafety +
+ zonepropdelay;
+ zsk_min_lifetime = ISC_MAX(ipub, iret);
+
+ (void)confget(maps, "keys", &keys);
+ if (keys != NULL) {
+ char role[256] = { 0 };
+ bool warn[256][2] = { { false } };
+ dns_kasp_key_t *kkey = NULL;
+
+ for (element = cfg_list_first(keys); element != NULL;
+ element = cfg_list_next(element))
+ {
+ cfg_obj_t *kobj = cfg_listelt_value(element);
+ result = cfg_kaspkey_fromconfig(kobj, kasp, logctx,
+ ksk_min_lifetime,
+ zsk_min_lifetime);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ }
+ dns_kasp_freeze(kasp);
+ for (kkey = ISC_LIST_HEAD(dns_kasp_keys(kasp)); kkey != NULL;
+ kkey = ISC_LIST_NEXT(kkey, link))
+ {
+ uint32_t keyalg = dns_kasp_key_algorithm(kkey);
+ INSIST(keyalg < ARRAY_SIZE(role));
+
+ if (dns_kasp_key_zsk(kkey)) {
+ if ((role[keyalg] & DNS_KASP_KEY_ROLE_ZSK) != 0)
+ {
+ warn[keyalg][0] = true;
+ }
+ role[keyalg] |= DNS_KASP_KEY_ROLE_ZSK;
+ }
+
+ if (dns_kasp_key_ksk(kkey)) {
+ if ((role[keyalg] & DNS_KASP_KEY_ROLE_KSK) != 0)
+ {
+ warn[keyalg][1] = true;
+ }
+ role[keyalg] |= DNS_KASP_KEY_ROLE_KSK;
+ }
+ }
+ dns_kasp_thaw(kasp);
+ for (i = 0; i < ARRAY_SIZE(role); i++) {
+ if (role[i] == 0) {
+ continue;
+ }
+ if (role[i] !=
+ (DNS_KASP_KEY_ROLE_ZSK | DNS_KASP_KEY_ROLE_KSK))
+ {
+ cfg_obj_log(keys, logctx, ISC_LOG_ERROR,
+ "dnssec-policy: algorithm %zu "
+ "requires both KSK and ZSK roles",
+ i);
+ result = ISC_R_FAILURE;
+ }
+ if (warn[i][0]) {
+ cfg_obj_log(keys, logctx, ISC_LOG_WARNING,
+ "dnssec-policy: algorithm %zu has "
+ "multiple keys with ZSK role",
+ i);
+ }
+ if (warn[i][1]) {
+ cfg_obj_log(keys, logctx, ISC_LOG_WARNING,
+ "dnssec-policy: algorithm %zu has "
+ "multiple keys with KSK role",
+ i);
+ }
+ }
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ } else if (default_kasp) {
+ dns_kasp_key_t *key, *new_key;
+ /*
+ * If there are no specific keys configured in the policy,
+ * inherit from the default policy (except for the built-in
+ * "insecure" policy).
+ */
+ for (key = ISC_LIST_HEAD(dns_kasp_keys(default_kasp));
+ key != NULL; key = ISC_LIST_NEXT(key, link))
+ {
+ /* Create a new key reference. */
+ new_key = NULL;
+ result = dns_kasp_key_create(kasp, &new_key);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ if (dns_kasp_key_ksk(key)) {
+ new_key->role |= DNS_KASP_KEY_ROLE_KSK;
+ }
+ if (dns_kasp_key_zsk(key)) {
+ new_key->role |= DNS_KASP_KEY_ROLE_ZSK;
+ }
+ new_key->lifetime = dns_kasp_key_lifetime(key);
+ new_key->algorithm = dns_kasp_key_algorithm(key);
+ new_key->length = dns_kasp_key_size(key);
+ dns_kasp_addkey(kasp, new_key);
+ }
+ }
+
+ if (strcmp(kaspname, "insecure") == 0) {
+ /* "dnssec-policy insecure": key list must be empty */
+ INSIST(dns_kasp_keylist_empty(kasp));
+ } else if (default_kasp != NULL) {
+ /* There must be keys configured. */
+ INSIST(!(dns_kasp_keylist_empty(kasp)));
+ }
+
+ /* Configuration: NSEC3 */
+ (void)confget(maps, "nsec3param", &nsec3);
+ if (nsec3 == NULL) {
+ if (default_kasp != NULL && dns_kasp_nsec3(default_kasp)) {
+ dns_kasp_setnsec3param(
+ kasp, dns_kasp_nsec3iter(default_kasp),
+ (dns_kasp_nsec3flags(default_kasp) == 0x01),
+ dns_kasp_nsec3saltlen(default_kasp));
+ } else {
+ dns_kasp_setnsec3(kasp, false);
+ }
+ } else {
+ dns_kasp_setnsec3(kasp, true);
+ result = cfg_nsec3param_fromconfig(nsec3, kasp, logctx);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ }
+
+ /* Append it to the list for future lookups. */
+ ISC_LIST_APPEND(*kasplist, kasp, link);
+ INSIST(!(ISC_LIST_EMPTY(*kasplist)));
+
+ /* Success: Attach the kasp to the pointer and return. */
+ dns_kasp_attach(kasp, kaspp);
+
+ /* Don't detach as kasp is on '*kasplist' */
+ return (ISC_R_SUCCESS);
+
+cleanup:
+
+ /* Something bad happened, detach (destroys kasp) and return error. */
+ dns_kasp_detach(&kasp);
+ return (result);
+}
diff --git a/lib/isccfg/log.c b/lib/isccfg/log.c
new file mode 100644
index 0000000..22979ef
--- /dev/null
+++ b/lib/isccfg/log.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <isc/util.h>
+
+#include <isccfg/log.h>
+
+/*%
+ * When adding a new category, be sure to add the appropriate
+ * \#define to <isccfg/log.h>.
+ */
+isc_logcategory_t cfg_categories[] = { { "config", 0 }, { NULL, 0 } };
+
+/*%
+ * When adding a new module, be sure to add the appropriate
+ * \#define to <isccfg/log.h>.
+ */
+isc_logmodule_t cfg_modules[] = { { "isccfg/parser", 0 }, { NULL, 0 } };
+
+void
+cfg_log_init(isc_log_t *lctx) {
+ REQUIRE(lctx != NULL);
+
+ isc_log_registercategories(lctx, cfg_categories);
+ isc_log_registermodules(lctx, cfg_modules);
+}
diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c
new file mode 100644
index 0000000..4e4c098
--- /dev/null
+++ b/lib/isccfg/namedconf.c
@@ -0,0 +1,3998 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/lex.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/ttl.h>
+
+#include <isccfg/cfg.h>
+#include <isccfg/grammar.h>
+#include <isccfg/log.h>
+#include <isccfg/namedconf.h>
+
+#define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)
+
+/*% Check a return value. */
+#define CHECK(op) \
+ do { \
+ result = (op); \
+ if (result != ISC_R_SUCCESS) \
+ goto cleanup; \
+ } while (0)
+
+/*% Clean up a configuration object if non-NULL. */
+#define CLEANUP_OBJ(obj) \
+ do { \
+ if ((obj) != NULL) \
+ cfg_obj_destroy(pctx, &(obj)); \
+ } while (0)
+
+/*%
+ * Forward declarations of static functions.
+ */
+
+static isc_result_t
+parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
+
+static isc_result_t
+parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret);
+
+static isc_result_t
+parse_updatepolicy(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
+static void
+print_updatepolicy(cfg_printer_t *pctx, const cfg_obj_t *obj);
+
+static void
+doc_updatepolicy(cfg_printer_t *pctx, const cfg_type_t *type);
+
+static void
+print_keyvalue(cfg_printer_t *pctx, const cfg_obj_t *obj);
+
+static void
+doc_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type);
+
+static void
+doc_optional_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type);
+
+static isc_result_t
+cfg_parse_kv_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
+
+static void
+cfg_print_kv_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj);
+
+static void
+cfg_doc_kv_tuple(cfg_printer_t *pctx, const cfg_type_t *type);
+
+static cfg_type_t cfg_type_acl;
+static cfg_type_t cfg_type_bracketed_dscpsockaddrlist;
+static cfg_type_t cfg_type_bracketed_namesockaddrkeylist;
+static cfg_type_t cfg_type_bracketed_netaddrlist;
+static cfg_type_t cfg_type_bracketed_sockaddrnameportlist;
+static cfg_type_t cfg_type_bracketed_http_endpoint_list;
+static cfg_type_t cfg_type_controls;
+static cfg_type_t cfg_type_controls_sockaddr;
+static cfg_type_t cfg_type_destinationlist;
+static cfg_type_t cfg_type_dialuptype;
+static cfg_type_t cfg_type_dlz;
+static cfg_type_t cfg_type_dnssecpolicy;
+static cfg_type_t cfg_type_dnstap;
+static cfg_type_t cfg_type_dnstapoutput;
+static cfg_type_t cfg_type_dyndb;
+static cfg_type_t cfg_type_http_description;
+static cfg_type_t cfg_type_ixfrdifftype;
+static cfg_type_t cfg_type_ixfrratio;
+static cfg_type_t cfg_type_key;
+static cfg_type_t cfg_type_logfile;
+static cfg_type_t cfg_type_logging;
+static cfg_type_t cfg_type_logseverity;
+static cfg_type_t cfg_type_logsuffix;
+static cfg_type_t cfg_type_logversions;
+static cfg_type_t cfg_type_remoteselement;
+static cfg_type_t cfg_type_maxduration;
+static cfg_type_t cfg_type_minimal;
+static cfg_type_t cfg_type_nameportiplist;
+static cfg_type_t cfg_type_notifytype;
+static cfg_type_t cfg_type_optional_allow;
+static cfg_type_t cfg_type_optional_class;
+static cfg_type_t cfg_type_optional_dscp;
+static cfg_type_t cfg_type_optional_facility;
+static cfg_type_t cfg_type_optional_keyref;
+static cfg_type_t cfg_type_optional_port;
+static cfg_type_t cfg_type_optional_uint32;
+static cfg_type_t cfg_type_optional_tls;
+static cfg_type_t cfg_type_options;
+static cfg_type_t cfg_type_plugin;
+static cfg_type_t cfg_type_portiplist;
+static cfg_type_t cfg_type_printtime;
+static cfg_type_t cfg_type_qminmethod;
+static cfg_type_t cfg_type_querysource4;
+static cfg_type_t cfg_type_querysource6;
+static cfg_type_t cfg_type_querysource;
+static cfg_type_t cfg_type_server;
+static cfg_type_t cfg_type_server_key_kludge;
+static cfg_type_t cfg_type_size;
+static cfg_type_t cfg_type_sizenodefault;
+static cfg_type_t cfg_type_sizeorpercent;
+static cfg_type_t cfg_type_sizeval;
+static cfg_type_t cfg_type_sockaddr4wild;
+static cfg_type_t cfg_type_sockaddr6wild;
+static cfg_type_t cfg_type_statschannels;
+static cfg_type_t cfg_type_tlsconf;
+static cfg_type_t cfg_type_view;
+static cfg_type_t cfg_type_viewopts;
+static cfg_type_t cfg_type_zone;
+
+/*% tkey-dhkey */
+
+static cfg_tuplefielddef_t tkey_dhkey_fields[] = {
+ { "name", &cfg_type_qstring, 0 },
+ { "keyid", &cfg_type_uint32, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_tkey_dhkey = { "tkey-dhkey", cfg_parse_tuple,
+ cfg_print_tuple, cfg_doc_tuple,
+ &cfg_rep_tuple, tkey_dhkey_fields };
+
+/*% listen-on */
+
+static cfg_tuplefielddef_t listenon_tuple_fields[] = {
+ { "port", &cfg_type_optional_port, 0 },
+ { "dscp", &cfg_type_uint32,
+ CFG_CLAUSEFLAG_OBSOLETE | CFG_CLAUSEFLAG_NODOC },
+ { "tls", &cfg_type_astring, 0 },
+#if HAVE_LIBNGHTTP2
+ { "http", &cfg_type_astring, 0 },
+#else
+ { "http", &cfg_type_astring, CFG_CLAUSEFLAG_NOTCONFIGURED },
+#endif
+ { NULL, NULL, 0 }
+};
+static cfg_type_t cfg_type_listen_tuple = {
+ "listenon tuple", cfg_parse_kv_tuple, cfg_print_kv_tuple,
+ cfg_doc_kv_tuple, &cfg_rep_tuple, listenon_tuple_fields
+};
+
+static cfg_tuplefielddef_t listenon_fields[] = {
+ { "tuple", &cfg_type_listen_tuple, 0 },
+ { "acl", &cfg_type_bracketed_aml, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_listenon = { "listenon", cfg_parse_tuple,
+ cfg_print_tuple, cfg_doc_tuple,
+ &cfg_rep_tuple, listenon_fields };
+
+/*% acl */
+
+/*
+ * Encrypted transfer related definitions
+ */
+
+static cfg_tuplefielddef_t cfg_transport_acl_tuple_fields[] = {
+ { "port", &cfg_type_optional_port, 0 },
+ { "transport", &cfg_type_astring, 0 },
+ { NULL, NULL, 0 }
+};
+static cfg_type_t cfg_transport_acl_tuple = {
+ "transport-acl tuple", cfg_parse_kv_tuple,
+ cfg_print_kv_tuple, cfg_doc_kv_tuple,
+ &cfg_rep_tuple, cfg_transport_acl_tuple_fields
+};
+
+static cfg_tuplefielddef_t cfg_transport_acl_fields[] = {
+ { "port-transport", &cfg_transport_acl_tuple, 0 },
+ { "aml", &cfg_type_bracketed_aml, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_transport_acl = {
+ "transport-acl", cfg_parse_tuple, cfg_print_tuple,
+ cfg_doc_tuple, &cfg_rep_tuple, cfg_transport_acl_fields
+};
+
+/*
+ * NOTE: To enable syntax which allows specifying port and protocol,
+ * replace 'cfg_type_bracketed_aml' with
+ * 'cfg_type_transport_acl'.
+ *
+ * Example: acl port 853 protocol tls { ... };
+ */
+static cfg_tuplefielddef_t acl_fields[] = { { "name", &cfg_type_astring, 0 },
+ { "value", &cfg_type_bracketed_aml,
+ 0 },
+ { NULL, NULL, 0 } };
+
+static cfg_type_t cfg_type_acl = { "acl", cfg_parse_tuple,
+ cfg_print_tuple, cfg_doc_tuple,
+ &cfg_rep_tuple, acl_fields };
+
+/*% remote servers, used for primaries and parental agents */
+static cfg_tuplefielddef_t remotes_fields[] = {
+ { "name", &cfg_type_astring, 0 },
+ { "port", &cfg_type_optional_port, 0 },
+ { "dscp", &cfg_type_optional_dscp, CFG_CLAUSEFLAG_OBSOLETE },
+ { "addresses", &cfg_type_bracketed_namesockaddrkeylist, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_remoteservers = { "remote-servers", cfg_parse_tuple,
+ cfg_print_tuple, cfg_doc_tuple,
+ &cfg_rep_tuple, remotes_fields };
+
+/*%
+ * "sockaddrkeylist", a list of socket addresses with optional keys
+ * and an optional default port, as used in the remote-servers option.
+ * E.g.,
+ * "port 1234 { myservers; 10.0.0.1 key foo; 1::2 port 69; }"
+ */
+
+static cfg_tuplefielddef_t namesockaddrkey_fields[] = {
+ { "remoteselement", &cfg_type_remoteselement, 0 },
+ { "key", &cfg_type_optional_keyref, 0 },
+ { "tls", &cfg_type_optional_tls, 0 },
+ { NULL, NULL, 0 },
+};
+
+static cfg_type_t cfg_type_namesockaddrkey = {
+ "namesockaddrkey", cfg_parse_tuple, cfg_print_tuple,
+ cfg_doc_tuple, &cfg_rep_tuple, namesockaddrkey_fields
+};
+
+static cfg_type_t cfg_type_bracketed_namesockaddrkeylist = {
+ "bracketed_namesockaddrkeylist",
+ cfg_parse_bracketed_list,
+ cfg_print_bracketed_list,
+ cfg_doc_bracketed_list,
+ &cfg_rep_list,
+ &cfg_type_namesockaddrkey
+};
+
+static cfg_tuplefielddef_t namesockaddrkeylist_fields[] = {
+ { "port", &cfg_type_optional_port, 0 },
+ { "dscp", &cfg_type_optional_dscp, CFG_CLAUSEFLAG_OBSOLETE },
+ { "addresses", &cfg_type_bracketed_namesockaddrkeylist, 0 },
+ { NULL, NULL, 0 }
+};
+static cfg_type_t cfg_type_namesockaddrkeylist = {
+ "sockaddrkeylist", cfg_parse_tuple, cfg_print_tuple,
+ cfg_doc_tuple, &cfg_rep_tuple, namesockaddrkeylist_fields
+};
+
+/*%
+ * A list of socket addresses with an optional default port, as used
+ * in the 'listen-on' option. E.g., "{ 10.0.0.1; 1::2 port 69; }"
+ */
+static cfg_tuplefielddef_t portiplist_fields[] = {
+ { "port", &cfg_type_optional_port, 0 },
+ { "dscp", &cfg_type_optional_dscp, CFG_CLAUSEFLAG_OBSOLETE },
+ { "addresses", &cfg_type_bracketed_dscpsockaddrlist, 0 },
+ { NULL, NULL, 0 }
+};
+static cfg_type_t cfg_type_portiplist = { "portiplist", cfg_parse_tuple,
+ cfg_print_tuple, cfg_doc_tuple,
+ &cfg_rep_tuple, portiplist_fields };
+
+/*%
+ * A list of RR types, used in grant statements.
+ * Note that the old parser allows quotes around the RR type names.
+ */
+static cfg_type_t cfg_type_rrtypelist = {
+ "rrtypelist", cfg_parse_spacelist, cfg_print_spacelist,
+ cfg_doc_terminal, &cfg_rep_list, &cfg_type_astring
+};
+
+static const char *mode_enums[] = { "deny", "grant", NULL };
+static cfg_type_t cfg_type_mode = {
+ "mode", cfg_parse_enum, cfg_print_ustring,
+ cfg_doc_enum, &cfg_rep_string, &mode_enums
+};
+
+static isc_result_t
+parse_matchtype(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+
+ CHECK(cfg_peektoken(pctx, 0));
+ if (pctx->token.type == isc_tokentype_string &&
+ strcasecmp(TOKEN_STRING(pctx), "zonesub") == 0)
+ {
+ pctx->flags |= CFG_PCTX_SKIP;
+ }
+ return (cfg_parse_enum(pctx, type, ret));
+
+cleanup:
+ return (result);
+}
+
+static isc_result_t
+parse_matchname(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+ cfg_obj_t *obj = NULL;
+
+ if ((pctx->flags & CFG_PCTX_SKIP) != 0) {
+ pctx->flags &= ~CFG_PCTX_SKIP;
+ CHECK(cfg_parse_void(pctx, NULL, &obj));
+ } else {
+ result = cfg_parse_astring(pctx, type, &obj);
+ }
+
+ *ret = obj;
+cleanup:
+ return (result);
+}
+
+static void
+doc_matchname(cfg_printer_t *pctx, const cfg_type_t *type) {
+ cfg_print_cstr(pctx, "[ ");
+ cfg_doc_obj(pctx, type->of);
+ cfg_print_cstr(pctx, " ]");
+}
+
+static const char *matchtype_enums[] = { "6to4-self",
+ "external",
+ "krb5-self",
+ "krb5-selfsub",
+ "krb5-subdomain",
+ "krb5-subdomain-self-rhs",
+ "ms-self",
+ "ms-selfsub",
+ "ms-subdomain",
+ "ms-subdomain-self-rhs",
+ "name",
+ "self",
+ "selfsub",
+ "selfwild",
+ "subdomain",
+ "tcp-self",
+ "wildcard",
+ "zonesub",
+ NULL };
+
+static cfg_type_t cfg_type_matchtype = { "matchtype", parse_matchtype,
+ cfg_print_ustring, cfg_doc_enum,
+ &cfg_rep_string, &matchtype_enums };
+
+static cfg_type_t cfg_type_matchname = {
+ "optional_matchname", parse_matchname, cfg_print_ustring,
+ doc_matchname, &cfg_rep_tuple, &cfg_type_ustring
+};
+
+/*%
+ * A grant statement, used in the update policy.
+ */
+static cfg_tuplefielddef_t grant_fields[] = {
+ { "mode", &cfg_type_mode, 0 },
+ { "identity", &cfg_type_astring, 0 }, /* domain name */
+ { "matchtype", &cfg_type_matchtype, 0 },
+ { "name", &cfg_type_matchname, 0 }, /* domain name */
+ { "types", &cfg_type_rrtypelist, 0 },
+ { NULL, NULL, 0 }
+};
+static cfg_type_t cfg_type_grant = { "grant", cfg_parse_tuple,
+ cfg_print_tuple, cfg_doc_tuple,
+ &cfg_rep_tuple, grant_fields };
+
+static cfg_type_t cfg_type_updatepolicy = {
+ "update_policy", parse_updatepolicy, print_updatepolicy,
+ doc_updatepolicy, &cfg_rep_list, &cfg_type_grant
+};
+
+static isc_result_t
+parse_updatepolicy(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ isc_result_t result;
+ CHECK(cfg_gettoken(pctx, 0));
+ if (pctx->token.type == isc_tokentype_special &&
+ pctx->token.value.as_char == '{')
+ {
+ cfg_ungettoken(pctx);
+ return (cfg_parse_bracketed_list(pctx, type, ret));
+ }
+
+ if (pctx->token.type == isc_tokentype_string &&
+ strcasecmp(TOKEN_STRING(pctx), "local") == 0)
+ {
+ cfg_obj_t *obj = NULL;
+ CHECK(cfg_create_obj(pctx, &cfg_type_ustring, &obj));
+ obj->value.string.length = strlen("local");
+ obj->value.string.base =
+ isc_mem_get(pctx->mctx, obj->value.string.length + 1);
+ memmove(obj->value.string.base, "local", 5);
+ obj->value.string.base[5] = '\0';
+ *ret = obj;
+ return (ISC_R_SUCCESS);
+ }
+
+ cfg_ungettoken(pctx);
+ return (ISC_R_UNEXPECTEDTOKEN);
+
+cleanup:
+ return (result);
+}
+
+static void
+print_updatepolicy(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ if (cfg_obj_isstring(obj)) {
+ cfg_print_ustring(pctx, obj);
+ } else {
+ cfg_print_bracketed_list(pctx, obj);
+ }
+}
+
+static void
+doc_updatepolicy(cfg_printer_t *pctx, const cfg_type_t *type) {
+ cfg_print_cstr(pctx, "( local | { ");
+ cfg_doc_obj(pctx, type->of);
+ cfg_print_cstr(pctx, "; ... } )");
+}
+
+/*%
+ * A view statement.
+ */
+static cfg_tuplefielddef_t view_fields[] = {
+ { "name", &cfg_type_astring, 0 },
+ { "class", &cfg_type_optional_class, 0 },
+ { "options", &cfg_type_viewopts, 0 },
+ { NULL, NULL, 0 }
+};
+static cfg_type_t cfg_type_view = { "view", cfg_parse_tuple,
+ cfg_print_tuple, cfg_doc_tuple,
+ &cfg_rep_tuple, view_fields };
+
+/*%
+ * A zone statement.
+ */
+static cfg_tuplefielddef_t zone_fields[] = {
+ { "name", &cfg_type_astring, 0 },
+ { "class", &cfg_type_optional_class, 0 },
+ { "options", &cfg_type_zoneopts, 0 },
+ { NULL, NULL, 0 }
+};
+static cfg_type_t cfg_type_zone = { "zone", cfg_parse_tuple,
+ cfg_print_tuple, cfg_doc_tuple,
+ &cfg_rep_tuple, zone_fields };
+
+/*%
+ * A dnssec-policy statement.
+ */
+static cfg_tuplefielddef_t dnssecpolicy_fields[] = {
+ { "name", &cfg_type_astring, 0 },
+ { "options", &cfg_type_dnssecpolicyopts, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_dnssecpolicy = {
+ "dnssec-policy", cfg_parse_tuple, cfg_print_tuple,
+ cfg_doc_tuple, &cfg_rep_tuple, dnssecpolicy_fields
+};
+
+/*%
+ * A "category" clause in the "logging" statement.
+ */
+static cfg_tuplefielddef_t category_fields[] = {
+ { "name", &cfg_type_astring, 0 },
+ { "destinations", &cfg_type_destinationlist, 0 },
+ { NULL, NULL, 0 }
+};
+static cfg_type_t cfg_type_category = { "category", cfg_parse_tuple,
+ cfg_print_tuple, cfg_doc_tuple,
+ &cfg_rep_tuple, category_fields };
+
+static isc_result_t
+parse_maxduration(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ return (cfg_parse_enum_or_other(pctx, type, &cfg_type_duration, ret));
+}
+
+static void
+doc_maxduration(cfg_printer_t *pctx, const cfg_type_t *type) {
+ cfg_doc_enum_or_other(pctx, type, &cfg_type_duration);
+}
+
+/*%
+ * A duration or "unlimited", but not "default".
+ */
+static const char *maxduration_enums[] = { "unlimited", NULL };
+static cfg_type_t cfg_type_maxduration = {
+ "maxduration_no_default", parse_maxduration, cfg_print_ustring,
+ doc_maxduration, &cfg_rep_duration, maxduration_enums
+};
+
+/*%
+ * A dnssec key, as used in the "trusted-keys" statement.
+ */
+static cfg_tuplefielddef_t dnsseckey_fields[] = {
+ { "name", &cfg_type_astring, 0 },
+ { "anchortype", &cfg_type_void, 0 },
+ { "rdata1", &cfg_type_uint32, 0 },
+ { "rdata2", &cfg_type_uint32, 0 },
+ { "rdata3", &cfg_type_uint32, 0 },
+ { "data", &cfg_type_qstring, 0 },
+ { NULL, NULL, 0 }
+};
+static cfg_type_t cfg_type_dnsseckey = { "dnsseckey", cfg_parse_tuple,
+ cfg_print_tuple, cfg_doc_tuple,
+ &cfg_rep_tuple, dnsseckey_fields };
+
+/*%
+ * Optional enums.
+ *
+ */
+static isc_result_t
+parse_optional_enum(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ return (cfg_parse_enum_or_other(pctx, type, &cfg_type_void, ret));
+}
+
+static void
+doc_optional_enum(cfg_printer_t *pctx, const cfg_type_t *type) {
+ UNUSED(type);
+ cfg_print_cstr(pctx, "[ ");
+ cfg_doc_enum(pctx, type);
+ cfg_print_cstr(pctx, " ]");
+}
+
+/*%
+ * A key initialization specifier, as used in the
+ * "trust-anchors" (or synonymous "managed-keys") statement.
+ */
+static const char *anchortype_enums[] = { "static-key", "initial-key",
+ "static-ds", "initial-ds", NULL };
+static cfg_type_t cfg_type_anchortype = { "anchortype", cfg_parse_enum,
+ cfg_print_ustring, cfg_doc_enum,
+ &cfg_rep_string, anchortype_enums };
+static cfg_tuplefielddef_t managedkey_fields[] = {
+ { "name", &cfg_type_astring, 0 },
+ { "anchortype", &cfg_type_anchortype, 0 },
+ { "rdata1", &cfg_type_uint32, 0 },
+ { "rdata2", &cfg_type_uint32, 0 },
+ { "rdata3", &cfg_type_uint32, 0 },
+ { "data", &cfg_type_qstring, 0 },
+ { NULL, NULL, 0 }
+};
+static cfg_type_t cfg_type_managedkey = { "managedkey", cfg_parse_tuple,
+ cfg_print_tuple, cfg_doc_tuple,
+ &cfg_rep_tuple, managedkey_fields };
+
+/*%
+ * DNSSEC key roles.
+ */
+static const char *dnsseckeyrole_enums[] = { "csk", "ksk", "zsk", NULL };
+static cfg_type_t cfg_type_dnsseckeyrole = {
+ "dnssec-key-role", cfg_parse_enum, cfg_print_ustring,
+ cfg_doc_enum, &cfg_rep_string, &dnsseckeyrole_enums
+};
+
+/*%
+ * DNSSEC key storage types.
+ */
+static const char *dnsseckeystore_enums[] = { "key-directory", NULL };
+static cfg_type_t cfg_type_dnsseckeystore = {
+ "dnssec-key-storage", parse_optional_enum, cfg_print_ustring,
+ doc_optional_enum, &cfg_rep_string, dnsseckeystore_enums
+};
+
+/*%
+ * A dnssec key, as used in the "keys" statement in a "dnssec-policy".
+ */
+static keyword_type_t algorithm_kw = { "algorithm", &cfg_type_ustring };
+static cfg_type_t cfg_type_algorithm = { "algorithm", parse_keyvalue,
+ print_keyvalue, doc_keyvalue,
+ &cfg_rep_string, &algorithm_kw };
+
+static keyword_type_t lifetime_kw = { "lifetime",
+ &cfg_type_duration_or_unlimited };
+static cfg_type_t cfg_type_lifetime = { "lifetime", parse_keyvalue,
+ print_keyvalue, doc_keyvalue,
+ &cfg_rep_duration, &lifetime_kw };
+
+static cfg_tuplefielddef_t kaspkey_fields[] = {
+ { "role", &cfg_type_dnsseckeyrole, 0 },
+ { "keystore-type", &cfg_type_dnsseckeystore, 0 },
+ { "lifetime", &cfg_type_lifetime, 0 },
+ { "algorithm", &cfg_type_algorithm, 0 },
+ { "length", &cfg_type_optional_uint32, 0 },
+ { NULL, NULL, 0 }
+};
+static cfg_type_t cfg_type_kaspkey = { "kaspkey", cfg_parse_tuple,
+ cfg_print_tuple, cfg_doc_tuple,
+ &cfg_rep_tuple, kaspkey_fields };
+
+/*%
+ * NSEC3 parameters.
+ */
+static keyword_type_t nsec3iter_kw = { "iterations", &cfg_type_uint32 };
+static cfg_type_t cfg_type_nsec3iter = {
+ "iterations", parse_optional_keyvalue, print_keyvalue,
+ doc_optional_keyvalue, &cfg_rep_uint32, &nsec3iter_kw
+};
+
+static keyword_type_t nsec3optout_kw = { "optout", &cfg_type_boolean };
+static cfg_type_t cfg_type_nsec3optout = {
+ "optout", parse_optional_keyvalue,
+ print_keyvalue, doc_optional_keyvalue,
+ &cfg_rep_boolean, &nsec3optout_kw
+};
+
+static keyword_type_t nsec3salt_kw = { "salt-length", &cfg_type_uint32 };
+static cfg_type_t cfg_type_nsec3salt = {
+ "salt-length", parse_optional_keyvalue, print_keyvalue,
+ doc_optional_keyvalue, &cfg_rep_uint32, &nsec3salt_kw
+};
+
+static cfg_tuplefielddef_t nsec3param_fields[] = {
+ { "iterations", &cfg_type_nsec3iter, 0 },
+ { "optout", &cfg_type_nsec3optout, 0 },
+ { "salt-length", &cfg_type_nsec3salt, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_nsec3 = { "nsec3param", cfg_parse_tuple,
+ cfg_print_tuple, cfg_doc_tuple,
+ &cfg_rep_tuple, nsec3param_fields };
+
+/*%
+ * Wild class, type, name.
+ */
+static keyword_type_t wild_class_kw = { "class", &cfg_type_ustring };
+
+static cfg_type_t cfg_type_optional_wild_class = {
+ "optional_wild_class", parse_optional_keyvalue, print_keyvalue,
+ doc_optional_keyvalue, &cfg_rep_string, &wild_class_kw
+};
+
+static keyword_type_t wild_type_kw = { "type", &cfg_type_ustring };
+
+static cfg_type_t cfg_type_optional_wild_type = {
+ "optional_wild_type", parse_optional_keyvalue, print_keyvalue,
+ doc_optional_keyvalue, &cfg_rep_string, &wild_type_kw
+};
+
+static keyword_type_t wild_name_kw = { "name", &cfg_type_qstring };
+
+static cfg_type_t cfg_type_optional_wild_name = {
+ "optional_wild_name", parse_optional_keyvalue, print_keyvalue,
+ doc_optional_keyvalue, &cfg_rep_string, &wild_name_kw
+};
+
+/*%
+ * An rrset ordering element.
+ */
+static cfg_tuplefielddef_t rrsetorderingelement_fields[] = {
+ { "class", &cfg_type_optional_wild_class, 0 },
+ { "type", &cfg_type_optional_wild_type, 0 },
+ { "name", &cfg_type_optional_wild_name, 0 },
+ { "order", &cfg_type_ustring, 0 }, /* must be literal "order" */
+ { "ordering", &cfg_type_ustring, 0 },
+ { NULL, NULL, 0 }
+};
+static cfg_type_t cfg_type_rrsetorderingelement = {
+ "rrsetorderingelement", cfg_parse_tuple, cfg_print_tuple,
+ cfg_doc_tuple, &cfg_rep_tuple, rrsetorderingelement_fields
+};
+
+/*%
+ * A global or view "check-names" option. Note that the zone
+ * "check-names" option has a different syntax.
+ */
+
+static const char *checktype_enums[] = { "primary", "master", "secondary",
+ "slave", "response", NULL };
+static cfg_type_t cfg_type_checktype = { "checktype", cfg_parse_enum,
+ cfg_print_ustring, cfg_doc_enum,
+ &cfg_rep_string, &checktype_enums };
+
+static const char *checkmode_enums[] = { "fail", "warn", "ignore", NULL };
+static cfg_type_t cfg_type_checkmode = { "checkmode", cfg_parse_enum,
+ cfg_print_ustring, cfg_doc_enum,
+ &cfg_rep_string, &checkmode_enums };
+
+static const char *warn_enums[] = { "warn", "ignore", NULL };
+static cfg_type_t cfg_type_warn = {
+ "warn", cfg_parse_enum, cfg_print_ustring,
+ cfg_doc_enum, &cfg_rep_string, &warn_enums
+};
+
+static cfg_tuplefielddef_t checknames_fields[] = {
+ { "type", &cfg_type_checktype, 0 },
+ { "mode", &cfg_type_checkmode, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_checknames = { "checknames", cfg_parse_tuple,
+ cfg_print_tuple, cfg_doc_tuple,
+ &cfg_rep_tuple, checknames_fields };
+
+static cfg_type_t cfg_type_bracketed_dscpsockaddrlist = {
+ "bracketed_sockaddrlist",
+ cfg_parse_bracketed_list,
+ cfg_print_bracketed_list,
+ cfg_doc_bracketed_list,
+ &cfg_rep_list,
+ &cfg_type_sockaddrdscp
+};
+
+static cfg_type_t cfg_type_bracketed_netaddrlist = { "bracketed_netaddrlist",
+ cfg_parse_bracketed_list,
+ cfg_print_bracketed_list,
+ cfg_doc_bracketed_list,
+ &cfg_rep_list,
+ &cfg_type_netaddr };
+
+static const char *autodnssec_enums[] = { "allow", "maintain", "off", NULL };
+static cfg_type_t cfg_type_autodnssec = {
+ "autodnssec", cfg_parse_enum, cfg_print_ustring,
+ cfg_doc_enum, &cfg_rep_string, &autodnssec_enums
+};
+
+static const char *dnssecupdatemode_enums[] = { "maintain", "no-resign", NULL };
+static cfg_type_t cfg_type_dnssecupdatemode = {
+ "dnssecupdatemode", cfg_parse_enum, cfg_print_ustring,
+ cfg_doc_enum, &cfg_rep_string, &dnssecupdatemode_enums
+};
+
+static const char *updatemethods_enums[] = { "date", "increment", "unixtime",
+ NULL };
+static cfg_type_t cfg_type_updatemethod = {
+ "updatemethod", cfg_parse_enum, cfg_print_ustring,
+ cfg_doc_enum, &cfg_rep_string, &updatemethods_enums
+};
+
+/*
+ * zone-statistics: full, terse, or none.
+ *
+ * for backward compatibility, we also support boolean values.
+ * yes represents "full", no represents "terse". in the future we
+ * may change no to mean "none".
+ */
+static const char *zonestat_enums[] = { "full", "terse", "none", NULL };
+static isc_result_t
+parse_zonestat(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ return (cfg_parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
+}
+static void
+doc_zonestat(cfg_printer_t *pctx, const cfg_type_t *type) {
+ cfg_doc_enum_or_other(pctx, type, &cfg_type_boolean);
+}
+static cfg_type_t cfg_type_zonestat = { "zonestat", parse_zonestat,
+ cfg_print_ustring, doc_zonestat,
+ &cfg_rep_string, zonestat_enums };
+
+static cfg_type_t cfg_type_rrsetorder = { "rrsetorder",
+ cfg_parse_bracketed_list,
+ cfg_print_bracketed_list,
+ cfg_doc_bracketed_list,
+ &cfg_rep_list,
+ &cfg_type_rrsetorderingelement };
+
+static keyword_type_t dscp_kw = { "dscp", &cfg_type_uint32 };
+
+static cfg_type_t cfg_type_optional_dscp = {
+ "optional_dscp", parse_optional_keyvalue, print_keyvalue,
+ cfg_doc_void, &cfg_rep_uint32, &dscp_kw
+};
+
+static keyword_type_t port_kw = { "port", &cfg_type_uint32 };
+
+static cfg_type_t cfg_type_optional_port = {
+ "optional_port", parse_optional_keyvalue, print_keyvalue,
+ doc_optional_keyvalue, &cfg_rep_uint32, &port_kw
+};
+
+/*% A list of keys, as in the "key" clause of the controls statement. */
+static cfg_type_t cfg_type_keylist = { "keylist",
+ cfg_parse_bracketed_list,
+ cfg_print_bracketed_list,
+ cfg_doc_bracketed_list,
+ &cfg_rep_list,
+ &cfg_type_astring };
+
+/*% A list of dnssec keys, as in "trusted-keys". Deprecated. */
+static cfg_type_t cfg_type_trustedkeys = { "trustedkeys",
+ cfg_parse_bracketed_list,
+ cfg_print_bracketed_list,
+ cfg_doc_bracketed_list,
+ &cfg_rep_list,
+ &cfg_type_dnsseckey };
+
+/*%
+ * A list of managed trust anchors. Each entry contains a name, a keyword
+ * ("static-key", initial-key", "static-ds" or "initial-ds"), and the
+ * fields associated with either a DNSKEY or a DS record.
+ */
+static cfg_type_t cfg_type_dnsseckeys = { "dnsseckeys",
+ cfg_parse_bracketed_list,
+ cfg_print_bracketed_list,
+ cfg_doc_bracketed_list,
+ &cfg_rep_list,
+ &cfg_type_managedkey };
+
+/*%
+ * A list of key entries, used in a DNSSEC Key and Signing Policy.
+ */
+static cfg_type_t cfg_type_kaspkeys = { "kaspkeys",
+ cfg_parse_bracketed_list,
+ cfg_print_bracketed_list,
+ cfg_doc_bracketed_list,
+ &cfg_rep_list,
+ &cfg_type_kaspkey };
+
+static const char *forwardtype_enums[] = { "first", "only", NULL };
+static cfg_type_t cfg_type_forwardtype = {
+ "forwardtype", cfg_parse_enum, cfg_print_ustring,
+ cfg_doc_enum, &cfg_rep_string, &forwardtype_enums
+};
+
+static const char *zonetype_enums[] = {
+ "primary", "master", "secondary", "slave",
+ "mirror", "delegation-only", "forward", "hint",
+ "redirect", "static-stub", "stub", NULL
+};
+static cfg_type_t cfg_type_zonetype = { "zonetype", cfg_parse_enum,
+ cfg_print_ustring, cfg_doc_enum,
+ &cfg_rep_string, &zonetype_enums };
+
+static const char *loglevel_enums[] = { "critical", "error", "warning",
+ "notice", "info", "dynamic",
+ NULL };
+static cfg_type_t cfg_type_loglevel = { "loglevel", cfg_parse_enum,
+ cfg_print_ustring, cfg_doc_enum,
+ &cfg_rep_string, &loglevel_enums };
+
+static const char *transferformat_enums[] = { "many-answers", "one-answer",
+ NULL };
+static cfg_type_t cfg_type_transferformat = {
+ "transferformat", cfg_parse_enum, cfg_print_ustring,
+ cfg_doc_enum, &cfg_rep_string, &transferformat_enums
+};
+
+/*%
+ * The special keyword "none", as used in the pid-file option.
+ */
+
+static void
+print_none(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ UNUSED(obj);
+ cfg_print_cstr(pctx, "none");
+}
+
+static cfg_type_t cfg_type_none = { "none", NULL, print_none,
+ NULL, &cfg_rep_void, NULL };
+
+/*%
+ * A quoted string or the special keyword "none". Used in the pid-file option.
+ */
+static isc_result_t
+parse_qstringornone(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ isc_result_t result;
+
+ CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
+ if (pctx->token.type == isc_tokentype_string &&
+ strcasecmp(TOKEN_STRING(pctx), "none") == 0)
+ {
+ return (cfg_create_obj(pctx, &cfg_type_none, ret));
+ }
+ cfg_ungettoken(pctx);
+ return (cfg_parse_qstring(pctx, type, ret));
+cleanup:
+ return (result);
+}
+
+static void
+doc_qstringornone(cfg_printer_t *pctx, const cfg_type_t *type) {
+ UNUSED(type);
+ cfg_print_cstr(pctx, "( <quoted_string> | none )");
+}
+
+static cfg_type_t cfg_type_qstringornone = { "qstringornone",
+ parse_qstringornone,
+ NULL,
+ doc_qstringornone,
+ NULL,
+ NULL };
+
+/*%
+ * A boolean ("yes" or "no"), or the special keyword "auto".
+ * Used in the dnssec-validation option.
+ */
+static void
+print_auto(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ UNUSED(obj);
+ cfg_print_cstr(pctx, "auto");
+}
+
+static cfg_type_t cfg_type_auto = { "auto", NULL, print_auto,
+ NULL, &cfg_rep_void, NULL };
+
+static isc_result_t
+parse_boolorauto(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+
+ CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
+ if (pctx->token.type == isc_tokentype_string &&
+ strcasecmp(TOKEN_STRING(pctx), "auto") == 0)
+ {
+ return (cfg_create_obj(pctx, &cfg_type_auto, ret));
+ }
+ cfg_ungettoken(pctx);
+ return (cfg_parse_boolean(pctx, type, ret));
+cleanup:
+ return (result);
+}
+
+static void
+print_boolorauto(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ if (obj->type->rep == &cfg_rep_void) {
+ cfg_print_cstr(pctx, "auto");
+ } else if (obj->value.boolean) {
+ cfg_print_cstr(pctx, "yes");
+ } else {
+ cfg_print_cstr(pctx, "no");
+ }
+}
+
+static void
+doc_boolorauto(cfg_printer_t *pctx, const cfg_type_t *type) {
+ UNUSED(type);
+ cfg_print_cstr(pctx, "( yes | no | auto )");
+}
+
+static cfg_type_t cfg_type_boolorauto = {
+ "boolorauto", parse_boolorauto, print_boolorauto, doc_boolorauto, NULL,
+ NULL
+};
+
+/*%
+ * keyword hostname
+ */
+static void
+print_hostname(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ UNUSED(obj);
+ cfg_print_cstr(pctx, "hostname");
+}
+
+static cfg_type_t cfg_type_hostname = { "hostname", NULL,
+ print_hostname, NULL,
+ &cfg_rep_boolean, NULL };
+
+/*%
+ * "server-id" argument.
+ */
+
+static isc_result_t
+parse_serverid(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+ CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
+ if (pctx->token.type == isc_tokentype_string &&
+ strcasecmp(TOKEN_STRING(pctx), "none") == 0)
+ {
+ return (cfg_create_obj(pctx, &cfg_type_none, ret));
+ }
+ if (pctx->token.type == isc_tokentype_string &&
+ strcasecmp(TOKEN_STRING(pctx), "hostname") == 0)
+ {
+ result = cfg_create_obj(pctx, &cfg_type_hostname, ret);
+ if (result == ISC_R_SUCCESS) {
+ (*ret)->value.boolean = true;
+ }
+ return (result);
+ }
+ cfg_ungettoken(pctx);
+ return (cfg_parse_qstring(pctx, type, ret));
+cleanup:
+ return (result);
+}
+
+static void
+doc_serverid(cfg_printer_t *pctx, const cfg_type_t *type) {
+ UNUSED(type);
+ cfg_print_cstr(pctx, "( <quoted_string> | none | hostname )");
+}
+
+static cfg_type_t cfg_type_serverid = { "serverid", parse_serverid, NULL,
+ doc_serverid, NULL, NULL };
+
+/*%
+ * Port list.
+ */
+static void
+print_porttuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ cfg_print_cstr(pctx, "range ");
+ cfg_print_tuple(pctx, obj);
+}
+static cfg_tuplefielddef_t porttuple_fields[] = {
+ { "loport", &cfg_type_uint32, 0 },
+ { "hiport", &cfg_type_uint32, 0 },
+ { NULL, NULL, 0 }
+};
+static cfg_type_t cfg_type_porttuple = { "porttuple", cfg_parse_tuple,
+ print_porttuple, cfg_doc_tuple,
+ &cfg_rep_tuple, porttuple_fields };
+
+static isc_result_t
+parse_port(cfg_parser_t *pctx, cfg_obj_t **ret) {
+ isc_result_t result;
+
+ CHECK(cfg_parse_uint32(pctx, NULL, ret));
+ if ((*ret)->value.uint32 > 0xffff) {
+ cfg_parser_error(pctx, CFG_LOG_NEAR, "invalid port");
+ cfg_obj_destroy(pctx, ret);
+ result = ISC_R_RANGE;
+ }
+
+cleanup:
+ return (result);
+}
+
+static isc_result_t
+parse_portrange(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+ cfg_obj_t *obj = NULL;
+
+ UNUSED(type);
+
+ CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
+ if (pctx->token.type == isc_tokentype_number) {
+ CHECK(parse_port(pctx, ret));
+ } else {
+ CHECK(cfg_gettoken(pctx, 0));
+ if (pctx->token.type != isc_tokentype_string ||
+ strcasecmp(TOKEN_STRING(pctx), "range") != 0)
+ {
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "expected integer or 'range'");
+ return (ISC_R_UNEXPECTEDTOKEN);
+ }
+ CHECK(cfg_create_tuple(pctx, &cfg_type_porttuple, &obj));
+ CHECK(parse_port(pctx, &obj->value.tuple[0]));
+ CHECK(parse_port(pctx, &obj->value.tuple[1]));
+ if (obj->value.tuple[0]->value.uint32 >
+ obj->value.tuple[1]->value.uint32)
+ {
+ cfg_parser_error(pctx, CFG_LOG_NOPREP,
+ "low port '%u' must not be larger "
+ "than high port",
+ obj->value.tuple[0]->value.uint32);
+ result = ISC_R_RANGE;
+ goto cleanup;
+ }
+ *ret = obj;
+ obj = NULL;
+ }
+
+cleanup:
+ if (obj != NULL) {
+ cfg_obj_destroy(pctx, &obj);
+ }
+ return (result);
+}
+
+static cfg_type_t cfg_type_portrange = { "portrange", parse_portrange,
+ NULL, cfg_doc_terminal,
+ NULL, NULL };
+
+static cfg_type_t cfg_type_bracketed_portlist = { "bracketed_sockaddrlist",
+ cfg_parse_bracketed_list,
+ cfg_print_bracketed_list,
+ cfg_doc_bracketed_list,
+ &cfg_rep_list,
+ &cfg_type_portrange };
+
+static const char *cookiealg_enums[] = { "aes", "siphash24", NULL };
+static cfg_type_t cfg_type_cookiealg = { "cookiealg", cfg_parse_enum,
+ cfg_print_ustring, cfg_doc_enum,
+ &cfg_rep_string, &cookiealg_enums };
+
+/*%
+ * fetch-quota-params
+ */
+
+static cfg_tuplefielddef_t fetchquota_fields[] = {
+ { "frequency", &cfg_type_uint32, 0 },
+ { "low", &cfg_type_fixedpoint, 0 },
+ { "high", &cfg_type_fixedpoint, 0 },
+ { "discount", &cfg_type_fixedpoint, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_fetchquota = { "fetchquota", cfg_parse_tuple,
+ cfg_print_tuple, cfg_doc_tuple,
+ &cfg_rep_tuple, fetchquota_fields };
+
+/*%
+ * fetches-per-server or fetches-per-zone
+ */
+
+static const char *response_enums[] = { "drop", "fail", NULL };
+
+static cfg_type_t cfg_type_responsetype = {
+ "responsetype", parse_optional_enum, cfg_print_ustring,
+ doc_optional_enum, &cfg_rep_string, response_enums
+};
+
+static cfg_tuplefielddef_t fetchesper_fields[] = {
+ { "fetches", &cfg_type_uint32, 0 },
+ { "response", &cfg_type_responsetype, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_fetchesper = { "fetchesper", cfg_parse_tuple,
+ cfg_print_tuple, cfg_doc_tuple,
+ &cfg_rep_tuple, fetchesper_fields };
+
+/*%
+ * Clauses that can be found within the top level of the named.conf
+ * file only.
+ */
+static cfg_clausedef_t namedconf_clauses[] = {
+ { "acl", &cfg_type_acl, CFG_CLAUSEFLAG_MULTI },
+ { "controls", &cfg_type_controls, CFG_CLAUSEFLAG_MULTI },
+ { "dnssec-policy", &cfg_type_dnssecpolicy, CFG_CLAUSEFLAG_MULTI },
+#if HAVE_LIBNGHTTP2
+ { "http", &cfg_type_http_description, CFG_CLAUSEFLAG_MULTI },
+#else
+ { "http", &cfg_type_http_description,
+ CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_NOTCONFIGURED },
+#endif
+ { "logging", &cfg_type_logging, 0 },
+ { "lwres", NULL, CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_ANCIENT },
+ { "masters", &cfg_type_remoteservers,
+ CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_NODOC },
+ { "options", &cfg_type_options, 0 },
+ { "parental-agents", &cfg_type_remoteservers, CFG_CLAUSEFLAG_MULTI },
+ { "primaries", &cfg_type_remoteservers, CFG_CLAUSEFLAG_MULTI },
+ { "statistics-channels", &cfg_type_statschannels,
+ CFG_CLAUSEFLAG_MULTI },
+ { "tls", &cfg_type_tlsconf, CFG_CLAUSEFLAG_MULTI },
+ { "view", &cfg_type_view, CFG_CLAUSEFLAG_MULTI },
+ { NULL, NULL, 0 }
+};
+
+/*%
+ * Clauses that can occur at the top level or in the view
+ * statement, but not in the options block.
+ */
+static cfg_clausedef_t namedconf_or_view_clauses[] = {
+ { "dlz", &cfg_type_dlz, CFG_CLAUSEFLAG_MULTI },
+ { "dyndb", &cfg_type_dyndb, CFG_CLAUSEFLAG_MULTI },
+ { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI },
+ { "managed-keys", &cfg_type_dnsseckeys,
+ CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_DEPRECATED },
+ { "plugin", &cfg_type_plugin, CFG_CLAUSEFLAG_MULTI },
+ { "server", &cfg_type_server, CFG_CLAUSEFLAG_MULTI },
+ { "trust-anchors", &cfg_type_dnsseckeys, CFG_CLAUSEFLAG_MULTI },
+ { "trusted-keys", &cfg_type_trustedkeys,
+ CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_DEPRECATED },
+ { "zone", &cfg_type_zone, CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_NODOC },
+ { NULL, NULL, 0 }
+};
+
+/*%
+ * Clauses that can occur in the bind.keys file.
+ */
+static cfg_clausedef_t bindkeys_clauses[] = {
+ { "managed-keys", &cfg_type_dnsseckeys,
+ CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_DEPRECATED },
+ { "trust-anchors", &cfg_type_dnsseckeys, CFG_CLAUSEFLAG_MULTI },
+ { "trusted-keys", &cfg_type_trustedkeys,
+ CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_DEPRECATED },
+ { NULL, NULL, 0 }
+};
+
+static const char *fstrm_model_enums[] = { "mpsc", "spsc", NULL };
+static cfg_type_t cfg_type_fstrm_model = {
+ "model", cfg_parse_enum, cfg_print_ustring,
+ cfg_doc_enum, &cfg_rep_string, &fstrm_model_enums
+};
+
+/*%
+ * Clauses that can be found within the 'options' statement.
+ */
+static cfg_clausedef_t options_clauses[] = {
+ { "answer-cookie", &cfg_type_boolean, 0 },
+ { "automatic-interface-scan", &cfg_type_boolean, 0 },
+ { "avoid-v4-udp-ports", &cfg_type_bracketed_portlist,
+ CFG_CLAUSEFLAG_DEPRECATED },
+ { "avoid-v6-udp-ports", &cfg_type_bracketed_portlist,
+ CFG_CLAUSEFLAG_DEPRECATED },
+ { "bindkeys-file", &cfg_type_qstring, 0 },
+ { "blackhole", &cfg_type_bracketed_aml, 0 },
+ { "cookie-algorithm", &cfg_type_cookiealg, 0 },
+ { "cookie-secret", &cfg_type_sstring, CFG_CLAUSEFLAG_MULTI },
+ { "coresize", &cfg_type_size, CFG_CLAUSEFLAG_DEPRECATED },
+ { "datasize", &cfg_type_size, CFG_CLAUSEFLAG_DEPRECATED },
+ { "deallocate-on-exit", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "directory", &cfg_type_qstring, CFG_CLAUSEFLAG_CALLBACK },
+#ifdef HAVE_DNSTAP
+ { "dnstap-output", &cfg_type_dnstapoutput, 0 },
+ { "dnstap-identity", &cfg_type_serverid, 0 },
+ { "dnstap-version", &cfg_type_qstringornone, 0 },
+#else /* ifdef HAVE_DNSTAP */
+ { "dnstap-output", &cfg_type_dnstapoutput,
+ CFG_CLAUSEFLAG_NOTCONFIGURED },
+ { "dnstap-identity", &cfg_type_serverid, CFG_CLAUSEFLAG_NOTCONFIGURED },
+ { "dnstap-version", &cfg_type_qstringornone,
+ CFG_CLAUSEFLAG_NOTCONFIGURED },
+#endif /* ifdef HAVE_DNSTAP */
+ { "dscp", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE },
+ { "dump-file", &cfg_type_qstring, 0 },
+ { "fake-iquery", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "files", &cfg_type_size, CFG_CLAUSEFLAG_DEPRECATED },
+ { "flush-zones-on-shutdown", &cfg_type_boolean, 0 },
+#ifdef HAVE_DNSTAP
+ { "fstrm-set-buffer-hint", &cfg_type_uint32, 0 },
+ { "fstrm-set-flush-timeout", &cfg_type_uint32, 0 },
+ { "fstrm-set-input-queue-size", &cfg_type_uint32, 0 },
+ { "fstrm-set-output-notify-threshold", &cfg_type_uint32, 0 },
+ { "fstrm-set-output-queue-model", &cfg_type_fstrm_model, 0 },
+ { "fstrm-set-output-queue-size", &cfg_type_uint32, 0 },
+ { "fstrm-set-reopen-interval", &cfg_type_duration, 0 },
+#else /* ifdef HAVE_DNSTAP */
+ { "fstrm-set-buffer-hint", &cfg_type_uint32,
+ CFG_CLAUSEFLAG_NOTCONFIGURED },
+ { "fstrm-set-flush-timeout", &cfg_type_uint32,
+ CFG_CLAUSEFLAG_NOTCONFIGURED },
+ { "fstrm-set-input-queue-size", &cfg_type_uint32,
+ CFG_CLAUSEFLAG_NOTCONFIGURED },
+ { "fstrm-set-output-notify-threshold", &cfg_type_uint32,
+ CFG_CLAUSEFLAG_NOTCONFIGURED },
+ { "fstrm-set-output-queue-model", &cfg_type_fstrm_model,
+ CFG_CLAUSEFLAG_NOTCONFIGURED },
+ { "fstrm-set-output-queue-size", &cfg_type_uint32,
+ CFG_CLAUSEFLAG_NOTCONFIGURED },
+ { "fstrm-set-reopen-interval", &cfg_type_duration,
+ CFG_CLAUSEFLAG_NOTCONFIGURED },
+#endif /* HAVE_DNSTAP */
+#if defined(HAVE_GEOIP2)
+ { "geoip-directory", &cfg_type_qstringornone, 0 },
+#else /* if defined(HAVE_GEOIP2) */
+ { "geoip-directory", &cfg_type_qstringornone,
+ CFG_CLAUSEFLAG_NOTCONFIGURED },
+#endif /* HAVE_GEOIP2 */
+ { "geoip-use-ecs", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "has-old-clients", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "heartbeat-interval", &cfg_type_uint32, CFG_CLAUSEFLAG_DEPRECATED },
+ { "host-statistics", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "host-statistics-max", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "hostname", &cfg_type_qstringornone, 0 },
+ { "interface-interval", &cfg_type_duration, 0 },
+ { "keep-response-order", &cfg_type_bracketed_aml, 0 },
+ { "listen-on", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI },
+ { "listen-on-v6", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI },
+ { "lock-file", &cfg_type_qstringornone, 0 },
+ { "managed-keys-directory", &cfg_type_qstring, 0 },
+ { "match-mapped-addresses", &cfg_type_boolean, 0 },
+ { "max-rsa-exponent-size", &cfg_type_uint32, 0 },
+ { "memstatistics", &cfg_type_boolean, 0 },
+ { "memstatistics-file", &cfg_type_qstring, 0 },
+ { "multiple-cnames", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "named-xfer", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "notify-rate", &cfg_type_uint32, 0 },
+ { "pid-file", &cfg_type_qstringornone, 0 },
+ { "port", &cfg_type_uint32, 0 },
+ { "tls-port", &cfg_type_uint32, 0 },
+#if HAVE_LIBNGHTTP2
+ { "http-port", &cfg_type_uint32, 0 },
+ { "http-listener-clients", &cfg_type_uint32, 0 },
+ { "http-streams-per-connection", &cfg_type_uint32, 0 },
+ { "https-port", &cfg_type_uint32, 0 },
+#else
+ { "http-port", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTCONFIGURED },
+ { "http-listener-clients", &cfg_type_uint32,
+ CFG_CLAUSEFLAG_NOTCONFIGURED },
+ { "http-streams-per-connection", &cfg_type_uint32,
+ CFG_CLAUSEFLAG_NOTCONFIGURED },
+ { "https-port", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTCONFIGURED },
+#endif
+ { "querylog", &cfg_type_boolean, 0 },
+ { "random-device", &cfg_type_qstringornone, CFG_CLAUSEFLAG_OBSOLETE },
+ { "recursing-file", &cfg_type_qstring, 0 },
+ { "recursive-clients", &cfg_type_uint32, 0 },
+ { "reuseport", &cfg_type_boolean, 0 },
+ { "reserved-sockets", &cfg_type_uint32, CFG_CLAUSEFLAG_DEPRECATED },
+ { "secroots-file", &cfg_type_qstring, 0 },
+ { "serial-queries", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "serial-query-rate", &cfg_type_uint32, 0 },
+ { "server-id", &cfg_type_serverid, 0 },
+ { "session-keyalg", &cfg_type_astring, 0 },
+ { "session-keyfile", &cfg_type_qstringornone, 0 },
+ { "session-keyname", &cfg_type_astring, 0 },
+ { "sit-secret", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "stacksize", &cfg_type_size, CFG_CLAUSEFLAG_DEPRECATED },
+ { "startup-notify-rate", &cfg_type_uint32, 0 },
+ { "statistics-file", &cfg_type_qstring, 0 },
+ { "statistics-interval", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "tcp-advertised-timeout", &cfg_type_uint32, 0 },
+ { "tcp-clients", &cfg_type_uint32, 0 },
+ { "tcp-idle-timeout", &cfg_type_uint32, 0 },
+ { "tcp-initial-timeout", &cfg_type_uint32, 0 },
+ { "tcp-keepalive-timeout", &cfg_type_uint32, 0 },
+ { "tcp-listen-queue", &cfg_type_uint32, 0 },
+ { "tcp-receive-buffer", &cfg_type_uint32, 0 },
+ { "tcp-send-buffer", &cfg_type_uint32, 0 },
+ { "tkey-dhkey", &cfg_type_tkey_dhkey, CFG_CLAUSEFLAG_DEPRECATED },
+ { "tkey-domain", &cfg_type_qstring, 0 },
+ { "tkey-gssapi-credential", &cfg_type_qstring, 0 },
+ { "tkey-gssapi-keytab", &cfg_type_qstring, 0 },
+ { "transfer-message-size", &cfg_type_uint32, 0 },
+ { "transfers-in", &cfg_type_uint32, 0 },
+ { "transfers-out", &cfg_type_uint32, 0 },
+ { "transfers-per-ns", &cfg_type_uint32, 0 },
+ { "treat-cr-as-space", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "udp-receive-buffer", &cfg_type_uint32, 0 },
+ { "udp-send-buffer", &cfg_type_uint32, 0 },
+ { "update-quota", &cfg_type_uint32, 0 },
+ { "use-id-pool", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "use-ixfr", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "use-v4-udp-ports", &cfg_type_bracketed_portlist,
+ CFG_CLAUSEFLAG_DEPRECATED },
+ { "use-v6-udp-ports", &cfg_type_bracketed_portlist,
+ CFG_CLAUSEFLAG_DEPRECATED },
+ { "version", &cfg_type_qstringornone, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_namelist = { "namelist",
+ cfg_parse_bracketed_list,
+ cfg_print_bracketed_list,
+ cfg_doc_bracketed_list,
+ &cfg_rep_list,
+ &cfg_type_astring };
+
+static keyword_type_t exclude_kw = { "exclude", &cfg_type_namelist };
+
+static cfg_type_t cfg_type_optional_exclude = {
+ "optional_exclude", parse_optional_keyvalue, print_keyvalue,
+ doc_optional_keyvalue, &cfg_rep_list, &exclude_kw
+};
+
+static keyword_type_t exceptionnames_kw = { "except-from", &cfg_type_namelist };
+
+static cfg_type_t cfg_type_optional_exceptionnames = {
+ "optional_allow", parse_optional_keyvalue, print_keyvalue,
+ doc_optional_keyvalue, &cfg_rep_list, &exceptionnames_kw
+};
+
+static cfg_tuplefielddef_t denyaddresses_fields[] = {
+ { "acl", &cfg_type_bracketed_aml, 0 },
+ { "except-from", &cfg_type_optional_exceptionnames, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_denyaddresses = {
+ "denyaddresses", cfg_parse_tuple, cfg_print_tuple,
+ cfg_doc_tuple, &cfg_rep_tuple, denyaddresses_fields
+};
+
+static cfg_tuplefielddef_t denyaliases_fields[] = {
+ { "name", &cfg_type_namelist, 0 },
+ { "except-from", &cfg_type_optional_exceptionnames, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_denyaliases = {
+ "denyaliases", cfg_parse_tuple, cfg_print_tuple,
+ cfg_doc_tuple, &cfg_rep_tuple, denyaliases_fields
+};
+
+static cfg_type_t cfg_type_algorithmlist = { "algorithmlist",
+ cfg_parse_bracketed_list,
+ cfg_print_bracketed_list,
+ cfg_doc_bracketed_list,
+ &cfg_rep_list,
+ &cfg_type_astring };
+
+static cfg_tuplefielddef_t disablealgorithm_fields[] = {
+ { "name", &cfg_type_astring, 0 },
+ { "algorithms", &cfg_type_algorithmlist, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_disablealgorithm = {
+ "disablealgorithm", cfg_parse_tuple, cfg_print_tuple,
+ cfg_doc_tuple, &cfg_rep_tuple, disablealgorithm_fields
+};
+
+static cfg_type_t cfg_type_dsdigestlist = { "dsdigestlist",
+ cfg_parse_bracketed_list,
+ cfg_print_bracketed_list,
+ cfg_doc_bracketed_list,
+ &cfg_rep_list,
+ &cfg_type_astring };
+
+static cfg_tuplefielddef_t disabledsdigest_fields[] = {
+ { "name", &cfg_type_astring, 0 },
+ { "digests", &cfg_type_dsdigestlist, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_disabledsdigest = {
+ "disabledsdigest", cfg_parse_tuple, cfg_print_tuple,
+ cfg_doc_tuple, &cfg_rep_tuple, disabledsdigest_fields
+};
+
+static cfg_tuplefielddef_t mustbesecure_fields[] = {
+ { "name", &cfg_type_astring, 0 },
+ { "value", &cfg_type_boolean, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_mustbesecure = {
+ "mustbesecure", cfg_parse_tuple, cfg_print_tuple,
+ cfg_doc_tuple, &cfg_rep_tuple, mustbesecure_fields
+};
+
+static const char *masterformat_enums[] = { "raw", "text", NULL };
+static cfg_type_t cfg_type_masterformat = {
+ "masterformat", cfg_parse_enum, cfg_print_ustring,
+ cfg_doc_enum, &cfg_rep_string, &masterformat_enums
+};
+
+static const char *masterstyle_enums[] = { "full", "relative", NULL };
+static cfg_type_t cfg_type_masterstyle = {
+ "masterstyle", cfg_parse_enum, cfg_print_ustring,
+ cfg_doc_enum, &cfg_rep_string, &masterstyle_enums
+};
+
+static keyword_type_t blocksize_kw = { "block-size", &cfg_type_uint32 };
+
+static cfg_type_t cfg_type_blocksize = { "blocksize", parse_keyvalue,
+ print_keyvalue, doc_keyvalue,
+ &cfg_rep_uint32, &blocksize_kw };
+
+static cfg_tuplefielddef_t resppadding_fields[] = {
+ { "acl", &cfg_type_bracketed_aml, 0 },
+ { "block-size", &cfg_type_blocksize, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_resppadding = {
+ "resppadding", cfg_parse_tuple, cfg_print_tuple,
+ cfg_doc_tuple, &cfg_rep_tuple, resppadding_fields
+};
+
+/*%
+ * dnstap {
+ * &lt;message type&gt; [query | response] ;
+ * ...
+ * }
+ *
+ * ... where message type is one of: client, resolver, auth, forwarder,
+ * update, all
+ */
+static const char *dnstap_types[] = { "all", "auth", "client",
+ "forwarder", "resolver", "update",
+ NULL };
+
+static const char *dnstap_modes[] = { "query", "response", NULL };
+
+static cfg_type_t cfg_type_dnstap_type = { "dnstap_type", cfg_parse_enum,
+ cfg_print_ustring, cfg_doc_enum,
+ &cfg_rep_string, dnstap_types };
+
+static cfg_type_t cfg_type_dnstap_mode = {
+ "dnstap_mode", parse_optional_enum, cfg_print_ustring,
+ doc_optional_enum, &cfg_rep_string, dnstap_modes
+};
+
+static cfg_tuplefielddef_t dnstap_fields[] = {
+ { "type", &cfg_type_dnstap_type, 0 },
+ { "mode", &cfg_type_dnstap_mode, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_dnstap_entry = { "dnstap_value", cfg_parse_tuple,
+ cfg_print_tuple, cfg_doc_tuple,
+ &cfg_rep_tuple, dnstap_fields };
+
+static cfg_type_t cfg_type_dnstap = { "dnstap",
+ cfg_parse_bracketed_list,
+ cfg_print_bracketed_list,
+ cfg_doc_bracketed_list,
+ &cfg_rep_list,
+ &cfg_type_dnstap_entry };
+
+/*%
+ * dnstap-output
+ */
+static isc_result_t
+parse_dtout(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+ cfg_obj_t *obj = NULL;
+ const cfg_tuplefielddef_t *fields = type->of;
+
+ CHECK(cfg_create_tuple(pctx, type, &obj));
+
+ /* Parse the mandatory "mode" and "path" fields */
+ CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0]));
+ CHECK(cfg_parse_obj(pctx, fields[1].type, &obj->value.tuple[1]));
+
+ /* Parse "versions" and "size" fields in any order. */
+ for (;;) {
+ CHECK(cfg_peektoken(pctx, 0));
+ if (pctx->token.type == isc_tokentype_string) {
+ CHECK(cfg_gettoken(pctx, 0));
+ if (strcasecmp(TOKEN_STRING(pctx), "size") == 0 &&
+ obj->value.tuple[2] == NULL)
+ {
+ CHECK(cfg_parse_obj(pctx, fields[2].type,
+ &obj->value.tuple[2]));
+ } else if (strcasecmp(TOKEN_STRING(pctx), "versions") ==
+ 0 &&
+ obj->value.tuple[3] == NULL)
+ {
+ CHECK(cfg_parse_obj(pctx, fields[3].type,
+ &obj->value.tuple[3]));
+ } else if (strcasecmp(TOKEN_STRING(pctx), "suffix") ==
+ 0 &&
+ obj->value.tuple[4] == NULL)
+ {
+ CHECK(cfg_parse_obj(pctx, fields[4].type,
+ &obj->value.tuple[4]));
+ } else {
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "unexpected token");
+ result = ISC_R_UNEXPECTEDTOKEN;
+ goto cleanup;
+ }
+ } else {
+ break;
+ }
+ }
+
+ /* Create void objects for missing optional values. */
+ if (obj->value.tuple[2] == NULL) {
+ CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[2]));
+ }
+ if (obj->value.tuple[3] == NULL) {
+ CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[3]));
+ }
+ if (obj->value.tuple[4] == NULL) {
+ CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[4]));
+ }
+
+ *ret = obj;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ CLEANUP_OBJ(obj);
+ return (result);
+}
+
+static void
+print_dtout(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ cfg_print_obj(pctx, obj->value.tuple[0]); /* mode */
+ cfg_print_obj(pctx, obj->value.tuple[1]); /* file */
+ if (obj->value.tuple[2]->type->print != cfg_print_void) {
+ cfg_print_cstr(pctx, " size ");
+ cfg_print_obj(pctx, obj->value.tuple[2]);
+ }
+ if (obj->value.tuple[3]->type->print != cfg_print_void) {
+ cfg_print_cstr(pctx, " versions ");
+ cfg_print_obj(pctx, obj->value.tuple[3]);
+ }
+ if (obj->value.tuple[4]->type->print != cfg_print_void) {
+ cfg_print_cstr(pctx, " suffix ");
+ cfg_print_obj(pctx, obj->value.tuple[4]);
+ }
+}
+
+static void
+doc_dtout(cfg_printer_t *pctx, const cfg_type_t *type) {
+ UNUSED(type);
+ cfg_print_cstr(pctx, "( file | unix ) <quoted_string>");
+ cfg_print_cstr(pctx, " ");
+ cfg_print_cstr(pctx, "[ size ( unlimited | <size> ) ]");
+ cfg_print_cstr(pctx, " ");
+ cfg_print_cstr(pctx, "[ versions ( unlimited | <integer> ) ]");
+ cfg_print_cstr(pctx, " ");
+ cfg_print_cstr(pctx, "[ suffix ( increment | timestamp ) ]");
+}
+
+static const char *dtoutmode_enums[] = { "file", "unix", NULL };
+static cfg_type_t cfg_type_dtmode = { "dtmode", cfg_parse_enum,
+ cfg_print_ustring, cfg_doc_enum,
+ &cfg_rep_string, &dtoutmode_enums };
+
+static cfg_tuplefielddef_t dtout_fields[] = {
+ { "mode", &cfg_type_dtmode, 0 },
+ { "path", &cfg_type_qstring, 0 },
+ { "size", &cfg_type_sizenodefault, 0 },
+ { "versions", &cfg_type_logversions, 0 },
+ { "suffix", &cfg_type_logsuffix, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_dnstapoutput = { "dnstapoutput", parse_dtout,
+ print_dtout, doc_dtout,
+ &cfg_rep_tuple, dtout_fields };
+
+/*%
+ * response-policy {
+ * zone &lt;string&gt; [ policy (given|disabled|passthru|drop|tcp-only|
+ * nxdomain|nodata|cname &lt;domain&gt; ) ]
+ * [ recursive-only yes|no ] [ log yes|no ]
+ * [ max-policy-ttl number ]
+ * [ nsip-enable yes|no ] [ nsdname-enable yes|no ];
+ * } [ recursive-only yes|no ] [ max-policy-ttl number ]
+ * [ min-update-interval number ]
+ * [ break-dnssec yes|no ] [ min-ns-dots number ]
+ * [ qname-wait-recurse yes|no ]
+ * [ nsip-enable yes|no ] [ nsdname-enable yes|no ]
+ * [ dnsrps-enable yes|no ]
+ * [ dnsrps-options { DNSRPS configuration string } ];
+ */
+
+static void
+doc_rpz_policy(cfg_printer_t *pctx, const cfg_type_t *type) {
+ const char *const *p;
+ /*
+ * This is cfg_doc_enum() without the trailing " )".
+ */
+ cfg_print_cstr(pctx, "( ");
+ for (p = type->of; *p != NULL; p++) {
+ cfg_print_cstr(pctx, *p);
+ if (p[1] != NULL) {
+ cfg_print_cstr(pctx, " | ");
+ }
+ }
+}
+
+static void
+doc_rpz_cname(cfg_printer_t *pctx, const cfg_type_t *type) {
+ cfg_doc_terminal(pctx, type);
+ cfg_print_cstr(pctx, " )");
+}
+
+/*
+ * Parse
+ * given|disabled|passthru|drop|tcp-only|nxdomain|nodata|cname <domain>
+ */
+static isc_result_t
+cfg_parse_rpz_policy(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ isc_result_t result;
+ cfg_obj_t *obj = NULL;
+ const cfg_tuplefielddef_t *fields;
+
+ CHECK(cfg_create_tuple(pctx, type, &obj));
+
+ fields = type->of;
+ CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0]));
+ /*
+ * parse cname domain only after "policy cname"
+ */
+ if (strcasecmp("cname", cfg_obj_asstring(obj->value.tuple[0])) != 0) {
+ CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[1]));
+ } else {
+ CHECK(cfg_parse_obj(pctx, fields[1].type,
+ &obj->value.tuple[1]));
+ }
+
+ *ret = obj;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ CLEANUP_OBJ(obj);
+ return (result);
+}
+
+/*
+ * Parse a tuple consisting of any kind of required field followed
+ * by 2 or more optional keyvalues that can be in any order.
+ */
+static isc_result_t
+cfg_parse_kv_tuple(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ const cfg_tuplefielddef_t *fields, *f;
+ cfg_obj_t *obj = NULL;
+ int fn;
+ isc_result_t result;
+
+ CHECK(cfg_create_tuple(pctx, type, &obj));
+
+ /*
+ * The zone first field is required and always first.
+ */
+ fields = type->of;
+ CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0]));
+
+ for (;;) {
+ CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
+ if (pctx->token.type != isc_tokentype_string) {
+ break;
+ }
+
+ for (fn = 1, f = &fields[1];; ++fn, ++f) {
+ if (f->name == NULL) {
+ cfg_parser_error(pctx, 0, "unexpected '%s'",
+ TOKEN_STRING(pctx));
+ result = ISC_R_UNEXPECTEDTOKEN;
+ goto cleanup;
+ }
+ if (obj->value.tuple[fn] == NULL &&
+ strcasecmp(f->name, TOKEN_STRING(pctx)) == 0)
+ {
+ break;
+ }
+ }
+
+ CHECK(cfg_gettoken(pctx, 0));
+ CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[fn]));
+ }
+
+ for (fn = 1, f = &fields[1]; f->name != NULL; ++fn, ++f) {
+ if (obj->value.tuple[fn] == NULL) {
+ CHECK(cfg_parse_void(pctx, NULL,
+ &obj->value.tuple[fn]));
+ }
+ }
+
+ *ret = obj;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ CLEANUP_OBJ(obj);
+ return (result);
+}
+
+static void
+cfg_print_kv_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ unsigned int i;
+ const cfg_tuplefielddef_t *fields, *f;
+ const cfg_obj_t *fieldobj;
+
+ fields = obj->type->of;
+ for (f = fields, i = 0; f->name != NULL; f++, i++) {
+ fieldobj = obj->value.tuple[i];
+ if (fieldobj->type->print == cfg_print_void) {
+ continue;
+ }
+ if (i != 0) {
+ cfg_print_cstr(pctx, " ");
+ cfg_print_cstr(pctx, f->name);
+ cfg_print_cstr(pctx, " ");
+ }
+ cfg_print_obj(pctx, fieldobj);
+ }
+}
+
+static void
+cfg_doc_kv_tuple(cfg_printer_t *pctx, const cfg_type_t *type) {
+ const cfg_tuplefielddef_t *fields, *f;
+
+ fields = type->of;
+ for (f = fields; f->name != NULL; f++) {
+ if ((f->flags & CFG_CLAUSEFLAG_NODOC) != 0) {
+ continue;
+ }
+ if (f != fields) {
+ cfg_print_cstr(pctx, " [ ");
+ cfg_print_cstr(pctx, f->name);
+ if (f->type->doc != cfg_doc_void) {
+ cfg_print_cstr(pctx, " ");
+ }
+ }
+ cfg_doc_obj(pctx, f->type);
+ if (f != fields) {
+ cfg_print_cstr(pctx, " ]");
+ }
+ }
+}
+
+static keyword_type_t zone_kw = { "zone", &cfg_type_astring };
+static cfg_type_t cfg_type_rpz_zone = { "zone", parse_keyvalue,
+ print_keyvalue, doc_keyvalue,
+ &cfg_rep_string, &zone_kw };
+/*
+ * "no-op" is an obsolete equivalent of "passthru".
+ */
+static const char *rpz_policies[] = { "cname", "disabled", "drop",
+ "given", "no-op", "nodata",
+ "nxdomain", "passthru", "tcp-only",
+ NULL };
+static cfg_type_t cfg_type_rpz_policy_name = {
+ "policy name", cfg_parse_enum, cfg_print_ustring,
+ doc_rpz_policy, &cfg_rep_string, &rpz_policies
+};
+static cfg_type_t cfg_type_rpz_cname = {
+ "quoted_string", cfg_parse_astring, NULL,
+ doc_rpz_cname, &cfg_rep_string, NULL
+};
+static cfg_tuplefielddef_t rpz_policy_fields[] = {
+ { "policy name", &cfg_type_rpz_policy_name, 0 },
+ { "cname", &cfg_type_rpz_cname, 0 },
+ { NULL, NULL, 0 }
+};
+static cfg_type_t cfg_type_rpz_policy = { "policy tuple", cfg_parse_rpz_policy,
+ cfg_print_tuple, cfg_doc_tuple,
+ &cfg_rep_tuple, rpz_policy_fields };
+static cfg_tuplefielddef_t rpz_zone_fields[] = {
+ { "zone name", &cfg_type_rpz_zone, 0 },
+ { "add-soa", &cfg_type_boolean, 0 },
+ { "log", &cfg_type_boolean, 0 },
+ { "max-policy-ttl", &cfg_type_duration, 0 },
+ { "min-update-interval", &cfg_type_duration, 0 },
+ { "policy", &cfg_type_rpz_policy, 0 },
+ { "recursive-only", &cfg_type_boolean, 0 },
+ { "nsip-enable", &cfg_type_boolean, 0 },
+ { "nsdname-enable", &cfg_type_boolean, 0 },
+ { NULL, NULL, 0 }
+};
+static cfg_type_t cfg_type_rpz_tuple = { "rpz tuple", cfg_parse_kv_tuple,
+ cfg_print_kv_tuple, cfg_doc_kv_tuple,
+ &cfg_rep_tuple, rpz_zone_fields };
+static cfg_type_t cfg_type_rpz_list = { "zone list",
+ cfg_parse_bracketed_list,
+ cfg_print_bracketed_list,
+ cfg_doc_bracketed_list,
+ &cfg_rep_list,
+ &cfg_type_rpz_tuple };
+static cfg_tuplefielddef_t rpz_fields[] = {
+ { "zone list", &cfg_type_rpz_list, 0 },
+ { "add-soa", &cfg_type_boolean, 0 },
+ { "break-dnssec", &cfg_type_boolean, 0 },
+ { "max-policy-ttl", &cfg_type_duration, 0 },
+ { "min-update-interval", &cfg_type_duration, 0 },
+ { "min-ns-dots", &cfg_type_uint32, 0 },
+ { "nsip-wait-recurse", &cfg_type_boolean, 0 },
+ { "nsdname-wait-recurse", &cfg_type_boolean, 0 },
+ { "qname-wait-recurse", &cfg_type_boolean, 0 },
+ { "recursive-only", &cfg_type_boolean, 0 },
+ { "nsip-enable", &cfg_type_boolean, 0 },
+ { "nsdname-enable", &cfg_type_boolean, 0 },
+#ifdef USE_DNSRPS
+ { "dnsrps-enable", &cfg_type_boolean, 0 },
+ { "dnsrps-options", &cfg_type_bracketed_text, 0 },
+#else /* ifdef USE_DNSRPS */
+ { "dnsrps-enable", &cfg_type_boolean, CFG_CLAUSEFLAG_NOTCONFIGURED },
+ { "dnsrps-options", &cfg_type_bracketed_text,
+ CFG_CLAUSEFLAG_NOTCONFIGURED },
+#endif /* ifdef USE_DNSRPS */
+ { NULL, NULL, 0 }
+};
+static cfg_type_t cfg_type_rpz = { "rpz",
+ cfg_parse_kv_tuple,
+ cfg_print_kv_tuple,
+ cfg_doc_kv_tuple,
+ &cfg_rep_tuple,
+ rpz_fields };
+
+/*
+ * Catalog zones
+ */
+static cfg_type_t cfg_type_catz_zone = { "zone", parse_keyvalue,
+ print_keyvalue, doc_keyvalue,
+ &cfg_rep_string, &zone_kw };
+
+static cfg_tuplefielddef_t catz_zone_fields[] = {
+ { "zone name", &cfg_type_catz_zone, 0 },
+ { "default-masters", &cfg_type_namesockaddrkeylist,
+ CFG_CLAUSEFLAG_NODOC },
+ { "default-primaries", &cfg_type_namesockaddrkeylist, 0 },
+ { "zone-directory", &cfg_type_qstring, 0 },
+ { "in-memory", &cfg_type_boolean, 0 },
+ { "min-update-interval", &cfg_type_duration, 0 },
+ { NULL, NULL, 0 }
+};
+static cfg_type_t cfg_type_catz_tuple = {
+ "catz tuple", cfg_parse_kv_tuple, cfg_print_kv_tuple,
+ cfg_doc_kv_tuple, &cfg_rep_tuple, catz_zone_fields
+};
+static cfg_type_t cfg_type_catz_list = { "zone list",
+ cfg_parse_bracketed_list,
+ cfg_print_bracketed_list,
+ cfg_doc_bracketed_list,
+ &cfg_rep_list,
+ &cfg_type_catz_tuple };
+static cfg_tuplefielddef_t catz_fields[] = {
+ { "zone list", &cfg_type_catz_list, 0 }, { NULL, NULL, 0 }
+};
+static cfg_type_t cfg_type_catz = {
+ "catz", cfg_parse_kv_tuple, cfg_print_kv_tuple,
+ cfg_doc_kv_tuple, &cfg_rep_tuple, catz_fields
+};
+
+/*
+ * rate-limit
+ */
+static cfg_clausedef_t rrl_clauses[] = {
+ { "all-per-second", &cfg_type_uint32, 0 },
+ { "errors-per-second", &cfg_type_uint32, 0 },
+ { "exempt-clients", &cfg_type_bracketed_aml, 0 },
+ { "ipv4-prefix-length", &cfg_type_uint32, 0 },
+ { "ipv6-prefix-length", &cfg_type_uint32, 0 },
+ { "log-only", &cfg_type_boolean, 0 },
+ { "max-table-size", &cfg_type_uint32, 0 },
+ { "min-table-size", &cfg_type_uint32, 0 },
+ { "nodata-per-second", &cfg_type_uint32, 0 },
+ { "nxdomains-per-second", &cfg_type_uint32, 0 },
+ { "qps-scale", &cfg_type_uint32, 0 },
+ { "referrals-per-second", &cfg_type_uint32, 0 },
+ { "responses-per-second", &cfg_type_uint32, 0 },
+ { "slip", &cfg_type_uint32, 0 },
+ { "window", &cfg_type_uint32, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_clausedef_t *rrl_clausesets[] = { rrl_clauses, NULL };
+
+static cfg_type_t cfg_type_rrl = { "rate-limit", cfg_parse_map, cfg_print_map,
+ cfg_doc_map, &cfg_rep_map, rrl_clausesets };
+
+static isc_result_t
+parse_optional_uint32(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ isc_result_t result;
+ UNUSED(type);
+
+ CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
+ if (pctx->token.type == isc_tokentype_number) {
+ CHECK(cfg_parse_obj(pctx, &cfg_type_uint32, ret));
+ } else {
+ CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
+ }
+cleanup:
+ return (result);
+}
+
+static void
+doc_optional_uint32(cfg_printer_t *pctx, const cfg_type_t *type) {
+ UNUSED(type);
+ cfg_print_cstr(pctx, "[ <integer> ]");
+}
+
+static cfg_type_t cfg_type_optional_uint32 = { "optional_uint32",
+ parse_optional_uint32,
+ NULL,
+ doc_optional_uint32,
+ NULL,
+ NULL };
+
+static cfg_tuplefielddef_t prefetch_fields[] = {
+ { "trigger", &cfg_type_uint32, 0 },
+ { "eligible", &cfg_type_optional_uint32, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_prefetch = { "prefetch", cfg_parse_tuple,
+ cfg_print_tuple, cfg_doc_tuple,
+ &cfg_rep_tuple, prefetch_fields };
+/*
+ * DNS64.
+ */
+static cfg_clausedef_t dns64_clauses[] = {
+ { "break-dnssec", &cfg_type_boolean, 0 },
+ { "clients", &cfg_type_bracketed_aml, 0 },
+ { "exclude", &cfg_type_bracketed_aml, 0 },
+ { "mapped", &cfg_type_bracketed_aml, 0 },
+ { "recursive-only", &cfg_type_boolean, 0 },
+ { "suffix", &cfg_type_netaddr6, 0 },
+ { NULL, NULL, 0 },
+};
+
+static cfg_clausedef_t *dns64_clausesets[] = { dns64_clauses, NULL };
+
+static cfg_type_t cfg_type_dns64 = { "dns64", cfg_parse_netprefix_map,
+ cfg_print_map, cfg_doc_map,
+ &cfg_rep_map, dns64_clausesets };
+
+static const char *staleanswerclienttimeout_enums[] = { "disabled", "off",
+ NULL };
+static isc_result_t
+parse_staleanswerclienttimeout(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ return (cfg_parse_enum_or_other(pctx, type, &cfg_type_uint32, ret));
+}
+
+static void
+doc_staleanswerclienttimeout(cfg_printer_t *pctx, const cfg_type_t *type) {
+ cfg_doc_enum_or_other(pctx, type, &cfg_type_uint32);
+}
+
+static cfg_type_t cfg_type_staleanswerclienttimeout = {
+ "staleanswerclienttimeout",
+ parse_staleanswerclienttimeout,
+ cfg_print_ustring,
+ doc_staleanswerclienttimeout,
+ &cfg_rep_string,
+ staleanswerclienttimeout_enums
+};
+
+/*%
+ * Clauses that can be found within the 'view' statement,
+ * with defaults in the 'options' statement.
+ */
+
+static cfg_clausedef_t view_clauses[] = {
+ { "acache-cleaning-interval", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "acache-enable", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "additional-from-auth", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "additional-from-cache", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "allow-new-zones", &cfg_type_boolean, 0 },
+ { "allow-query-cache", &cfg_type_bracketed_aml, 0 },
+ { "allow-query-cache-on", &cfg_type_bracketed_aml, 0 },
+ { "allow-recursion", &cfg_type_bracketed_aml, 0 },
+ { "allow-recursion-on", &cfg_type_bracketed_aml, 0 },
+ { "allow-v6-synthesis", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "attach-cache", &cfg_type_astring, 0 },
+ { "auth-nxdomain", &cfg_type_boolean, 0 },
+ { "cache-file", &cfg_type_qstring, CFG_CLAUSEFLAG_ANCIENT },
+ { "catalog-zones", &cfg_type_catz, 0 },
+ { "check-names", &cfg_type_checknames, CFG_CLAUSEFLAG_MULTI },
+ { "cleaning-interval", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "clients-per-query", &cfg_type_uint32, 0 },
+ { "deny-answer-addresses", &cfg_type_denyaddresses, 0 },
+ { "deny-answer-aliases", &cfg_type_denyaliases, 0 },
+ { "disable-algorithms", &cfg_type_disablealgorithm,
+ CFG_CLAUSEFLAG_MULTI },
+ { "disable-ds-digests", &cfg_type_disabledsdigest,
+ CFG_CLAUSEFLAG_MULTI },
+ { "disable-empty-zone", &cfg_type_astring, CFG_CLAUSEFLAG_MULTI },
+ { "dns64", &cfg_type_dns64, CFG_CLAUSEFLAG_MULTI },
+ { "dns64-contact", &cfg_type_astring, 0 },
+ { "dns64-server", &cfg_type_astring, 0 },
+#ifdef USE_DNSRPS
+ { "dnsrps-enable", &cfg_type_boolean, 0 },
+ { "dnsrps-options", &cfg_type_bracketed_text, 0 },
+#else /* ifdef USE_DNSRPS */
+ { "dnsrps-enable", &cfg_type_boolean, CFG_CLAUSEFLAG_NOTCONFIGURED },
+ { "dnsrps-options", &cfg_type_bracketed_text,
+ CFG_CLAUSEFLAG_NOTCONFIGURED },
+#endif /* ifdef USE_DNSRPS */
+ { "dnssec-accept-expired", &cfg_type_boolean, 0 },
+ { "dnssec-enable", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "dnssec-lookaside", NULL,
+ CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_ANCIENT },
+ { "dnssec-must-be-secure", &cfg_type_mustbesecure,
+ CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_DEPRECATED },
+ { "dnssec-validation", &cfg_type_boolorauto, 0 },
+#ifdef HAVE_DNSTAP
+ { "dnstap", &cfg_type_dnstap, 0 },
+#else /* ifdef HAVE_DNSTAP */
+ { "dnstap", &cfg_type_dnstap, CFG_CLAUSEFLAG_NOTCONFIGURED },
+#endif /* HAVE_DNSTAP */
+ { "dual-stack-servers", &cfg_type_nameportiplist, 0 },
+ { "edns-udp-size", &cfg_type_uint32, 0 },
+ { "empty-contact", &cfg_type_astring, 0 },
+ { "empty-server", &cfg_type_astring, 0 },
+ { "empty-zones-enable", &cfg_type_boolean, 0 },
+ { "fetch-glue", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "fetch-quota-params", &cfg_type_fetchquota, 0 },
+ { "fetches-per-server", &cfg_type_fetchesper, 0 },
+ { "fetches-per-zone", &cfg_type_fetchesper, 0 },
+ { "filter-aaaa", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_ANCIENT },
+ { "filter-aaaa-on-v4", &cfg_type_boolean, CFG_CLAUSEFLAG_ANCIENT },
+ { "filter-aaaa-on-v6", &cfg_type_boolean, CFG_CLAUSEFLAG_ANCIENT },
+ { "glue-cache", &cfg_type_boolean, CFG_CLAUSEFLAG_DEPRECATED },
+ { "ipv4only-enable", &cfg_type_boolean, 0 },
+ { "ipv4only-contact", &cfg_type_astring, 0 },
+ { "ipv4only-server", &cfg_type_astring, 0 },
+ { "ixfr-from-differences", &cfg_type_ixfrdifftype, 0 },
+ { "lame-ttl", &cfg_type_duration, 0 },
+#ifdef HAVE_LMDB
+ { "lmdb-mapsize", &cfg_type_sizeval, 0 },
+#else /* ifdef HAVE_LMDB */
+ { "lmdb-mapsize", &cfg_type_sizeval, CFG_CLAUSEFLAG_NOTCONFIGURED },
+#endif /* ifdef HAVE_LMDB */
+ { "max-acache-size", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "max-cache-size", &cfg_type_sizeorpercent, 0 },
+ { "max-cache-ttl", &cfg_type_duration, 0 },
+ { "max-clients-per-query", &cfg_type_uint32, 0 },
+ { "max-ncache-ttl", &cfg_type_duration, 0 },
+ { "max-recursion-depth", &cfg_type_uint32, 0 },
+ { "max-recursion-queries", &cfg_type_uint32, 0 },
+ { "max-stale-ttl", &cfg_type_duration, 0 },
+ { "max-udp-size", &cfg_type_uint32, 0 },
+ { "message-compression", &cfg_type_boolean, 0 },
+ { "min-cache-ttl", &cfg_type_duration, 0 },
+ { "min-ncache-ttl", &cfg_type_duration, 0 },
+ { "min-roots", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "minimal-any", &cfg_type_boolean, 0 },
+ { "minimal-responses", &cfg_type_minimal, 0 },
+ { "new-zones-directory", &cfg_type_qstring, 0 },
+ { "no-case-compress", &cfg_type_bracketed_aml, 0 },
+ { "nocookie-udp-size", &cfg_type_uint32, 0 },
+ { "nosit-udp-size", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "nta-lifetime", &cfg_type_duration, 0 },
+ { "nta-recheck", &cfg_type_duration, 0 },
+ { "nxdomain-redirect", &cfg_type_astring, 0 },
+ { "preferred-glue", &cfg_type_astring, 0 },
+ { "prefetch", &cfg_type_prefetch, 0 },
+ { "provide-ixfr", &cfg_type_boolean, 0 },
+ { "qname-minimization", &cfg_type_qminmethod, 0 },
+ /*
+ * Note that the query-source option syntax is different
+ * from the other -source options.
+ */
+ { "query-source", &cfg_type_querysource4, 0 },
+ { "query-source-v6", &cfg_type_querysource6, 0 },
+ { "queryport-pool-ports", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "queryport-pool-updateinterval", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "rate-limit", &cfg_type_rrl, 0 },
+ { "recursion", &cfg_type_boolean, 0 },
+ { "request-nsid", &cfg_type_boolean, 0 },
+ { "request-sit", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "require-server-cookie", &cfg_type_boolean, 0 },
+ { "resolver-nonbackoff-tries", &cfg_type_uint32, 0 },
+ { "resolver-query-timeout", &cfg_type_uint32, 0 },
+ { "resolver-retry-interval", &cfg_type_uint32, 0 },
+ { "response-padding", &cfg_type_resppadding, 0 },
+ { "response-policy", &cfg_type_rpz, 0 },
+ { "rfc2308-type1", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "root-delegation-only", &cfg_type_optional_exclude,
+ CFG_CLAUSEFLAG_DEPRECATED },
+ { "root-key-sentinel", &cfg_type_boolean, 0 },
+ { "rrset-order", &cfg_type_rrsetorder, 0 },
+ { "send-cookie", &cfg_type_boolean, 0 },
+ { "servfail-ttl", &cfg_type_duration, 0 },
+ { "sortlist", &cfg_type_bracketed_aml, 0 },
+ { "stale-answer-enable", &cfg_type_boolean, 0 },
+ { "stale-answer-client-timeout", &cfg_type_staleanswerclienttimeout,
+ 0 },
+ { "stale-answer-ttl", &cfg_type_duration, 0 },
+ { "stale-cache-enable", &cfg_type_boolean, 0 },
+ { "stale-refresh-time", &cfg_type_duration, 0 },
+ { "suppress-initial-notify", &cfg_type_boolean,
+ CFG_CLAUSEFLAG_OBSOLETE },
+ { "synth-from-dnssec", &cfg_type_boolean, 0 },
+ { "topology", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "transfer-format", &cfg_type_transferformat, 0 },
+ { "trust-anchor-telemetry", &cfg_type_boolean,
+ CFG_CLAUSEFLAG_EXPERIMENTAL },
+ { "use-queryport-pool", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "validate-except", &cfg_type_namelist, 0 },
+ { "v6-bias", &cfg_type_uint32, 0 },
+ { "zero-no-soa-ttl-cache", &cfg_type_boolean, 0 },
+ { NULL, NULL, 0 }
+};
+
+/*%
+ * Clauses that can be found within the 'view' statement only.
+ */
+static cfg_clausedef_t view_only_clauses[] = {
+ { "match-clients", &cfg_type_bracketed_aml, 0 },
+ { "match-destinations", &cfg_type_bracketed_aml, 0 },
+ { "match-recursive-only", &cfg_type_boolean, 0 },
+ { NULL, NULL, 0 }
+};
+
+/*%
+ * Sig-validity-interval.
+ */
+
+static cfg_tuplefielddef_t validityinterval_fields[] = {
+ { "validity", &cfg_type_uint32, 0 },
+ { "re-sign", &cfg_type_optional_uint32, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_validityinterval = {
+ "validityinterval", cfg_parse_tuple, cfg_print_tuple,
+ cfg_doc_tuple, &cfg_rep_tuple, validityinterval_fields
+};
+
+/*%
+ * Clauses that can be found in a 'dnssec-policy' statement.
+ */
+static cfg_clausedef_t dnssecpolicy_clauses[] = {
+ { "dnskey-ttl", &cfg_type_duration, 0 },
+ { "keys", &cfg_type_kaspkeys, 0 },
+ { "max-zone-ttl", &cfg_type_duration, 0 },
+ { "nsec3param", &cfg_type_nsec3, 0 },
+ { "parent-ds-ttl", &cfg_type_duration, 0 },
+ { "parent-propagation-delay", &cfg_type_duration, 0 },
+ { "parent-registration-delay", &cfg_type_duration,
+ CFG_CLAUSEFLAG_OBSOLETE },
+ { "publish-safety", &cfg_type_duration, 0 },
+ { "purge-keys", &cfg_type_duration, 0 },
+ { "retire-safety", &cfg_type_duration, 0 },
+ { "signatures-refresh", &cfg_type_duration, 0 },
+ { "signatures-validity", &cfg_type_duration, 0 },
+ { "signatures-validity-dnskey", &cfg_type_duration, 0 },
+ { "zone-propagation-delay", &cfg_type_duration, 0 },
+ { NULL, NULL, 0 }
+};
+
+/*%
+ * Clauses that can be found in a 'zone' statement,
+ * with defaults in the 'view' or 'options' statement.
+ *
+ * Note: CFG_ZONE_* options indicate in which zone types this clause is
+ * legal.
+ */
+/*
+ * NOTE: To enable syntax which allows specifying port and protocol
+ * within 'allow-*' clauses, replace 'cfg_type_bracketed_aml' with
+ * 'cfg_type_transport_acl'.
+ *
+ * Example: allow-transfer port 853 protocol tls { ... };
+ */
+static cfg_clausedef_t zone_clauses[] = {
+ { "allow-notify", &cfg_type_bracketed_aml,
+ CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR },
+ { "allow-query", &cfg_type_bracketed_aml,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR |
+ CFG_ZONE_STUB | CFG_ZONE_REDIRECT | CFG_ZONE_STATICSTUB },
+ { "allow-query-on", &cfg_type_bracketed_aml,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR |
+ CFG_ZONE_STUB | CFG_ZONE_REDIRECT | CFG_ZONE_STATICSTUB },
+ { "allow-transfer", &cfg_type_transport_acl,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR },
+ { "allow-update", &cfg_type_bracketed_aml, CFG_ZONE_PRIMARY },
+ { "allow-update-forwarding", &cfg_type_bracketed_aml,
+ CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR },
+ { "also-notify", &cfg_type_namesockaddrkeylist,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR },
+ { "alt-transfer-source", &cfg_type_sockaddr4wild,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR |
+ CFG_CLAUSEFLAG_DEPRECATED },
+ { "alt-transfer-source-v6", &cfg_type_sockaddr6wild,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR |
+ CFG_CLAUSEFLAG_DEPRECATED },
+ { "auto-dnssec", &cfg_type_autodnssec,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_CLAUSEFLAG_DEPRECATED },
+ { "check-dup-records", &cfg_type_checkmode, CFG_ZONE_PRIMARY },
+ { "check-integrity", &cfg_type_boolean, CFG_ZONE_PRIMARY },
+ { "check-mx", &cfg_type_checkmode, CFG_ZONE_PRIMARY },
+ { "check-mx-cname", &cfg_type_checkmode, CFG_ZONE_PRIMARY },
+ { "check-sibling", &cfg_type_boolean, CFG_ZONE_PRIMARY },
+ { "check-spf", &cfg_type_warn, CFG_ZONE_PRIMARY },
+ { "check-srv-cname", &cfg_type_checkmode, CFG_ZONE_PRIMARY },
+ { "check-wildcard", &cfg_type_boolean, CFG_ZONE_PRIMARY },
+ { "dialup", &cfg_type_dialuptype,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_STUB |
+ CFG_CLAUSEFLAG_DEPRECATED },
+ { "dnssec-dnskey-kskonly", &cfg_type_boolean,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY },
+ { "dnssec-loadkeys-interval", &cfg_type_uint32,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY },
+ { "dnssec-policy", &cfg_type_astring,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY },
+ { "dnssec-secure-to-insecure", &cfg_type_boolean, CFG_ZONE_PRIMARY },
+ { "dnssec-update-mode", &cfg_type_dnssecupdatemode,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY },
+ { "forward", &cfg_type_forwardtype,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_STUB |
+ CFG_ZONE_STATICSTUB | CFG_ZONE_FORWARD },
+ { "forwarders", &cfg_type_portiplist,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_STUB |
+ CFG_ZONE_STATICSTUB | CFG_ZONE_FORWARD },
+ { "key-directory", &cfg_type_qstring,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY },
+ { "maintain-ixfr-base", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "masterfile-format", &cfg_type_masterformat,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR |
+ CFG_ZONE_STUB | CFG_ZONE_REDIRECT },
+ { "masterfile-style", &cfg_type_masterstyle,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR |
+ CFG_ZONE_STUB | CFG_ZONE_REDIRECT },
+ { "max-ixfr-log-size", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "max-ixfr-ratio", &cfg_type_ixfrratio,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR },
+ { "max-journal-size", &cfg_type_size,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR },
+ { "max-records", &cfg_type_uint32,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR |
+ CFG_ZONE_STUB | CFG_ZONE_STATICSTUB | CFG_ZONE_REDIRECT },
+ { "max-refresh-time", &cfg_type_uint32,
+ CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | CFG_ZONE_STUB },
+ { "max-retry-time", &cfg_type_uint32,
+ CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | CFG_ZONE_STUB },
+ { "max-transfer-idle-in", &cfg_type_uint32,
+ CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | CFG_ZONE_STUB },
+ { "max-transfer-idle-out", &cfg_type_uint32,
+ CFG_ZONE_PRIMARY | CFG_ZONE_MIRROR | CFG_ZONE_SECONDARY },
+ { "max-transfer-time-in", &cfg_type_uint32,
+ CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | CFG_ZONE_STUB },
+ { "max-transfer-time-out", &cfg_type_uint32,
+ CFG_ZONE_PRIMARY | CFG_ZONE_MIRROR | CFG_ZONE_SECONDARY },
+ { "max-zone-ttl", &cfg_type_maxduration,
+ CFG_ZONE_PRIMARY | CFG_ZONE_REDIRECT },
+ { "min-refresh-time", &cfg_type_uint32,
+ CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | CFG_ZONE_STUB },
+ { "min-retry-time", &cfg_type_uint32,
+ CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | CFG_ZONE_STUB },
+ { "multi-master", &cfg_type_boolean,
+ CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | CFG_ZONE_STUB },
+ { "notify", &cfg_type_notifytype,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR },
+ { "notify-delay", &cfg_type_uint32,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR },
+ { "notify-source", &cfg_type_sockaddr4wild,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR },
+ { "notify-source-v6", &cfg_type_sockaddr6wild,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR },
+ { "notify-to-soa", &cfg_type_boolean,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY },
+ { "nsec3-test-zone", &cfg_type_boolean,
+ CFG_CLAUSEFLAG_TESTONLY | CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY },
+ { "parental-source", &cfg_type_sockaddr4wild,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY },
+ { "parental-source-v6", &cfg_type_sockaddr6wild,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY },
+ { "request-expire", &cfg_type_boolean,
+ CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR },
+ { "request-ixfr", &cfg_type_boolean,
+ CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR },
+ { "serial-update-method", &cfg_type_updatemethod, CFG_ZONE_PRIMARY },
+ { "sig-signing-nodes", &cfg_type_uint32,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY },
+ { "sig-signing-signatures", &cfg_type_uint32,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY },
+ { "sig-signing-type", &cfg_type_uint32,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY },
+ { "sig-validity-interval", &cfg_type_validityinterval,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY },
+ { "dnskey-sig-validity", &cfg_type_uint32,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY },
+ { "transfer-source", &cfg_type_sockaddr4wild,
+ CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | CFG_ZONE_STUB },
+ { "transfer-source-v6", &cfg_type_sockaddr6wild,
+ CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | CFG_ZONE_STUB },
+ { "try-tcp-refresh", &cfg_type_boolean,
+ CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR },
+ { "update-check-ksk", &cfg_type_boolean,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY },
+ { "use-alt-transfer-source", &cfg_type_boolean,
+ CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | CFG_ZONE_STUB |
+ CFG_CLAUSEFLAG_DEPRECATED },
+ { "zero-no-soa-ttl", &cfg_type_boolean,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR },
+ { "zone-statistics", &cfg_type_zonestat,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR |
+ CFG_ZONE_STUB | CFG_ZONE_STATICSTUB | CFG_ZONE_REDIRECT },
+ { NULL, NULL, 0 }
+};
+
+/*%
+ * Clauses that can be found in a 'zone' statement only.
+ *
+ * Note: CFG_ZONE_* options indicate in which zone types this clause is
+ * legal.
+ */
+static cfg_clausedef_t zone_only_clauses[] = {
+ /*
+ * Note that the format of the check-names option is different between
+ * the zone options and the global/view options. Ugh.
+ */
+ { "type", &cfg_type_zonetype,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR |
+ CFG_ZONE_STUB | CFG_ZONE_STATICSTUB | CFG_ZONE_DELEGATION |
+ CFG_ZONE_HINT | CFG_ZONE_REDIRECT | CFG_ZONE_FORWARD },
+ { "check-names", &cfg_type_checkmode,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR |
+ CFG_ZONE_HINT | CFG_ZONE_STUB },
+ { "database", &cfg_type_astring,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR |
+ CFG_ZONE_STUB },
+ { "delegation-only", &cfg_type_boolean,
+ CFG_ZONE_HINT | CFG_ZONE_STUB | CFG_ZONE_FORWARD |
+ CFG_CLAUSEFLAG_DEPRECATED },
+ { "dlz", &cfg_type_astring,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_REDIRECT },
+ { "file", &cfg_type_qstring,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR |
+ CFG_ZONE_STUB | CFG_ZONE_HINT | CFG_ZONE_REDIRECT },
+ { "in-view", &cfg_type_astring, CFG_ZONE_INVIEW },
+ { "inline-signing", &cfg_type_boolean,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY },
+ { "ixfr-base", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "ixfr-from-differences", &cfg_type_boolean,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR },
+ { "ixfr-tmp-file", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "journal", &cfg_type_qstring,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR },
+ { "masters", &cfg_type_namesockaddrkeylist,
+ CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | CFG_ZONE_STUB |
+ CFG_ZONE_REDIRECT | CFG_CLAUSEFLAG_NODOC },
+ { "parental-agents", &cfg_type_namesockaddrkeylist,
+ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY },
+ { "primaries", &cfg_type_namesockaddrkeylist,
+ CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | CFG_ZONE_STUB |
+ CFG_ZONE_REDIRECT },
+ { "pubkey", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "server-addresses", &cfg_type_bracketed_netaddrlist,
+ CFG_ZONE_STATICSTUB },
+ { "server-names", &cfg_type_namelist, CFG_ZONE_STATICSTUB },
+ { "update-policy", &cfg_type_updatepolicy, CFG_ZONE_PRIMARY },
+ { NULL, NULL, 0 }
+};
+
+/*% The top-level named.conf syntax. */
+
+static cfg_clausedef_t *namedconf_clausesets[] = { namedconf_clauses,
+ namedconf_or_view_clauses,
+ NULL };
+cfg_type_t cfg_type_namedconf = { "namedconf", cfg_parse_mapbody,
+ cfg_print_mapbody, cfg_doc_mapbody,
+ &cfg_rep_map, namedconf_clausesets };
+
+/*% The bind.keys syntax (trust-anchors/managed-keys/trusted-keys only). */
+static cfg_clausedef_t *bindkeys_clausesets[] = { bindkeys_clauses, NULL };
+cfg_type_t cfg_type_bindkeys = { "bindkeys", cfg_parse_mapbody,
+ cfg_print_mapbody, cfg_doc_mapbody,
+ &cfg_rep_map, bindkeys_clausesets };
+
+/*% The "options" statement syntax. */
+
+static cfg_clausedef_t *options_clausesets[] = { options_clauses, view_clauses,
+ zone_clauses, NULL };
+static cfg_type_t cfg_type_options = { "options", cfg_parse_map,
+ cfg_print_map, cfg_doc_map,
+ &cfg_rep_map, options_clausesets };
+
+/*% The "view" statement syntax. */
+
+static cfg_clausedef_t *view_clausesets[] = { view_only_clauses,
+ namedconf_or_view_clauses,
+ view_clauses, zone_clauses,
+ NULL };
+
+static cfg_type_t cfg_type_viewopts = { "view", cfg_parse_map,
+ cfg_print_map, cfg_doc_map,
+ &cfg_rep_map, view_clausesets };
+
+/*% The "zone" statement syntax. */
+
+static cfg_clausedef_t *zone_clausesets[] = { zone_only_clauses, zone_clauses,
+ NULL };
+cfg_type_t cfg_type_zoneopts = { "zoneopts", cfg_parse_map, cfg_print_map,
+ cfg_doc_map, &cfg_rep_map, zone_clausesets };
+
+/*% The "dnssec-policy" statement syntax. */
+static cfg_clausedef_t *dnssecpolicy_clausesets[] = { dnssecpolicy_clauses,
+ NULL };
+cfg_type_t cfg_type_dnssecpolicyopts = {
+ "dnssecpolicyopts", cfg_parse_map, cfg_print_map,
+ cfg_doc_map, &cfg_rep_map, dnssecpolicy_clausesets
+};
+
+/*% The "dynamically loadable zones" statement syntax. */
+
+static cfg_clausedef_t dlz_clauses[] = { { "database", &cfg_type_astring, 0 },
+ { "search", &cfg_type_boolean, 0 },
+ { NULL, NULL, 0 } };
+static cfg_clausedef_t *dlz_clausesets[] = { dlz_clauses, NULL };
+static cfg_type_t cfg_type_dlz = { "dlz", cfg_parse_named_map,
+ cfg_print_map, cfg_doc_map,
+ &cfg_rep_map, dlz_clausesets };
+
+/*%
+ * The "dyndb" statement syntax.
+ */
+
+static cfg_tuplefielddef_t dyndb_fields[] = {
+ { "name", &cfg_type_astring, 0 },
+ { "library", &cfg_type_qstring, 0 },
+ { "parameters", &cfg_type_bracketed_text, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_dyndb = { "dyndb", cfg_parse_tuple,
+ cfg_print_tuple, cfg_doc_tuple,
+ &cfg_rep_tuple, dyndb_fields };
+
+/*%
+ * The "plugin" statement syntax.
+ * Currently only one plugin type is supported: query.
+ */
+
+static const char *plugin_enums[] = { "query", NULL };
+static cfg_type_t cfg_type_plugintype = { "plugintype", cfg_parse_enum,
+ cfg_print_ustring, cfg_doc_enum,
+ &cfg_rep_string, plugin_enums };
+static cfg_tuplefielddef_t plugin_fields[] = {
+ { "type", &cfg_type_plugintype, 0 },
+ { "library", &cfg_type_astring, 0 },
+ { "parameters", &cfg_type_optional_bracketed_text, 0 },
+ { NULL, NULL, 0 }
+};
+static cfg_type_t cfg_type_plugin = { "plugin", cfg_parse_tuple,
+ cfg_print_tuple, cfg_doc_tuple,
+ &cfg_rep_tuple, plugin_fields };
+
+/*%
+ * Clauses that can be found within the 'key' statement.
+ */
+static cfg_clausedef_t key_clauses[] = { { "algorithm", &cfg_type_astring, 0 },
+ { "secret", &cfg_type_sstring, 0 },
+ { NULL, NULL, 0 } };
+
+static cfg_clausedef_t *key_clausesets[] = { key_clauses, NULL };
+static cfg_type_t cfg_type_key = { "key", cfg_parse_named_map,
+ cfg_print_map, cfg_doc_map,
+ &cfg_rep_map, key_clausesets };
+
+/*%
+ * Clauses that can be found in a 'server' statement.
+ *
+ * Please update lib/bind9/check.c and
+ * bin/tests/system/checkconf/good-server-christmas-tree.conf.in to
+ * exercise the new clause when adding new clauses.
+ */
+static cfg_clausedef_t server_clauses[] = {
+ { "bogus", &cfg_type_boolean, 0 },
+ { "edns", &cfg_type_boolean, 0 },
+ { "edns-udp-size", &cfg_type_uint32, 0 },
+ { "edns-version", &cfg_type_uint32, 0 },
+ { "keys", &cfg_type_server_key_kludge, 0 },
+ { "max-udp-size", &cfg_type_uint32, 0 },
+ { "notify-source", &cfg_type_sockaddr4wild, 0 },
+ { "notify-source-v6", &cfg_type_sockaddr6wild, 0 },
+ { "padding", &cfg_type_uint32, 0 },
+ { "provide-ixfr", &cfg_type_boolean, 0 },
+ { "query-source", &cfg_type_querysource4, 0 },
+ { "query-source-v6", &cfg_type_querysource6, 0 },
+ { "request-expire", &cfg_type_boolean, 0 },
+ { "request-ixfr", &cfg_type_boolean, 0 },
+ { "request-nsid", &cfg_type_boolean, 0 },
+ { "request-sit", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "send-cookie", &cfg_type_boolean, 0 },
+ { "support-ixfr", NULL, CFG_CLAUSEFLAG_ANCIENT },
+ { "tcp-keepalive", &cfg_type_boolean, 0 },
+ { "tcp-only", &cfg_type_boolean, 0 },
+ { "transfer-format", &cfg_type_transferformat, 0 },
+ { "transfer-source", &cfg_type_sockaddr4wild, 0 },
+ { "transfer-source-v6", &cfg_type_sockaddr6wild, 0 },
+ { "transfers", &cfg_type_uint32, 0 },
+ { NULL, NULL, 0 }
+};
+static cfg_clausedef_t *server_clausesets[] = { server_clauses, NULL };
+static cfg_type_t cfg_type_server = { "server", cfg_parse_netprefix_map,
+ cfg_print_map, cfg_doc_map,
+ &cfg_rep_map, server_clausesets };
+
+/*%
+ * Clauses that can be found in a 'channel' clause in the
+ * 'logging' statement.
+ *
+ * These have some additional constraints that need to be
+ * checked after parsing:
+ * - There must exactly one of file/syslog/null/stderr
+ */
+
+static const char *printtime_enums[] = { "iso8601", "iso8601-utc", "local",
+ NULL };
+static isc_result_t
+parse_printtime(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ return (cfg_parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
+}
+static void
+doc_printtime(cfg_printer_t *pctx, const cfg_type_t *type) {
+ cfg_doc_enum_or_other(pctx, type, &cfg_type_boolean);
+}
+static cfg_type_t cfg_type_printtime = { "printtime", parse_printtime,
+ cfg_print_ustring, doc_printtime,
+ &cfg_rep_string, printtime_enums };
+
+static cfg_clausedef_t channel_clauses[] = {
+ /* Destinations. We no longer require these to be first. */
+ { "file", &cfg_type_logfile, 0 },
+ { "syslog", &cfg_type_optional_facility, 0 },
+ { "null", &cfg_type_void, 0 },
+ { "stderr", &cfg_type_void, 0 },
+ /* Options. We now accept these for the null channel, too. */
+ { "severity", &cfg_type_logseverity, 0 },
+ { "print-time", &cfg_type_printtime, 0 },
+ { "print-severity", &cfg_type_boolean, 0 },
+ { "print-category", &cfg_type_boolean, 0 },
+ { "buffered", &cfg_type_boolean, 0 },
+ { NULL, NULL, 0 }
+};
+static cfg_clausedef_t *channel_clausesets[] = { channel_clauses, NULL };
+static cfg_type_t cfg_type_channel = { "channel", cfg_parse_named_map,
+ cfg_print_map, cfg_doc_map,
+ &cfg_rep_map, channel_clausesets };
+
+/*% A list of log destination, used in the "category" clause. */
+static cfg_type_t cfg_type_destinationlist = { "destinationlist",
+ cfg_parse_bracketed_list,
+ cfg_print_bracketed_list,
+ cfg_doc_bracketed_list,
+ &cfg_rep_list,
+ &cfg_type_astring };
+
+/*%
+ * Clauses that can be found in a 'logging' statement.
+ */
+static cfg_clausedef_t logging_clauses[] = {
+ { "channel", &cfg_type_channel, CFG_CLAUSEFLAG_MULTI },
+ { "category", &cfg_type_category, CFG_CLAUSEFLAG_MULTI },
+ { NULL, NULL, 0 }
+};
+static cfg_clausedef_t *logging_clausesets[] = { logging_clauses, NULL };
+static cfg_type_t cfg_type_logging = { "logging", cfg_parse_map,
+ cfg_print_map, cfg_doc_map,
+ &cfg_rep_map, logging_clausesets };
+
+/*%
+ * For parsing an 'addzone' statement
+ */
+static cfg_tuplefielddef_t addzone_fields[] = {
+ { "name", &cfg_type_astring, 0 },
+ { "class", &cfg_type_optional_class, 0 },
+ { "view", &cfg_type_optional_class, 0 },
+ { "options", &cfg_type_zoneopts, 0 },
+ { NULL, NULL, 0 }
+};
+static cfg_type_t cfg_type_addzone = { "zone", cfg_parse_tuple,
+ cfg_print_tuple, cfg_doc_tuple,
+ &cfg_rep_tuple, addzone_fields };
+
+static cfg_clausedef_t addzoneconf_clauses[] = {
+ { "zone", &cfg_type_addzone, CFG_CLAUSEFLAG_MULTI }, { NULL, NULL, 0 }
+};
+
+static cfg_clausedef_t *addzoneconf_clausesets[] = { addzoneconf_clauses,
+ NULL };
+
+cfg_type_t cfg_type_addzoneconf = { "addzoneconf", cfg_parse_mapbody,
+ cfg_print_mapbody, cfg_doc_mapbody,
+ &cfg_rep_map, addzoneconf_clausesets };
+
+static isc_result_t
+parse_unitstring(char *str, isc_resourcevalue_t *valuep) {
+ char *endp;
+ unsigned int len;
+ uint64_t value;
+ uint64_t unit;
+
+ value = strtoull(str, &endp, 10);
+ if (*endp == 0) {
+ *valuep = value;
+ return (ISC_R_SUCCESS);
+ }
+
+ len = strlen(str);
+ if (len < 2 || endp[1] != '\0') {
+ return (ISC_R_FAILURE);
+ }
+
+ switch (str[len - 1]) {
+ case 'k':
+ case 'K':
+ unit = 1024;
+ break;
+ case 'm':
+ case 'M':
+ unit = 1024 * 1024;
+ break;
+ case 'g':
+ case 'G':
+ unit = 1024 * 1024 * 1024;
+ break;
+ default:
+ return (ISC_R_FAILURE);
+ }
+ if (value > ((uint64_t)UINT64_MAX / unit)) {
+ return (ISC_R_FAILURE);
+ }
+ *valuep = value * unit;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+parse_sizeval(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+ cfg_obj_t *obj = NULL;
+ uint64_t val;
+
+ UNUSED(type);
+
+ CHECK(cfg_gettoken(pctx, 0));
+ if (pctx->token.type != isc_tokentype_string) {
+ result = ISC_R_UNEXPECTEDTOKEN;
+ goto cleanup;
+ }
+ CHECK(parse_unitstring(TOKEN_STRING(pctx), &val));
+
+ CHECK(cfg_create_obj(pctx, &cfg_type_uint64, &obj));
+ obj->value.uint64 = val;
+ *ret = obj;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "expected integer and optional unit");
+ return (result);
+}
+
+static isc_result_t
+parse_sizeval_percent(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ char *endp;
+ isc_result_t result;
+ cfg_obj_t *obj = NULL;
+ uint64_t val;
+ uint64_t percent;
+
+ UNUSED(type);
+
+ CHECK(cfg_gettoken(pctx, 0));
+ if (pctx->token.type != isc_tokentype_string) {
+ result = ISC_R_UNEXPECTEDTOKEN;
+ goto cleanup;
+ }
+
+ percent = strtoull(TOKEN_STRING(pctx), &endp, 10);
+
+ if (*endp == '%' && *(endp + 1) == 0) {
+ CHECK(cfg_create_obj(pctx, &cfg_type_percentage, &obj));
+ obj->value.uint32 = (uint32_t)percent;
+ *ret = obj;
+ return (ISC_R_SUCCESS);
+ } else {
+ CHECK(parse_unitstring(TOKEN_STRING(pctx), &val));
+ CHECK(cfg_create_obj(pctx, &cfg_type_uint64, &obj));
+ obj->value.uint64 = val;
+ *ret = obj;
+ return (ISC_R_SUCCESS);
+ }
+
+cleanup:
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "expected integer and optional unit or percent");
+ return (result);
+}
+
+static void
+doc_sizeval_percent(cfg_printer_t *pctx, const cfg_type_t *type) {
+ UNUSED(type);
+
+ cfg_print_cstr(pctx, "( ");
+ cfg_doc_terminal(pctx, &cfg_type_size);
+ cfg_print_cstr(pctx, " | ");
+ cfg_doc_terminal(pctx, &cfg_type_percentage);
+ cfg_print_cstr(pctx, " )");
+}
+
+/*%
+ * A size value (number + optional unit).
+ */
+static cfg_type_t cfg_type_sizeval = { "sizeval", parse_sizeval,
+ cfg_print_uint64, cfg_doc_terminal,
+ &cfg_rep_uint64, NULL };
+
+/*%
+ * A size, "unlimited", or "default".
+ */
+
+static isc_result_t
+parse_size(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ return (cfg_parse_enum_or_other(pctx, type, &cfg_type_sizeval, ret));
+}
+
+static void
+doc_size(cfg_printer_t *pctx, const cfg_type_t *type) {
+ cfg_doc_enum_or_other(pctx, type, &cfg_type_sizeval);
+}
+
+static const char *size_enums[] = { "default", "unlimited", NULL };
+static cfg_type_t cfg_type_size = {
+ "size", parse_size, cfg_print_ustring,
+ doc_size, &cfg_rep_string, size_enums
+};
+
+/*%
+ * A size or "unlimited", but not "default".
+ */
+static const char *sizenodefault_enums[] = { "unlimited", NULL };
+static cfg_type_t cfg_type_sizenodefault = {
+ "size_no_default", parse_size, cfg_print_ustring,
+ doc_size, &cfg_rep_string, sizenodefault_enums
+};
+
+/*%
+ * A size in absolute values or percents.
+ */
+static cfg_type_t cfg_type_sizeval_percent = {
+ "sizeval_percent", parse_sizeval_percent, cfg_print_ustring,
+ doc_sizeval_percent, &cfg_rep_string, NULL
+};
+
+/*%
+ * A size in absolute values or percents, or "unlimited", or "default"
+ */
+
+static isc_result_t
+parse_size_or_percent(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ return (cfg_parse_enum_or_other(pctx, type, &cfg_type_sizeval_percent,
+ ret));
+}
+
+static void
+doc_parse_size_or_percent(cfg_printer_t *pctx, const cfg_type_t *type) {
+ UNUSED(type);
+ cfg_print_cstr(pctx, "( default | unlimited | ");
+ cfg_doc_terminal(pctx, &cfg_type_sizeval);
+ cfg_print_cstr(pctx, " | ");
+ cfg_doc_terminal(pctx, &cfg_type_percentage);
+ cfg_print_cstr(pctx, " )");
+}
+
+static const char *sizeorpercent_enums[] = { "default", "unlimited", NULL };
+static cfg_type_t cfg_type_sizeorpercent = {
+ "size_or_percent", parse_size_or_percent, cfg_print_ustring,
+ doc_parse_size_or_percent, &cfg_rep_string, sizeorpercent_enums
+};
+
+/*%
+ * An IXFR size ratio: percentage, or "unlimited".
+ */
+
+static isc_result_t
+parse_ixfrratio(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ return (cfg_parse_enum_or_other(pctx, type, &cfg_type_percentage, ret));
+}
+
+static void
+doc_ixfrratio(cfg_printer_t *pctx, const cfg_type_t *type) {
+ UNUSED(type);
+ cfg_print_cstr(pctx, "( unlimited | ");
+ cfg_doc_terminal(pctx, &cfg_type_percentage);
+ cfg_print_cstr(pctx, " )");
+}
+
+static const char *ixfrratio_enums[] = { "unlimited", NULL };
+static cfg_type_t cfg_type_ixfrratio = { "ixfr_ratio", parse_ixfrratio,
+ NULL, doc_ixfrratio,
+ NULL, ixfrratio_enums };
+
+/*%
+ * optional_keyvalue
+ */
+static isc_result_t
+parse_maybe_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type,
+ bool optional, cfg_obj_t **ret) {
+ isc_result_t result;
+ cfg_obj_t *obj = NULL;
+ const keyword_type_t *kw = type->of;
+
+ CHECK(cfg_peektoken(pctx, 0));
+ if (pctx->token.type == isc_tokentype_string &&
+ strcasecmp(TOKEN_STRING(pctx), kw->name) == 0)
+ {
+ CHECK(cfg_gettoken(pctx, 0));
+ CHECK(kw->type->parse(pctx, kw->type, &obj));
+ obj->type = type; /* XXX kludge */
+ } else {
+ if (optional) {
+ CHECK(cfg_parse_void(pctx, NULL, &obj));
+ } else {
+ cfg_parser_error(pctx, CFG_LOG_NEAR, "expected '%s'",
+ kw->name);
+ result = ISC_R_UNEXPECTEDTOKEN;
+ goto cleanup;
+ }
+ }
+ *ret = obj;
+cleanup:
+ return (result);
+}
+
+static isc_result_t
+parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ return (parse_maybe_optional_keyvalue(pctx, type, false, ret));
+}
+
+static isc_result_t
+parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ return (parse_maybe_optional_keyvalue(pctx, type, true, ret));
+}
+
+static void
+print_keyvalue(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ const keyword_type_t *kw = obj->type->of;
+ cfg_print_cstr(pctx, kw->name);
+ cfg_print_cstr(pctx, " ");
+ kw->type->print(pctx, obj);
+}
+
+static void
+doc_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type) {
+ const keyword_type_t *kw = type->of;
+ cfg_print_cstr(pctx, kw->name);
+ cfg_print_cstr(pctx, " ");
+ cfg_doc_obj(pctx, kw->type);
+}
+
+static void
+doc_optional_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type) {
+ const keyword_type_t *kw = type->of;
+ cfg_print_cstr(pctx, "[ ");
+ cfg_print_cstr(pctx, kw->name);
+ cfg_print_cstr(pctx, " ");
+ cfg_doc_obj(pctx, kw->type);
+ cfg_print_cstr(pctx, " ]");
+}
+
+static const char *dialup_enums[] = { "notify", "notify-passive", "passive",
+ "refresh", NULL };
+static isc_result_t
+parse_dialup_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ return (cfg_parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
+}
+static void
+doc_dialup_type(cfg_printer_t *pctx, const cfg_type_t *type) {
+ cfg_doc_enum_or_other(pctx, type, &cfg_type_boolean);
+}
+static cfg_type_t cfg_type_dialuptype = { "dialuptype", parse_dialup_type,
+ cfg_print_ustring, doc_dialup_type,
+ &cfg_rep_string, dialup_enums };
+
+static const char *notify_enums[] = { "explicit", "master-only", "primary-only",
+ NULL };
+static isc_result_t
+parse_notify_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ return (cfg_parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
+}
+static void
+doc_notify_type(cfg_printer_t *pctx, const cfg_type_t *type) {
+ cfg_doc_enum_or_other(pctx, type, &cfg_type_boolean);
+}
+static cfg_type_t cfg_type_notifytype = {
+ "notifytype", parse_notify_type, cfg_print_ustring,
+ doc_notify_type, &cfg_rep_string, notify_enums,
+};
+
+static const char *minimal_enums[] = { "no-auth", "no-auth-recursive", NULL };
+static isc_result_t
+parse_minimal(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ return (cfg_parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
+}
+static void
+doc_minimal(cfg_printer_t *pctx, const cfg_type_t *type) {
+ cfg_doc_enum_or_other(pctx, type, &cfg_type_boolean);
+}
+static cfg_type_t cfg_type_minimal = {
+ "minimal", parse_minimal, cfg_print_ustring,
+ doc_minimal, &cfg_rep_string, minimal_enums,
+};
+
+static const char *ixfrdiff_enums[] = { "primary", "master", "secondary",
+ "slave", NULL };
+static isc_result_t
+parse_ixfrdiff_type(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ return (cfg_parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
+}
+static void
+doc_ixfrdiff_type(cfg_printer_t *pctx, const cfg_type_t *type) {
+ cfg_doc_enum_or_other(pctx, type, &cfg_type_boolean);
+}
+static cfg_type_t cfg_type_ixfrdifftype = {
+ "ixfrdiff", parse_ixfrdiff_type, cfg_print_ustring,
+ doc_ixfrdiff_type, &cfg_rep_string, ixfrdiff_enums,
+};
+
+static keyword_type_t key_kw = { "key", &cfg_type_astring };
+
+cfg_type_t cfg_type_keyref = { "keyref", parse_keyvalue, print_keyvalue,
+ doc_keyvalue, &cfg_rep_string, &key_kw };
+
+static cfg_type_t cfg_type_optional_keyref = {
+ "optional_keyref", parse_optional_keyvalue, print_keyvalue,
+ doc_optional_keyvalue, &cfg_rep_string, &key_kw
+};
+
+static const char *qminmethod_enums[] = { "strict", "relaxed", "disabled",
+ "off", NULL };
+
+static cfg_type_t cfg_type_qminmethod = { "qminmethod", cfg_parse_enum,
+ cfg_print_ustring, cfg_doc_enum,
+ &cfg_rep_string, qminmethod_enums };
+
+/*%
+ * A "controls" statement is represented as a map with the multivalued
+ * "inet" and "unix" clauses.
+ */
+
+static keyword_type_t controls_allow_kw = { "allow", &cfg_type_bracketed_aml };
+
+static cfg_type_t cfg_type_controls_allow = {
+ "controls_allow", parse_keyvalue, print_keyvalue,
+ doc_keyvalue, &cfg_rep_list, &controls_allow_kw
+};
+
+static keyword_type_t controls_keys_kw = { "keys", &cfg_type_keylist };
+
+static cfg_type_t cfg_type_controls_keys = {
+ "controls_keys", parse_optional_keyvalue, print_keyvalue,
+ doc_optional_keyvalue, &cfg_rep_list, &controls_keys_kw
+};
+
+static keyword_type_t controls_readonly_kw = { "read-only", &cfg_type_boolean };
+
+static cfg_type_t cfg_type_controls_readonly = {
+ "controls_readonly", parse_optional_keyvalue, print_keyvalue,
+ doc_optional_keyvalue, &cfg_rep_boolean, &controls_readonly_kw
+};
+
+static cfg_tuplefielddef_t inetcontrol_fields[] = {
+ { "address", &cfg_type_controls_sockaddr, 0 },
+ { "allow", &cfg_type_controls_allow, 0 },
+ { "keys", &cfg_type_controls_keys, 0 },
+ { "read-only", &cfg_type_controls_readonly, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_inetcontrol = {
+ "inetcontrol", cfg_parse_tuple, cfg_print_tuple,
+ cfg_doc_tuple, &cfg_rep_tuple, inetcontrol_fields
+};
+
+static keyword_type_t controls_perm_kw = { "perm", &cfg_type_uint32 };
+
+static cfg_type_t cfg_type_controls_perm = {
+ "controls_perm", parse_keyvalue, print_keyvalue,
+ doc_keyvalue, &cfg_rep_uint32, &controls_perm_kw
+};
+
+static keyword_type_t controls_owner_kw = { "owner", &cfg_type_uint32 };
+
+static cfg_type_t cfg_type_controls_owner = {
+ "controls_owner", parse_keyvalue, print_keyvalue,
+ doc_keyvalue, &cfg_rep_uint32, &controls_owner_kw
+};
+
+static keyword_type_t controls_group_kw = { "group", &cfg_type_uint32 };
+
+static cfg_type_t cfg_type_controls_group = {
+ "controls_allow", parse_keyvalue, print_keyvalue,
+ doc_keyvalue, &cfg_rep_uint32, &controls_group_kw
+};
+
+static cfg_tuplefielddef_t unixcontrol_fields[] = {
+ { "path", &cfg_type_qstring, 0 },
+ { "perm", &cfg_type_controls_perm, 0 },
+ { "owner", &cfg_type_controls_owner, 0 },
+ { "group", &cfg_type_controls_group, 0 },
+ { "keys", &cfg_type_controls_keys, 0 },
+ { "read-only", &cfg_type_controls_readonly, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_unixcontrol = {
+ "unixcontrol", cfg_parse_tuple, cfg_print_tuple,
+ cfg_doc_tuple, &cfg_rep_tuple, unixcontrol_fields
+};
+
+static cfg_clausedef_t controls_clauses[] = {
+ { "inet", &cfg_type_inetcontrol, CFG_CLAUSEFLAG_MULTI },
+ { "unix", &cfg_type_unixcontrol, CFG_CLAUSEFLAG_MULTI },
+ { NULL, NULL, 0 }
+};
+
+static cfg_clausedef_t *controls_clausesets[] = { controls_clauses, NULL };
+static cfg_type_t cfg_type_controls = { "controls", cfg_parse_map,
+ cfg_print_map, cfg_doc_map,
+ &cfg_rep_map, &controls_clausesets };
+
+/*%
+ * A "statistics-channels" statement is represented as a map with the
+ * multivalued "inet" clauses.
+ */
+static void
+doc_optional_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) {
+ const keyword_type_t *kw = type->of;
+ cfg_print_cstr(pctx, "[ ");
+ cfg_print_cstr(pctx, kw->name);
+ cfg_print_cstr(pctx, " ");
+ cfg_doc_obj(pctx, kw->type);
+ cfg_print_cstr(pctx, " ]");
+}
+
+static cfg_type_t cfg_type_optional_allow = {
+ "optional_allow", parse_optional_keyvalue,
+ print_keyvalue, doc_optional_bracketed_list,
+ &cfg_rep_list, &controls_allow_kw
+};
+
+static cfg_tuplefielddef_t statserver_fields[] = {
+ { "address", &cfg_type_controls_sockaddr, 0 }, /* reuse controls def */
+ { "allow", &cfg_type_optional_allow, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_statschannel = {
+ "statschannel", cfg_parse_tuple, cfg_print_tuple,
+ cfg_doc_tuple, &cfg_rep_tuple, statserver_fields
+};
+
+static cfg_clausedef_t statservers_clauses[] = {
+ { "inet", &cfg_type_statschannel, CFG_CLAUSEFLAG_MULTI },
+ { NULL, NULL, 0 }
+};
+
+static cfg_clausedef_t *statservers_clausesets[] = { statservers_clauses,
+ NULL };
+
+static cfg_type_t cfg_type_statschannels = {
+ "statistics-channels", cfg_parse_map, cfg_print_map,
+ cfg_doc_map, &cfg_rep_map, &statservers_clausesets
+};
+
+/*%
+ * An optional class, as used in view and zone statements.
+ */
+static isc_result_t
+parse_optional_class(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ isc_result_t result;
+ UNUSED(type);
+ CHECK(cfg_peektoken(pctx, 0));
+ if (pctx->token.type == isc_tokentype_string) {
+ CHECK(cfg_parse_obj(pctx, &cfg_type_ustring, ret));
+ } else {
+ CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
+ }
+cleanup:
+ return (result);
+}
+
+static void
+doc_optional_class(cfg_printer_t *pctx, const cfg_type_t *type) {
+ UNUSED(type);
+ cfg_print_cstr(pctx, "[ <class> ]");
+}
+
+static cfg_type_t cfg_type_optional_class = { "optional_class",
+ parse_optional_class,
+ NULL,
+ doc_optional_class,
+ NULL,
+ NULL };
+
+static isc_result_t
+parse_querysource(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+ cfg_obj_t *obj = NULL;
+ isc_netaddr_t netaddr;
+ in_port_t port = 0;
+ unsigned int have_address = 0;
+ unsigned int have_port = 0;
+ unsigned int have_dscp = 0;
+ const unsigned int *flagp = type->of;
+ int dscp = -1;
+
+ if ((*flagp & CFG_ADDR_V4OK) != 0) {
+ isc_netaddr_any(&netaddr);
+ } else if ((*flagp & CFG_ADDR_V6OK) != 0) {
+ isc_netaddr_any6(&netaddr);
+ } else {
+ UNREACHABLE();
+ }
+
+ for (;;) {
+ CHECK(cfg_peektoken(pctx, 0));
+ if (pctx->token.type == isc_tokentype_string) {
+ if (strcasecmp(TOKEN_STRING(pctx), "address") == 0) {
+ /* read "address" */
+ CHECK(cfg_gettoken(pctx, 0));
+ CHECK(cfg_parse_rawaddr(pctx, *flagp,
+ &netaddr));
+ have_address++;
+ } else if (strcasecmp(TOKEN_STRING(pctx), "port") == 0)
+ {
+ /* read "port" */
+ if ((pctx->flags & CFG_PCTX_NODEPRECATED) == 0)
+ {
+ cfg_parser_warning(
+ pctx, 0,
+ "token 'port' is deprecated");
+ }
+ CHECK(cfg_gettoken(pctx, 0));
+ CHECK(cfg_parse_rawport(pctx, CFG_ADDR_WILDOK,
+ &port));
+ have_port++;
+ } else if (strcasecmp(TOKEN_STRING(pctx), "dscp") == 0)
+ {
+ /* read "dscp" */
+ cfg_parser_warning(pctx, 0,
+ "'dscp' is obsolete and "
+ "should be removed");
+ CHECK(cfg_gettoken(pctx, 0));
+ CHECK(cfg_parse_uint32(pctx, NULL, &obj));
+ dscp = cfg_obj_asuint32(obj);
+ cfg_obj_destroy(pctx, &obj);
+ have_dscp++;
+ } else if (have_port == 0 && have_dscp == 0 &&
+ have_address == 0)
+ {
+ return (cfg_parse_sockaddr(pctx, type, ret));
+ } else {
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "expected 'address' "
+ "or 'port'");
+ return (ISC_R_UNEXPECTEDTOKEN);
+ }
+ } else {
+ break;
+ }
+ }
+ if (have_address > 1 || have_port > 1 || have_address + have_port == 0)
+ {
+ cfg_parser_error(pctx, 0, "expected one address and/or port");
+ return (ISC_R_UNEXPECTEDTOKEN);
+ }
+
+ if (have_dscp > 1) {
+ cfg_parser_error(pctx, 0, "expected at most one dscp");
+ return (ISC_R_UNEXPECTEDTOKEN);
+ }
+
+ CHECK(cfg_create_obj(pctx, &cfg_type_querysource, &obj));
+ isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
+ obj->value.sockaddrdscp.dscp = dscp;
+ *ret = obj;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ cfg_parser_error(pctx, CFG_LOG_NEAR, "invalid query source");
+ CLEANUP_OBJ(obj);
+ return (result);
+}
+
+static void
+print_querysource(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ isc_netaddr_t na;
+ isc_netaddr_fromsockaddr(&na, &obj->value.sockaddr);
+ cfg_print_cstr(pctx, "address ");
+ cfg_print_rawaddr(pctx, &na);
+ cfg_print_cstr(pctx, " port ");
+ cfg_print_rawuint(pctx, isc_sockaddr_getport(&obj->value.sockaddr));
+ if (obj->value.sockaddrdscp.dscp != -1) {
+ cfg_print_cstr(pctx, " dscp ");
+ cfg_print_rawuint(pctx, obj->value.sockaddrdscp.dscp);
+ }
+}
+
+static void
+doc_querysource(cfg_printer_t *pctx, const cfg_type_t *type) {
+ const unsigned int *flagp = type->of;
+
+ cfg_print_cstr(pctx, "[ address ] ( ");
+ if ((*flagp & CFG_ADDR_V4OK) != 0) {
+ cfg_print_cstr(pctx, "<ipv4_address>");
+ } else if ((*flagp & CFG_ADDR_V6OK) != 0) {
+ cfg_print_cstr(pctx, "<ipv6_address>");
+ } else {
+ UNREACHABLE();
+ }
+ cfg_print_cstr(pctx, " | * )");
+}
+
+static unsigned int sockaddr4wild_flags = CFG_ADDR_WILDOK | CFG_ADDR_V4OK |
+ CFG_ADDR_DSCPOK;
+static unsigned int sockaddr6wild_flags = CFG_ADDR_WILDOK | CFG_ADDR_V6OK |
+ CFG_ADDR_DSCPOK;
+
+static cfg_type_t cfg_type_querysource4 = {
+ "querysource4", parse_querysource, NULL, doc_querysource,
+ NULL, &sockaddr4wild_flags
+};
+
+static cfg_type_t cfg_type_querysource6 = {
+ "querysource6", parse_querysource, NULL, doc_querysource,
+ NULL, &sockaddr6wild_flags
+};
+
+static cfg_type_t cfg_type_querysource = { "querysource", NULL,
+ print_querysource, NULL,
+ &cfg_rep_sockaddr, NULL };
+
+/*%
+ * The socket address syntax in the "controls" statement is silly.
+ * It allows both socket address families, but also allows "*",
+ * which is gratuitously interpreted as the IPv4 wildcard address.
+ */
+static unsigned int controls_sockaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK |
+ CFG_ADDR_WILDOK | CFG_ADDR_PORTOK;
+static cfg_type_t cfg_type_controls_sockaddr = {
+ "controls_sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr,
+ cfg_doc_sockaddr, &cfg_rep_sockaddr, &controls_sockaddr_flags
+};
+
+/*%
+ * Handle the special kludge syntax of the "keys" clause in the "server"
+ * statement, which takes a single key with or without braces and semicolon.
+ */
+static isc_result_t
+parse_server_key_kludge(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ isc_result_t result;
+ bool braces = false;
+ UNUSED(type);
+
+ /* Allow opening brace. */
+ CHECK(cfg_peektoken(pctx, 0));
+ if (pctx->token.type == isc_tokentype_special &&
+ pctx->token.value.as_char == '{')
+ {
+ CHECK(cfg_gettoken(pctx, 0));
+ braces = true;
+ }
+
+ CHECK(cfg_parse_obj(pctx, &cfg_type_astring, ret));
+
+ if (braces) {
+ /* Skip semicolon if present. */
+ CHECK(cfg_peektoken(pctx, 0));
+ if (pctx->token.type == isc_tokentype_special &&
+ pctx->token.value.as_char == ';')
+ {
+ CHECK(cfg_gettoken(pctx, 0));
+ }
+
+ CHECK(cfg_parse_special(pctx, '}'));
+ }
+cleanup:
+ return (result);
+}
+static cfg_type_t cfg_type_server_key_kludge = {
+ "server_key", parse_server_key_kludge, NULL, cfg_doc_terminal, NULL,
+ NULL
+};
+
+/*%
+ * An optional logging facility.
+ */
+
+static isc_result_t
+parse_optional_facility(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ isc_result_t result;
+ UNUSED(type);
+
+ CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
+ if (pctx->token.type == isc_tokentype_string ||
+ pctx->token.type == isc_tokentype_qstring)
+ {
+ CHECK(cfg_parse_obj(pctx, &cfg_type_astring, ret));
+ } else {
+ CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
+ }
+cleanup:
+ return (result);
+}
+
+static void
+doc_optional_facility(cfg_printer_t *pctx, const cfg_type_t *type) {
+ UNUSED(type);
+ cfg_print_cstr(pctx, "[ <syslog_facility> ]");
+}
+
+static cfg_type_t cfg_type_optional_facility = { "optional_facility",
+ parse_optional_facility,
+ NULL,
+ doc_optional_facility,
+ NULL,
+ NULL };
+
+/*%
+ * A log severity. Return as a string, except "debug N",
+ * which is returned as a keyword object.
+ */
+
+static keyword_type_t debug_kw = { "debug", &cfg_type_uint32 };
+static cfg_type_t cfg_type_debuglevel = { "debuglevel", parse_keyvalue,
+ print_keyvalue, doc_keyvalue,
+ &cfg_rep_uint32, &debug_kw };
+
+static isc_result_t
+parse_logseverity(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+ UNUSED(type);
+
+ CHECK(cfg_peektoken(pctx, 0));
+ if (pctx->token.type == isc_tokentype_string &&
+ strcasecmp(TOKEN_STRING(pctx), "debug") == 0)
+ {
+ CHECK(cfg_gettoken(pctx, 0)); /* read "debug" */
+ CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER));
+ if (pctx->token.type == isc_tokentype_number) {
+ CHECK(cfg_parse_uint32(pctx, NULL, ret));
+ } else {
+ /*
+ * The debug level is optional and defaults to 1.
+ * This makes little sense, but we support it for
+ * compatibility with BIND 8.
+ */
+ CHECK(cfg_create_obj(pctx, &cfg_type_uint32, ret));
+ (*ret)->value.uint32 = 1;
+ }
+ (*ret)->type = &cfg_type_debuglevel; /* XXX kludge */
+ } else {
+ CHECK(cfg_parse_obj(pctx, &cfg_type_loglevel, ret));
+ }
+cleanup:
+ return (result);
+}
+
+static cfg_type_t cfg_type_logseverity = { "log_severity", parse_logseverity,
+ NULL, cfg_doc_terminal,
+ NULL, NULL };
+
+/*%
+ * The "file" clause of the "channel" statement.
+ * This is yet another special case.
+ */
+
+static const char *logversions_enums[] = { "unlimited", NULL };
+static isc_result_t
+parse_logversions(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ return (cfg_parse_enum_or_other(pctx, type, &cfg_type_uint32, ret));
+}
+
+static void
+doc_logversions(cfg_printer_t *pctx, const cfg_type_t *type) {
+ cfg_doc_enum_or_other(pctx, type, &cfg_type_uint32);
+}
+
+static cfg_type_t cfg_type_logversions = {
+ "logversions", parse_logversions, cfg_print_ustring,
+ doc_logversions, &cfg_rep_string, logversions_enums
+};
+
+static const char *logsuffix_enums[] = { "increment", "timestamp", NULL };
+static cfg_type_t cfg_type_logsuffix = { "logsuffix", cfg_parse_enum,
+ cfg_print_ustring, cfg_doc_enum,
+ &cfg_rep_string, &logsuffix_enums };
+
+static cfg_tuplefielddef_t logfile_fields[] = {
+ { "file", &cfg_type_qstring, 0 },
+ { "versions", &cfg_type_logversions, 0 },
+ { "size", &cfg_type_size, 0 },
+ { "suffix", &cfg_type_logsuffix, 0 },
+ { NULL, NULL, 0 }
+};
+
+static isc_result_t
+parse_logfile(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+ cfg_obj_t *obj = NULL;
+ const cfg_tuplefielddef_t *fields = type->of;
+
+ CHECK(cfg_create_tuple(pctx, type, &obj));
+
+ /* Parse the mandatory "file" field */
+ CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0]));
+
+ /* Parse "versions" and "size" fields in any order. */
+ for (;;) {
+ CHECK(cfg_peektoken(pctx, 0));
+ if (pctx->token.type == isc_tokentype_string) {
+ CHECK(cfg_gettoken(pctx, 0));
+ if (strcasecmp(TOKEN_STRING(pctx), "versions") == 0 &&
+ obj->value.tuple[1] == NULL)
+ {
+ CHECK(cfg_parse_obj(pctx, fields[1].type,
+ &obj->value.tuple[1]));
+ } else if (strcasecmp(TOKEN_STRING(pctx), "size") ==
+ 0 &&
+ obj->value.tuple[2] == NULL)
+ {
+ CHECK(cfg_parse_obj(pctx, fields[2].type,
+ &obj->value.tuple[2]));
+ } else if (strcasecmp(TOKEN_STRING(pctx), "suffix") ==
+ 0 &&
+ obj->value.tuple[3] == NULL)
+ {
+ CHECK(cfg_parse_obj(pctx, fields[3].type,
+ &obj->value.tuple[3]));
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+
+ /* Create void objects for missing optional values. */
+ if (obj->value.tuple[1] == NULL) {
+ CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[1]));
+ }
+ if (obj->value.tuple[2] == NULL) {
+ CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[2]));
+ }
+ if (obj->value.tuple[3] == NULL) {
+ CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[3]));
+ }
+
+ *ret = obj;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ CLEANUP_OBJ(obj);
+ return (result);
+}
+
+static void
+print_logfile(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ cfg_print_obj(pctx, obj->value.tuple[0]); /* file */
+ if (obj->value.tuple[1]->type->print != cfg_print_void) {
+ cfg_print_cstr(pctx, " versions ");
+ cfg_print_obj(pctx, obj->value.tuple[1]);
+ }
+ if (obj->value.tuple[2]->type->print != cfg_print_void) {
+ cfg_print_cstr(pctx, " size ");
+ cfg_print_obj(pctx, obj->value.tuple[2]);
+ }
+ if (obj->value.tuple[3]->type->print != cfg_print_void) {
+ cfg_print_cstr(pctx, " suffix ");
+ cfg_print_obj(pctx, obj->value.tuple[3]);
+ }
+}
+
+static void
+doc_logfile(cfg_printer_t *pctx, const cfg_type_t *type) {
+ UNUSED(type);
+ cfg_print_cstr(pctx, "<quoted_string>");
+ cfg_print_cstr(pctx, " ");
+ cfg_print_cstr(pctx, "[ versions ( unlimited | <integer> ) ]");
+ cfg_print_cstr(pctx, " ");
+ cfg_print_cstr(pctx, "[ size <size> ]");
+ cfg_print_cstr(pctx, " ");
+ cfg_print_cstr(pctx, "[ suffix ( increment | timestamp ) ]");
+}
+
+static cfg_type_t cfg_type_logfile = { "log_file", parse_logfile,
+ print_logfile, doc_logfile,
+ &cfg_rep_tuple, logfile_fields };
+
+/*% An IPv4 address, "*" accepted as wildcard. */
+static cfg_type_t cfg_type_sockaddr4wild = {
+ "sockaddr4wild", cfg_parse_sockaddr, cfg_print_sockaddr,
+ cfg_doc_sockaddr, &cfg_rep_sockaddr, &sockaddr4wild_flags
+};
+
+/*% An IPv6 address, "*" accepted as wildcard. */
+static cfg_type_t cfg_type_sockaddr6wild = {
+ "v6addrportwild", cfg_parse_sockaddr, cfg_print_sockaddr,
+ cfg_doc_sockaddr, &cfg_rep_sockaddr, &sockaddr6wild_flags
+};
+
+/*%
+ * rndc
+ */
+
+static cfg_clausedef_t rndcconf_options_clauses[] = {
+ { "default-key", &cfg_type_astring, 0 },
+ { "default-port", &cfg_type_uint32, 0 },
+ { "default-server", &cfg_type_astring, 0 },
+ { "default-source-address", &cfg_type_netaddr4wild, 0 },
+ { "default-source-address-v6", &cfg_type_netaddr6wild, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_clausedef_t *rndcconf_options_clausesets[] = {
+ rndcconf_options_clauses, NULL
+};
+
+static cfg_type_t cfg_type_rndcconf_options = {
+ "rndcconf_options", cfg_parse_map, cfg_print_map,
+ cfg_doc_map, &cfg_rep_map, rndcconf_options_clausesets
+};
+
+static cfg_clausedef_t rndcconf_server_clauses[] = {
+ { "key", &cfg_type_astring, 0 },
+ { "port", &cfg_type_uint32, 0 },
+ { "source-address", &cfg_type_netaddr4wild, 0 },
+ { "source-address-v6", &cfg_type_netaddr6wild, 0 },
+ { "addresses", &cfg_type_bracketed_sockaddrnameportlist, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_clausedef_t *rndcconf_server_clausesets[] = {
+ rndcconf_server_clauses, NULL
+};
+
+static cfg_type_t cfg_type_rndcconf_server = {
+ "rndcconf_server", cfg_parse_named_map, cfg_print_map,
+ cfg_doc_map, &cfg_rep_map, rndcconf_server_clausesets
+};
+
+static cfg_clausedef_t rndcconf_clauses[] = {
+ { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI },
+ { "server", &cfg_type_rndcconf_server, CFG_CLAUSEFLAG_MULTI },
+ { "options", &cfg_type_rndcconf_options, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_clausedef_t *rndcconf_clausesets[] = { rndcconf_clauses, NULL };
+
+cfg_type_t cfg_type_rndcconf = { "rndcconf", cfg_parse_mapbody,
+ cfg_print_mapbody, cfg_doc_mapbody,
+ &cfg_rep_map, rndcconf_clausesets };
+
+static cfg_clausedef_t rndckey_clauses[] = { { "key", &cfg_type_key, 0 },
+ { NULL, NULL, 0 } };
+
+static cfg_clausedef_t *rndckey_clausesets[] = { rndckey_clauses, NULL };
+
+cfg_type_t cfg_type_rndckey = { "rndckey", cfg_parse_mapbody,
+ cfg_print_mapbody, cfg_doc_mapbody,
+ &cfg_rep_map, rndckey_clausesets };
+
+/*
+ * session.key has exactly the same syntax as rndc.key, but it's defined
+ * separately for clarity (and so we can extend it someday, if needed).
+ */
+cfg_type_t cfg_type_sessionkey = { "sessionkey", cfg_parse_mapbody,
+ cfg_print_mapbody, cfg_doc_mapbody,
+ &cfg_rep_map, rndckey_clausesets };
+
+static cfg_tuplefielddef_t nameport_fields[] = {
+ { "name", &cfg_type_astring, 0 },
+ { "port", &cfg_type_optional_port, 0 },
+ { "dscp", &cfg_type_optional_dscp, CFG_CLAUSEFLAG_OBSOLETE },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_nameport = { "nameport", cfg_parse_tuple,
+ cfg_print_tuple, cfg_doc_tuple,
+ &cfg_rep_tuple, nameport_fields };
+
+static void
+doc_sockaddrnameport(cfg_printer_t *pctx, const cfg_type_t *type) {
+ UNUSED(type);
+ cfg_print_cstr(pctx, "( ");
+ cfg_print_cstr(pctx, "<quoted_string>");
+ cfg_print_cstr(pctx, " ");
+ cfg_print_cstr(pctx, "[ port <integer> ]");
+ cfg_print_cstr(pctx, " | ");
+ cfg_print_cstr(pctx, "<ipv4_address>");
+ cfg_print_cstr(pctx, " ");
+ cfg_print_cstr(pctx, "[ port <integer> ]");
+ cfg_print_cstr(pctx, " | ");
+ cfg_print_cstr(pctx, "<ipv6_address>");
+ cfg_print_cstr(pctx, " ");
+ cfg_print_cstr(pctx, "[ port <integer> ]");
+ cfg_print_cstr(pctx, " )");
+}
+
+static isc_result_t
+parse_sockaddrnameport(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ isc_result_t result;
+ cfg_obj_t *obj = NULL;
+ UNUSED(type);
+
+ CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
+ if (pctx->token.type == isc_tokentype_string ||
+ pctx->token.type == isc_tokentype_qstring)
+ {
+ if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK))
+ {
+ CHECK(cfg_parse_sockaddr(pctx, &cfg_type_sockaddr,
+ ret));
+ } else {
+ const cfg_tuplefielddef_t *fields =
+ cfg_type_nameport.of;
+ CHECK(cfg_create_tuple(pctx, &cfg_type_nameport, &obj));
+ CHECK(cfg_parse_obj(pctx, fields[0].type,
+ &obj->value.tuple[0]));
+ CHECK(cfg_parse_obj(pctx, fields[1].type,
+ &obj->value.tuple[1]));
+ CHECK(cfg_parse_obj(pctx, fields[2].type,
+ &obj->value.tuple[2]));
+ *ret = obj;
+ obj = NULL;
+ }
+ } else {
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "expected IP address or hostname");
+ return (ISC_R_UNEXPECTEDTOKEN);
+ }
+cleanup:
+ CLEANUP_OBJ(obj);
+ return (result);
+}
+
+static cfg_type_t cfg_type_sockaddrnameport = { "sockaddrnameport_element",
+ parse_sockaddrnameport,
+ NULL,
+ doc_sockaddrnameport,
+ NULL,
+ NULL };
+
+static cfg_type_t cfg_type_bracketed_sockaddrnameportlist = {
+ "bracketed_sockaddrnameportlist",
+ cfg_parse_bracketed_list,
+ cfg_print_bracketed_list,
+ cfg_doc_bracketed_list,
+ &cfg_rep_list,
+ &cfg_type_sockaddrnameport
+};
+
+/*%
+ * A list of socket addresses or name with an optional default port,
+ * as used in the dual-stack-servers option. E.g.,
+ * "port 1234 { dual-stack-servers.net; 10.0.0.1; 1::2 port 69; }"
+ */
+static cfg_tuplefielddef_t nameportiplist_fields[] = {
+ { "port", &cfg_type_optional_port, 0 },
+ { "addresses", &cfg_type_bracketed_sockaddrnameportlist, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_nameportiplist = {
+ "nameportiplist", cfg_parse_tuple, cfg_print_tuple,
+ cfg_doc_tuple, &cfg_rep_tuple, nameportiplist_fields
+};
+
+/*%
+ * remote servers element.
+ */
+
+static void
+doc_remoteselement(cfg_printer_t *pctx, const cfg_type_t *type) {
+ UNUSED(type);
+ cfg_print_cstr(pctx, "( ");
+ cfg_print_cstr(pctx, "<remote-servers>");
+ cfg_print_cstr(pctx, " | ");
+ cfg_print_cstr(pctx, "<ipv4_address>");
+ cfg_print_cstr(pctx, " ");
+ cfg_print_cstr(pctx, "[ port <integer> ]");
+ cfg_print_cstr(pctx, " | ");
+ cfg_print_cstr(pctx, "<ipv6_address>");
+ cfg_print_cstr(pctx, " ");
+ cfg_print_cstr(pctx, "[ port <integer> ]");
+ cfg_print_cstr(pctx, " )");
+}
+
+static isc_result_t
+parse_remoteselement(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ isc_result_t result;
+ cfg_obj_t *obj = NULL;
+ UNUSED(type);
+
+ CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
+ if (pctx->token.type == isc_tokentype_string ||
+ pctx->token.type == isc_tokentype_qstring)
+ {
+ if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK))
+ {
+ CHECK(cfg_parse_sockaddr(pctx, &cfg_type_sockaddr,
+ ret));
+ } else {
+ CHECK(cfg_parse_astring(pctx, &cfg_type_astring, ret));
+ }
+ } else {
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "expected IP address or remote servers list "
+ "name");
+ return (ISC_R_UNEXPECTEDTOKEN);
+ }
+cleanup:
+ CLEANUP_OBJ(obj);
+ return (result);
+}
+
+static cfg_type_t cfg_type_remoteselement = { "remotes_element",
+ parse_remoteselement,
+ NULL,
+ doc_remoteselement,
+ NULL,
+ NULL };
+
+static int
+cmp_clause(const void *ap, const void *bp) {
+ const cfg_clausedef_t *a = (const cfg_clausedef_t *)ap;
+ const cfg_clausedef_t *b = (const cfg_clausedef_t *)bp;
+ return (strcmp(a->name, b->name));
+}
+
+bool
+cfg_clause_validforzone(const char *name, unsigned int ztype) {
+ const cfg_clausedef_t *clause;
+ bool valid = false;
+
+ for (clause = zone_clauses; clause->name != NULL; clause++) {
+ if ((clause->flags & ztype) == 0 ||
+ strcmp(clause->name, name) != 0)
+ {
+ continue;
+ }
+ valid = true;
+ }
+ for (clause = zone_only_clauses; clause->name != NULL; clause++) {
+ if ((clause->flags & ztype) == 0 ||
+ strcmp(clause->name, name) != 0)
+ {
+ continue;
+ }
+ valid = true;
+ }
+
+ return (valid);
+}
+
+void
+cfg_print_zonegrammar(const unsigned int zonetype, unsigned int flags,
+ void (*f)(void *closure, const char *text, int textlen),
+ void *closure) {
+#define NCLAUSES \
+ (((sizeof(zone_clauses) + sizeof(zone_only_clauses)) / \
+ sizeof(clause[0])) - \
+ 1)
+
+ cfg_printer_t pctx;
+ cfg_clausedef_t *clause = NULL;
+ cfg_clausedef_t clauses[NCLAUSES];
+
+ pctx.f = f;
+ pctx.closure = closure;
+ pctx.indent = 0;
+ pctx.flags = flags;
+
+ memmove(clauses, zone_clauses, sizeof(zone_clauses));
+ memmove(clauses + sizeof(zone_clauses) / sizeof(zone_clauses[0]) - 1,
+ zone_only_clauses, sizeof(zone_only_clauses));
+ qsort(clauses, NCLAUSES - 1, sizeof(clause[0]), cmp_clause);
+
+ cfg_print_cstr(&pctx, "zone <string> [ <class> ] {\n");
+ pctx.indent++;
+
+ switch (zonetype) {
+ case CFG_ZONE_PRIMARY:
+ cfg_print_indent(&pctx);
+ cfg_print_cstr(&pctx, "type primary;\n");
+ break;
+ case CFG_ZONE_SECONDARY:
+ cfg_print_indent(&pctx);
+ cfg_print_cstr(&pctx, "type secondary;\n");
+ break;
+ case CFG_ZONE_MIRROR:
+ cfg_print_indent(&pctx);
+ cfg_print_cstr(&pctx, "type mirror;\n");
+ break;
+ case CFG_ZONE_STUB:
+ cfg_print_indent(&pctx);
+ cfg_print_cstr(&pctx, "type stub;\n");
+ break;
+ case CFG_ZONE_HINT:
+ cfg_print_indent(&pctx);
+ cfg_print_cstr(&pctx, "type hint;\n");
+ break;
+ case CFG_ZONE_FORWARD:
+ cfg_print_indent(&pctx);
+ cfg_print_cstr(&pctx, "type forward;\n");
+ break;
+ case CFG_ZONE_STATICSTUB:
+ cfg_print_indent(&pctx);
+ cfg_print_cstr(&pctx, "type static-stub;\n");
+ break;
+ case CFG_ZONE_REDIRECT:
+ cfg_print_indent(&pctx);
+ cfg_print_cstr(&pctx, "type redirect;\n");
+ break;
+ case CFG_ZONE_DELEGATION:
+ cfg_print_indent(&pctx);
+ cfg_print_cstr(&pctx, "type delegation-only;\n");
+ break;
+ case CFG_ZONE_INVIEW:
+ /* no zone type is specified for these */
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ for (clause = clauses; clause->name != NULL; clause++) {
+ if (((pctx.flags & CFG_PRINTER_ACTIVEONLY) != 0) &&
+ (((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0) ||
+ ((clause->flags & CFG_CLAUSEFLAG_TESTONLY) != 0)))
+ {
+ continue;
+ }
+ if ((clause->flags & CFG_CLAUSEFLAG_ANCIENT) != 0 ||
+ (clause->flags & CFG_CLAUSEFLAG_NODOC) != 0)
+ {
+ continue;
+ }
+
+ if ((clause->flags & zonetype) == 0 ||
+ strcasecmp(clause->name, "type") == 0)
+ {
+ continue;
+ }
+ cfg_print_indent(&pctx);
+ cfg_print_cstr(&pctx, clause->name);
+ cfg_print_cstr(&pctx, " ");
+ cfg_doc_obj(&pctx, clause->type);
+ cfg_print_cstr(&pctx, ";");
+ cfg_print_clauseflags(&pctx, clause->flags);
+ cfg_print_cstr(&pctx, "\n");
+ }
+
+ pctx.indent--;
+ cfg_print_cstr(&pctx, "};\n");
+}
+
+/*%
+ * "tls" and related statement syntax.
+ */
+static cfg_type_t cfg_type_tlsprotos = { "tls_protocols",
+ cfg_parse_bracketed_list,
+ cfg_print_bracketed_list,
+ cfg_doc_bracketed_list,
+ &cfg_rep_list,
+ &cfg_type_astring };
+
+static cfg_clausedef_t tls_clauses[] = {
+ { "key-file", &cfg_type_qstring, 0 },
+ { "cert-file", &cfg_type_qstring, 0 },
+ { "ca-file", &cfg_type_qstring, 0 },
+ { "remote-hostname", &cfg_type_qstring, 0 },
+ { "dhparam-file", &cfg_type_qstring, 0 },
+ { "protocols", &cfg_type_tlsprotos, 0 },
+ { "ciphers", &cfg_type_astring, 0 },
+ { "prefer-server-ciphers", &cfg_type_boolean, 0 },
+ { "session-tickets", &cfg_type_boolean, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_clausedef_t *tls_clausesets[] = { tls_clauses, NULL };
+static cfg_type_t cfg_type_tlsconf = { "tlsconf", cfg_parse_named_map,
+ cfg_print_map, cfg_doc_map,
+ &cfg_rep_map, tls_clausesets };
+
+static keyword_type_t tls_kw = { "tls", &cfg_type_astring };
+static cfg_type_t cfg_type_optional_tls = {
+ "tlsoptional", parse_optional_keyvalue, print_keyvalue,
+ doc_optional_keyvalue, &cfg_rep_string, &tls_kw
+};
+
+/* http and https */
+
+static cfg_type_t cfg_type_bracketed_http_endpoint_list = {
+ "bracketed_http_endpoint_list",
+ cfg_parse_bracketed_list,
+ cfg_print_bracketed_list,
+ cfg_doc_bracketed_list,
+ &cfg_rep_list,
+ &cfg_type_qstring
+};
+
+static cfg_clausedef_t cfg_http_description_clauses[] = {
+ { "endpoints", &cfg_type_bracketed_http_endpoint_list, 0 },
+ { "listener-clients", &cfg_type_uint32, 0 },
+ { "streams-per-connection", &cfg_type_uint32, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_clausedef_t *http_description_clausesets[] = {
+ cfg_http_description_clauses, NULL
+};
+
+static cfg_type_t cfg_type_http_description = {
+ "http_desc", cfg_parse_named_map, cfg_print_map,
+ cfg_doc_map, &cfg_rep_map, http_description_clausesets
+};
diff --git a/lib/isccfg/parser.c b/lib/isccfg/parser.c
new file mode 100644
index 0000000..8bee734
--- /dev/null
+++ b/lib/isccfg/parser.c
@@ -0,0 +1,3901 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0 AND BSD-2-Clause
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*
+ * Copyright (c) 2009-2018 NLNet Labs.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
+ */
+
+/*! \file */
+
+#include <ctype.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/buffer.h>
+#include <isc/dir.h>
+#include <isc/errno.h>
+#include <isc/formatcheck.h>
+#include <isc/glob.h>
+#include <isc/lex.h>
+#include <isc/log.h>
+#include <isc/mem.h>
+#include <isc/net.h>
+#include <isc/netaddr.h>
+#include <isc/netmgr.h>
+#include <isc/netscope.h>
+#include <isc/print.h>
+#include <isc/sockaddr.h>
+#include <isc/string.h>
+#include <isc/symtab.h>
+#include <isc/util.h>
+
+#include <dns/ttl.h>
+
+#include <isccfg/cfg.h>
+#include <isccfg/grammar.h>
+#include <isccfg/log.h>
+
+/* Shorthand */
+#define CAT CFG_LOGCATEGORY_CONFIG
+#define MOD CFG_LOGMODULE_PARSER
+
+#define MAP_SYM 1 /* Unique type for isc_symtab */
+
+#define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)
+
+/* Check a return value. */
+#define CHECK(op) \
+ do { \
+ result = (op); \
+ if (result != ISC_R_SUCCESS) \
+ goto cleanup; \
+ } while (0)
+
+/* Clean up a configuration object if non-NULL. */
+#define CLEANUP_OBJ(obj) \
+ do { \
+ if ((obj) != NULL) \
+ cfg_obj_destroy(pctx, &(obj)); \
+ } while (0)
+
+/*
+ * Forward declarations of static functions.
+ */
+
+static void
+free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj);
+
+static isc_result_t
+parse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
+
+static void
+print_list(cfg_printer_t *pctx, const cfg_obj_t *obj);
+
+static void
+free_list(cfg_parser_t *pctx, cfg_obj_t *obj);
+
+static isc_result_t
+create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
+
+static isc_result_t
+create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
+ cfg_obj_t **ret);
+
+static void
+free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
+
+static isc_result_t
+create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
+
+static void
+free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
+
+static isc_result_t
+parse_symtab_elt(cfg_parser_t *pctx, const char *name, cfg_type_t *elttype,
+ isc_symtab_t *symtab, bool callback);
+
+static void
+free_noop(cfg_parser_t *pctx, cfg_obj_t *obj);
+
+static isc_result_t
+cfg_getstringtoken(cfg_parser_t *pctx);
+
+static void
+parser_complain(cfg_parser_t *pctx, bool is_warning, unsigned int flags,
+ const char *format, va_list args);
+
+#if defined(HAVE_GEOIP2)
+static isc_result_t
+parse_geoip(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
+
+static void
+print_geoip(cfg_printer_t *pctx, const cfg_obj_t *obj);
+
+static void
+doc_geoip(cfg_printer_t *pctx, const cfg_type_t *type);
+#endif /* HAVE_GEOIP2 */
+
+/*
+ * Data representations. These correspond to members of the
+ * "value" union in struct cfg_obj (except "void", which does
+ * not need a union member).
+ */
+
+cfg_rep_t cfg_rep_uint32 = { "uint32", free_noop };
+cfg_rep_t cfg_rep_uint64 = { "uint64", free_noop };
+cfg_rep_t cfg_rep_string = { "string", free_string };
+cfg_rep_t cfg_rep_boolean = { "boolean", free_noop };
+cfg_rep_t cfg_rep_map = { "map", free_map };
+cfg_rep_t cfg_rep_list = { "list", free_list };
+cfg_rep_t cfg_rep_tuple = { "tuple", free_tuple };
+cfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop };
+cfg_rep_t cfg_rep_netprefix = { "netprefix", free_noop };
+cfg_rep_t cfg_rep_void = { "void", free_noop };
+cfg_rep_t cfg_rep_fixedpoint = { "fixedpoint", free_noop };
+cfg_rep_t cfg_rep_percentage = { "percentage", free_noop };
+cfg_rep_t cfg_rep_duration = { "duration", free_noop };
+
+/*
+ * Configuration type definitions.
+ */
+
+/*%
+ * An implicit list. These are formed by clauses that occur multiple times.
+ */
+static cfg_type_t cfg_type_implicitlist = { "implicitlist", NULL,
+ print_list, NULL,
+ &cfg_rep_list, NULL };
+
+/* Functions. */
+
+void
+cfg_print_obj(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ REQUIRE(pctx != NULL);
+ REQUIRE(obj != NULL);
+
+ obj->type->print(pctx, obj);
+}
+
+void
+cfg_print_chars(cfg_printer_t *pctx, const char *text, int len) {
+ REQUIRE(pctx != NULL);
+ REQUIRE(text != NULL);
+
+ pctx->f(pctx->closure, text, len);
+}
+
+static void
+print_open(cfg_printer_t *pctx) {
+ if ((pctx->flags & CFG_PRINTER_ONELINE) != 0) {
+ cfg_print_cstr(pctx, "{ ");
+ } else {
+ cfg_print_cstr(pctx, "{\n");
+ pctx->indent++;
+ }
+}
+
+void
+cfg_print_indent(cfg_printer_t *pctx) {
+ int indent = pctx->indent;
+ if ((pctx->flags & CFG_PRINTER_ONELINE) != 0) {
+ cfg_print_cstr(pctx, " ");
+ return;
+ }
+ while (indent > 0) {
+ cfg_print_cstr(pctx, "\t");
+ indent--;
+ }
+}
+
+static void
+print_close(cfg_printer_t *pctx) {
+ if ((pctx->flags & CFG_PRINTER_ONELINE) == 0) {
+ pctx->indent--;
+ cfg_print_indent(pctx);
+ }
+ cfg_print_cstr(pctx, "}");
+}
+
+isc_result_t
+cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(type != NULL);
+ REQUIRE(ret != NULL && *ret == NULL);
+
+ result = type->parse(pctx, type, ret);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ ENSURE(*ret != NULL);
+ return (ISC_R_SUCCESS);
+}
+
+void
+cfg_print(const cfg_obj_t *obj,
+ void (*f)(void *closure, const char *text, int textlen),
+ void *closure) {
+ REQUIRE(obj != NULL);
+ REQUIRE(f != NULL);
+
+ cfg_printx(obj, 0, f, closure);
+}
+
+void
+cfg_printx(const cfg_obj_t *obj, unsigned int flags,
+ void (*f)(void *closure, const char *text, int textlen),
+ void *closure) {
+ cfg_printer_t pctx;
+
+ REQUIRE(obj != NULL);
+ REQUIRE(f != NULL);
+
+ pctx.f = f;
+ pctx.closure = closure;
+ pctx.indent = 0;
+ pctx.flags = flags;
+ obj->type->print(&pctx, obj);
+}
+
+/* Tuples. */
+
+isc_result_t
+cfg_create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+ const cfg_tuplefielddef_t *fields;
+ const cfg_tuplefielddef_t *f;
+ cfg_obj_t *obj = NULL;
+ unsigned int nfields = 0;
+ int i;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(type != NULL);
+ REQUIRE(ret != NULL && *ret == NULL);
+
+ fields = type->of;
+
+ for (f = fields; f->name != NULL; f++) {
+ nfields++;
+ }
+
+ CHECK(cfg_create_obj(pctx, type, &obj));
+ obj->value.tuple = isc_mem_get(pctx->mctx,
+ nfields * sizeof(cfg_obj_t *));
+ for (f = fields, i = 0; f->name != NULL; f++, i++) {
+ obj->value.tuple[i] = NULL;
+ }
+ *ret = obj;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ if (obj != NULL) {
+ isc_mem_put(pctx->mctx, obj, sizeof(*obj));
+ }
+ return (result);
+}
+
+isc_result_t
+cfg_parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+ const cfg_tuplefielddef_t *fields;
+ const cfg_tuplefielddef_t *f;
+ cfg_obj_t *obj = NULL;
+ unsigned int i;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(type != NULL);
+ REQUIRE(ret != NULL && *ret == NULL);
+
+ fields = type->of;
+
+ CHECK(cfg_create_tuple(pctx, type, &obj));
+ for (f = fields, i = 0; f->name != NULL; f++, i++) {
+ CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[i]));
+ }
+
+ *ret = obj;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ CLEANUP_OBJ(obj);
+ return (result);
+}
+
+void
+cfg_print_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ unsigned int i;
+ const cfg_tuplefielddef_t *fields;
+ const cfg_tuplefielddef_t *f;
+ bool need_space = false;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(obj != NULL);
+
+ fields = obj->type->of;
+
+ for (f = fields, i = 0; f->name != NULL; f++, i++) {
+ const cfg_obj_t *fieldobj = obj->value.tuple[i];
+ if (need_space && fieldobj->type->rep != &cfg_rep_void) {
+ cfg_print_cstr(pctx, " ");
+ }
+ cfg_print_obj(pctx, fieldobj);
+ need_space = (need_space ||
+ fieldobj->type->print != cfg_print_void);
+ }
+}
+
+void
+cfg_doc_tuple(cfg_printer_t *pctx, const cfg_type_t *type) {
+ const cfg_tuplefielddef_t *fields;
+ const cfg_tuplefielddef_t *f;
+ bool need_space = false;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(type != NULL);
+
+ fields = type->of;
+
+ for (f = fields; f->name != NULL; f++) {
+ if (need_space) {
+ cfg_print_cstr(pctx, " ");
+ }
+ cfg_doc_obj(pctx, f->type);
+ need_space = (f->type->print != cfg_print_void);
+ }
+}
+
+static void
+free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj) {
+ unsigned int i;
+ const cfg_tuplefielddef_t *fields = obj->type->of;
+ const cfg_tuplefielddef_t *f;
+ unsigned int nfields = 0;
+
+ if (obj->value.tuple == NULL) {
+ return;
+ }
+
+ for (f = fields, i = 0; f->name != NULL; f++, i++) {
+ CLEANUP_OBJ(obj->value.tuple[i]);
+ nfields++;
+ }
+ isc_mem_put(pctx->mctx, obj->value.tuple,
+ nfields * sizeof(cfg_obj_t *));
+}
+
+bool
+cfg_obj_istuple(const cfg_obj_t *obj) {
+ REQUIRE(obj != NULL);
+ return (obj->type->rep == &cfg_rep_tuple);
+}
+
+const cfg_obj_t *
+cfg_tuple_get(const cfg_obj_t *tupleobj, const char *name) {
+ unsigned int i;
+ const cfg_tuplefielddef_t *fields;
+ const cfg_tuplefielddef_t *f;
+
+ REQUIRE(tupleobj != NULL && tupleobj->type->rep == &cfg_rep_tuple);
+ REQUIRE(name != NULL);
+
+ fields = tupleobj->type->of;
+ for (f = fields, i = 0; f->name != NULL; f++, i++) {
+ if (strcmp(f->name, name) == 0) {
+ return (tupleobj->value.tuple[i]);
+ }
+ }
+ UNREACHABLE();
+}
+
+isc_result_t
+cfg_parse_special(cfg_parser_t *pctx, int special) {
+ isc_result_t result;
+
+ REQUIRE(pctx != NULL);
+
+ CHECK(cfg_gettoken(pctx, 0));
+ if (pctx->token.type == isc_tokentype_special &&
+ pctx->token.value.as_char == special)
+ {
+ return (ISC_R_SUCCESS);
+ }
+
+ cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special);
+ return (ISC_R_UNEXPECTEDTOKEN);
+cleanup:
+ return (result);
+}
+
+/*
+ * Parse a required semicolon. If it is not there, log
+ * an error and increment the error count but continue
+ * parsing. Since the next token is pushed back,
+ * care must be taken to make sure it is eventually
+ * consumed or an infinite loop may result.
+ */
+static isc_result_t
+parse_semicolon(cfg_parser_t *pctx) {
+ isc_result_t result;
+
+ CHECK(cfg_gettoken(pctx, 0));
+ if (pctx->token.type == isc_tokentype_special &&
+ pctx->token.value.as_char == ';')
+ {
+ return (ISC_R_SUCCESS);
+ }
+
+ cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'");
+ cfg_ungettoken(pctx);
+cleanup:
+ return (result);
+}
+
+/*
+ * Parse EOF, logging and returning an error if not there.
+ */
+static isc_result_t
+parse_eof(cfg_parser_t *pctx) {
+ isc_result_t result;
+
+ CHECK(cfg_gettoken(pctx, 0));
+
+ if (pctx->token.type == isc_tokentype_eof) {
+ return (ISC_R_SUCCESS);
+ }
+
+ cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error");
+ return (ISC_R_UNEXPECTEDTOKEN);
+cleanup:
+ return (result);
+}
+
+/* A list of files, used internally for pctx->files. */
+
+static cfg_type_t cfg_type_filelist = { "filelist", NULL,
+ print_list, NULL,
+ &cfg_rep_list, &cfg_type_qstring };
+
+isc_result_t
+cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) {
+ isc_result_t result;
+ cfg_parser_t *pctx;
+ isc_lexspecials_t specials;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(ret != NULL && *ret == NULL);
+
+ pctx = isc_mem_get(mctx, sizeof(*pctx));
+
+ pctx->mctx = NULL;
+ isc_mem_attach(mctx, &pctx->mctx);
+
+ isc_refcount_init(&pctx->references, 1);
+
+ pctx->lctx = lctx;
+ pctx->lexer = NULL;
+ pctx->seen_eof = false;
+ pctx->ungotten = false;
+ pctx->errors = 0;
+ pctx->warnings = 0;
+ pctx->open_files = NULL;
+ pctx->closed_files = NULL;
+ pctx->line = 0;
+ pctx->callback = NULL;
+ pctx->callbackarg = NULL;
+ pctx->token.type = isc_tokentype_unknown;
+ pctx->flags = 0;
+ pctx->buf_name = NULL;
+
+ memset(specials, 0, sizeof(specials));
+ specials['{'] = 1;
+ specials['}'] = 1;
+ specials[';'] = 1;
+ specials['/'] = 1;
+ specials['"'] = 1;
+ specials['!'] = 1;
+
+ CHECK(isc_lex_create(pctx->mctx, 1024, &pctx->lexer));
+
+ isc_lex_setspecials(pctx->lexer, specials);
+ isc_lex_setcomments(pctx->lexer,
+ (ISC_LEXCOMMENT_C | ISC_LEXCOMMENT_CPLUSPLUS |
+ ISC_LEXCOMMENT_SHELL));
+
+ CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files));
+ CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files));
+
+ *ret = pctx;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ if (pctx->lexer != NULL) {
+ isc_lex_destroy(&pctx->lexer);
+ }
+ CLEANUP_OBJ(pctx->open_files);
+ CLEANUP_OBJ(pctx->closed_files);
+ isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
+ return (result);
+}
+
+void
+cfg_parser_setflags(cfg_parser_t *pctx, unsigned int flags, bool turn_on) {
+ REQUIRE(pctx != NULL);
+
+ if (turn_on) {
+ pctx->flags |= flags;
+ } else {
+ pctx->flags &= ~flags;
+ }
+}
+
+static isc_result_t
+parser_openfile(cfg_parser_t *pctx, const char *filename) {
+ isc_result_t result;
+ cfg_listelt_t *elt = NULL;
+ cfg_obj_t *stringobj = NULL;
+
+ result = isc_lex_openfile(pctx->lexer, filename);
+ if (result != ISC_R_SUCCESS) {
+ cfg_parser_error(pctx, 0, "open: %s: %s", filename,
+ isc_result_totext(result));
+ goto cleanup;
+ }
+
+ CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj));
+ CHECK(create_listelt(pctx, &elt));
+ elt->obj = stringobj;
+ ISC_LIST_APPEND(pctx->open_files->value.list, elt, link);
+
+ return (ISC_R_SUCCESS);
+cleanup:
+ CLEANUP_OBJ(stringobj);
+ return (result);
+}
+
+void
+cfg_parser_setcallback(cfg_parser_t *pctx, cfg_parsecallback_t callback,
+ void *arg) {
+ REQUIRE(pctx != NULL);
+
+ pctx->callback = callback;
+ pctx->callbackarg = arg;
+}
+
+void
+cfg_parser_reset(cfg_parser_t *pctx) {
+ REQUIRE(pctx != NULL);
+
+ if (pctx->lexer != NULL) {
+ isc_lex_close(pctx->lexer);
+ }
+
+ pctx->seen_eof = false;
+ pctx->ungotten = false;
+ pctx->errors = 0;
+ pctx->warnings = 0;
+ pctx->line = 0;
+}
+
+/*
+ * Parse a configuration using a pctx where a lexer has already
+ * been set up with a source.
+ */
+static isc_result_t
+parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+ cfg_obj_t *obj = NULL;
+
+ result = cfg_parse_obj(pctx, type, &obj);
+
+ if (pctx->errors != 0) {
+ /* Errors have been logged. */
+ if (result == ISC_R_SUCCESS) {
+ result = ISC_R_FAILURE;
+ }
+ goto cleanup;
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ /* Parsing failed but no errors have been logged. */
+ cfg_parser_error(pctx, 0, "parsing failed: %s",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+
+ CHECK(parse_eof(pctx));
+
+ *ret = obj;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ CLEANUP_OBJ(obj);
+ return (result);
+}
+
+isc_result_t
+cfg_parse_file(cfg_parser_t *pctx, const char *filename, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ isc_result_t result;
+ cfg_listelt_t *elt;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(filename != NULL);
+ REQUIRE(type != NULL);
+ REQUIRE(ret != NULL && *ret == NULL);
+
+ CHECK(parser_openfile(pctx, filename));
+
+ result = parse2(pctx, type, ret);
+
+ /* Clean up the opened file */
+ elt = ISC_LIST_TAIL(pctx->open_files->value.list);
+ INSIST(elt != NULL);
+ ISC_LIST_UNLINK(pctx->open_files->value.list, elt, link);
+ ISC_LIST_APPEND(pctx->closed_files->value.list, elt, link);
+
+cleanup:
+ return (result);
+}
+
+isc_result_t
+cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer, const char *file,
+ unsigned int line, const cfg_type_t *type, unsigned int flags,
+ cfg_obj_t **ret) {
+ isc_result_t result;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(type != NULL);
+ REQUIRE(buffer != NULL);
+ REQUIRE(ret != NULL && *ret == NULL);
+ REQUIRE((flags & ~(CFG_PCTX_NODEPRECATED)) == 0);
+
+ CHECK(isc_lex_openbuffer(pctx->lexer, buffer));
+
+ pctx->buf_name = file;
+ pctx->flags = flags;
+
+ if (line != 0U) {
+ CHECK(isc_lex_setsourceline(pctx->lexer, line));
+ }
+
+ CHECK(parse2(pctx, type, ret));
+ pctx->buf_name = NULL;
+
+cleanup:
+ return (result);
+}
+
+void
+cfg_parser_attach(cfg_parser_t *src, cfg_parser_t **dest) {
+ REQUIRE(src != NULL);
+ REQUIRE(dest != NULL && *dest == NULL);
+
+ isc_refcount_increment(&src->references);
+ *dest = src;
+}
+
+void
+cfg_parser_destroy(cfg_parser_t **pctxp) {
+ cfg_parser_t *pctx;
+
+ REQUIRE(pctxp != NULL && *pctxp != NULL);
+ pctx = *pctxp;
+ *pctxp = NULL;
+
+ if (isc_refcount_decrement(&pctx->references) == 1) {
+ isc_lex_destroy(&pctx->lexer);
+ /*
+ * Cleaning up open_files does not
+ * close the files; that was already done
+ * by closing the lexer.
+ */
+ CLEANUP_OBJ(pctx->open_files);
+ CLEANUP_OBJ(pctx->closed_files);
+ isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
+ }
+}
+
+/*
+ * void
+ */
+isc_result_t
+cfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ REQUIRE(pctx != NULL);
+ REQUIRE(ret != NULL && *ret == NULL);
+
+ UNUSED(type);
+
+ return (cfg_create_obj(pctx, &cfg_type_void, ret));
+}
+
+void
+cfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ REQUIRE(pctx != NULL);
+ REQUIRE(obj != NULL);
+
+ UNUSED(pctx);
+ UNUSED(obj);
+}
+
+void
+cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) {
+ REQUIRE(pctx != NULL);
+ REQUIRE(type != NULL);
+
+ UNUSED(pctx);
+ UNUSED(type);
+}
+
+bool
+cfg_obj_isvoid(const cfg_obj_t *obj) {
+ REQUIRE(obj != NULL);
+ return (obj->type->rep == &cfg_rep_void);
+}
+
+cfg_type_t cfg_type_void = { "void", cfg_parse_void, cfg_print_void,
+ cfg_doc_void, &cfg_rep_void, NULL };
+
+/*
+ * percentage
+ */
+isc_result_t
+cfg_parse_percentage(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ char *endp;
+ isc_result_t result;
+ cfg_obj_t *obj = NULL;
+ uint64_t percent;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(ret != NULL && *ret == NULL);
+
+ UNUSED(type);
+
+ CHECK(cfg_gettoken(pctx, 0));
+ if (pctx->token.type != isc_tokentype_string) {
+ cfg_parser_error(pctx, CFG_LOG_NEAR, "expected percentage");
+ return (ISC_R_UNEXPECTEDTOKEN);
+ }
+
+ percent = strtoull(TOKEN_STRING(pctx), &endp, 10);
+ if (*endp != '%' || *(endp + 1) != 0) {
+ cfg_parser_error(pctx, CFG_LOG_NEAR, "expected percentage");
+ return (ISC_R_UNEXPECTEDTOKEN);
+ }
+
+ CHECK(cfg_create_obj(pctx, &cfg_type_percentage, &obj));
+ obj->value.uint32 = (uint32_t)percent;
+ *ret = obj;
+
+cleanup:
+ return (result);
+}
+
+void
+cfg_print_percentage(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ char buf[64];
+ int n;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(obj != NULL);
+
+ n = snprintf(buf, sizeof(buf), "%u%%", obj->value.uint32);
+ INSIST(n > 0 && (size_t)n < sizeof(buf));
+ cfg_print_chars(pctx, buf, strlen(buf));
+}
+
+uint32_t
+cfg_obj_aspercentage(const cfg_obj_t *obj) {
+ REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_percentage);
+ return (obj->value.uint32);
+}
+
+cfg_type_t cfg_type_percentage = { "percentage", cfg_parse_percentage,
+ cfg_print_percentage, cfg_doc_terminal,
+ &cfg_rep_percentage, NULL };
+
+bool
+cfg_obj_ispercentage(const cfg_obj_t *obj) {
+ REQUIRE(obj != NULL);
+ return (obj->type->rep == &cfg_rep_percentage);
+}
+
+/*
+ * Fixed point
+ */
+isc_result_t
+cfg_parse_fixedpoint(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ isc_result_t result;
+ cfg_obj_t *obj = NULL;
+ size_t n1, n2, n3, l;
+ const char *p;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(ret != NULL && *ret == NULL);
+
+ UNUSED(type);
+
+ CHECK(cfg_gettoken(pctx, 0));
+ if (pctx->token.type != isc_tokentype_string) {
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "expected fixed point number");
+ return (ISC_R_UNEXPECTEDTOKEN);
+ }
+
+ p = TOKEN_STRING(pctx);
+ l = strlen(p);
+ n1 = strspn(p, "0123456789");
+ n2 = strspn(p + n1, ".");
+ n3 = strspn(p + n1 + n2, "0123456789");
+
+ if ((n1 + n2 + n3 != l) || (n1 + n3 == 0) || n1 > 5 || n2 > 1 || n3 > 2)
+ {
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "expected fixed point number");
+ return (ISC_R_UNEXPECTEDTOKEN);
+ }
+
+ CHECK(cfg_create_obj(pctx, &cfg_type_fixedpoint, &obj));
+
+ obj->value.uint32 = strtoul(p, NULL, 10) * 100;
+ switch (n3) {
+ case 2:
+ obj->value.uint32 += strtoul(p + n1 + n2, NULL, 10);
+ break;
+ case 1:
+ obj->value.uint32 += strtoul(p + n1 + n2, NULL, 10) * 10;
+ break;
+ }
+ *ret = obj;
+
+cleanup:
+ return (result);
+}
+
+void
+cfg_print_fixedpoint(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ char buf[64];
+ int n;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(obj != NULL);
+
+ n = snprintf(buf, sizeof(buf), "%u.%02u", obj->value.uint32 / 100,
+ obj->value.uint32 % 100);
+ INSIST(n > 0 && (size_t)n < sizeof(buf));
+ cfg_print_chars(pctx, buf, strlen(buf));
+}
+
+uint32_t
+cfg_obj_asfixedpoint(const cfg_obj_t *obj) {
+ REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_fixedpoint);
+ return (obj->value.uint32);
+}
+
+cfg_type_t cfg_type_fixedpoint = { "fixedpoint", cfg_parse_fixedpoint,
+ cfg_print_fixedpoint, cfg_doc_terminal,
+ &cfg_rep_fixedpoint, NULL };
+
+bool
+cfg_obj_isfixedpoint(const cfg_obj_t *obj) {
+ REQUIRE(obj != NULL);
+ return (obj->type->rep == &cfg_rep_fixedpoint);
+}
+
+/*
+ * uint32
+ */
+isc_result_t
+cfg_parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+ cfg_obj_t *obj = NULL;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(ret != NULL && *ret == NULL);
+
+ UNUSED(type);
+
+ CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
+ if (pctx->token.type != isc_tokentype_number) {
+ cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number");
+ return (ISC_R_UNEXPECTEDTOKEN);
+ }
+
+ CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj));
+
+ obj->value.uint32 = pctx->token.value.as_ulong;
+ *ret = obj;
+cleanup:
+ return (result);
+}
+
+void
+cfg_print_cstr(cfg_printer_t *pctx, const char *s) {
+ cfg_print_chars(pctx, s, strlen(s));
+}
+
+void
+cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) {
+ char buf[32];
+
+ snprintf(buf, sizeof(buf), "%u", u);
+ cfg_print_cstr(pctx, buf);
+}
+
+void
+cfg_print_uint32(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ cfg_print_rawuint(pctx, obj->value.uint32);
+}
+
+bool
+cfg_obj_isuint32(const cfg_obj_t *obj) {
+ REQUIRE(obj != NULL);
+ return (obj->type->rep == &cfg_rep_uint32);
+}
+
+uint32_t
+cfg_obj_asuint32(const cfg_obj_t *obj) {
+ REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32);
+ return (obj->value.uint32);
+}
+
+cfg_type_t cfg_type_uint32 = { "integer", cfg_parse_uint32,
+ cfg_print_uint32, cfg_doc_terminal,
+ &cfg_rep_uint32, NULL };
+
+/*
+ * uint64
+ */
+bool
+cfg_obj_isuint64(const cfg_obj_t *obj) {
+ REQUIRE(obj != NULL);
+ return (obj->type->rep == &cfg_rep_uint64);
+}
+
+uint64_t
+cfg_obj_asuint64(const cfg_obj_t *obj) {
+ REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64);
+ return (obj->value.uint64);
+}
+
+void
+cfg_print_uint64(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ char buf[32];
+
+ snprintf(buf, sizeof(buf), "%" PRIu64, obj->value.uint64);
+ cfg_print_cstr(pctx, buf);
+}
+
+cfg_type_t cfg_type_uint64 = { "64_bit_integer", NULL,
+ cfg_print_uint64, cfg_doc_terminal,
+ &cfg_rep_uint64, NULL };
+
+/*
+ * Get the number of digits in a number.
+ */
+static size_t
+numlen(uint32_t num) {
+ uint32_t period = num;
+ size_t count = 0;
+
+ if (period == 0) {
+ return (1);
+ }
+ while (period > 0) {
+ count++;
+ period /= 10;
+ }
+ return (count);
+}
+
+/*
+ * duration
+ */
+void
+cfg_print_duration(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ char buf[CFG_DURATION_MAXLEN];
+ char *str;
+ const char *indicators = "YMWDHMS";
+ int count, i;
+ int durationlen[7] = { 0 };
+ isccfg_duration_t duration;
+ /*
+ * D ? The duration has a date part.
+ * T ? The duration has a time part.
+ */
+ bool D = false, T = false;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(obj != NULL);
+
+ duration = obj->value.duration;
+
+ /* If this is not an ISO 8601 duration, just print it as a number. */
+ if (!duration.iso8601) {
+ cfg_print_rawuint(pctx, duration.parts[6]);
+ return;
+ }
+
+ /* Calculate length of string. */
+ buf[0] = 'P';
+ buf[1] = '\0';
+ str = &buf[1];
+ count = 2;
+ for (i = 0; i < 6; i++) {
+ if (duration.parts[i] > 0) {
+ durationlen[i] = 1 + numlen(duration.parts[i]);
+ if (i < 4) {
+ D = true;
+ } else {
+ T = true;
+ }
+ count += durationlen[i];
+ }
+ }
+ /*
+ * Special case for seconds which is not taken into account in the
+ * above for loop: Count the length of the seconds part if it is
+ * non-zero, or if all the other parts are also zero. In the latter
+ * case this function will print "PT0S".
+ */
+ if (duration.parts[6] > 0 ||
+ (!D && !duration.parts[4] && !duration.parts[5]))
+ {
+ durationlen[6] = 1 + numlen(duration.parts[6]);
+ T = true;
+ count += durationlen[6];
+ }
+ /* Add one character for the time indicator. */
+ if (T) {
+ count++;
+ }
+ INSIST(count < CFG_DURATION_MAXLEN);
+
+ /* Now print the duration. */
+ for (i = 0; i < 6; i++) {
+ /*
+ * We don't check here if weeks and other time indicator are
+ * used mutually exclusively.
+ */
+ if (duration.parts[i] > 0) {
+ snprintf(str, durationlen[i] + 2, "%u%c",
+ (uint32_t)duration.parts[i], indicators[i]);
+ str += durationlen[i];
+ }
+ if (i == 3 && T) {
+ snprintf(str, 2, "T");
+ str += 1;
+ }
+ }
+ /* Special case for seconds. */
+ if (duration.parts[6] > 0 ||
+ (!D && !duration.parts[4] && !duration.parts[5]))
+ {
+ snprintf(str, durationlen[6] + 2, "%u%c",
+ (uint32_t)duration.parts[6], indicators[6]);
+ }
+ cfg_print_chars(pctx, buf, strlen(buf));
+}
+
+void
+cfg_print_duration_or_unlimited(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ isccfg_duration_t duration;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(obj != NULL);
+
+ duration = obj->value.duration;
+
+ if (duration.unlimited) {
+ cfg_print_cstr(pctx, "unlimited");
+ } else {
+ cfg_print_duration(pctx, obj);
+ }
+}
+
+bool
+cfg_obj_isduration(const cfg_obj_t *obj) {
+ REQUIRE(obj != NULL);
+ return (obj->type->rep == &cfg_rep_duration);
+}
+
+uint32_t
+cfg_obj_asduration(const cfg_obj_t *obj) {
+ REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_duration);
+ return isccfg_duration_toseconds(&(obj->value.duration));
+}
+
+static isc_result_t
+parse_duration(cfg_parser_t *pctx, cfg_obj_t **ret) {
+ isc_result_t result;
+ cfg_obj_t *obj = NULL;
+ isccfg_duration_t duration;
+
+ result = isccfg_parse_duration(&pctx->token.value.as_textregion,
+ &duration);
+
+ if (result == ISC_R_RANGE) {
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "duration or TTL out of range");
+ return (result);
+ } else if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ CHECK(cfg_create_obj(pctx, &cfg_type_duration, &obj));
+ obj->value.duration = duration;
+ *ret = obj;
+
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "expected ISO 8601 duration or TTL value");
+ return (result);
+}
+
+isc_result_t
+cfg_parse_duration(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ isc_result_t result;
+
+ UNUSED(type);
+
+ CHECK(cfg_gettoken(pctx, 0));
+ if (pctx->token.type != isc_tokentype_string) {
+ result = ISC_R_UNEXPECTEDTOKEN;
+ goto cleanup;
+ }
+
+ return (parse_duration(pctx, ret));
+
+cleanup:
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "expected ISO 8601 duration or TTL value");
+ return (result);
+}
+
+isc_result_t
+cfg_parse_duration_or_unlimited(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ isc_result_t result;
+ cfg_obj_t *obj = NULL;
+ isccfg_duration_t duration;
+
+ UNUSED(type);
+
+ CHECK(cfg_gettoken(pctx, 0));
+ if (pctx->token.type != isc_tokentype_string) {
+ result = ISC_R_UNEXPECTEDTOKEN;
+ goto cleanup;
+ }
+
+ if (strcmp(TOKEN_STRING(pctx), "unlimited") == 0) {
+ for (int i = 0; i < 7; i++) {
+ duration.parts[i] = 0;
+ }
+ duration.iso8601 = false;
+ duration.unlimited = true;
+
+ CHECK(cfg_create_obj(pctx, &cfg_type_duration, &obj));
+ obj->value.duration = duration;
+ *ret = obj;
+ return (ISC_R_SUCCESS);
+ }
+
+ return (parse_duration(pctx, ret));
+
+cleanup:
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "expected ISO 8601 duration, TTL value, or unlimited");
+ return (result);
+}
+
+/*%
+ * A duration as defined by ISO 8601 (P[n]Y[n]M[n]DT[n]H[n]M[n]S).
+ * - P is the duration indicator ("period") placed at the start.
+ * - Y is the year indicator that follows the value for the number of years.
+ * - M is the month indicator that follows the value for the number of months.
+ * - D is the day indicator that follows the value for the number of days.
+ * - T is the time indicator that precedes the time components.
+ * - H is the hour indicator that follows the value for the number of hours.
+ * - M is the minute indicator that follows the value for the number of
+ * minutes.
+ * - S is the second indicator that follows the value for the number of
+ * seconds.
+ *
+ * A duration can also be a TTL value (number + optional unit).
+ */
+cfg_type_t cfg_type_duration = { "duration", cfg_parse_duration,
+ cfg_print_duration, cfg_doc_terminal,
+ &cfg_rep_duration, NULL };
+cfg_type_t cfg_type_duration_or_unlimited = { "duration_or_unlimited",
+ cfg_parse_duration_or_unlimited,
+ cfg_print_duration_or_unlimited,
+ cfg_doc_terminal,
+ &cfg_rep_duration,
+ NULL };
+
+/*
+ * qstring (quoted string), ustring (unquoted string), astring
+ * (any string), sstring (secret string)
+ */
+
+/* Create a string object from a null-terminated C string. */
+static isc_result_t
+create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ isc_result_t result;
+ cfg_obj_t *obj = NULL;
+ int len;
+
+ CHECK(cfg_create_obj(pctx, type, &obj));
+ len = strlen(contents);
+ obj->value.string.length = len;
+ obj->value.string.base = isc_mem_get(pctx->mctx, len + 1);
+ if (obj->value.string.base == 0) {
+ isc_mem_put(pctx->mctx, obj, sizeof(*obj));
+ return (ISC_R_NOMEMORY);
+ }
+ memmove(obj->value.string.base, contents, len);
+ obj->value.string.base[len] = '\0';
+
+ *ret = obj;
+cleanup:
+ return (result);
+}
+
+isc_result_t
+cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(ret != NULL && *ret == NULL);
+
+ UNUSED(type);
+
+ CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
+ if (pctx->token.type != isc_tokentype_qstring) {
+ cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string");
+ return (ISC_R_UNEXPECTEDTOKEN);
+ }
+ return (create_string(pctx, TOKEN_STRING(pctx), &cfg_type_qstring,
+ ret));
+cleanup:
+ return (result);
+}
+
+static isc_result_t
+parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+
+ UNUSED(type);
+
+ CHECK(cfg_gettoken(pctx, 0));
+ if (pctx->token.type != isc_tokentype_string) {
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "expected unquoted string");
+ return (ISC_R_UNEXPECTEDTOKEN);
+ }
+ return (create_string(pctx, TOKEN_STRING(pctx), &cfg_type_ustring,
+ ret));
+cleanup:
+ return (result);
+}
+
+isc_result_t
+cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(ret != NULL && *ret == NULL);
+
+ UNUSED(type);
+
+ CHECK(cfg_getstringtoken(pctx));
+ return (create_string(pctx, TOKEN_STRING(pctx), &cfg_type_qstring,
+ ret));
+cleanup:
+ return (result);
+}
+
+isc_result_t
+cfg_parse_sstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(ret != NULL && *ret == NULL);
+
+ UNUSED(type);
+
+ CHECK(cfg_getstringtoken(pctx));
+ return (create_string(pctx, TOKEN_STRING(pctx), &cfg_type_sstring,
+ ret));
+cleanup:
+ return (result);
+}
+
+static isc_result_t
+parse_btext(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+
+ UNUSED(type);
+
+ CHECK(cfg_gettoken(pctx, ISC_LEXOPT_BTEXT));
+ if (pctx->token.type != isc_tokentype_btext) {
+ cfg_parser_error(pctx, CFG_LOG_NEAR, "expected bracketed text");
+ return (ISC_R_UNEXPECTEDTOKEN);
+ }
+ return (create_string(pctx, TOKEN_STRING(pctx),
+ &cfg_type_bracketed_text, ret));
+cleanup:
+ return (result);
+}
+
+static void
+print_btext(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ /*
+ * We need to print "{" instead of running print_open()
+ * in order to preserve the exact original formatting
+ * of the bracketed text. But we increment the indent value
+ * so that print_close() will leave us back in our original
+ * state.
+ */
+ pctx->indent++;
+ cfg_print_cstr(pctx, "{");
+ cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
+ print_close(pctx);
+}
+
+static void
+doc_btext(cfg_printer_t *pctx, const cfg_type_t *type) {
+ UNUSED(type);
+
+ cfg_print_cstr(pctx, "{ <unspecified-text> }");
+}
+
+bool
+cfg_is_enum(const char *s, const char *const *enums) {
+ const char *const *p;
+
+ REQUIRE(s != NULL);
+ REQUIRE(enums != NULL);
+
+ for (p = enums; *p != NULL; p++) {
+ if (strcasecmp(*p, s) == 0) {
+ return (true);
+ }
+ }
+ return (false);
+}
+
+static isc_result_t
+check_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) {
+ const char *s = obj->value.string.base;
+
+ if (cfg_is_enum(s, enums)) {
+ return (ISC_R_SUCCESS);
+ }
+ cfg_parser_error(pctx, 0, "'%s' unexpected", s);
+ return (ISC_R_UNEXPECTEDTOKEN);
+}
+
+isc_result_t
+cfg_parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+ cfg_obj_t *obj = NULL;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(type != NULL);
+ REQUIRE(ret != NULL && *ret == NULL);
+
+ CHECK(parse_ustring(pctx, NULL, &obj));
+ CHECK(check_enum(pctx, obj, type->of));
+ *ret = obj;
+ return (ISC_R_SUCCESS);
+cleanup:
+ CLEANUP_OBJ(obj);
+ return (result);
+}
+
+void
+cfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type) {
+ const char *const *p;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(type != NULL);
+
+ cfg_print_cstr(pctx, "( ");
+ for (p = type->of; *p != NULL; p++) {
+ cfg_print_cstr(pctx, *p);
+ if (p[1] != NULL) {
+ cfg_print_cstr(pctx, " | ");
+ }
+ }
+ cfg_print_cstr(pctx, " )");
+}
+
+isc_result_t
+cfg_parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype,
+ const cfg_type_t *othertype, cfg_obj_t **ret) {
+ isc_result_t result;
+ CHECK(cfg_peektoken(pctx, 0));
+ if (pctx->token.type == isc_tokentype_string &&
+ cfg_is_enum(TOKEN_STRING(pctx), enumtype->of))
+ {
+ CHECK(cfg_parse_enum(pctx, enumtype, ret));
+ } else {
+ CHECK(cfg_parse_obj(pctx, othertype, ret));
+ }
+cleanup:
+ return (result);
+}
+
+void
+cfg_doc_enum_or_other(cfg_printer_t *pctx, const cfg_type_t *enumtype,
+ const cfg_type_t *othertype) {
+ const char *const *p;
+ bool first = true;
+
+ /*
+ * If othertype is cfg_type_void, it means that enumtype is
+ * optional.
+ */
+
+ if (othertype == &cfg_type_void) {
+ cfg_print_cstr(pctx, "[ ");
+ }
+ cfg_print_cstr(pctx, "( ");
+ for (p = enumtype->of; *p != NULL; p++) {
+ if (!first) {
+ cfg_print_cstr(pctx, " | ");
+ }
+ first = false;
+ cfg_print_cstr(pctx, *p);
+ }
+ if (othertype != &cfg_type_void) {
+ if (!first) {
+ cfg_print_cstr(pctx, " | ");
+ }
+ cfg_doc_terminal(pctx, othertype);
+ }
+ cfg_print_cstr(pctx, " )");
+ if (othertype == &cfg_type_void) {
+ cfg_print_cstr(pctx, " ]");
+ }
+}
+
+void
+cfg_print_ustring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ REQUIRE(pctx != NULL);
+ REQUIRE(obj != NULL);
+
+ cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
+}
+
+static void
+print_qstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ cfg_print_cstr(pctx, "\"");
+ for (size_t i = 0; i < obj->value.string.length; i++) {
+ if (obj->value.string.base[i] == '"') {
+ cfg_print_cstr(pctx, "\\");
+ }
+ cfg_print_chars(pctx, &obj->value.string.base[i], 1);
+ }
+ cfg_print_cstr(pctx, "\"");
+}
+
+static void
+print_sstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ cfg_print_cstr(pctx, "\"");
+ if ((pctx->flags & CFG_PRINTER_XKEY) != 0) {
+ unsigned int len = obj->value.string.length;
+ while (len-- > 0) {
+ cfg_print_cstr(pctx, "?");
+ }
+ } else {
+ cfg_print_ustring(pctx, obj);
+ }
+ cfg_print_cstr(pctx, "\"");
+}
+
+static void
+free_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
+ isc_mem_put(pctx->mctx, obj->value.string.base,
+ obj->value.string.length + 1);
+}
+
+bool
+cfg_obj_isstring(const cfg_obj_t *obj) {
+ REQUIRE(obj != NULL);
+ return (obj->type->rep == &cfg_rep_string);
+}
+
+const char *
+cfg_obj_asstring(const cfg_obj_t *obj) {
+ REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string);
+ return (obj->value.string.base);
+}
+
+/* Quoted string only */
+cfg_type_t cfg_type_qstring = { "quoted_string", cfg_parse_qstring,
+ print_qstring, cfg_doc_terminal,
+ &cfg_rep_string, NULL };
+
+/* Unquoted string only */
+cfg_type_t cfg_type_ustring = { "string", parse_ustring,
+ cfg_print_ustring, cfg_doc_terminal,
+ &cfg_rep_string, NULL };
+
+/* Any string (quoted or unquoted); printed with quotes */
+cfg_type_t cfg_type_astring = { "string", cfg_parse_astring,
+ print_qstring, cfg_doc_terminal,
+ &cfg_rep_string, NULL };
+
+/*
+ * Any string (quoted or unquoted); printed with quotes.
+ * If CFG_PRINTER_XKEY is set when printing the string will be '?' out.
+ */
+cfg_type_t cfg_type_sstring = { "string", cfg_parse_sstring,
+ print_sstring, cfg_doc_terminal,
+ &cfg_rep_string, NULL };
+
+/*
+ * Text enclosed in brackets. Used to pass a block of configuration
+ * text to dynamic library or external application. Checked for
+ * bracket balance, but not otherwise parsed.
+ */
+cfg_type_t cfg_type_bracketed_text = { "bracketed_text", parse_btext,
+ print_btext, doc_btext,
+ &cfg_rep_string, NULL };
+
+#if defined(HAVE_GEOIP2)
+/*
+ * "geoip" ACL element:
+ * geoip [ db <database> ] search-type <string>
+ */
+static const char *geoiptype_enums[] = {
+ "area", "areacode", "asnum", "city", "continent",
+ "country", "country3", "countryname", "domain", "isp",
+ "metro", "metrocode", "netspeed", "org", "postal",
+ "postalcode", "region", "regionname", "timezone", "tz",
+ NULL
+};
+static cfg_type_t cfg_type_geoiptype = { "geoiptype", cfg_parse_enum,
+ cfg_print_ustring, cfg_doc_enum,
+ &cfg_rep_string, &geoiptype_enums };
+
+static cfg_tuplefielddef_t geoip_fields[] = {
+ { "negated", &cfg_type_void, 0 },
+ { "db", &cfg_type_astring, 0 },
+ { "subtype", &cfg_type_geoiptype, 0 },
+ { "search", &cfg_type_astring, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_geoip = { "geoip", parse_geoip, print_geoip,
+ doc_geoip, &cfg_rep_tuple, geoip_fields };
+
+static isc_result_t
+parse_geoip(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+ cfg_obj_t *obj = NULL;
+ const cfg_tuplefielddef_t *fields = type->of;
+
+ CHECK(cfg_create_tuple(pctx, type, &obj));
+ CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[0]));
+
+ /* Parse the optional "db" field. */
+ CHECK(cfg_peektoken(pctx, 0));
+ if (pctx->token.type == isc_tokentype_string) {
+ CHECK(cfg_gettoken(pctx, 0));
+ if (strcasecmp(TOKEN_STRING(pctx), "db") == 0 &&
+ obj->value.tuple[1] == NULL)
+ {
+ CHECK(cfg_parse_obj(pctx, fields[1].type,
+ &obj->value.tuple[1]));
+ } else {
+ CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[1]));
+ cfg_ungettoken(pctx);
+ }
+ }
+
+ CHECK(cfg_parse_obj(pctx, fields[2].type, &obj->value.tuple[2]));
+ CHECK(cfg_parse_obj(pctx, fields[3].type, &obj->value.tuple[3]));
+
+ *ret = obj;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ CLEANUP_OBJ(obj);
+ return (result);
+}
+
+static void
+print_geoip(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ if (obj->value.tuple[1]->type->print != cfg_print_void) {
+ cfg_print_cstr(pctx, " db ");
+ cfg_print_obj(pctx, obj->value.tuple[1]);
+ }
+ cfg_print_obj(pctx, obj->value.tuple[2]);
+ cfg_print_obj(pctx, obj->value.tuple[3]);
+}
+
+static void
+doc_geoip(cfg_printer_t *pctx, const cfg_type_t *type) {
+ UNUSED(type);
+ cfg_print_cstr(pctx, "[ db ");
+ cfg_doc_obj(pctx, &cfg_type_astring);
+ cfg_print_cstr(pctx, " ]");
+ cfg_print_cstr(pctx, " ");
+ cfg_doc_enum(pctx, &cfg_type_geoiptype);
+ cfg_print_cstr(pctx, " ");
+ cfg_doc_obj(pctx, &cfg_type_astring);
+}
+#endif /* HAVE_GEOIP2 */
+
+static cfg_type_t cfg_type_addrmatchelt;
+static cfg_type_t cfg_type_negated;
+
+static isc_result_t
+parse_addrmatchelt(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ isc_result_t result;
+ UNUSED(type);
+
+ CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
+
+ if (pctx->token.type == isc_tokentype_string ||
+ pctx->token.type == isc_tokentype_qstring)
+ {
+ if (pctx->token.type == isc_tokentype_string &&
+ (strcasecmp(TOKEN_STRING(pctx), "key") == 0))
+ {
+ CHECK(cfg_parse_obj(pctx, &cfg_type_keyref, ret));
+ } else if (pctx->token.type == isc_tokentype_string &&
+ (strcasecmp(TOKEN_STRING(pctx), "geoip") == 0))
+ {
+#if defined(HAVE_GEOIP2)
+ CHECK(cfg_gettoken(pctx, 0));
+ CHECK(cfg_parse_obj(pctx, &cfg_type_geoip, ret));
+#else /* if defined(HAVE_GEOIP2) */
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "'geoip' "
+ "not supported in this build");
+ return (ISC_R_UNEXPECTEDTOKEN);
+#endif /* if defined(HAVE_GEOIP2) */
+ } else {
+ if (cfg_lookingat_netaddr(
+ pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK |
+ CFG_ADDR_V6OK))
+ {
+ CHECK(cfg_parse_netprefix(pctx, NULL, ret));
+ } else {
+ CHECK(cfg_parse_astring(pctx, NULL, ret));
+ }
+ }
+ } else if (pctx->token.type == isc_tokentype_special) {
+ if (pctx->token.value.as_char == '{') {
+ /* Nested match list. */
+ CHECK(cfg_parse_obj(pctx, &cfg_type_bracketed_aml,
+ ret));
+ } else if (pctx->token.value.as_char == '!') {
+ CHECK(cfg_gettoken(pctx, 0)); /* read "!" */
+ CHECK(cfg_parse_obj(pctx, &cfg_type_negated, ret));
+ } else {
+ goto bad;
+ }
+ } else {
+ bad:
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "expected IP match list element");
+ return (ISC_R_UNEXPECTEDTOKEN);
+ }
+cleanup:
+ return (result);
+}
+
+/*%
+ * A negated address match list element (like "! 10.0.0.1").
+ * Somewhat sneakily, the caller is expected to parse the
+ * "!", but not to print it.
+ */
+static cfg_tuplefielddef_t negated_fields[] = {
+ { "negated", &cfg_type_addrmatchelt, 0 }, { NULL, NULL, 0 }
+};
+
+static void
+print_negated(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ cfg_print_cstr(pctx, "!");
+ cfg_print_tuple(pctx, obj);
+}
+
+static cfg_type_t cfg_type_negated = { "negated", cfg_parse_tuple,
+ print_negated, NULL,
+ &cfg_rep_tuple, &negated_fields };
+
+/*% An address match list element */
+
+static cfg_type_t cfg_type_addrmatchelt = { "address_match_element",
+ parse_addrmatchelt,
+ NULL,
+ cfg_doc_terminal,
+ NULL,
+ NULL };
+
+/*%
+ * A bracketed address match list
+ */
+cfg_type_t cfg_type_bracketed_aml = { "bracketed_aml",
+ cfg_parse_bracketed_list,
+ cfg_print_bracketed_list,
+ cfg_doc_bracketed_list,
+ &cfg_rep_list,
+ &cfg_type_addrmatchelt };
+
+/*
+ * Optional bracketed text
+ */
+static isc_result_t
+parse_optional_btext(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ isc_result_t result;
+
+ UNUSED(type);
+
+ CHECK(cfg_peektoken(pctx, ISC_LEXOPT_BTEXT));
+ if (pctx->token.type == isc_tokentype_btext) {
+ CHECK(cfg_parse_obj(pctx, &cfg_type_bracketed_text, ret));
+ } else {
+ CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
+ }
+cleanup:
+ return (result);
+}
+
+static void
+print_optional_btext(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ if (obj->type == &cfg_type_void) {
+ return;
+ }
+
+ pctx->indent++;
+ cfg_print_cstr(pctx, "{");
+ cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
+ print_close(pctx);
+}
+
+static void
+doc_optional_btext(cfg_printer_t *pctx, const cfg_type_t *type) {
+ UNUSED(type);
+
+ cfg_print_cstr(pctx, "[ { <unspecified-text> } ]");
+}
+
+cfg_type_t cfg_type_optional_bracketed_text = { "optional_btext",
+ parse_optional_btext,
+ print_optional_btext,
+ doc_optional_btext,
+ NULL,
+ NULL };
+
+/*
+ * Booleans
+ */
+
+bool
+cfg_obj_isboolean(const cfg_obj_t *obj) {
+ REQUIRE(obj != NULL);
+ return (obj->type->rep == &cfg_rep_boolean);
+}
+
+bool
+cfg_obj_asboolean(const cfg_obj_t *obj) {
+ REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean);
+ return (obj->value.boolean);
+}
+
+isc_result_t
+cfg_parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+ bool value;
+ cfg_obj_t *obj = NULL;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(ret != NULL && *ret == NULL);
+
+ UNUSED(type);
+
+ result = cfg_gettoken(pctx, 0);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ if (pctx->token.type != isc_tokentype_string) {
+ goto bad_boolean;
+ }
+
+ if ((strcasecmp(TOKEN_STRING(pctx), "true") == 0) ||
+ (strcasecmp(TOKEN_STRING(pctx), "yes") == 0) ||
+ (strcmp(TOKEN_STRING(pctx), "1") == 0))
+ {
+ value = true;
+ } else if ((strcasecmp(TOKEN_STRING(pctx), "false") == 0) ||
+ (strcasecmp(TOKEN_STRING(pctx), "no") == 0) ||
+ (strcmp(TOKEN_STRING(pctx), "0") == 0))
+ {
+ value = false;
+ } else {
+ goto bad_boolean;
+ }
+
+ CHECK(cfg_create_obj(pctx, &cfg_type_boolean, &obj));
+ obj->value.boolean = value;
+ *ret = obj;
+ return (result);
+
+bad_boolean:
+ cfg_parser_error(pctx, CFG_LOG_NEAR, "boolean expected");
+ return (ISC_R_UNEXPECTEDTOKEN);
+
+cleanup:
+ return (result);
+}
+
+void
+cfg_print_boolean(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ REQUIRE(pctx != NULL);
+ REQUIRE(obj != NULL);
+
+ if (obj->value.boolean) {
+ cfg_print_cstr(pctx, "yes");
+ } else {
+ cfg_print_cstr(pctx, "no");
+ }
+}
+
+cfg_type_t cfg_type_boolean = { "boolean", cfg_parse_boolean,
+ cfg_print_boolean, cfg_doc_terminal,
+ &cfg_rep_boolean, NULL };
+
+/*
+ * Lists.
+ */
+
+isc_result_t
+cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) {
+ isc_result_t result;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(type != NULL);
+ REQUIRE(obj != NULL && *obj == NULL);
+
+ CHECK(cfg_create_obj(pctx, type, obj));
+ ISC_LIST_INIT((*obj)->value.list);
+cleanup:
+ return (result);
+}
+
+static isc_result_t
+create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) {
+ cfg_listelt_t *elt;
+
+ elt = isc_mem_get(pctx->mctx, sizeof(*elt));
+ elt->obj = NULL;
+ ISC_LINK_INIT(elt, link);
+ *eltp = elt;
+ return (ISC_R_SUCCESS);
+}
+
+static void
+free_listelt(cfg_parser_t *pctx, cfg_listelt_t *elt) {
+ if (elt->obj != NULL) {
+ cfg_obj_destroy(pctx, &elt->obj);
+ }
+ isc_mem_put(pctx->mctx, elt, sizeof(*elt));
+}
+
+static void
+free_list(cfg_parser_t *pctx, cfg_obj_t *obj) {
+ cfg_listelt_t *elt, *next;
+ for (elt = ISC_LIST_HEAD(obj->value.list); elt != NULL; elt = next) {
+ next = ISC_LIST_NEXT(elt, link);
+ free_listelt(pctx, elt);
+ }
+}
+
+isc_result_t
+cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
+ cfg_listelt_t **ret) {
+ isc_result_t result;
+ cfg_listelt_t *elt = NULL;
+ cfg_obj_t *value = NULL;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(elttype != NULL);
+ REQUIRE(ret != NULL && *ret == NULL);
+
+ CHECK(create_listelt(pctx, &elt));
+
+ result = cfg_parse_obj(pctx, elttype, &value);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ elt->obj = value;
+
+ *ret = elt;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ isc_mem_put(pctx->mctx, elt, sizeof(*elt));
+ return (result);
+}
+
+/*
+ * Parse a homogeneous list whose elements are of type 'elttype'
+ * and where each element is terminated by a semicolon.
+ */
+static isc_result_t
+parse_list(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret) {
+ cfg_obj_t *listobj = NULL;
+ const cfg_type_t *listof = listtype->of;
+ isc_result_t result;
+ cfg_listelt_t *elt = NULL;
+
+ CHECK(cfg_create_list(pctx, listtype, &listobj));
+
+ for (;;) {
+ CHECK(cfg_peektoken(pctx, 0));
+ if (pctx->token.type == isc_tokentype_special &&
+ pctx->token.value.as_char == /*{*/ '}')
+ {
+ break;
+ }
+ CHECK(cfg_parse_listelt(pctx, listof, &elt));
+ CHECK(parse_semicolon(pctx));
+ ISC_LIST_APPEND(listobj->value.list, elt, link);
+ elt = NULL;
+ }
+ *ret = listobj;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ if (elt != NULL) {
+ free_listelt(pctx, elt);
+ }
+ CLEANUP_OBJ(listobj);
+ return (result);
+}
+
+static void
+print_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ const cfg_list_t *list = &obj->value.list;
+ const cfg_listelt_t *elt;
+
+ for (elt = ISC_LIST_HEAD(*list); elt != NULL;
+ elt = ISC_LIST_NEXT(elt, link))
+ {
+ if ((pctx->flags & CFG_PRINTER_ONELINE) != 0) {
+ cfg_print_obj(pctx, elt->obj);
+ cfg_print_cstr(pctx, "; ");
+ } else {
+ cfg_print_indent(pctx);
+ cfg_print_obj(pctx, elt->obj);
+ cfg_print_cstr(pctx, ";\n");
+ }
+ }
+}
+
+isc_result_t
+cfg_parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ isc_result_t result;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(type != NULL);
+ REQUIRE(ret != NULL && *ret == NULL);
+
+ CHECK(cfg_parse_special(pctx, '{'));
+ CHECK(parse_list(pctx, type, ret));
+ CHECK(cfg_parse_special(pctx, '}'));
+cleanup:
+ return (result);
+}
+
+void
+cfg_print_bracketed_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ REQUIRE(pctx != NULL);
+ REQUIRE(obj != NULL);
+
+ print_open(pctx);
+ print_list(pctx, obj);
+ print_close(pctx);
+}
+
+void
+cfg_doc_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) {
+ REQUIRE(pctx != NULL);
+ REQUIRE(type != NULL);
+
+ cfg_print_cstr(pctx, "{ ");
+ cfg_doc_obj(pctx, type->of);
+ cfg_print_cstr(pctx, "; ... }");
+}
+
+/*
+ * Parse a homogeneous list whose elements are of type 'elttype'
+ * and where elements are separated by space. The list ends
+ * before the first semicolon.
+ */
+isc_result_t
+cfg_parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype,
+ cfg_obj_t **ret) {
+ cfg_obj_t *listobj = NULL;
+ const cfg_type_t *listof;
+ isc_result_t result;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(listtype != NULL);
+ REQUIRE(ret != NULL && *ret == NULL);
+
+ listof = listtype->of;
+
+ CHECK(cfg_create_list(pctx, listtype, &listobj));
+
+ for (;;) {
+ cfg_listelt_t *elt = NULL;
+
+ CHECK(cfg_peektoken(pctx, 0));
+ if (pctx->token.type == isc_tokentype_special &&
+ pctx->token.value.as_char == ';')
+ {
+ break;
+ }
+ CHECK(cfg_parse_listelt(pctx, listof, &elt));
+ ISC_LIST_APPEND(listobj->value.list, elt, link);
+ }
+ *ret = listobj;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ CLEANUP_OBJ(listobj);
+ return (result);
+}
+
+void
+cfg_print_spacelist(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ const cfg_list_t *list = NULL;
+ const cfg_listelt_t *elt = NULL;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(obj != NULL);
+
+ list = &obj->value.list;
+
+ for (elt = ISC_LIST_HEAD(*list); elt != NULL;
+ elt = ISC_LIST_NEXT(elt, link))
+ {
+ cfg_print_obj(pctx, elt->obj);
+ if (ISC_LIST_NEXT(elt, link) != NULL) {
+ cfg_print_cstr(pctx, " ");
+ }
+ }
+}
+
+bool
+cfg_obj_islist(const cfg_obj_t *obj) {
+ REQUIRE(obj != NULL);
+ return (obj->type->rep == &cfg_rep_list);
+}
+
+const cfg_listelt_t *
+cfg_list_first(const cfg_obj_t *obj) {
+ REQUIRE(obj == NULL || obj->type->rep == &cfg_rep_list);
+ if (obj == NULL) {
+ return (NULL);
+ }
+ return (ISC_LIST_HEAD(obj->value.list));
+}
+
+const cfg_listelt_t *
+cfg_list_next(const cfg_listelt_t *elt) {
+ REQUIRE(elt != NULL);
+ return (ISC_LIST_NEXT(elt, link));
+}
+
+/*
+ * Return the length of a list object. If obj is NULL or is not
+ * a list, return 0.
+ */
+unsigned int
+cfg_list_length(const cfg_obj_t *obj, bool recurse) {
+ const cfg_listelt_t *elt;
+ unsigned int count = 0;
+
+ if (obj == NULL || !cfg_obj_islist(obj)) {
+ return (0U);
+ }
+ for (elt = cfg_list_first(obj); elt != NULL; elt = cfg_list_next(elt)) {
+ if (recurse && cfg_obj_islist(elt->obj)) {
+ count += cfg_list_length(elt->obj, recurse);
+ } else {
+ count++;
+ }
+ }
+ return (count);
+}
+
+cfg_obj_t *
+cfg_listelt_value(const cfg_listelt_t *elt) {
+ REQUIRE(elt != NULL);
+ return (elt->obj);
+}
+
+/*
+ * Maps.
+ */
+
+/*
+ * Parse a map body. That's something like
+ *
+ * "foo 1; bar { glub; }; zap true; zap false;"
+ *
+ * i.e., a sequence of option names followed by values and
+ * terminated by semicolons. Used for the top level of
+ * the named.conf syntax, as well as for the body of the
+ * options, view, zone, and other statements.
+ */
+isc_result_t
+cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ const cfg_clausedef_t *const *clausesets;
+ isc_result_t result;
+ const cfg_clausedef_t *const *clauseset;
+ const cfg_clausedef_t *clause;
+ cfg_obj_t *value = NULL;
+ cfg_obj_t *obj = NULL;
+ cfg_obj_t *eltobj = NULL;
+ cfg_obj_t *includename = NULL;
+ isc_symvalue_t symval;
+ cfg_list_t *list = NULL;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(type != NULL);
+ REQUIRE(ret != NULL && *ret == NULL);
+
+ clausesets = type->of;
+
+ CHECK(create_map(pctx, type, &obj));
+
+ obj->value.map.clausesets = clausesets;
+
+ for (;;) {
+ cfg_listelt_t *elt;
+
+ redo:
+ /*
+ * Parse the option name and see if it is known.
+ */
+ CHECK(cfg_gettoken(pctx, 0));
+
+ if (pctx->token.type != isc_tokentype_string) {
+ cfg_ungettoken(pctx);
+ break;
+ }
+
+ /*
+ * We accept "include" statements wherever a map body
+ * clause can occur.
+ */
+ if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) {
+ /*
+ * Turn the file name into a temporary configuration
+ * object just so that it is not overwritten by the
+ * semicolon token.
+ */
+ CHECK(cfg_parse_obj(pctx, &cfg_type_qstring,
+ &includename));
+ CHECK(parse_semicolon(pctx));
+
+ /* Allow include to specify a pattern that follows
+ * the same rules as the shell e.g "/path/zone*.conf" */
+ glob_t glob_obj;
+ CHECK(isc_glob(includename->value.string.base,
+ &glob_obj));
+ cfg_obj_destroy(pctx, &includename);
+
+ for (size_t i = 0; i < glob_obj.gl_pathc; ++i) {
+ CHECK(parser_openfile(pctx,
+ glob_obj.gl_pathv[i]));
+ }
+
+ isc_globfree(&glob_obj);
+
+ goto redo;
+ }
+
+ clause = NULL;
+ for (clauseset = clausesets; *clauseset != NULL; clauseset++) {
+ for (clause = *clauseset; clause->name != NULL;
+ clause++)
+ {
+ if (strcasecmp(TOKEN_STRING(pctx),
+ clause->name) == 0)
+ {
+ goto done;
+ }
+ }
+ }
+ done:
+ if (clause == NULL || clause->name == NULL) {
+ cfg_parser_error(pctx, CFG_LOG_NOPREP,
+ "unknown option");
+ /*
+ * Try to recover by parsing this option as an unknown
+ * option and discarding it.
+ */
+ CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported,
+ &eltobj));
+ cfg_obj_destroy(pctx, &eltobj);
+ CHECK(parse_semicolon(pctx));
+ continue;
+ }
+
+ /* Clause is known. */
+
+ /* Issue fatal errors if appropriate */
+ if ((clause->flags & CFG_CLAUSEFLAG_ANCIENT) != 0) {
+ cfg_parser_error(pctx, 0,
+ "option '%s' no longer exists",
+ clause->name);
+ CHECK(ISC_R_FAILURE);
+ }
+ if ((clause->flags & CFG_CLAUSEFLAG_NOTCONFIGURED) != 0) {
+ cfg_parser_error(pctx, 0,
+ "option '%s' was not "
+ "enabled at compile time",
+ clause->name);
+ CHECK(ISC_R_FAILURE);
+ }
+
+ /* Issue warnings if appropriate */
+ if ((pctx->flags & CFG_PCTX_NODEPRECATED) == 0 &&
+ (clause->flags & CFG_CLAUSEFLAG_DEPRECATED) != 0)
+ {
+ cfg_parser_warning(pctx, 0, "option '%s' is deprecated",
+ clause->name);
+ }
+ if ((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0) {
+ cfg_parser_warning(pctx, 0,
+ "option '%s' is obsolete and "
+ "should be removed ",
+ clause->name);
+ }
+ if ((clause->flags & CFG_CLAUSEFLAG_EXPERIMENTAL) != 0) {
+ cfg_parser_warning(pctx, 0,
+ "option '%s' is experimental and "
+ "subject to change in the future",
+ clause->name);
+ }
+
+ /* See if the clause already has a value; if not create one. */
+ result = isc_symtab_lookup(obj->value.map.symtab, clause->name,
+ 0, &symval);
+
+ if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) {
+ /* Multivalued clause */
+ cfg_obj_t *listobj = NULL;
+ if (result == ISC_R_NOTFOUND) {
+ CHECK(cfg_create_list(pctx,
+ &cfg_type_implicitlist,
+ &listobj));
+ symval.as_pointer = listobj;
+ result = isc_symtab_define(
+ obj->value.map.symtab, clause->name, 1,
+ symval, isc_symexists_reject);
+ if (result != ISC_R_SUCCESS) {
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "isc_symtab_define(%s)"
+ " "
+ "failed",
+ clause->name);
+ isc_mem_put(pctx->mctx, list,
+ sizeof(cfg_list_t));
+ goto cleanup;
+ }
+ } else {
+ INSIST(result == ISC_R_SUCCESS);
+ listobj = symval.as_pointer;
+ }
+
+ elt = NULL;
+ CHECK(cfg_parse_listelt(pctx, clause->type, &elt));
+ CHECK(parse_semicolon(pctx));
+
+ ISC_LIST_APPEND(listobj->value.list, elt, link);
+ } else {
+ /* Single-valued clause */
+ if (result == ISC_R_NOTFOUND) {
+ bool callback = ((clause->flags &
+ CFG_CLAUSEFLAG_CALLBACK) !=
+ 0);
+ CHECK(parse_symtab_elt(
+ pctx, clause->name, clause->type,
+ obj->value.map.symtab, callback));
+ CHECK(parse_semicolon(pctx));
+ } else if (result == ISC_R_SUCCESS) {
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "'%s' redefined",
+ clause->name);
+ result = ISC_R_EXISTS;
+ goto cleanup;
+ } else {
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "isc_symtab_define() failed");
+ goto cleanup;
+ }
+ }
+ }
+
+ *ret = obj;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ CLEANUP_OBJ(value);
+ CLEANUP_OBJ(obj);
+ CLEANUP_OBJ(eltobj);
+ CLEANUP_OBJ(includename);
+ return (result);
+}
+
+static isc_result_t
+parse_symtab_elt(cfg_parser_t *pctx, const char *name, cfg_type_t *elttype,
+ isc_symtab_t *symtab, bool callback) {
+ isc_result_t result;
+ cfg_obj_t *obj = NULL;
+ isc_symvalue_t symval;
+
+ CHECK(cfg_parse_obj(pctx, elttype, &obj));
+
+ if (callback && pctx->callback != NULL) {
+ CHECK(pctx->callback(name, obj, pctx->callbackarg));
+ }
+
+ symval.as_pointer = obj;
+ CHECK(isc_symtab_define(symtab, name, 1, symval, isc_symexists_reject));
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ CLEANUP_OBJ(obj);
+ return (result);
+}
+
+/*
+ * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
+ */
+isc_result_t
+cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(type != NULL);
+ REQUIRE(ret != NULL && *ret == NULL);
+
+ CHECK(cfg_parse_special(pctx, '{'));
+ CHECK(cfg_parse_mapbody(pctx, type, ret));
+ CHECK(cfg_parse_special(pctx, '}'));
+cleanup:
+ return (result);
+}
+
+/*
+ * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map().
+ */
+static isc_result_t
+parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype,
+ const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+ cfg_obj_t *idobj = NULL;
+ cfg_obj_t *mapobj = NULL;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(nametype != NULL);
+ REQUIRE(type != NULL);
+ REQUIRE(ret != NULL && *ret == NULL);
+
+ CHECK(cfg_parse_obj(pctx, nametype, &idobj));
+ CHECK(cfg_parse_map(pctx, type, &mapobj));
+ mapobj->value.map.id = idobj;
+ *ret = mapobj;
+ return (result);
+cleanup:
+ CLEANUP_OBJ(idobj);
+ CLEANUP_OBJ(mapobj);
+ return (result);
+}
+
+/*
+ * Parse a map identified by a string name. E.g., "name { foo 1; }".
+ * Used for the "key" and "channel" statements.
+ */
+isc_result_t
+cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ return (parse_any_named_map(pctx, &cfg_type_astring, type, ret));
+}
+
+/*
+ * Parse a map identified by a network address.
+ * Used to be used for the "server" statement.
+ */
+isc_result_t
+cfg_parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ return (parse_any_named_map(pctx, &cfg_type_netaddr, type, ret));
+}
+
+/*
+ * Parse a map identified by a network prefix.
+ * Used for the "server" statement.
+ */
+isc_result_t
+cfg_parse_netprefix_map(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ return (parse_any_named_map(pctx, &cfg_type_netprefix, type, ret));
+}
+
+static void
+print_symval(cfg_printer_t *pctx, const char *name, cfg_obj_t *obj) {
+ if ((pctx->flags & CFG_PRINTER_ONELINE) == 0) {
+ cfg_print_indent(pctx);
+ }
+
+ cfg_print_cstr(pctx, name);
+ cfg_print_cstr(pctx, " ");
+ cfg_print_obj(pctx, obj);
+
+ if ((pctx->flags & CFG_PRINTER_ONELINE) == 0) {
+ cfg_print_cstr(pctx, ";\n");
+ } else {
+ cfg_print_cstr(pctx, "; ");
+ }
+}
+
+void
+cfg_print_mapbody(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ const cfg_clausedef_t *const *clauseset;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(obj != NULL);
+
+ for (clauseset = obj->value.map.clausesets; *clauseset != NULL;
+ clauseset++)
+ {
+ isc_symvalue_t symval;
+ const cfg_clausedef_t *clause;
+
+ for (clause = *clauseset; clause->name != NULL; clause++) {
+ isc_result_t result;
+ result = isc_symtab_lookup(obj->value.map.symtab,
+ clause->name, 0, &symval);
+ if (result == ISC_R_SUCCESS) {
+ cfg_obj_t *symobj = symval.as_pointer;
+ if (symobj->type == &cfg_type_implicitlist) {
+ /* Multivalued. */
+ cfg_list_t *list = &symobj->value.list;
+ cfg_listelt_t *elt;
+ for (elt = ISC_LIST_HEAD(*list);
+ elt != NULL;
+ elt = ISC_LIST_NEXT(elt, link))
+ {
+ print_symval(pctx, clause->name,
+ elt->obj);
+ }
+ } else {
+ /* Single-valued. */
+ print_symval(pctx, clause->name,
+ symobj);
+ }
+ } else if (result == ISC_R_NOTFOUND) {
+ /* do nothing */
+ } else {
+ UNREACHABLE();
+ }
+ }
+ }
+}
+
+static struct flagtext {
+ unsigned int flag;
+ const char *text;
+} flagtexts[] = { { CFG_CLAUSEFLAG_OBSOLETE, "obsolete" },
+ { CFG_CLAUSEFLAG_TESTONLY, "test only" },
+ { CFG_CLAUSEFLAG_NOTCONFIGURED, "not configured" },
+ { CFG_CLAUSEFLAG_MULTI, "may occur multiple times" },
+ { CFG_CLAUSEFLAG_EXPERIMENTAL, "experimental" },
+ { CFG_CLAUSEFLAG_DEPRECATED, "deprecated" },
+ { CFG_CLAUSEFLAG_ANCIENT, "ancient" },
+ { 0, NULL } };
+
+void
+cfg_print_clauseflags(cfg_printer_t *pctx, unsigned int flags) {
+ struct flagtext *p;
+ bool first = true;
+ for (p = flagtexts; p->flag != 0; p++) {
+ if ((flags & p->flag) != 0) {
+ if (first) {
+ cfg_print_cstr(pctx, " // ");
+ } else {
+ cfg_print_cstr(pctx, ", ");
+ }
+ cfg_print_cstr(pctx, p->text);
+ first = false;
+ }
+ }
+}
+
+void
+cfg_doc_mapbody(cfg_printer_t *pctx, const cfg_type_t *type) {
+ const cfg_clausedef_t *const *clauseset;
+ const cfg_clausedef_t *clause;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(type != NULL);
+
+ for (clauseset = type->of; *clauseset != NULL; clauseset++) {
+ for (clause = *clauseset; clause->name != NULL; clause++) {
+ if (((pctx->flags & CFG_PRINTER_ACTIVEONLY) != 0) &&
+ (((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0) ||
+ ((clause->flags & CFG_CLAUSEFLAG_TESTONLY) != 0)))
+ {
+ continue;
+ }
+ if ((clause->flags & CFG_CLAUSEFLAG_ANCIENT) != 0 ||
+ (clause->flags & CFG_CLAUSEFLAG_NODOC) != 0)
+ {
+ continue;
+ }
+ cfg_print_cstr(pctx, clause->name);
+ cfg_print_cstr(pctx, " ");
+ cfg_doc_obj(pctx, clause->type);
+ cfg_print_cstr(pctx, ";");
+ cfg_print_clauseflags(pctx, clause->flags);
+ cfg_print_cstr(pctx, "\n\n");
+ }
+ }
+}
+
+void
+cfg_print_map(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ REQUIRE(pctx != NULL);
+ REQUIRE(obj != NULL);
+
+ if (obj->value.map.id != NULL) {
+ cfg_print_obj(pctx, obj->value.map.id);
+ cfg_print_cstr(pctx, " ");
+ }
+ print_open(pctx);
+ cfg_print_mapbody(pctx, obj);
+ print_close(pctx);
+}
+
+void
+cfg_doc_map(cfg_printer_t *pctx, const cfg_type_t *type) {
+ const cfg_clausedef_t *const *clauseset;
+ const cfg_clausedef_t *clause;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(type != NULL);
+
+ if (type->parse == cfg_parse_named_map) {
+ cfg_doc_obj(pctx, &cfg_type_astring);
+ cfg_print_cstr(pctx, " ");
+ } else if (type->parse == cfg_parse_addressed_map) {
+ cfg_doc_obj(pctx, &cfg_type_netaddr);
+ cfg_print_cstr(pctx, " ");
+ } else if (type->parse == cfg_parse_netprefix_map) {
+ cfg_doc_obj(pctx, &cfg_type_netprefix);
+ cfg_print_cstr(pctx, " ");
+ }
+
+ print_open(pctx);
+
+ for (clauseset = type->of; *clauseset != NULL; clauseset++) {
+ for (clause = *clauseset; clause->name != NULL; clause++) {
+ if (((pctx->flags & CFG_PRINTER_ACTIVEONLY) != 0) &&
+ (((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0) ||
+ ((clause->flags & CFG_CLAUSEFLAG_TESTONLY) != 0)))
+ {
+ continue;
+ }
+ if ((clause->flags & CFG_CLAUSEFLAG_ANCIENT) != 0 ||
+ (clause->flags & CFG_CLAUSEFLAG_NODOC) != 0)
+ {
+ continue;
+ }
+ cfg_print_indent(pctx);
+ cfg_print_cstr(pctx, clause->name);
+ if (clause->type->print != cfg_print_void) {
+ cfg_print_cstr(pctx, " ");
+ }
+ cfg_doc_obj(pctx, clause->type);
+ cfg_print_cstr(pctx, ";");
+ cfg_print_clauseflags(pctx, clause->flags);
+ cfg_print_cstr(pctx, "\n");
+ }
+ }
+ print_close(pctx);
+}
+
+bool
+cfg_obj_ismap(const cfg_obj_t *obj) {
+ REQUIRE(obj != NULL);
+ return (obj->type->rep == &cfg_rep_map);
+}
+
+isc_result_t
+cfg_map_get(const cfg_obj_t *mapobj, const char *name, const cfg_obj_t **obj) {
+ isc_result_t result;
+ isc_symvalue_t val;
+ const cfg_map_t *map;
+
+ REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
+ REQUIRE(name != NULL);
+ REQUIRE(obj != NULL && *obj == NULL);
+
+ map = &mapobj->value.map;
+
+ result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ *obj = val.as_pointer;
+ return (ISC_R_SUCCESS);
+}
+
+const cfg_obj_t *
+cfg_map_getname(const cfg_obj_t *mapobj) {
+ REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
+ return (mapobj->value.map.id);
+}
+
+unsigned int
+cfg_map_count(const cfg_obj_t *mapobj) {
+ const cfg_map_t *map;
+
+ REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
+
+ map = &mapobj->value.map;
+ return (isc_symtab_count(map->symtab));
+}
+
+const char *
+cfg_map_firstclause(const cfg_type_t *map, const void **clauses,
+ unsigned int *idx) {
+ cfg_clausedef_t *const *clauseset;
+
+ REQUIRE(map != NULL && map->rep == &cfg_rep_map);
+ REQUIRE(idx != NULL);
+ REQUIRE(clauses != NULL && *clauses == NULL);
+
+ clauseset = map->of;
+ if (*clauseset == NULL) {
+ return (NULL);
+ }
+ *clauses = *clauseset;
+ *idx = 0;
+ while ((*clauseset)[*idx].name == NULL) {
+ *clauses = (*++clauseset);
+ if (*clauses == NULL) {
+ return (NULL);
+ }
+ }
+ return ((*clauseset)[*idx].name);
+}
+
+const char *
+cfg_map_nextclause(const cfg_type_t *map, const void **clauses,
+ unsigned int *idx) {
+ cfg_clausedef_t *const *clauseset;
+
+ REQUIRE(map != NULL && map->rep == &cfg_rep_map);
+ REQUIRE(idx != NULL);
+ REQUIRE(clauses != NULL && *clauses != NULL);
+
+ clauseset = map->of;
+ while (*clauseset != NULL && *clauseset != *clauses) {
+ clauseset++;
+ }
+ INSIST(*clauseset == *clauses);
+ (*idx)++;
+ while ((*clauseset)[*idx].name == NULL) {
+ *idx = 0;
+ *clauses = (*++clauseset);
+ if (*clauses == NULL) {
+ return (NULL);
+ }
+ }
+ return ((*clauseset)[*idx].name);
+}
+
+/* Parse an arbitrary token, storing its raw text representation. */
+static isc_result_t
+parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ cfg_obj_t *obj = NULL;
+ isc_result_t result;
+ isc_region_t r;
+
+ UNUSED(type);
+
+ CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj));
+ CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
+ if (pctx->token.type == isc_tokentype_eof) {
+ cfg_ungettoken(pctx);
+ result = ISC_R_EOF;
+ goto cleanup;
+ }
+
+ isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r);
+
+ obj->value.string.base = isc_mem_get(pctx->mctx, r.length + 1);
+ obj->value.string.length = r.length;
+ memmove(obj->value.string.base, r.base, r.length);
+ obj->value.string.base[r.length] = '\0';
+ *ret = obj;
+ return (result);
+
+cleanup:
+ if (obj != NULL) {
+ isc_mem_put(pctx->mctx, obj, sizeof(*obj));
+ }
+ return (result);
+}
+
+cfg_type_t cfg_type_token = { "token", parse_token,
+ cfg_print_ustring, cfg_doc_terminal,
+ &cfg_rep_string, NULL };
+
+/*
+ * An unsupported option. This is just a list of tokens with balanced braces
+ * ending in a semicolon.
+ */
+
+static isc_result_t
+parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ cfg_obj_t *listobj = NULL;
+ isc_result_t result;
+ int braces = 0;
+
+ CHECK(cfg_create_list(pctx, type, &listobj));
+
+ for (;;) {
+ cfg_listelt_t *elt = NULL;
+
+ CHECK(cfg_peektoken(pctx, 0));
+ if (pctx->token.type == isc_tokentype_special) {
+ if (pctx->token.value.as_char == '{') {
+ braces++;
+ } else if (pctx->token.value.as_char == '}') {
+ braces--;
+ } else if (pctx->token.value.as_char == ';') {
+ if (braces == 0) {
+ break;
+ }
+ }
+ }
+ if (pctx->token.type == isc_tokentype_eof || braces < 0) {
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "unexpected token");
+ result = ISC_R_UNEXPECTEDTOKEN;
+ goto cleanup;
+ }
+
+ CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt));
+ ISC_LIST_APPEND(listobj->value.list, elt, link);
+ }
+ INSIST(braces == 0);
+ *ret = listobj;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ CLEANUP_OBJ(listobj);
+ return (result);
+}
+
+cfg_type_t cfg_type_unsupported = { "unsupported", parse_unsupported,
+ cfg_print_spacelist, cfg_doc_terminal,
+ &cfg_rep_list, NULL };
+
+/*
+ * Try interpreting the current token as a network address.
+ *
+ * If CFG_ADDR_WILDOK is set in flags, "*" can be used as a wildcard
+ * and at least one of CFG_ADDR_V4OK and CFG_ADDR_V6OK must also be set. The
+ * "*" is interpreted as the IPv4 wildcard address if CFG_ADDR_V4OK is
+ * set (including the case where CFG_ADDR_V4OK and CFG_ADDR_V6OK are both set),
+ * and the IPv6 wildcard address otherwise.
+ */
+static isc_result_t
+token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
+ char *s;
+ struct in_addr in4a;
+ struct in6_addr in6a;
+
+ if (pctx->token.type != isc_tokentype_string) {
+ return (ISC_R_UNEXPECTEDTOKEN);
+ }
+
+ s = TOKEN_STRING(pctx);
+ if ((flags & CFG_ADDR_WILDOK) != 0 && strcmp(s, "*") == 0) {
+ if ((flags & CFG_ADDR_V4OK) != 0) {
+ isc_netaddr_any(na);
+ return (ISC_R_SUCCESS);
+ } else if ((flags & CFG_ADDR_V6OK) != 0) {
+ isc_netaddr_any6(na);
+ return (ISC_R_SUCCESS);
+ } else {
+ UNREACHABLE();
+ }
+ } else {
+ if ((flags & (CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK)) != 0) {
+ if (inet_pton(AF_INET, s, &in4a) == 1) {
+ isc_netaddr_fromin(na, &in4a);
+ return (ISC_R_SUCCESS);
+ }
+ }
+ if ((flags & CFG_ADDR_V4PREFIXOK) != 0 && strlen(s) <= 15U) {
+ char buf[64];
+ int i;
+
+ strlcpy(buf, s, sizeof(buf));
+ for (i = 0; i < 3; i++) {
+ strlcat(buf, ".0", sizeof(buf));
+ if (inet_pton(AF_INET, buf, &in4a) == 1) {
+ isc_netaddr_fromin(na, &in4a);
+ return (ISC_R_IPV4PREFIX);
+ }
+ }
+ }
+ if ((flags & CFG_ADDR_V6OK) != 0 && strlen(s) <= 127U) {
+ char buf[128]; /* see lib/bind9/getaddresses.c */
+ char *d; /* zone delimiter */
+ uint32_t zone = 0; /* scope zone ID */
+
+ strlcpy(buf, s, sizeof(buf));
+ d = strchr(buf, '%');
+ if (d != NULL) {
+ *d = '\0';
+ }
+
+ if (inet_pton(AF_INET6, buf, &in6a) == 1) {
+ if (d != NULL) {
+ isc_result_t result;
+
+ result = isc_netscope_pton(
+ AF_INET6, d + 1, &in6a, &zone);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ }
+
+ isc_netaddr_fromin6(na, &in6a);
+ isc_netaddr_setzone(na, zone);
+ return (ISC_R_SUCCESS);
+ }
+ }
+ }
+ return (ISC_R_UNEXPECTEDTOKEN);
+}
+
+isc_result_t
+cfg_parse_rawaddr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
+ isc_result_t result;
+ const char *wild = "";
+ const char *prefix = "";
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(na != NULL);
+
+ CHECK(cfg_gettoken(pctx, 0));
+ result = token_addr(pctx, flags, na);
+ if (result == ISC_R_UNEXPECTEDTOKEN) {
+ if ((flags & CFG_ADDR_WILDOK) != 0) {
+ wild = " or '*'";
+ }
+ if ((flags & CFG_ADDR_V4PREFIXOK) != 0) {
+ wild = " or IPv4 prefix";
+ }
+ if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V4OK) {
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "expected IPv4 address%s%s", prefix,
+ wild);
+ } else if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V6OK) {
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "expected IPv6 address%s%s", prefix,
+ wild);
+ } else {
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "expected IP address%s%s", prefix,
+ wild);
+ }
+ }
+cleanup:
+ return (result);
+}
+
+bool
+cfg_lookingat_netaddr(cfg_parser_t *pctx, unsigned int flags) {
+ isc_result_t result;
+ isc_netaddr_t na_dummy;
+
+ REQUIRE(pctx != NULL);
+
+ result = token_addr(pctx, flags, &na_dummy);
+ return (result == ISC_R_SUCCESS || result == ISC_R_IPV4PREFIX);
+}
+
+isc_result_t
+cfg_parse_rawport(cfg_parser_t *pctx, unsigned int flags, in_port_t *port) {
+ isc_result_t result;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(port != NULL);
+
+ CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
+
+ if ((flags & CFG_ADDR_WILDOK) != 0 &&
+ pctx->token.type == isc_tokentype_string &&
+ strcmp(TOKEN_STRING(pctx), "*") == 0)
+ {
+ *port = 0;
+ return (ISC_R_SUCCESS);
+ }
+ if (pctx->token.type != isc_tokentype_number) {
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "expected port number or '*'");
+ return (ISC_R_UNEXPECTEDTOKEN);
+ }
+ if (pctx->token.value.as_ulong >= 65536U) {
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "port number out of range");
+ return (ISC_R_UNEXPECTEDTOKEN);
+ }
+ *port = (in_port_t)(pctx->token.value.as_ulong);
+ return (ISC_R_SUCCESS);
+cleanup:
+ return (result);
+}
+
+void
+cfg_print_rawaddr(cfg_printer_t *pctx, const isc_netaddr_t *na) {
+ isc_result_t result;
+ char text[128];
+ isc_buffer_t buf;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(na != NULL);
+
+ isc_buffer_init(&buf, text, sizeof(text));
+ result = isc_netaddr_totext(na, &buf);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ cfg_print_chars(pctx, isc_buffer_base(&buf),
+ isc_buffer_usedlength(&buf));
+}
+
+/* netaddr */
+
+static unsigned int netaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
+static unsigned int netaddr4_flags = CFG_ADDR_V4OK;
+static unsigned int netaddr4wild_flags = CFG_ADDR_V4OK | CFG_ADDR_WILDOK;
+static unsigned int netaddr6_flags = CFG_ADDR_V6OK;
+static unsigned int netaddr6wild_flags = CFG_ADDR_V6OK | CFG_ADDR_WILDOK;
+
+static isc_result_t
+parse_netaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+ cfg_obj_t *obj = NULL;
+ isc_netaddr_t netaddr;
+ unsigned int flags = *(const unsigned int *)type->of;
+
+ CHECK(cfg_create_obj(pctx, type, &obj));
+ CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
+ isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, 0);
+ obj->value.sockaddrdscp.dscp = -1;
+ *ret = obj;
+ return (ISC_R_SUCCESS);
+cleanup:
+ CLEANUP_OBJ(obj);
+ return (result);
+}
+
+static void
+cfg_doc_netaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
+ const unsigned int *flagp = type->of;
+ int n = 0;
+ if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK) {
+ cfg_print_cstr(pctx, "( ");
+ }
+ if ((*flagp & CFG_ADDR_V4OK) != 0) {
+ cfg_print_cstr(pctx, "<ipv4_address>");
+ n++;
+ }
+ if ((*flagp & CFG_ADDR_V6OK) != 0) {
+ if (n != 0) {
+ cfg_print_cstr(pctx, " | ");
+ }
+ cfg_print_cstr(pctx, "<ipv6_address>");
+ n++;
+ }
+ if ((*flagp & CFG_ADDR_WILDOK) != 0) {
+ if (n != 0) {
+ cfg_print_cstr(pctx, " | ");
+ }
+ cfg_print_cstr(pctx, "*");
+ n++;
+ POST(n);
+ }
+ if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK) {
+ cfg_print_cstr(pctx, " )");
+ }
+}
+
+cfg_type_t cfg_type_netaddr = { "netaddr", parse_netaddr,
+ cfg_print_sockaddr, cfg_doc_netaddr,
+ &cfg_rep_sockaddr, &netaddr_flags };
+
+cfg_type_t cfg_type_netaddr4 = { "netaddr4", parse_netaddr,
+ cfg_print_sockaddr, cfg_doc_netaddr,
+ &cfg_rep_sockaddr, &netaddr4_flags };
+
+cfg_type_t cfg_type_netaddr4wild = { "netaddr4wild", parse_netaddr,
+ cfg_print_sockaddr, cfg_doc_netaddr,
+ &cfg_rep_sockaddr, &netaddr4wild_flags };
+
+cfg_type_t cfg_type_netaddr6 = { "netaddr6", parse_netaddr,
+ cfg_print_sockaddr, cfg_doc_netaddr,
+ &cfg_rep_sockaddr, &netaddr6_flags };
+
+cfg_type_t cfg_type_netaddr6wild = { "netaddr6wild", parse_netaddr,
+ cfg_print_sockaddr, cfg_doc_netaddr,
+ &cfg_rep_sockaddr, &netaddr6wild_flags };
+
+/* netprefix */
+
+isc_result_t
+cfg_parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ cfg_obj_t *obj = NULL;
+ isc_result_t result;
+ isc_netaddr_t netaddr;
+ unsigned int addrlen = 0, prefixlen;
+ bool expectprefix;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(ret != NULL && *ret == NULL);
+
+ UNUSED(type);
+
+ result = cfg_parse_rawaddr(
+ pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK | CFG_ADDR_V6OK,
+ &netaddr);
+ if (result != ISC_R_SUCCESS && result != ISC_R_IPV4PREFIX) {
+ CHECK(result);
+ }
+ switch (netaddr.family) {
+ case AF_INET:
+ addrlen = 32;
+ break;
+ case AF_INET6:
+ addrlen = 128;
+ break;
+ default:
+ UNREACHABLE();
+ }
+ expectprefix = (result == ISC_R_IPV4PREFIX);
+ CHECK(cfg_peektoken(pctx, 0));
+ if (pctx->token.type == isc_tokentype_special &&
+ pctx->token.value.as_char == '/')
+ {
+ CHECK(cfg_gettoken(pctx, 0)); /* read "/" */
+ CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
+ if (pctx->token.type != isc_tokentype_number) {
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "expected prefix length");
+ return (ISC_R_UNEXPECTEDTOKEN);
+ }
+ prefixlen = pctx->token.value.as_ulong;
+ if (prefixlen > addrlen) {
+ cfg_parser_error(pctx, CFG_LOG_NOPREP,
+ "invalid prefix length");
+ return (ISC_R_RANGE);
+ }
+ result = isc_netaddr_prefixok(&netaddr, prefixlen);
+ if (result != ISC_R_SUCCESS) {
+ char buf[ISC_NETADDR_FORMATSIZE + 1];
+ isc_netaddr_format(&netaddr, buf, sizeof(buf));
+ cfg_parser_error(pctx, CFG_LOG_NOPREP,
+ "'%s/%u': address/prefix length "
+ "mismatch",
+ buf, prefixlen);
+ return (ISC_R_FAILURE);
+ }
+ } else {
+ if (expectprefix) {
+ cfg_parser_error(pctx, CFG_LOG_NEAR,
+ "incomplete IPv4 address or prefix");
+ return (ISC_R_FAILURE);
+ }
+ prefixlen = addrlen;
+ }
+ CHECK(cfg_create_obj(pctx, &cfg_type_netprefix, &obj));
+ obj->value.netprefix.address = netaddr;
+ obj->value.netprefix.prefixlen = prefixlen;
+ *ret = obj;
+ return (ISC_R_SUCCESS);
+cleanup:
+ cfg_parser_error(pctx, CFG_LOG_NEAR, "expected network prefix");
+ return (result);
+}
+
+static void
+print_netprefix(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ const cfg_netprefix_t *p = &obj->value.netprefix;
+
+ cfg_print_rawaddr(pctx, &p->address);
+ cfg_print_cstr(pctx, "/");
+ cfg_print_rawuint(pctx, p->prefixlen);
+}
+
+bool
+cfg_obj_isnetprefix(const cfg_obj_t *obj) {
+ REQUIRE(obj != NULL);
+ return (obj->type->rep == &cfg_rep_netprefix);
+}
+
+void
+cfg_obj_asnetprefix(const cfg_obj_t *obj, isc_netaddr_t *netaddr,
+ unsigned int *prefixlen) {
+ REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_netprefix);
+ REQUIRE(netaddr != NULL);
+ REQUIRE(prefixlen != NULL);
+
+ *netaddr = obj->value.netprefix.address;
+ *prefixlen = obj->value.netprefix.prefixlen;
+}
+
+cfg_type_t cfg_type_netprefix = { "netprefix", cfg_parse_netprefix,
+ print_netprefix, cfg_doc_terminal,
+ &cfg_rep_netprefix, NULL };
+
+static isc_result_t
+parse_sockaddrsub(cfg_parser_t *pctx, const cfg_type_t *type, int flags,
+ cfg_obj_t **ret) {
+ isc_result_t result;
+ isc_netaddr_t netaddr;
+ in_port_t port = 0;
+ cfg_obj_t *obj = NULL;
+ int have_port = 0, have_dscp = 0;
+ cfg_obj_t *dscp = NULL;
+
+ CHECK(cfg_create_obj(pctx, type, &obj));
+ CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
+ obj->value.sockaddrdscp.dscp = -1;
+ for (;;) {
+ CHECK(cfg_peektoken(pctx, 0));
+ if (pctx->token.type == isc_tokentype_string) {
+ if (strcasecmp(TOKEN_STRING(pctx), "port") == 0) {
+ if ((pctx->flags & CFG_PCTX_NODEPRECATED) ==
+ 0 &&
+ (flags & CFG_ADDR_PORTOK) == 0)
+ {
+ cfg_parser_warning(
+ pctx, 0,
+ "token 'port' is deprecated");
+ }
+ CHECK(cfg_gettoken(pctx, 0)); /* read "port" */
+ CHECK(cfg_parse_rawport(pctx, flags, &port));
+ ++have_port;
+ } else if ((flags & CFG_ADDR_DSCPOK) != 0 &&
+ strcasecmp(TOKEN_STRING(pctx), "dscp") == 0)
+ {
+ cfg_parser_warning(pctx, 0,
+ "'dscp' is obsolete and "
+ "should be removed");
+ CHECK(cfg_gettoken(pctx, 0)); /* read "dscp" */
+ CHECK(cfg_parse_uint32(pctx, NULL, &dscp));
+ obj->value.sockaddrdscp.dscp =
+ cfg_obj_asuint32(dscp);
+ cfg_obj_destroy(pctx, &dscp);
+ ++have_dscp;
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ if (have_port > 1) {
+ cfg_parser_error(pctx, 0, "expected at most one port");
+ result = ISC_R_UNEXPECTEDTOKEN;
+ goto cleanup;
+ }
+
+ if (have_dscp > 1) {
+ cfg_parser_error(pctx, 0, "expected at most one dscp");
+ result = ISC_R_UNEXPECTEDTOKEN;
+ goto cleanup;
+ }
+ isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
+ *ret = obj;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ CLEANUP_OBJ(obj);
+ return (result);
+}
+
+static unsigned int sockaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK |
+ CFG_ADDR_PORTOK;
+cfg_type_t cfg_type_sockaddr = { "sockaddr", cfg_parse_sockaddr,
+ cfg_print_sockaddr, cfg_doc_sockaddr,
+ &cfg_rep_sockaddr, &sockaddr_flags };
+
+static unsigned int sockaddrdscp_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK |
+ CFG_ADDR_DSCPOK | CFG_ADDR_PORTOK;
+cfg_type_t cfg_type_sockaddrdscp = { "sockaddr", cfg_parse_sockaddr,
+ cfg_print_sockaddr, cfg_doc_sockaddr,
+ &cfg_rep_sockaddr, &sockaddrdscp_flags };
+
+isc_result_t
+cfg_parse_sockaddr(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret) {
+ const unsigned int *flagp;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(type != NULL);
+ REQUIRE(ret != NULL && *ret == NULL);
+
+ flagp = type->of;
+
+ return (parse_sockaddrsub(pctx, &cfg_type_sockaddr, *flagp, ret));
+}
+
+void
+cfg_print_sockaddr(cfg_printer_t *pctx, const cfg_obj_t *obj) {
+ isc_netaddr_t netaddr;
+ in_port_t port;
+ char buf[ISC_NETADDR_FORMATSIZE];
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(obj != NULL);
+
+ isc_netaddr_fromsockaddr(&netaddr, &obj->value.sockaddr);
+ isc_netaddr_format(&netaddr, buf, sizeof(buf));
+ cfg_print_cstr(pctx, buf);
+ port = isc_sockaddr_getport(&obj->value.sockaddr);
+ if (port != 0) {
+ cfg_print_cstr(pctx, " port ");
+ cfg_print_rawuint(pctx, port);
+ }
+ if (obj->value.sockaddrdscp.dscp != -1) {
+ cfg_print_cstr(pctx, " dscp ");
+ cfg_print_rawuint(pctx, obj->value.sockaddrdscp.dscp);
+ }
+}
+
+void
+cfg_doc_sockaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
+ const unsigned int *flagp;
+ int n = 0;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(type != NULL);
+
+ flagp = type->of;
+
+ cfg_print_cstr(pctx, "( ");
+ if ((*flagp & CFG_ADDR_V4OK) != 0) {
+ cfg_print_cstr(pctx, "<ipv4_address>");
+ n++;
+ }
+ if ((*flagp & CFG_ADDR_V6OK) != 0) {
+ if (n != 0) {
+ cfg_print_cstr(pctx, " | ");
+ }
+ cfg_print_cstr(pctx, "<ipv6_address>");
+ n++;
+ }
+ if ((*flagp & CFG_ADDR_WILDOK) != 0) {
+ if (n != 0) {
+ cfg_print_cstr(pctx, " | ");
+ }
+ cfg_print_cstr(pctx, "*");
+ n++;
+ POST(n);
+ }
+ cfg_print_cstr(pctx, " ) ");
+ if ((*flagp & CFG_ADDR_PORTOK) != 0) {
+ if ((*flagp & CFG_ADDR_WILDOK) != 0) {
+ cfg_print_cstr(pctx, "[ port ( <integer> | * ) ]");
+ } else {
+ cfg_print_cstr(pctx, "[ port <integer> ]");
+ }
+ }
+}
+
+bool
+cfg_obj_issockaddr(const cfg_obj_t *obj) {
+ REQUIRE(obj != NULL);
+ return (obj->type->rep == &cfg_rep_sockaddr);
+}
+
+const isc_sockaddr_t *
+cfg_obj_assockaddr(const cfg_obj_t *obj) {
+ REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddr);
+ return (&obj->value.sockaddr);
+}
+
+isc_result_t
+cfg_gettoken(cfg_parser_t *pctx, int options) {
+ isc_result_t result;
+
+ REQUIRE(pctx != NULL);
+
+ if (pctx->seen_eof) {
+ return (ISC_R_SUCCESS);
+ }
+
+ options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE);
+
+redo:
+ pctx->token.type = isc_tokentype_unknown;
+ result = isc_lex_gettoken(pctx->lexer, options, &pctx->token);
+ pctx->ungotten = false;
+ pctx->line = isc_lex_getsourceline(pctx->lexer);
+
+ switch (result) {
+ case ISC_R_SUCCESS:
+ if (pctx->token.type == isc_tokentype_eof) {
+ result = isc_lex_close(pctx->lexer);
+ INSIST(result == ISC_R_NOMORE ||
+ result == ISC_R_SUCCESS);
+
+ if (isc_lex_getsourcename(pctx->lexer) != NULL) {
+ /*
+ * Closed an included file, not the main file.
+ */
+ cfg_listelt_t *elt;
+ elt = ISC_LIST_TAIL(
+ pctx->open_files->value.list);
+ INSIST(elt != NULL);
+ ISC_LIST_UNLINK(pctx->open_files->value.list,
+ elt, link);
+ ISC_LIST_APPEND(pctx->closed_files->value.list,
+ elt, link);
+ goto redo;
+ }
+ pctx->seen_eof = true;
+ }
+ break;
+
+ case ISC_R_NOSPACE:
+ /* More understandable than "ran out of space". */
+ cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big");
+ break;
+
+ case ISC_R_IOERROR:
+ cfg_parser_error(pctx, 0, "%s", isc_result_totext(result));
+ break;
+
+ default:
+ cfg_parser_error(pctx, CFG_LOG_NEAR, "%s",
+ isc_result_totext(result));
+ break;
+ }
+ return (result);
+}
+
+void
+cfg_ungettoken(cfg_parser_t *pctx) {
+ REQUIRE(pctx != NULL);
+
+ if (pctx->seen_eof) {
+ return;
+ }
+ isc_lex_ungettoken(pctx->lexer, &pctx->token);
+ pctx->ungotten = true;
+}
+
+isc_result_t
+cfg_peektoken(cfg_parser_t *pctx, int options) {
+ isc_result_t result;
+
+ REQUIRE(pctx != NULL);
+
+ CHECK(cfg_gettoken(pctx, options));
+ cfg_ungettoken(pctx);
+cleanup:
+ return (result);
+}
+
+/*
+ * Get a string token, accepting both the quoted and the unquoted form.
+ * Log an error if the next token is not a string.
+ */
+static isc_result_t
+cfg_getstringtoken(cfg_parser_t *pctx) {
+ isc_result_t result;
+
+ result = cfg_gettoken(pctx, CFG_LEXOPT_QSTRING);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ if (pctx->token.type != isc_tokentype_string &&
+ pctx->token.type != isc_tokentype_qstring)
+ {
+ cfg_parser_error(pctx, CFG_LOG_NEAR, "expected string");
+ return (ISC_R_UNEXPECTEDTOKEN);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+void
+cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
+ va_list args;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(fmt != NULL);
+
+ va_start(args, fmt);
+ parser_complain(pctx, false, flags, fmt, args);
+ va_end(args);
+ pctx->errors++;
+}
+
+void
+cfg_parser_warning(cfg_parser_t *pctx, unsigned int flags, const char *fmt,
+ ...) {
+ va_list args;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(fmt != NULL);
+
+ va_start(args, fmt);
+ parser_complain(pctx, true, flags, fmt, args);
+ va_end(args);
+ pctx->warnings++;
+}
+
+#define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */
+
+static bool
+have_current_file(cfg_parser_t *pctx) {
+ cfg_listelt_t *elt;
+ if (pctx->open_files == NULL) {
+ return (false);
+ }
+
+ elt = ISC_LIST_TAIL(pctx->open_files->value.list);
+ if (elt == NULL) {
+ return (false);
+ }
+
+ return (true);
+}
+
+static char *
+current_file(cfg_parser_t *pctx) {
+ static char none[] = "none";
+ cfg_listelt_t *elt;
+ cfg_obj_t *fileobj;
+
+ if (!have_current_file(pctx)) {
+ return (none);
+ }
+
+ elt = ISC_LIST_TAIL(pctx->open_files->value.list);
+ if (elt == NULL) { /* shouldn't be possible, but... */
+ return (none);
+ }
+
+ fileobj = elt->obj;
+ INSIST(fileobj->type == &cfg_type_qstring);
+ return (fileobj->value.string.base);
+}
+
+static void
+parser_complain(cfg_parser_t *pctx, bool is_warning, unsigned int flags,
+ const char *format, va_list args) {
+ char tokenbuf[MAX_LOG_TOKEN + 10];
+ static char where[PATH_MAX + 100];
+ static char message[2048];
+ int level = ISC_LOG_ERROR;
+ const char *prep = "";
+ size_t len;
+
+ if (is_warning) {
+ level = ISC_LOG_WARNING;
+ }
+
+ where[0] = '\0';
+ if (have_current_file(pctx)) {
+ snprintf(where, sizeof(where), "%s:%u: ", current_file(pctx),
+ pctx->line);
+ } else if (pctx->buf_name != NULL) {
+ snprintf(where, sizeof(where), "%s: ", pctx->buf_name);
+ }
+
+ len = vsnprintf(message, sizeof(message), format, args);
+#define ELLIPSIS " ... "
+ if (len >= sizeof(message)) {
+ message[sizeof(message) - sizeof(ELLIPSIS)] = 0;
+ strlcat(message, ELLIPSIS, sizeof(message));
+ }
+
+ if ((flags & (CFG_LOG_NEAR | CFG_LOG_BEFORE | CFG_LOG_NOPREP)) != 0) {
+ isc_region_t r;
+
+ if (pctx->ungotten) {
+ (void)cfg_gettoken(pctx, 0);
+ }
+
+ if (pctx->token.type == isc_tokentype_eof) {
+ snprintf(tokenbuf, sizeof(tokenbuf), "end of file");
+ } else if (pctx->token.type == isc_tokentype_unknown) {
+ flags = 0;
+ tokenbuf[0] = '\0';
+ } else {
+ isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r);
+ if (r.length > MAX_LOG_TOKEN) {
+ snprintf(tokenbuf, sizeof(tokenbuf),
+ "'%.*s...'", MAX_LOG_TOKEN, r.base);
+ } else {
+ snprintf(tokenbuf, sizeof(tokenbuf), "'%.*s'",
+ (int)r.length, r.base);
+ }
+ }
+
+ /* Choose a preposition. */
+ if ((flags & CFG_LOG_NEAR) != 0) {
+ prep = " near ";
+ } else if ((flags & CFG_LOG_BEFORE) != 0) {
+ prep = " before ";
+ } else {
+ prep = " ";
+ }
+ } else {
+ tokenbuf[0] = '\0';
+ }
+ isc_log_write(pctx->lctx, CAT, MOD, level, "%s%s%s%s", where, message,
+ prep, tokenbuf);
+}
+
+void
+cfg_obj_log(const cfg_obj_t *obj, isc_log_t *lctx, int level, const char *fmt,
+ ...) {
+ va_list ap;
+ char msgbuf[2048];
+
+ REQUIRE(obj != NULL);
+ REQUIRE(fmt != NULL);
+
+ if (!isc_log_wouldlog(lctx, level)) {
+ return;
+ }
+
+ va_start(ap, fmt);
+ vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
+ va_end(ap);
+
+ if (obj->file != NULL) {
+ isc_log_write(lctx, CAT, MOD, level, "%s:%u: %s", obj->file,
+ obj->line, msgbuf);
+ } else {
+ isc_log_write(lctx, CAT, MOD, level, "%s", msgbuf);
+ }
+}
+
+const char *
+cfg_obj_file(const cfg_obj_t *obj) {
+ REQUIRE(obj != NULL);
+
+ return (obj->file);
+}
+
+unsigned int
+cfg_obj_line(const cfg_obj_t *obj) {
+ REQUIRE(obj != NULL);
+
+ return (obj->line);
+}
+
+isc_result_t
+cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ cfg_obj_t *obj;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(type != NULL);
+ REQUIRE(ret != NULL && *ret == NULL);
+
+ obj = isc_mem_get(pctx->mctx, sizeof(cfg_obj_t));
+
+ obj->type = type;
+ obj->file = current_file(pctx);
+ obj->line = pctx->line;
+ obj->pctx = pctx;
+
+ isc_refcount_init(&obj->references, 1);
+
+ *ret = obj;
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+map_symtabitem_destroy(char *key, unsigned int type, isc_symvalue_t symval,
+ void *userarg) {
+ cfg_obj_t *obj = symval.as_pointer;
+ cfg_parser_t *pctx = (cfg_parser_t *)userarg;
+
+ UNUSED(key);
+ UNUSED(type);
+
+ cfg_obj_destroy(pctx, &obj);
+}
+
+static isc_result_t
+create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+ isc_result_t result;
+ isc_symtab_t *symtab = NULL;
+ cfg_obj_t *obj = NULL;
+
+ CHECK(cfg_create_obj(pctx, type, &obj));
+ CHECK(isc_symtab_create(pctx->mctx, 5, /* XXX */
+ map_symtabitem_destroy, pctx, false, &symtab));
+ obj->value.map.symtab = symtab;
+ obj->value.map.id = NULL;
+
+ *ret = obj;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ if (obj != NULL) {
+ isc_mem_put(pctx->mctx, obj, sizeof(*obj));
+ }
+ return (result);
+}
+
+static void
+free_map(cfg_parser_t *pctx, cfg_obj_t *obj) {
+ CLEANUP_OBJ(obj->value.map.id);
+ isc_symtab_destroy(&obj->value.map.symtab);
+}
+
+bool
+cfg_obj_istype(const cfg_obj_t *obj, const cfg_type_t *type) {
+ REQUIRE(obj != NULL);
+ REQUIRE(type != NULL);
+
+ return (obj->type == type);
+}
+
+/*
+ * Destroy 'obj', a configuration object created in 'pctx'.
+ */
+void
+cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) {
+ REQUIRE(objp != NULL && *objp != NULL);
+ REQUIRE(pctx != NULL);
+
+ cfg_obj_t *obj = *objp;
+ *objp = NULL;
+
+ if (isc_refcount_decrement(&obj->references) == 1) {
+ obj->type->rep->free(pctx, obj);
+ isc_refcount_destroy(&obj->references);
+ isc_mem_put(pctx->mctx, obj, sizeof(cfg_obj_t));
+ }
+}
+
+void
+cfg_obj_attach(cfg_obj_t *src, cfg_obj_t **dest) {
+ REQUIRE(src != NULL);
+ REQUIRE(dest != NULL && *dest == NULL);
+
+ isc_refcount_increment(&src->references);
+ *dest = src;
+}
+
+static void
+free_noop(cfg_parser_t *pctx, cfg_obj_t *obj) {
+ UNUSED(pctx);
+ UNUSED(obj);
+}
+
+void
+cfg_doc_obj(cfg_printer_t *pctx, const cfg_type_t *type) {
+ REQUIRE(pctx != NULL);
+ REQUIRE(type != NULL);
+
+ type->doc(pctx, type);
+}
+
+void
+cfg_doc_terminal(cfg_printer_t *pctx, const cfg_type_t *type) {
+ REQUIRE(pctx != NULL);
+ REQUIRE(type != NULL);
+
+ cfg_print_cstr(pctx, "<");
+ cfg_print_cstr(pctx, type->name);
+ cfg_print_cstr(pctx, ">");
+}
+
+void
+cfg_print_grammar(const cfg_type_t *type, unsigned int flags,
+ void (*f)(void *closure, const char *text, int textlen),
+ void *closure) {
+ cfg_printer_t pctx;
+
+ pctx.f = f;
+ pctx.closure = closure;
+ pctx.indent = 0;
+ pctx.flags = flags;
+ cfg_doc_obj(&pctx, type);
+}
+
+isc_result_t
+cfg_parser_mapadd(cfg_parser_t *pctx, cfg_obj_t *mapobj, cfg_obj_t *obj,
+ const char *clausename) {
+ isc_result_t result = ISC_R_SUCCESS;
+ const cfg_map_t *map;
+ isc_symvalue_t symval;
+ cfg_obj_t *destobj = NULL;
+ cfg_listelt_t *elt = NULL;
+ const cfg_clausedef_t *const *clauseset;
+ const cfg_clausedef_t *clause;
+
+ REQUIRE(pctx != NULL);
+ REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
+ REQUIRE(obj != NULL);
+ REQUIRE(clausename != NULL);
+
+ map = &mapobj->value.map;
+
+ clause = NULL;
+ for (clauseset = map->clausesets; *clauseset != NULL; clauseset++) {
+ for (clause = *clauseset; clause->name != NULL; clause++) {
+ if (strcasecmp(clause->name, clausename) == 0) {
+ goto breakout;
+ }
+ }
+ }
+
+breakout:
+ if (clause == NULL || clause->name == NULL) {
+ return (ISC_R_FAILURE);
+ }
+
+ result = isc_symtab_lookup(map->symtab, clausename, 0, &symval);
+ if (result == ISC_R_NOTFOUND) {
+ if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) {
+ CHECK(cfg_create_list(pctx, &cfg_type_implicitlist,
+ &destobj));
+ CHECK(create_listelt(pctx, &elt));
+ cfg_obj_attach(obj, &elt->obj);
+ ISC_LIST_APPEND(destobj->value.list, elt, link);
+ symval.as_pointer = destobj;
+ } else {
+ symval.as_pointer = obj;
+ }
+
+ CHECK(isc_symtab_define(map->symtab, clausename, 1, symval,
+ isc_symexists_reject));
+ } else {
+ cfg_obj_t *destobj2 = symval.as_pointer;
+
+ INSIST(result == ISC_R_SUCCESS);
+
+ if (destobj2->type == &cfg_type_implicitlist) {
+ CHECK(create_listelt(pctx, &elt));
+ cfg_obj_attach(obj, &elt->obj);
+ ISC_LIST_APPEND(destobj2->value.list, elt, link);
+ } else {
+ result = ISC_R_EXISTS;
+ }
+ }
+
+ destobj = NULL;
+ elt = NULL;
+
+cleanup:
+ if (elt != NULL) {
+ free_listelt(pctx, elt);
+ }
+ CLEANUP_OBJ(destobj);
+
+ return (result);
+}
+
+isc_result_t
+cfg_pluginlist_foreach(const cfg_obj_t *config, const cfg_obj_t *list,
+ isc_log_t *lctx, pluginlist_cb_t *callback,
+ void *callback_data) {
+ isc_result_t result = ISC_R_SUCCESS;
+ const cfg_listelt_t *element;
+
+ REQUIRE(config != NULL);
+ REQUIRE(callback != NULL);
+
+ for (element = cfg_list_first(list); element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *plugin = cfg_listelt_value(element);
+ const cfg_obj_t *obj;
+ const char *type, *library;
+ const char *parameters = NULL;
+
+ /* Get the path to the plugin module. */
+ obj = cfg_tuple_get(plugin, "type");
+ type = cfg_obj_asstring(obj);
+
+ /* Only query plugins are supported currently. */
+ if (strcasecmp(type, "query") != 0) {
+ cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
+ "unsupported plugin type");
+ return (ISC_R_FAILURE);
+ }
+
+ library = cfg_obj_asstring(cfg_tuple_get(plugin, "library"));
+
+ obj = cfg_tuple_get(plugin, "parameters");
+ if (obj != NULL && cfg_obj_isstring(obj)) {
+ parameters = cfg_obj_asstring(obj);
+ }
+
+ result = callback(config, obj, library, parameters,
+ callback_data);
+ if (result != ISC_R_SUCCESS) {
+ break;
+ }
+ }
+
+ return (result);
+}