summaryrefslogtreecommitdiffstats
path: root/lib/dns
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dns')
-rw-r--r--lib/dns/Makefile.in179
-rw-r--r--lib/dns/acache.c1782
-rw-r--r--lib/dns/acl.c623
-rw-r--r--lib/dns/adb.c3621
-rw-r--r--lib/dns/api3
-rw-r--r--lib/dns/byaddr.c316
-rw-r--r--lib/dns/cache.c1089
-rw-r--r--lib/dns/callbacks.c113
-rw-r--r--lib/dns/compress.c341
-rw-r--r--lib/dns/db.c926
-rw-r--r--lib/dns/dbiterator.c143
-rw-r--r--lib/dns/dbtable.c291
-rw-r--r--lib/dns/diff.c638
-rw-r--r--lib/dns/dispatch.c3445
-rw-r--r--lib/dns/dlz.c510
-rw-r--r--lib/dns/dnssec.c892
-rw-r--r--lib/dns/ds.c104
-rw-r--r--lib/dns/dst_api.c1310
-rw-r--r--lib/dns/dst_internal.h217
-rw-r--r--lib/dns/dst_lib.c67
-rw-r--r--lib/dns/dst_openssl.h40
-rw-r--r--lib/dns/dst_parse.c532
-rw-r--r--lib/dns/dst_parse.h134
-rw-r--r--lib/dns/dst_result.c89
-rw-r--r--lib/dns/forward.c199
-rw-r--r--lib/dns/gen-unix.h97
-rw-r--r--lib/dns/gen-win32.h290
-rw-r--r--lib/dns/gen.c888
-rw-r--r--lib/dns/gssapi_link.c310
-rw-r--r--lib/dns/gssapictx.c738
-rw-r--r--lib/dns/hmac_link.c1687
-rw-r--r--lib/dns/include/Makefile.in25
-rw-r--r--lib/dns/include/dns/Makefile.in54
-rw-r--r--lib/dns/include/dns/acache.h445
-rw-r--r--lib/dns/include/dns/acl.h222
-rw-r--r--lib/dns/include/dns/adb.h634
-rw-r--r--lib/dns/include/dns/bit.h39
-rw-r--r--lib/dns/include/dns/byaddr.h171
-rw-r--r--lib/dns/include/dns/cache.h253
-rw-r--r--lib/dns/include/dns/callbacks.h87
-rw-r--r--lib/dns/include/dns/cert.h69
-rw-r--r--lib/dns/include/dns/compress.h269
-rw-r--r--lib/dns/include/dns/db.h1477
-rw-r--r--lib/dns/include/dns/dbiterator.h297
-rw-r--r--lib/dns/include/dns/dbtable.h165
-rw-r--r--lib/dns/include/dns/diff.h291
-rw-r--r--lib/dns/include/dns/dispatch.h501
-rw-r--r--lib/dns/include/dns/dlz.h290
-rw-r--r--lib/dns/include/dns/dnssec.h183
-rw-r--r--lib/dns/include/dns/ds.h63
-rw-r--r--lib/dns/include/dns/events.h75
-rw-r--r--lib/dns/include/dns/fixedname.h86
-rw-r--r--lib/dns/include/dns/forward.h118
-rw-r--r--lib/dns/include/dns/iptable.h70
-rw-r--r--lib/dns/include/dns/journal.h282
-rw-r--r--lib/dns/include/dns/keyflags.h54
-rw-r--r--lib/dns/include/dns/keytable.h255
-rw-r--r--lib/dns/include/dns/keyvalues.h100
-rw-r--r--lib/dns/include/dns/lib.h45
-rw-r--r--lib/dns/include/dns/log.h107
-rw-r--r--lib/dns/include/dns/lookup.h137
-rw-r--r--lib/dns/include/dns/master.h306
-rw-r--r--lib/dns/include/dns/masterdump.h340
-rw-r--r--lib/dns/include/dns/message.h1343
-rw-r--r--lib/dns/include/dns/name.h1297
-rw-r--r--lib/dns/include/dns/ncache.h179
-rw-r--r--lib/dns/include/dns/nsec.h80
-rw-r--r--lib/dns/include/dns/nsec3.h194
-rw-r--r--lib/dns/include/dns/opcode.h51
-rw-r--r--lib/dns/include/dns/order.h99
-rw-r--r--lib/dns/include/dns/peer.h219
-rw-r--r--lib/dns/include/dns/portlist.h101
-rw-r--r--lib/dns/include/dns/rbt.h939
-rw-r--r--lib/dns/include/dns/rcode.h113
-rw-r--r--lib/dns/include/dns/rdata.h702
-rw-r--r--lib/dns/include/dns/rdataclass.h81
-rw-r--r--lib/dns/include/dns/rdatalist.h124
-rw-r--r--lib/dns/include/dns/rdataset.h639
-rw-r--r--lib/dns/include/dns/rdatasetiter.h170
-rw-r--r--lib/dns/include/dns/rdataslab.h170
-rw-r--r--lib/dns/include/dns/rdatatype.h84
-rw-r--r--lib/dns/include/dns/request.h374
-rw-r--r--lib/dns/include/dns/resolver.h481
-rw-r--r--lib/dns/include/dns/result.h191
-rw-r--r--lib/dns/include/dns/rootns.h45
-rw-r--r--lib/dns/include/dns/sdb.h209
-rw-r--r--lib/dns/include/dns/sdlz.h266
-rw-r--r--lib/dns/include/dns/secalg.h71
-rw-r--r--lib/dns/include/dns/secproto.h71
-rw-r--r--lib/dns/include/dns/soa.h81
-rw-r--r--lib/dns/include/dns/ssu.h189
-rw-r--r--lib/dns/include/dns/stats.h345
-rw-r--r--lib/dns/include/dns/tcpmsg.h147
-rw-r--r--lib/dns/include/dns/time.h72
-rw-r--r--lib/dns/include/dns/timer.h52
-rw-r--r--lib/dns/include/dns/tkey.h250
-rw-r--r--lib/dns/include/dns/tsig.h257
-rw-r--r--lib/dns/include/dns/ttl.h78
-rw-r--r--lib/dns/include/dns/types.h346
-rw-r--r--lib/dns/include/dns/validator.h255
-rw-r--r--lib/dns/include/dns/version.h28
-rw-r--r--lib/dns/include/dns/view.h879
-rw-r--r--lib/dns/include/dns/xfrin.h111
-rw-r--r--lib/dns/include/dns/zone.h1736
-rw-r--r--lib/dns/include/dns/zonekey.h42
-rw-r--r--lib/dns/include/dns/zt.h183
-rw-r--r--lib/dns/include/dst/Makefile.in37
-rw-r--r--lib/dns/include/dst/dst.h642
-rw-r--r--lib/dns/include/dst/gssapi.h207
-rw-r--r--lib/dns/include/dst/lib.h41
-rw-r--r--lib/dns/include/dst/result.h71
-rw-r--r--lib/dns/iptable.c185
-rw-r--r--lib/dns/journal.c2217
-rw-r--r--lib/dns/key.c147
-rw-r--r--lib/dns/keytable.c395
-rw-r--r--lib/dns/lib.c65
-rw-r--r--lib/dns/log.c98
-rw-r--r--lib/dns/lookup.c497
-rw-r--r--lib/dns/master.c2892
-rw-r--r--lib/dns/masterdump.c1754
-rw-r--r--lib/dns/message.c3378
-rw-r--r--lib/dns/name.c2408
-rw-r--r--lib/dns/ncache.c658
-rw-r--r--lib/dns/nsec.c277
-rw-r--r--lib/dns/nsec3.c1377
-rw-r--r--lib/dns/openssl_link.c447
-rw-r--r--lib/dns/openssldh_link.c638
-rw-r--r--lib/dns/openssldsa_link.c607
-rw-r--r--lib/dns/opensslrsa_link.c986
-rw-r--r--lib/dns/order.c167
-rw-r--r--lib/dns/peer.c712
-rw-r--r--lib/dns/portlist.c266
-rw-r--r--lib/dns/rbt.c2657
-rw-r--r--lib/dns/rbtdb.c8435
-rw-r--r--lib/dns/rbtdb.h44
-rw-r--r--lib/dns/rbtdb64.c23
-rw-r--r--lib/dns/rbtdb64.h45
-rw-r--r--lib/dns/rcode.c489
-rw-r--r--lib/dns/rdata.c1769
-rw-r--r--lib/dns/rdata/any_255/tsig_250.c597
-rw-r--r--lib/dns/rdata/any_255/tsig_250.h38
-rw-r--r--lib/dns/rdata/ch_3/a_1.c316
-rw-r--r--lib/dns/rdata/ch_3/a_1.h34
-rw-r--r--lib/dns/rdata/generic/afsdb_18.c309
-rw-r--r--lib/dns/rdata/generic/afsdb_18.h34
-rw-r--r--lib/dns/rdata/generic/cert_37.c280
-rw-r--r--lib/dns/rdata/generic/cert_37.h34
-rw-r--r--lib/dns/rdata/generic/cname_5.c232
-rw-r--r--lib/dns/rdata/generic/cname_5.h29
-rw-r--r--lib/dns/rdata/generic/dlv_32769.c321
-rw-r--r--lib/dns/rdata/generic/dlv_32769.h33
-rw-r--r--lib/dns/rdata/generic/dname_39.c233
-rw-r--r--lib/dns/rdata/generic/dname_39.h32
-rw-r--r--lib/dns/rdata/generic/dnskey_48.c312
-rw-r--r--lib/dns/rdata/generic/dnskey_48.h37
-rw-r--r--lib/dns/rdata/generic/ds_43.c321
-rw-r--r--lib/dns/rdata/generic/ds_43.h35
-rw-r--r--lib/dns/rdata/generic/gpos_27.c252
-rw-r--r--lib/dns/rdata/generic/gpos_27.h37
-rw-r--r--lib/dns/rdata/generic/hinfo_13.c224
-rw-r--r--lib/dns/rdata/generic/hinfo_13.h32
-rw-r--r--lib/dns/rdata/generic/ipseckey_45.c462
-rw-r--r--lib/dns/rdata/generic/ipseckey_45.h35
-rw-r--r--lib/dns/rdata/generic/isdn_20.c234
-rw-r--r--lib/dns/rdata/generic/isdn_20.h35
-rw-r--r--lib/dns/rdata/generic/key_25.c312
-rw-r--r--lib/dns/rdata/generic/key_25.h37
-rw-r--r--lib/dns/rdata/generic/loc_29.c794
-rw-r--r--lib/dns/rdata/generic/loc_29.h43
-rw-r--r--lib/dns/rdata/generic/mb_7.c234
-rw-r--r--lib/dns/rdata/generic/mb_7.h30
-rw-r--r--lib/dns/rdata/generic/md_3.c236
-rw-r--r--lib/dns/rdata/generic/md_3.h31
-rw-r--r--lib/dns/rdata/generic/mf_4.c235
-rw-r--r--lib/dns/rdata/generic/mf_4.h30
-rw-r--r--lib/dns/rdata/generic/mg_8.c230
-rw-r--r--lib/dns/rdata/generic/mg_8.h30
-rw-r--r--lib/dns/rdata/generic/minfo_14.c324
-rw-r--r--lib/dns/rdata/generic/minfo_14.h31
-rw-r--r--lib/dns/rdata/generic/mr_9.c231
-rw-r--r--lib/dns/rdata/generic/mr_9.h30
-rw-r--r--lib/dns/rdata/generic/mx_15.c319
-rw-r--r--lib/dns/rdata/generic/mx_15.h31
-rw-r--r--lib/dns/rdata/generic/ns_2.c251
-rw-r--r--lib/dns/rdata/generic/ns_2.h31
-rw-r--r--lib/dns/rdata/generic/nsec3_50.c481
-rw-r--r--lib/dns/rdata/generic/nsec3_50.h93
-rw-r--r--lib/dns/rdata/generic/nsec3param_51.c314
-rw-r--r--lib/dns/rdata/generic/nsec3param_51.h38
-rw-r--r--lib/dns/rdata/generic/nsec_47.c366
-rw-r--r--lib/dns/rdata/generic/nsec_47.h34
-rw-r--r--lib/dns/rdata/generic/null_10.c192
-rw-r--r--lib/dns/rdata/generic/null_10.h32
-rw-r--r--lib/dns/rdata/generic/nxt_30.c329
-rw-r--r--lib/dns/rdata/generic/nxt_30.h34
-rw-r--r--lib/dns/rdata/generic/opt_41.c280
-rw-r--r--lib/dns/rdata/generic/opt_41.h55
-rw-r--r--lib/dns/rdata/generic/proforma.c173
-rw-r--r--lib/dns/rdata/generic/proforma.h30
-rw-r--r--lib/dns/rdata/generic/ptr_12.c291
-rw-r--r--lib/dns/rdata/generic/ptr_12.h30
-rw-r--r--lib/dns/rdata/generic/rp_17.c314
-rw-r--r--lib/dns/rdata/generic/rp_17.h34
-rw-r--r--lib/dns/rdata/generic/rrsig_46.c551
-rw-r--r--lib/dns/rdata/generic/rrsig_46.h41
-rw-r--r--lib/dns/rdata/generic/rt_21.c311
-rw-r--r--lib/dns/rdata/generic/rt_21.h33
-rw-r--r--lib/dns/rdata/generic/sig_24.c578
-rw-r--r--lib/dns/rdata/generic/sig_24.h42
-rw-r--r--lib/dns/rdata/generic/soa_6.c443
-rw-r--r--lib/dns/rdata/generic/soa_6.h37
-rw-r--r--lib/dns/rdata/generic/spf_99.c238
-rw-r--r--lib/dns/rdata/generic/spf_99.h51
-rw-r--r--lib/dns/rdata/generic/sshfp_44.c262
-rw-r--r--lib/dns/rdata/generic/sshfp_44.h35
-rw-r--r--lib/dns/rdata/generic/tkey_249.c555
-rw-r--r--lib/dns/rdata/generic/tkey_249.h41
-rw-r--r--lib/dns/rdata/generic/txt_16.c238
-rw-r--r--lib/dns/rdata/generic/txt_16.h52
-rw-r--r--lib/dns/rdata/generic/unspec_103.c189
-rw-r--r--lib/dns/rdata/generic/unspec_103.h31
-rw-r--r--lib/dns/rdata/generic/x25_19.c219
-rw-r--r--lib/dns/rdata/generic/x25_19.h33
-rw-r--r--lib/dns/rdata/hs_4/a_1.c232
-rw-r--r--lib/dns/rdata/hs_4/a_1.h29
-rw-r--r--lib/dns/rdata/in_1/a6_38.c461
-rw-r--r--lib/dns/rdata/in_1/a6_38.h34
-rw-r--r--lib/dns/rdata/in_1/a_1.c236
-rw-r--r--lib/dns/rdata/in_1/a_1.h29
-rw-r--r--lib/dns/rdata/in_1/aaaa_28.c233
-rw-r--r--lib/dns/rdata/in_1/aaaa_28.h31
-rw-r--r--lib/dns/rdata/in_1/apl_42.c453
-rw-r--r--lib/dns/rdata/in_1/apl_42.h56
-rw-r--r--lib/dns/rdata/in_1/dhcid_49.c229
-rw-r--r--lib/dns/rdata/in_1/dhcid_49.h30
-rw-r--r--lib/dns/rdata/in_1/kx_36.c288
-rw-r--r--lib/dns/rdata/in_1/kx_36.h33
-rw-r--r--lib/dns/rdata/in_1/naptr_35.c578
-rw-r--r--lib/dns/rdata/in_1/naptr_35.h40
-rw-r--r--lib/dns/rdata/in_1/nsap-ptr_23.c245
-rw-r--r--lib/dns/rdata/in_1/nsap-ptr_23.h32
-rw-r--r--lib/dns/rdata/in_1/nsap_22.c255
-rw-r--r--lib/dns/rdata/in_1/nsap_22.h33
-rw-r--r--lib/dns/rdata/in_1/px_26.c374
-rw-r--r--lib/dns/rdata/in_1/px_26.h34
-rw-r--r--lib/dns/rdata/in_1/srv_33.c373
-rw-r--r--lib/dns/rdata/in_1/srv_33.h37
-rw-r--r--lib/dns/rdata/in_1/wks_11.c349
-rw-r--r--lib/dns/rdata/in_1/wks_11.h32
-rw-r--r--lib/dns/rdata/rdatastructpre.h42
-rw-r--r--lib/dns/rdata/rdatastructsuf.h22
-rw-r--r--lib/dns/rdatalist.c347
-rw-r--r--lib/dns/rdatalist_p.h64
-rw-r--r--lib/dns/rdataset.c734
-rw-r--r--lib/dns/rdatasetiter.c80
-rw-r--r--lib/dns/rdataslab.c1092
-rw-r--r--lib/dns/request.c1497
-rw-r--r--lib/dns/resolver.c7602
-rw-r--r--lib/dns/result.c277
-rw-r--r--lib/dns/rootns.c525
-rw-r--r--lib/dns/sdb.c1551
-rw-r--r--lib/dns/sdlz.c1799
-rw-r--r--lib/dns/soa.c111
-rw-r--r--lib/dns/spnego.asn152
-rw-r--r--lib/dns/spnego.c1792
-rw-r--r--lib/dns/spnego.h71
-rw-r--r--lib/dns/spnego_asn1.c885
-rw-r--r--lib/dns/spnego_asn1.pl200
-rw-r--r--lib/dns/ssu.c552
-rw-r--r--lib/dns/stats.c564
-rw-r--r--lib/dns/tcpmsg.c243
-rw-r--r--lib/dns/time.c174
-rw-r--r--lib/dns/timer.c60
-rw-r--r--lib/dns/tkey.c1419
-rw-r--r--lib/dns/tsig.c1549
-rw-r--r--lib/dns/ttl.c216
-rw-r--r--lib/dns/validator.c3776
-rw-r--r--lib/dns/version.c28
-rw-r--r--lib/dns/view.c1467
-rw-r--r--lib/dns/win32/DLLMain.c57
-rw-r--r--lib/dns/win32/gen.dsp107
-rw-r--r--lib/dns/win32/gen.dsw29
-rw-r--r--lib/dns/win32/gen.mak267
-rw-r--r--lib/dns/win32/libdns.def861
-rw-r--r--lib/dns/win32/libdns.dsp737
-rw-r--r--lib/dns/win32/libdns.dsw29
-rw-r--r--lib/dns/win32/libdns.mak2188
-rw-r--r--lib/dns/win32/version.c28
-rw-r--r--lib/dns/xfrin.c1532
-rw-r--r--lib/dns/zone.c11502
-rw-r--r--lib/dns/zonekey.c55
-rw-r--r--lib/dns/zt.c411
292 files changed, 143577 insertions, 0 deletions
diff --git a/lib/dns/Makefile.in b/lib/dns/Makefile.in
new file mode 100644
index 0000000..ef5c12a
--- /dev/null
+++ b/lib/dns/Makefile.in
@@ -0,0 +1,179 @@
+# Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 1998-2003 Internet Software Consortium.
+#
+# 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 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.
+
+# $Id: Makefile.in,v 1.163 2008/09/24 02:46:22 marka Exp $
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+top_srcdir = @top_srcdir@
+
+# Attempt to disable parallel processing.
+.NOTPARALLEL:
+.NO_PARALLEL:
+
+@BIND9_VERSION@
+
+@LIBDNS_API@
+
+@BIND9_MAKE_INCLUDES@
+
+USE_ISC_SPNEGO = @USE_ISC_SPNEGO@
+
+CINCLUDES = -I. -Iinclude ${DNS_INCLUDES} \
+ ${ISC_INCLUDES} @DST_OPENSSL_INC@ @DST_GSSAPI_INC@
+
+CDEFINES = -DUSE_MD5 @USE_OPENSSL@ @USE_PKCS11@ @USE_GSSAPI@ \
+ ${USE_ISC_SPNEGO}
+
+CWARNINGS =
+
+ISCLIBS = ../../lib/isc/libisc.@A@
+
+ISCDEPLIBS = ../../lib/isc/libisc.@A@
+
+LIBS = @LIBS@
+
+# Alphabetically
+
+DSTOBJS = @DST_EXTRA_OBJS@ \
+ dst_api.@O@ dst_lib.@O@ dst_parse.@O@ dst_result.@O@ \
+ gssapi_link.@O@ gssapictx.@O@ hmac_link.@O@ key.@O@ \
+ openssl_link.@O@ openssldh_link.@O@ openssldsa_link.@O@ \
+ opensslrsa_link.@O@
+
+# Alphabetically
+DNSOBJS = acache.@O@ acl.@O@ adb.@O@ byaddr.@O@ \
+ cache.@O@ callbacks.@O@ compress.@O@ \
+ db.@O@ dbiterator.@O@ dbtable.@O@ diff.@O@ dispatch.@O@ \
+ dlz.@O@ dnssec.@O@ ds.@O@ forward.@O@ iptable.@O@ journal.@O@ \
+ keytable.@O@ lib.@O@ log.@O@ lookup.@O@ \
+ master.@O@ masterdump.@O@ message.@O@ \
+ name.@O@ ncache.@O@ nsec.@O@ nsec3.@O@ order.@O@ peer.@O@ portlist.@O@ \
+ rbt.@O@ rbtdb.@O@ rbtdb64.@O@ rcode.@O@ rdata.@O@ \
+ rdatalist.@O@ \
+ rdataset.@O@ rdatasetiter.@O@ rdataslab.@O@ request.@O@ \
+ resolver.@O@ result.@O@ rootns.@O@ sdb.@O@ sdlz.@O@ \
+ soa.@O@ ssu.@O@ \
+ stats.@O@ tcpmsg.@O@ time.@O@ timer.@O@ tkey.@O@ \
+ tsig.@O@ ttl.@O@ validator.@O@ \
+ version.@O@ view.@O@ xfrin.@O@ zone.@O@ zonekey.@O@ zt.@O@
+
+OBJS= ${DNSOBJS} ${OTHEROBJS} ${DSTOBJS}
+
+# Alphabetically
+DSTSRCS = @DST_EXTRA_SRCS@ \
+ dst_api.c dst_lib.c dst_parse.c \
+ dst_result.c gssapi_link.c gssapictx.c \
+ hmac_link.c key.c \
+ openssl_link.c openssldh_link.c \
+ openssldsa_link.c opensslrsa_link.c
+
+DNSSRCS = acache.c acl.c adb.c byaddr.c \
+ cache.c callbacks.c compress.c \
+ db.c dbiterator.c dbtable.c diff.c dispatch.c \
+ dlz.c dnssec.c ds.c forward.c iptable.c journal.c \
+ keytable.c lib.c log.c lookup.c \
+ master.c masterdump.c message.c \
+ name.c ncache.c nsec.c nsec3.c order.c peer.c portlist.c \
+ rbt.c rbtdb.c rbtdb64.c rcode.c rdata.c \
+ rdatalist.c \
+ rdataset.c rdatasetiter.c rdataslab.c request.c \
+ resolver.c result.c rootns.c sdb.c sdlz.c \
+ soa.c ssu.c \
+ stats.c tcpmsg.c time.c timer.c tkey.c \
+ tsig.c ttl.c validator.c \
+ version.c view.c xfrin.c zone.c zonekey.c zt.c ${OTHERSRCS}
+SRCS = ${DSTSRCS} ${DNSSRCS}
+
+SUBDIRS = include
+TARGETS = include/dns/enumtype.h include/dns/enumclass.h \
+ include/dns/rdatastruct.h timestamp
+
+DEPENDEXTRA = ./gen -F include/dns/rdatastruct.h \
+ -s ${srcdir} -d >> Makefile ;
+
+@BIND9_MAKE_RULES@
+
+version.@O@: version.c
+ ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} \
+ -DVERSION=\"${VERSION}\" \
+ -DLIBINTERFACE=${LIBINTERFACE} \
+ -DLIBREVISION=${LIBREVISION} \
+ -DLIBAGE=${LIBAGE} \
+ -c ${srcdir}/version.c
+
+libdns.@SA@: ${OBJS}
+ ${AR} ${ARFLAGS} $@ ${OBJS}
+ ${RANLIB} $@
+
+libdns.la: ${OBJS}
+ ${LIBTOOL_MODE_LINK} \
+ ${CC} ${ALL_CFLAGS} ${LDFLAGS} -o libdns.la -rpath ${libdir} \
+ -version-info ${LIBINTERFACE}:${LIBREVISION}:${LIBAGE} \
+ ${OBJS} ${ISCLIBS} @DNS_CRYPTO_LIBS@ ${LIBS}
+
+timestamp: libdns.@A@
+ touch timestamp
+
+installdirs:
+ $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${libdir}
+
+install:: timestamp installdirs
+ ${LIBTOOL_MODE_INSTALL} ${INSTALL_DATA} libdns.@A@ ${DESTDIR}${libdir}
+
+clean distclean::
+ rm -f libdns.@A@ timestamp
+ rm -f gen code.h include/dns/enumtype.h include/dns/enumclass.h
+ rm -f include/dns/rdatastruct.h
+
+newrr::
+ rm -f code.h include/dns/enumtype.h include/dns/enumclass.h
+ rm -f include/dns/rdatastruct.h
+
+include: include/dns/enumtype.h include/dns/enumclass.h \
+ include/dns/rdatastruct.h
+
+rdata.@O@: code.h
+
+include/dns/enumtype.h: gen
+ ./gen -s ${srcdir} -t > $@
+
+include/dns/enumclass.h: gen
+ ./gen -s ${srcdir} -c > $@
+
+include/dns/rdatastruct.h: gen \
+ ${srcdir}/rdata/rdatastructpre.h \
+ ${srcdir}/rdata/rdatastructsuf.h
+ ./gen -s ${srcdir} -i \
+ -P ${srcdir}/rdata/rdatastructpre.h \
+ -S ${srcdir}/rdata/rdatastructsuf.h > $@
+
+code.h: gen
+ ./gen -s ${srcdir} > code.h
+
+gen: gen.c
+ ${BUILD_CC} ${BUILD_CFLAGS} -I${top_srcdir}/lib/isc/include \
+ ${BUILD_CPPFLAGS} ${BUILD_LDFLAGS} -o $@ ${srcdir}/gen.c ${BUILD_LIBS}
+
+rbtdb64.@O@: rbtdb.c
+
+depend: include/dns/enumtype.h include/dns/enumclass.h \
+ include/dns/rdatastruct.h code.h
+subdirs: include/dns/enumtype.h include/dns/enumclass.h \
+ include/dns/rdatastruct.h code.h
+${OBJS}: include/dns/enumtype.h include/dns/enumclass.h \
+ include/dns/rdatastruct.h
+
+spnego.@O@: spnego_asn1.c spnego.h
diff --git a/lib/dns/acache.c b/lib/dns/acache.c
new file mode 100644
index 0000000..2ad4981
--- /dev/null
+++ b/lib/dns/acache.c
@@ -0,0 +1,1782 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 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.
+ */
+
+/* $Id: acache.c,v 1.22 2008/02/07 23:46:54 tbox Exp $ */
+
+#include <config.h>
+
+#include <isc/atomic.h>
+#include <isc/event.h>
+#include <isc/hash.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/mutex.h>
+#include <isc/random.h>
+#include <isc/refcount.h>
+#include <isc/rwlock.h>
+#include <isc/task.h>
+#include <isc/time.h>
+#include <isc/timer.h>
+
+#include <dns/acache.h>
+#include <dns/db.h>
+#include <dns/events.h>
+#include <dns/log.h>
+#include <dns/message.h>
+#include <dns/name.h>
+#include <dns/rdataset.h>
+#include <dns/result.h>
+#include <dns/zone.h>
+
+#define ACACHE_MAGIC ISC_MAGIC('A', 'C', 'H', 'E')
+#define DNS_ACACHE_VALID(acache) ISC_MAGIC_VALID(acache, ACACHE_MAGIC)
+
+#define ACACHEENTRY_MAGIC ISC_MAGIC('A', 'C', 'E', 'T')
+#define DNS_ACACHEENTRY_VALID(entry) ISC_MAGIC_VALID(entry, ACACHEENTRY_MAGIC)
+
+#define DBBUCKETS 67
+
+#if 0
+#define ATRACE(m) isc_log_write(dns_lctx, \
+ DNS_LOGCATEGORY_DATABASE, \
+ DNS_LOGMODULE_ACACHE, \
+ ISC_LOG_DEBUG(3), \
+ "acache %p: %s", acache, (m))
+#define AATRACE(a,m) isc_log_write(dns_lctx, \
+ DNS_LOGCATEGORY_DATABASE, \
+ DNS_LOGMODULE_ACACHE, \
+ ISC_LOG_DEBUG(3), \
+ "acache %p: %s", (a), (m))
+#else
+#define ATRACE(m)
+#define AATRACE(a, m)
+#endif
+
+/*
+ * The following variables control incremental cleaning.
+ * MINSIZE is how many bytes is the floor for dns_acache_setcachesize().
+ * CLEANERINCREMENT is how many entries are examined in one pass.
+ * (XXX simply derived from definitions in cache.c There may be better
+ * constants here.)
+ */
+#define DNS_ACACHE_MINSIZE 2097152 /* Bytes. 2097152 = 2 MB */
+#define DNS_ACACHE_CLEANERINCREMENT 1000 /* Number of entries. */
+
+#define DEFAULT_ACACHE_ENTRY_LOCK_COUNT 1009 /*%< Should be prime. */
+
+#if defined(ISC_RWLOCK_USEATOMIC) && defined(ISC_PLATFORM_HAVEATOMICSTORE)
+#define ACACHE_USE_RWLOCK 1
+#endif
+
+#ifdef ACACHE_USE_RWLOCK
+#define ACACHE_INITLOCK(l) isc_rwlock_init((l), 0, 0)
+#define ACACHE_DESTROYLOCK(l) isc_rwlock_destroy(l)
+#define ACACHE_LOCK(l, t) RWLOCK((l), (t))
+#define ACACHE_UNLOCK(l, t) RWUNLOCK((l), (t))
+
+#define acache_storetime(entry, t) \
+ (isc_atomic_store((isc_int32_t *)&(entry)->lastused, (t)))
+#else
+#define ACACHE_INITLOCK(l) isc_mutex_init(l)
+#define ACACHE_DESTROYLOCK(l) DESTROYLOCK(l)
+#define ACACHE_LOCK(l, t) LOCK(l)
+#define ACACHE_UNLOCK(l, t) UNLOCK(l)
+
+#define acache_storetime(entry, t) ((entry)->lastused = (t))
+#endif
+
+/* Locked by acache lock */
+typedef struct dbentry {
+ ISC_LINK(struct dbentry) link;
+
+ dns_db_t *db;
+ ISC_LIST(dns_acacheentry_t) originlist;
+ ISC_LIST(dns_acacheentry_t) referlist;
+} dbentry_t;
+
+typedef ISC_LIST(dbentry_t) dbentrylist_t;
+
+typedef struct acache_cleaner acache_cleaner_t;
+
+typedef enum {
+ cleaner_s_idle, /* Waiting for cleaning-interval to expire. */
+ cleaner_s_busy, /* Currently cleaning. */
+ cleaner_s_done /* Freed enough memory after being overmem. */
+} cleaner_state_t;
+
+/*
+ * Convenience macros for comprehensive assertion checking.
+ */
+#define CLEANER_IDLE(c) ((c)->state == cleaner_s_idle && \
+ (c)->resched_event != NULL)
+#define CLEANER_BUSY(c) ((c)->state == cleaner_s_busy && \
+ (c)->resched_event == NULL)
+
+struct acache_cleaner {
+ isc_mutex_t lock;
+ /*
+ * Locks overmem_event, overmem. (See cache.c)
+ */
+
+ dns_acache_t *acache;
+ unsigned int cleaning_interval; /* The cleaning-interval
+ from named.conf,
+ in seconds. */
+
+ isc_stdtime_t last_cleanup_time; /* The time when the last
+ cleanup task completed */
+
+ isc_timer_t *cleaning_timer;
+ isc_event_t *resched_event; /* Sent by cleaner task to
+ itself to reschedule */
+ isc_event_t *overmem_event;
+
+ dns_acacheentry_t *current_entry; /* The bookmark entry to
+ restart the cleaning.
+ Locked by acache lock. */
+ int increment; /* Number of entries to
+ clean in one increment */
+
+ unsigned long ncleaned; /* Number of entries cleaned
+ up (for logging purposes) */
+ cleaner_state_t state; /* Idle/Busy/Done. */
+ isc_boolean_t overmem; /* The acache is in an overmem
+ state. */
+};
+
+struct dns_acachestats {
+ unsigned int hits;
+ unsigned int queries;
+ unsigned int misses;
+ unsigned int adds;
+ unsigned int deleted;
+ unsigned int cleaned;
+ unsigned int cleaner_runs;
+ unsigned int overmem;
+ unsigned int overmem_nocreates;
+ unsigned int nomem;
+};
+
+/*
+ * The actual acache object.
+ */
+
+struct dns_acache {
+ unsigned int magic;
+
+ isc_mem_t *mctx;
+ isc_refcount_t refs;
+
+#ifdef ACACHE_USE_RWLOCK
+ isc_rwlock_t *entrylocks;
+#else
+ isc_mutex_t *entrylocks;
+#endif
+
+ isc_mutex_t lock;
+
+ int live_cleaners;
+ acache_cleaner_t cleaner;
+ ISC_LIST(dns_acacheentry_t) entries;
+ unsigned int dbentries;
+ dbentrylist_t dbbucket[DBBUCKETS];
+
+ isc_boolean_t shutting_down;
+
+ isc_task_t *task;
+ isc_event_t cevent;
+ isc_boolean_t cevent_sent;
+
+ dns_acachestats_t stats;
+};
+
+struct dns_acacheentry {
+ unsigned int magic;
+
+ unsigned int locknum;
+ isc_refcount_t references;
+
+ dns_acache_t *acache;
+
+ /* Data for Management of cache entries */
+ ISC_LINK(dns_acacheentry_t) link;
+ ISC_LINK(dns_acacheentry_t) olink;
+ ISC_LINK(dns_acacheentry_t) rlink;
+
+ dns_db_t *origdb; /* reference to the DB
+ holding this entry */
+
+ /* Cache data */
+ dns_zone_t *zone; /* zone this entry
+ belongs to */
+ dns_db_t *db; /* DB this entry belongs to */
+ dns_dbversion_t *version; /* the version of the DB */
+ dns_dbnode_t *node; /* node this entry
+ belongs to */
+ dns_name_t *foundname; /* corresponding DNS name
+ and rdataset */
+
+ /* Callback function and its argument */
+ void (*callback)(dns_acacheentry_t *, void **);
+ void *cbarg;
+
+ /* Timestamp of the last time this entry is referred to */
+ isc_stdtime32_t lastused;
+};
+
+/*
+ * Internal functions (and prototypes).
+ */
+static inline isc_boolean_t check_noentry(dns_acache_t *acache);
+static void destroy(dns_acache_t *acache);
+static void shutdown_entries(dns_acache_t *acache);
+static void shutdown_buckets(dns_acache_t *acache);
+static void destroy_entry(dns_acacheentry_t *ent);
+static inline void unlink_dbentries(dns_acache_t *acache,
+ dns_acacheentry_t *ent);
+static inline isc_result_t finddbent(dns_acache_t *acache,
+ dns_db_t *db, dbentry_t **dbentryp);
+static inline void clear_entry(dns_acache_t *acache, dns_acacheentry_t *entry);
+static isc_result_t acache_cleaner_init(dns_acache_t *acache,
+ isc_timermgr_t *timermgr,
+ acache_cleaner_t *cleaner);
+static void acache_cleaning_timer_action(isc_task_t *task, isc_event_t *event);
+static void acache_incremental_cleaning_action(isc_task_t *task,
+ isc_event_t *event);
+static void acache_overmem_cleaning_action(isc_task_t *task,
+ isc_event_t *event);
+static void acache_cleaner_shutdown_action(isc_task_t *task,
+ isc_event_t *event);
+
+/*
+ * acache should be locked. If it is not, the stats can get out of whack,
+ * which is not a big deal for us since this is for debugging / stats
+ */
+static void
+reset_stats(dns_acache_t *acache) {
+ acache->stats.hits = 0;
+ acache->stats.queries = 0;
+ acache->stats.misses = 0;
+ acache->stats.adds = 0;
+ acache->stats.deleted = 0;
+ acache->stats.cleaned = 0;
+ acache->stats.overmem = 0;
+ acache->stats.overmem_nocreates = 0;
+ acache->stats.nomem = 0;
+}
+
+/*
+ * The acache must be locked before calling.
+ */
+static inline isc_boolean_t
+check_noentry(dns_acache_t *acache) {
+ if (ISC_LIST_EMPTY(acache->entries) && acache->dbentries == 0) {
+ return (ISC_TRUE);
+ }
+
+ return (ISC_FALSE);
+}
+
+/*
+ * The acache must be locked before calling.
+ */
+static void
+shutdown_entries(dns_acache_t *acache) {
+ dns_acacheentry_t *entry, *entry_next;
+
+ REQUIRE(DNS_ACACHE_VALID(acache));
+ INSIST(acache->shutting_down);
+
+ /*
+ * Release the dependency of all entries, and detach them.
+ */
+ for (entry = ISC_LIST_HEAD(acache->entries);
+ entry != NULL;
+ entry = entry_next) {
+ entry_next = ISC_LIST_NEXT(entry, link);
+
+ ACACHE_LOCK(&acache->entrylocks[entry->locknum],
+ isc_rwlocktype_write);
+
+ /*
+ * If the cleaner holds this entry, it will be unlinked and
+ * freed in the cleaner later.
+ */
+ if (acache->cleaner.current_entry != entry)
+ ISC_LIST_UNLINK(acache->entries, entry, link);
+ unlink_dbentries(acache, entry);
+ if (entry->callback != NULL) {
+ (entry->callback)(entry, &entry->cbarg);
+ entry->callback = NULL;
+ }
+
+ ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
+ isc_rwlocktype_write);
+
+ if (acache->cleaner.current_entry != entry)
+ dns_acache_detachentry(&entry);
+ }
+}
+
+/*
+ * The acache must be locked before calling.
+ */
+static void
+shutdown_buckets(dns_acache_t *acache) {
+ int i;
+ dbentry_t *dbent;
+
+ REQUIRE(DNS_ACACHE_VALID(acache));
+ INSIST(acache->shutting_down);
+
+ for (i = 0; i < DBBUCKETS; i++) {
+ while ((dbent = ISC_LIST_HEAD(acache->dbbucket[i])) != NULL) {
+ INSIST(ISC_LIST_EMPTY(dbent->originlist) &&
+ ISC_LIST_EMPTY(dbent->referlist));
+ ISC_LIST_UNLINK(acache->dbbucket[i], dbent, link);
+
+ dns_db_detach(&dbent->db);
+
+ isc_mem_put(acache->mctx, dbent, sizeof(*dbent));
+
+ acache->dbentries--;
+ }
+ }
+
+ INSIST(acache->dbentries == 0);
+}
+
+static void
+shutdown_task(isc_task_t *task, isc_event_t *ev) {
+ dns_acache_t *acache;
+
+ UNUSED(task);
+
+ acache = ev->ev_arg;
+ INSIST(DNS_ACACHE_VALID(acache));
+
+ isc_event_free(&ev);
+
+ LOCK(&acache->lock);
+
+ shutdown_entries(acache);
+ shutdown_buckets(acache);
+
+ UNLOCK(&acache->lock);
+
+ dns_acache_detach(&acache);
+}
+
+/* The acache and the entry must be locked before calling. */
+static inline void
+unlink_dbentries(dns_acache_t *acache, dns_acacheentry_t *ent) {
+ isc_result_t result;
+ dbentry_t *dbent;
+
+ if (ISC_LINK_LINKED(ent, olink)) {
+ INSIST(ent->origdb != NULL);
+ dbent = NULL;
+ result = finddbent(acache, ent->origdb, &dbent);
+ INSIST(result == ISC_R_SUCCESS);
+
+ ISC_LIST_UNLINK(dbent->originlist, ent, olink);
+ }
+ if (ISC_LINK_LINKED(ent, rlink)) {
+ INSIST(ent->db != NULL);
+ dbent = NULL;
+ result = finddbent(acache, ent->db, &dbent);
+ INSIST(result == ISC_R_SUCCESS);
+
+ ISC_LIST_UNLINK(dbent->referlist, ent, rlink);
+ }
+}
+
+/* There must not be a reference to this entry. */
+static void
+destroy_entry(dns_acacheentry_t *entry) {
+ dns_acache_t *acache;
+
+ REQUIRE(DNS_ACACHEENTRY_VALID(entry));
+
+ acache = entry->acache;
+ REQUIRE(DNS_ACACHE_VALID(acache));
+
+ /*
+ * Since there is no reference to this entry, it is safe to call
+ * clear_entry() here.
+ */
+ clear_entry(acache, entry);
+
+ isc_mem_put(acache->mctx, entry, sizeof(*entry));
+
+ dns_acache_detach(&acache);
+}
+
+static void
+destroy(dns_acache_t *acache) {
+ int i;
+
+ REQUIRE(DNS_ACACHE_VALID(acache));
+
+ ATRACE("destroy");
+
+ isc_mem_setwater(acache->mctx, NULL, NULL, 0, 0);
+
+ if (acache->cleaner.overmem_event != NULL)
+ isc_event_free(&acache->cleaner.overmem_event);
+
+ if (acache->cleaner.resched_event != NULL)
+ isc_event_free(&acache->cleaner.resched_event);
+
+ if (acache->task != NULL)
+ isc_task_detach(&acache->task);
+
+ for (i = 0; i < DEFAULT_ACACHE_ENTRY_LOCK_COUNT; i++)
+ ACACHE_DESTROYLOCK(&acache->entrylocks[i]);
+ isc_mem_put(acache->mctx, acache->entrylocks,
+ sizeof(*acache->entrylocks) *
+ DEFAULT_ACACHE_ENTRY_LOCK_COUNT);
+
+ DESTROYLOCK(&acache->cleaner.lock);
+
+ DESTROYLOCK(&acache->lock);
+ acache->magic = 0;
+
+ isc_mem_putanddetach(&acache->mctx, acache, sizeof(*acache));
+}
+
+static inline isc_result_t
+finddbent(dns_acache_t *acache, dns_db_t *db, dbentry_t **dbentryp) {
+ int bucket;
+ dbentry_t *dbentry;
+
+ REQUIRE(DNS_ACACHE_VALID(acache));
+ REQUIRE(db != NULL);
+ REQUIRE(dbentryp != NULL && *dbentryp == NULL);
+
+ /*
+ * The caller must be holding the acache lock.
+ */
+
+ bucket = isc_hash_calc((const unsigned char *)&db,
+ sizeof(db), ISC_TRUE) % DBBUCKETS;
+
+ for (dbentry = ISC_LIST_HEAD(acache->dbbucket[bucket]);
+ dbentry != NULL;
+ dbentry = ISC_LIST_NEXT(dbentry, link)) {
+ if (dbentry->db == db)
+ break;
+ }
+
+ *dbentryp = dbentry;
+
+ if (dbentry == NULL)
+ return (ISC_R_NOTFOUND);
+ else
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+clear_entry(dns_acache_t *acache, dns_acacheentry_t *entry) {
+ REQUIRE(DNS_ACACHE_VALID(acache));
+ REQUIRE(DNS_ACACHEENTRY_VALID(entry));
+
+ /*
+ * The caller must be holing the entry lock.
+ */
+
+ if (entry->foundname) {
+ dns_rdataset_t *rdataset, *rdataset_next;
+
+ for (rdataset = ISC_LIST_HEAD(entry->foundname->list);
+ rdataset != NULL;
+ rdataset = rdataset_next) {
+ rdataset_next = ISC_LIST_NEXT(rdataset, link);
+ ISC_LIST_UNLINK(entry->foundname->list,
+ rdataset, link);
+ dns_rdataset_disassociate(rdataset);
+ isc_mem_put(acache->mctx, rdataset, sizeof(*rdataset));
+ }
+ if (dns_name_dynamic(entry->foundname))
+ dns_name_free(entry->foundname, acache->mctx);
+ isc_mem_put(acache->mctx, entry->foundname,
+ sizeof(*entry->foundname));
+ entry->foundname = NULL;
+ }
+
+ if (entry->node != NULL) {
+ INSIST(entry->db != NULL);
+ dns_db_detachnode(entry->db, &entry->node);
+ }
+ if (entry->version != NULL) {
+ INSIST(entry->db != NULL);
+ dns_db_closeversion(entry->db, &entry->version, ISC_FALSE);
+ }
+ if (entry->db != NULL)
+ dns_db_detach(&entry->db);
+ if (entry->zone != NULL)
+ dns_zone_detach(&entry->zone);
+
+ if (entry->origdb != NULL)
+ dns_db_detach(&entry->origdb);
+}
+
+static isc_result_t
+acache_cleaner_init(dns_acache_t *acache, isc_timermgr_t *timermgr,
+ acache_cleaner_t *cleaner)
+{
+ int result;
+
+ ATRACE("acache cleaner init");
+
+ result = isc_mutex_init(&cleaner->lock);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+
+ cleaner->increment = DNS_ACACHE_CLEANERINCREMENT;
+ cleaner->state = cleaner_s_idle;
+ cleaner->acache = acache;
+ cleaner->overmem = ISC_FALSE;
+
+ cleaner->cleaning_timer = NULL;
+ cleaner->resched_event = NULL;
+ cleaner->overmem_event = NULL;
+ cleaner->current_entry = NULL;
+
+ if (timermgr != NULL) {
+ cleaner->acache->live_cleaners++;
+
+ result = isc_task_onshutdown(acache->task,
+ acache_cleaner_shutdown_action,
+ acache);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "acache cleaner: "
+ "isc_task_onshutdown() failed: %s",
+ dns_result_totext(result));
+ goto cleanup;
+ }
+
+ cleaner->cleaning_interval = 0; /* Initially turned off. */
+ isc_stdtime_get(&cleaner->last_cleanup_time);
+ result = isc_timer_create(timermgr, isc_timertype_inactive,
+ NULL, NULL,
+ acache->task,
+ acache_cleaning_timer_action,
+ cleaner, &cleaner->cleaning_timer);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_timer_create() failed: %s",
+ dns_result_totext(result));
+ result = ISC_R_UNEXPECTED;
+ goto cleanup;
+ }
+
+ cleaner->resched_event =
+ isc_event_allocate(acache->mctx, cleaner,
+ DNS_EVENT_ACACHECLEAN,
+ acache_incremental_cleaning_action,
+ cleaner, sizeof(isc_event_t));
+ if (cleaner->resched_event == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ cleaner->overmem_event =
+ isc_event_allocate(acache->mctx, cleaner,
+ DNS_EVENT_ACACHEOVERMEM,
+ acache_overmem_cleaning_action,
+ cleaner, sizeof(isc_event_t));
+ if (cleaner->overmem_event == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ }
+
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (cleaner->overmem_event != NULL)
+ isc_event_free(&cleaner->overmem_event);
+ if (cleaner->resched_event != NULL)
+ isc_event_free(&cleaner->resched_event);
+ if (cleaner->cleaning_timer != NULL)
+ isc_timer_detach(&cleaner->cleaning_timer);
+ cleaner->acache->live_cleaners--;
+ DESTROYLOCK(&cleaner->lock);
+ fail:
+ return (result);
+}
+
+static void
+begin_cleaning(acache_cleaner_t *cleaner) {
+ dns_acacheentry_t *head;
+ dns_acache_t *acache = cleaner->acache;
+
+ /*
+ * This function does not have to lock the cleaner, since critical
+ * parameters (except current_entry, which is locked by acache lock,)
+ * are only used in a single task context.
+ */
+
+ REQUIRE(CLEANER_IDLE(cleaner));
+ INSIST(DNS_ACACHE_VALID(acache));
+ INSIST(cleaner->current_entry == NULL);
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_ACACHE, ISC_LOG_DEBUG(1),
+ "begin acache cleaning, mem inuse %lu",
+ (unsigned long)isc_mem_inuse(cleaner->acache->mctx));
+
+ LOCK(&acache->lock);
+
+ head = ISC_LIST_HEAD(acache->entries);
+ if (head != NULL)
+ dns_acache_attachentry(head, &cleaner->current_entry);
+
+ UNLOCK(&acache->lock);
+
+ if (cleaner->current_entry != NULL) {
+ cleaner->ncleaned = 0;
+ cleaner->state = cleaner_s_busy;
+ isc_task_send(acache->task, &cleaner->resched_event);
+ }
+
+ return;
+}
+
+static void
+end_cleaning(acache_cleaner_t *cleaner, isc_event_t *event) {
+ dns_acache_t *acache = cleaner->acache;
+
+ REQUIRE(CLEANER_BUSY(cleaner));
+ REQUIRE(event != NULL);
+ REQUIRE(DNS_ACACHEENTRY_VALID(cleaner->current_entry));
+
+ /* No need to lock the cleaner (see begin_cleaning()). */
+
+ LOCK(&acache->lock);
+
+ /*
+ * Even if the cleaner has the last reference to the entry, which means
+ * the entry has been unused, it may still be linked if unlinking the
+ * entry has been delayed due to the reference.
+ */
+ if (isc_refcount_current(&cleaner->current_entry->references) == 1) {
+ INSIST(cleaner->current_entry->callback == NULL);
+
+ if (ISC_LINK_LINKED(cleaner->current_entry, link)) {
+ ISC_LIST_UNLINK(acache->entries,
+ cleaner->current_entry, link);
+ }
+ }
+ dns_acache_detachentry(&cleaner->current_entry);
+
+ if (cleaner->overmem)
+ acache->stats.overmem++;
+ acache->stats.cleaned += cleaner->ncleaned;
+ acache->stats.cleaner_runs++;
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
+ ISC_LOG_NOTICE,
+ "acache %p stats: hits=%d misses=%d queries=%d "
+ "adds=%d deleted=%d "
+ "cleaned=%d cleaner_runs=%d overmem=%d "
+ "overmem_nocreates=%d nomem=%d",
+ acache,
+ acache->stats.hits, acache->stats.misses,
+ acache->stats.queries,
+ acache->stats.adds, acache->stats.deleted,
+ acache->stats.cleaned, acache->stats.cleaner_runs,
+ acache->stats.overmem, acache->stats.overmem_nocreates,
+ acache->stats.nomem);
+ reset_stats(acache);
+
+ isc_stdtime_get(&cleaner->last_cleanup_time);
+
+ UNLOCK(&acache->lock);
+
+ dns_acache_setcleaninginterval(cleaner->acache,
+ cleaner->cleaning_interval);
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
+ ISC_LOG_DEBUG(1), "end acache cleaning, "
+ "%lu entries cleaned, mem inuse %lu",
+ cleaner->ncleaned,
+ (unsigned long)isc_mem_inuse(cleaner->acache->mctx));
+
+ if (cleaner->overmem) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_ACACHE, ISC_LOG_NOTICE,
+ "acache is still in overmem state "
+ "after cleaning");
+ }
+
+ cleaner->ncleaned = 0;
+ cleaner->state = cleaner_s_idle;
+ cleaner->resched_event = event;
+}
+
+/*
+ * This is run once for every acache-cleaning-interval as defined
+ * in named.conf.
+ */
+static void
+acache_cleaning_timer_action(isc_task_t *task, isc_event_t *event) {
+ acache_cleaner_t *cleaner = event->ev_arg;
+
+ UNUSED(task);
+
+ INSIST(event->ev_type == ISC_TIMEREVENT_TICK);
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
+ ISC_LOG_DEBUG(1), "acache cleaning timer fired, "
+ "cleaner state = %d", cleaner->state);
+
+ if (cleaner->state == cleaner_s_idle)
+ begin_cleaning(cleaner);
+
+ isc_event_free(&event);
+}
+
+/* The caller must hold entry lock. */
+static inline isc_boolean_t
+entry_stale(acache_cleaner_t *cleaner, dns_acacheentry_t *entry,
+ isc_stdtime32_t now32, unsigned int interval)
+{
+ /*
+ * If the callback has been canceled, we definitely do not need the
+ * entry.
+ */
+ if (entry->callback == NULL)
+ return (ISC_TRUE);
+
+ if (interval > cleaner->cleaning_interval)
+ interval = cleaner->cleaning_interval;
+
+ if (entry->lastused + interval < now32)
+ return (ISC_TRUE);
+
+ /*
+ * If the acache is in the overmem state, probabilistically decide if
+ * the entry should be purged, based on the time passed from its last
+ * use and the cleaning interval.
+ */
+ if (cleaner->overmem) {
+ unsigned int passed =
+ now32 - entry->lastused; /* <= interval */
+ isc_uint32_t val;
+
+ if (passed > interval / 2)
+ return (ISC_TRUE);
+ isc_random_get(&val);
+ if (passed > interval / 4)
+ return (ISC_TF(val % 4 == 0));
+ return (ISC_TF(val % 8 == 0));
+ }
+
+ return (ISC_FALSE);
+}
+
+/*
+ * Do incremental cleaning.
+ */
+static void
+acache_incremental_cleaning_action(isc_task_t *task, isc_event_t *event) {
+ acache_cleaner_t *cleaner = event->ev_arg;
+ dns_acache_t *acache = cleaner->acache;
+ dns_acacheentry_t *entry, *next = NULL;
+ int n_entries;
+ isc_stdtime32_t now32, last32;
+ isc_stdtime_t now;
+ unsigned int interval;
+
+ INSIST(DNS_ACACHE_VALID(acache));
+ INSIST(task == acache->task);
+ INSIST(event->ev_type == DNS_EVENT_ACACHECLEAN);
+
+ if (cleaner->state == cleaner_s_done) {
+ cleaner->state = cleaner_s_busy;
+ end_cleaning(cleaner, event);
+ return;
+ }
+
+ INSIST(CLEANER_BUSY(cleaner));
+
+ n_entries = cleaner->increment;
+
+ isc_stdtime_get(&now);
+ isc_stdtime_convert32(now, &now32);
+
+ LOCK(&acache->lock);
+
+ entry = cleaner->current_entry;
+ isc_stdtime_convert32(cleaner->last_cleanup_time, &last32);
+ INSIST(now32 > last32);
+ interval = now32 - last32;
+
+ while (n_entries-- > 0) {
+ isc_boolean_t is_stale = ISC_FALSE;
+
+ INSIST(entry != NULL);
+
+ next = ISC_LIST_NEXT(entry, link);
+
+ ACACHE_LOCK(&acache->entrylocks[entry->locknum],
+ isc_rwlocktype_write);
+
+ is_stale = entry_stale(cleaner, entry, now32, interval);
+ if (is_stale) {
+ ISC_LIST_UNLINK(acache->entries, entry, link);
+ unlink_dbentries(acache, entry);
+ if (entry->callback != NULL)
+ (entry->callback)(entry, &entry->cbarg);
+ entry->callback = NULL;
+
+ cleaner->ncleaned++;
+ }
+
+ ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
+ isc_rwlocktype_write);
+
+ if (is_stale)
+ dns_acache_detachentry(&entry);
+
+ if (next == NULL) {
+ if (cleaner->overmem) {
+ entry = ISC_LIST_HEAD(acache->entries);
+ if (entry != NULL) {
+ /*
+ * If we are still in the overmem
+ * state, keep cleaning.
+ */
+ isc_log_write(dns_lctx,
+ DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_ACACHE,
+ ISC_LOG_DEBUG(1),
+ "acache cleaner: "
+ "still overmem, "
+ "reset and try again");
+ continue;
+ }
+ }
+
+ UNLOCK(&acache->lock);
+ end_cleaning(cleaner, event);
+ return;
+ }
+
+ entry = next;
+ }
+
+ /*
+ * We have successfully performed a cleaning increment but have
+ * not gone through the entire cache. Remember the entry that will
+ * be the starting point in the next clean-up, and reschedule another
+ * batch. If it fails, just try to continue anyway.
+ */
+ INSIST(next != NULL && next != cleaner->current_entry);
+ dns_acache_detachentry(&cleaner->current_entry);
+ dns_acache_attachentry(next, &cleaner->current_entry);
+
+ UNLOCK(&acache->lock);
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
+ ISC_LOG_DEBUG(1), "acache cleaner: checked %d entries, "
+ "mem inuse %lu, sleeping", cleaner->increment,
+ (unsigned long)isc_mem_inuse(cleaner->acache->mctx));
+
+ isc_task_send(task, &event);
+ INSIST(CLEANER_BUSY(cleaner));
+
+ return;
+}
+
+/*
+ * This is called when the acache either surpasses its upper limit
+ * or shrinks beyond its lower limit.
+ */
+static void
+acache_overmem_cleaning_action(isc_task_t *task, isc_event_t *event) {
+ acache_cleaner_t *cleaner = event->ev_arg;
+ isc_boolean_t want_cleaning = ISC_FALSE;
+
+ UNUSED(task);
+
+ INSIST(event->ev_type == DNS_EVENT_ACACHEOVERMEM);
+ INSIST(cleaner->overmem_event == NULL);
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ACACHE,
+ ISC_LOG_DEBUG(1), "overmem_cleaning_action called, "
+ "overmem = %d, state = %d", cleaner->overmem,
+ cleaner->state);
+
+ LOCK(&cleaner->lock);
+
+ if (cleaner->overmem) {
+ if (cleaner->state == cleaner_s_idle)
+ want_cleaning = ISC_TRUE;
+ } else {
+ if (cleaner->state == cleaner_s_busy)
+ /*
+ * end_cleaning() can't be called here because
+ * then both cleaner->overmem_event and
+ * cleaner->resched_event will point to this
+ * event. Set the state to done, and then
+ * when the acache_incremental_cleaning_action() event
+ * is posted, it will handle the end_cleaning.
+ */
+ cleaner->state = cleaner_s_done;
+ }
+
+ cleaner->overmem_event = event;
+
+ UNLOCK(&cleaner->lock);
+
+ if (want_cleaning)
+ begin_cleaning(cleaner);
+}
+
+static void
+water(void *arg, int mark) {
+ dns_acache_t *acache = arg;
+ isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER);
+
+ REQUIRE(DNS_ACACHE_VALID(acache));
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_ACACHE, ISC_LOG_DEBUG(1),
+ "acache memory reaches %s watermark, mem inuse %lu",
+ overmem ? "high" : "low",
+ (unsigned long)isc_mem_inuse(acache->mctx));
+
+ LOCK(&acache->cleaner.lock);
+
+ if (acache->cleaner.overmem != overmem) {
+ acache->cleaner.overmem = overmem;
+
+ if (acache->cleaner.overmem_event != NULL)
+ isc_task_send(acache->task,
+ &acache->cleaner.overmem_event);
+ isc_mem_waterack(acache->mctx, mark);
+ }
+
+ UNLOCK(&acache->cleaner.lock);
+}
+
+/*
+ * The cleaner task is shutting down; do the necessary cleanup.
+ */
+static void
+acache_cleaner_shutdown_action(isc_task_t *task, isc_event_t *event) {
+ dns_acache_t *acache = event->ev_arg;
+ isc_boolean_t should_free = ISC_FALSE;
+
+ INSIST(task == acache->task);
+ INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
+ INSIST(DNS_ACACHE_VALID(acache));
+
+ ATRACE("acache cleaner shutdown");
+
+ if (CLEANER_BUSY(&acache->cleaner))
+ end_cleaning(&acache->cleaner, event);
+ else
+ isc_event_free(&event);
+
+ LOCK(&acache->lock);
+
+ acache->live_cleaners--;
+ INSIST(acache->live_cleaners == 0);
+
+ if (isc_refcount_current(&acache->refs) == 0) {
+ INSIST(check_noentry(acache) == ISC_TRUE);
+ should_free = ISC_TRUE;
+ }
+
+ /*
+ * By detaching the timer in the context of its task,
+ * we are guaranteed that there will be no further timer
+ * events.
+ */
+ if (acache->cleaner.cleaning_timer != NULL)
+ isc_timer_detach(&acache->cleaner.cleaning_timer);
+
+ /* Make sure we don't reschedule anymore. */
+ (void)isc_task_purge(task, NULL, DNS_EVENT_ACACHECLEAN, NULL);
+
+ UNLOCK(&acache->lock);
+
+ if (should_free)
+ destroy(acache);
+}
+
+/*
+ * Public functions.
+ */
+
+isc_result_t
+dns_acache_create(dns_acache_t **acachep, isc_mem_t *mctx,
+ isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr)
+{
+ int i;
+ isc_result_t result;
+ dns_acache_t *acache;
+
+ REQUIRE(acachep != NULL && *acachep == NULL);
+ REQUIRE(mctx != NULL);
+ REQUIRE(taskmgr != NULL);
+
+ acache = isc_mem_get(mctx, sizeof(*acache));
+ if (acache == NULL)
+ return (ISC_R_NOMEMORY);
+
+ ATRACE("create");
+
+ result = isc_refcount_init(&acache->refs, 1);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, acache, sizeof(*acache));
+ return (result);
+ }
+
+ result = isc_mutex_init(&acache->lock);
+ if (result != ISC_R_SUCCESS) {
+ isc_refcount_decrement(&acache->refs, NULL);
+ isc_refcount_destroy(&acache->refs);
+ isc_mem_put(mctx, acache, sizeof(*acache));
+ return (result);
+ }
+
+ acache->mctx = NULL;
+ isc_mem_attach(mctx, &acache->mctx);
+ ISC_LIST_INIT(acache->entries);
+
+ acache->shutting_down = ISC_FALSE;
+
+ acache->task = NULL;
+ acache->entrylocks = NULL;
+
+ result = isc_task_create(taskmgr, 1, &acache->task);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_task_create() failed(): %s",
+ dns_result_totext(result));
+ result = ISC_R_UNEXPECTED;
+ goto cleanup;
+ }
+ isc_task_setname(acache->task, "acachetask", acache);
+ ISC_EVENT_INIT(&acache->cevent, sizeof(acache->cevent), 0, NULL,
+ DNS_EVENT_ACACHECONTROL, shutdown_task, NULL,
+ NULL, NULL, NULL);
+ acache->cevent_sent = ISC_FALSE;
+
+ acache->dbentries = 0;
+ for (i = 0; i < DBBUCKETS; i++)
+ ISC_LIST_INIT(acache->dbbucket[i]);
+
+ acache->entrylocks = isc_mem_get(mctx, sizeof(*acache->entrylocks) *
+ DEFAULT_ACACHE_ENTRY_LOCK_COUNT);
+ if (acache->entrylocks == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ for (i = 0; i < DEFAULT_ACACHE_ENTRY_LOCK_COUNT; i++) {
+ result = ACACHE_INITLOCK(&acache->entrylocks[i]);
+ if (result != ISC_R_SUCCESS) {
+ while (i-- > 0)
+ ACACHE_DESTROYLOCK(&acache->entrylocks[i]);
+ isc_mem_put(mctx, acache->entrylocks,
+ sizeof(*acache->entrylocks) *
+ DEFAULT_ACACHE_ENTRY_LOCK_COUNT);
+ acache->entrylocks = NULL;
+ goto cleanup;
+ }
+ }
+
+ acache->live_cleaners = 0;
+ result = acache_cleaner_init(acache, timermgr, &acache->cleaner);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ acache->stats.cleaner_runs = 0;
+ reset_stats(acache);
+
+ acache->magic = ACACHE_MAGIC;
+
+ *acachep = acache;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (acache->task != NULL)
+ isc_task_detach(&acache->task);
+ DESTROYLOCK(&acache->lock);
+ isc_refcount_decrement(&acache->refs, NULL);
+ isc_refcount_destroy(&acache->refs);
+ if (acache->entrylocks != NULL) {
+ for (i = 0; i < DEFAULT_ACACHE_ENTRY_LOCK_COUNT; i++)
+ ACACHE_DESTROYLOCK(&acache->entrylocks[i]);
+ isc_mem_put(mctx, acache->entrylocks,
+ sizeof(*acache->entrylocks) *
+ DEFAULT_ACACHE_ENTRY_LOCK_COUNT);
+ }
+ isc_mem_put(mctx, acache, sizeof(*acache));
+ isc_mem_detach(&mctx);
+
+ return (result);
+}
+
+void
+dns_acache_attach(dns_acache_t *source, dns_acache_t **targetp) {
+ REQUIRE(DNS_ACACHE_VALID(source));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ AATRACE(source, "attach");
+
+ isc_refcount_increment(&source->refs, NULL);
+
+ *targetp = source;
+}
+
+void
+dns_acache_countquerymiss(dns_acache_t *acache) {
+ acache->stats.misses++; /* XXXSK danger: unlocked! */
+ acache->stats.queries++; /* XXXSK danger: unlocked! */
+}
+
+void
+dns_acache_detach(dns_acache_t **acachep) {
+ dns_acache_t *acache;
+ unsigned int refs;
+ isc_boolean_t should_free = ISC_FALSE;
+
+ REQUIRE(acachep != NULL && DNS_ACACHE_VALID(*acachep));
+ acache = *acachep;
+
+ ATRACE("detach");
+
+ isc_refcount_decrement(&acache->refs, &refs);
+ if (refs == 0) {
+ INSIST(check_noentry(acache) == ISC_TRUE);
+ should_free = ISC_TRUE;
+ }
+
+ *acachep = NULL;
+
+ /*
+ * If we're exiting and the cleaner task exists, let it free the cache.
+ */
+ if (should_free && acache->live_cleaners > 0) {
+ isc_task_shutdown(acache->task);
+ should_free = ISC_FALSE;
+ }
+
+ if (should_free)
+ destroy(acache);
+}
+
+void
+dns_acache_shutdown(dns_acache_t *acache) {
+ REQUIRE(DNS_ACACHE_VALID(acache));
+
+ LOCK(&acache->lock);
+
+ ATRACE("shutdown");
+
+ if (!acache->shutting_down) {
+ isc_event_t *event;
+ dns_acache_t *acache_evarg = NULL;
+
+ INSIST(!acache->cevent_sent);
+
+ acache->shutting_down = ISC_TRUE;
+
+ isc_mem_setwater(acache->mctx, NULL, NULL, 0, 0);
+
+ /*
+ * Self attach the object in order to prevent it from being
+ * destroyed while waiting for the event.
+ */
+ dns_acache_attach(acache, &acache_evarg);
+ event = &acache->cevent;
+ event->ev_arg = acache_evarg;
+ isc_task_send(acache->task, &event);
+ acache->cevent_sent = ISC_TRUE;
+ }
+
+ UNLOCK(&acache->lock);
+}
+
+isc_result_t
+dns_acache_setdb(dns_acache_t *acache, dns_db_t *db) {
+ int bucket;
+ dbentry_t *dbentry;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ REQUIRE(DNS_ACACHE_VALID(acache));
+ REQUIRE(db != NULL);
+
+ ATRACE("setdb");
+
+ LOCK(&acache->lock);
+
+ dbentry = NULL;
+ result = finddbent(acache, db, &dbentry);
+ if (result == ISC_R_SUCCESS) {
+ result = ISC_R_EXISTS;
+ goto end;
+ }
+ result = ISC_R_SUCCESS;
+
+ dbentry = isc_mem_get(acache->mctx, sizeof(*dbentry));
+ if (dbentry == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto end;
+ }
+
+ ISC_LINK_INIT(dbentry, link);
+ ISC_LIST_INIT(dbentry->originlist);
+ ISC_LIST_INIT(dbentry->referlist);
+
+ dbentry->db = NULL;
+ dns_db_attach(db, &dbentry->db);
+
+ bucket = isc_hash_calc((const unsigned char *)&db,
+ sizeof(db), ISC_TRUE) % DBBUCKETS;
+
+ ISC_LIST_APPEND(acache->dbbucket[bucket], dbentry, link);
+
+ acache->dbentries++;
+
+ end:
+ UNLOCK(&acache->lock);
+
+ return (result);
+}
+
+isc_result_t
+dns_acache_putdb(dns_acache_t *acache, dns_db_t *db) {
+ int bucket;
+ isc_result_t result;
+ dbentry_t *dbentry;
+ dns_acacheentry_t *entry;
+
+ REQUIRE(DNS_ACACHE_VALID(acache));
+ REQUIRE(db != NULL);
+
+ ATRACE("putdb");
+
+ LOCK(&acache->lock);
+
+ dbentry = NULL;
+ result = finddbent(acache, db, &dbentry);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * The entry may have not been created due to memory shortage.
+ */
+ UNLOCK(&acache->lock);
+ return (ISC_R_NOTFOUND);
+ }
+
+ /*
+ * Release corresponding cache entries: for each entry, release all
+ * links the entry has, and then callback to the entry holder (if any).
+ * If no other external references exist (this can happen if the
+ * original holder has canceled callback,) destroy it here.
+ */
+ while ((entry = ISC_LIST_HEAD(dbentry->originlist)) != NULL) {
+ ACACHE_LOCK(&acache->entrylocks[entry->locknum],
+ isc_rwlocktype_write);
+
+ /*
+ * Releasing olink first would avoid finddbent() in
+ * unlink_dbentries().
+ */
+ ISC_LIST_UNLINK(dbentry->originlist, entry, olink);
+ if (acache->cleaner.current_entry != entry)
+ ISC_LIST_UNLINK(acache->entries, entry, link);
+ unlink_dbentries(acache, entry);
+
+ if (entry->callback != NULL)
+ (entry->callback)(entry, &entry->cbarg);
+ entry->callback = NULL;
+
+ ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
+ isc_rwlocktype_write);
+
+ if (acache->cleaner.current_entry != entry)
+ dns_acache_detachentry(&entry);
+ }
+ while ((entry = ISC_LIST_HEAD(dbentry->referlist)) != NULL) {
+ ACACHE_LOCK(&acache->entrylocks[entry->locknum],
+ isc_rwlocktype_write);
+
+ ISC_LIST_UNLINK(dbentry->referlist, entry, rlink);
+ if (acache->cleaner.current_entry != entry)
+ ISC_LIST_UNLINK(acache->entries, entry, link);
+ unlink_dbentries(acache, entry);
+
+ if (entry->callback != NULL)
+ (entry->callback)(entry, &entry->cbarg);
+ entry->callback = NULL;
+
+ ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
+ isc_rwlocktype_write);
+
+ if (acache->cleaner.current_entry != entry)
+ dns_acache_detachentry(&entry);
+ }
+
+ INSIST(ISC_LIST_EMPTY(dbentry->originlist) &&
+ ISC_LIST_EMPTY(dbentry->referlist));
+
+ bucket = isc_hash_calc((const unsigned char *)&db,
+ sizeof(db), ISC_TRUE) % DBBUCKETS;
+ ISC_LIST_UNLINK(acache->dbbucket[bucket], dbentry, link);
+ dns_db_detach(&dbentry->db);
+
+ isc_mem_put(acache->mctx, dbentry, sizeof(*dbentry));
+
+ acache->dbentries--;
+
+ acache->stats.deleted++;
+
+ UNLOCK(&acache->lock);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_acache_createentry(dns_acache_t *acache, dns_db_t *origdb,
+ void (*callback)(dns_acacheentry_t *, void **),
+ void *cbarg, dns_acacheentry_t **entryp)
+{
+ dns_acacheentry_t *newentry;
+ isc_result_t result;
+ isc_uint32_t r;
+
+ REQUIRE(DNS_ACACHE_VALID(acache));
+ REQUIRE(entryp != NULL && *entryp == NULL);
+ REQUIRE(origdb != NULL);
+
+ /*
+ * Should we exceed our memory limit for some reason (for
+ * example, if the cleaner does not run aggressively enough),
+ * then we will not create additional entries.
+ *
+ * XXXSK: It might be better to lock the acache->cleaner->lock,
+ * but locking may be an expensive bottleneck. If we misread
+ * the value, we will occasionally refuse to create a few
+ * cache entries, or create a few that we should not. I do not
+ * expect this to happen often, and it will not have very bad
+ * effects when it does. So no lock for now.
+ */
+ if (acache->cleaner.overmem) {
+ acache->stats.overmem_nocreates++; /* XXXSK danger: unlocked! */
+ return (ISC_R_NORESOURCES);
+ }
+
+ newentry = isc_mem_get(acache->mctx, sizeof(*newentry));
+ if (newentry == NULL) {
+ acache->stats.nomem++; /* XXXMLG danger: unlocked! */
+ return (ISC_R_NOMEMORY);
+ }
+
+ isc_random_get(&r);
+ newentry->locknum = r % DEFAULT_ACACHE_ENTRY_LOCK_COUNT;
+
+ result = isc_refcount_init(&newentry->references, 1);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(acache->mctx, newentry, sizeof(*newentry));
+ return (result);
+ };
+
+ ISC_LINK_INIT(newentry, link);
+ ISC_LINK_INIT(newentry, olink);
+ ISC_LINK_INIT(newentry, rlink);
+
+ newentry->acache = NULL;
+ dns_acache_attach(acache, &newentry->acache);
+
+ newentry->zone = NULL;
+ newentry->db = NULL;
+ newentry->version = NULL;
+ newentry->node = NULL;
+ newentry->foundname = NULL;
+
+ newentry->callback = callback;
+ newentry->cbarg = cbarg;
+ newentry->origdb = NULL;
+ dns_db_attach(origdb, &newentry->origdb);
+
+ isc_stdtime_get(&newentry->lastused);
+
+ newentry->magic = ACACHEENTRY_MAGIC;
+
+ *entryp = newentry;
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_acache_getentry(dns_acacheentry_t *entry, dns_zone_t **zonep,
+ dns_db_t **dbp, dns_dbversion_t **versionp,
+ dns_dbnode_t **nodep, dns_name_t *fname,
+ dns_message_t *msg, isc_stdtime_t now)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_rdataset_t *erdataset;
+ isc_stdtime32_t now32;
+ dns_acache_t *acache;
+ int locknum;
+
+ REQUIRE(DNS_ACACHEENTRY_VALID(entry));
+ REQUIRE(zonep == NULL || *zonep == NULL);
+ REQUIRE(dbp != NULL && *dbp == NULL);
+ REQUIRE(versionp != NULL && *versionp == NULL);
+ REQUIRE(nodep != NULL && *nodep == NULL);
+ REQUIRE(fname != NULL);
+ REQUIRE(msg != NULL);
+ acache = entry->acache;
+ REQUIRE(DNS_ACACHE_VALID(acache));
+
+ locknum = entry->locknum;
+ ACACHE_LOCK(&acache->entrylocks[locknum], isc_rwlocktype_read);
+
+ isc_stdtime_convert32(now, &now32);
+ acache_storetime(entry, now32);
+
+ if (entry->zone != NULL && zonep != NULL)
+ dns_zone_attach(entry->zone, zonep);
+
+ if (entry->db == NULL) {
+ *dbp = NULL;
+ *versionp = NULL;
+ } else {
+ dns_db_attach(entry->db, dbp);
+ dns_db_attachversion(entry->db, entry->version, versionp);
+ }
+ if (entry->node == NULL)
+ *nodep = NULL;
+ else {
+ dns_db_attachnode(entry->db, entry->node, nodep);
+
+ INSIST(entry->foundname != NULL);
+ dns_name_copy(entry->foundname, fname, NULL);
+ for (erdataset = ISC_LIST_HEAD(entry->foundname->list);
+ erdataset != NULL;
+ erdataset = ISC_LIST_NEXT(erdataset, link)) {
+ dns_rdataset_t *ardataset;
+
+ ardataset = NULL;
+ result = dns_message_gettemprdataset(msg, &ardataset);
+ if (result != ISC_R_SUCCESS) {
+ ACACHE_UNLOCK(&acache->entrylocks[locknum],
+ isc_rwlocktype_read);
+ goto fail;
+ }
+
+ /*
+ * XXXJT: if we simply clone the rdataset, we'll get
+ * lost wrt cyclic ordering. We'll need an additional
+ * trick to get the latest counter from the original
+ * header.
+ */
+ dns_rdataset_init(ardataset);
+ dns_rdataset_clone(erdataset, ardataset);
+ ISC_LIST_APPEND(fname->list, ardataset, link);
+ }
+ }
+
+ entry->acache->stats.hits++; /* XXXMLG danger: unlocked! */
+ entry->acache->stats.queries++;
+
+ ACACHE_UNLOCK(&acache->entrylocks[locknum], isc_rwlocktype_read);
+
+ return (result);
+
+ fail:
+ while ((erdataset = ISC_LIST_HEAD(fname->list)) != NULL) {
+ ISC_LIST_UNLINK(fname->list, erdataset, link);
+ dns_rdataset_disassociate(erdataset);
+ dns_message_puttemprdataset(msg, &erdataset);
+ }
+ if (*nodep != NULL)
+ dns_db_detachnode(*dbp, nodep);
+ if (*versionp != NULL)
+ dns_db_closeversion(*dbp, versionp, ISC_FALSE);
+ if (*dbp != NULL)
+ dns_db_detach(dbp);
+ if (zonep != NULL && *zonep != NULL)
+ dns_zone_detach(zonep);
+
+ return (result);
+}
+
+isc_result_t
+dns_acache_setentry(dns_acache_t *acache, dns_acacheentry_t *entry,
+ dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version,
+ dns_dbnode_t *node, dns_name_t *fname)
+{
+ isc_result_t result;
+ dbentry_t *odbent;
+ dbentry_t *rdbent = NULL;
+ isc_boolean_t close_version = ISC_FALSE;
+ dns_acacheentry_t *dummy_entry = NULL;
+
+ REQUIRE(DNS_ACACHE_VALID(acache));
+ REQUIRE(DNS_ACACHEENTRY_VALID(entry));
+
+ LOCK(&acache->lock); /* XXX: need to lock it here for ordering */
+ ACACHE_LOCK(&acache->entrylocks[entry->locknum], isc_rwlocktype_write);
+
+ /* Set zone */
+ if (zone != NULL)
+ dns_zone_attach(zone, &entry->zone);
+ /* Set DB */
+ if (db != NULL)
+ dns_db_attach(db, &entry->db);
+ /*
+ * Set DB version. If the version is not given by the caller,
+ * which is the case for glue or cache DBs, use the current version.
+ */
+ if (version == NULL) {
+ if (db != NULL) {
+ dns_db_currentversion(db, &version);
+ close_version = ISC_TRUE;
+ }
+ }
+ if (version != NULL) {
+ INSIST(db != NULL);
+ dns_db_attachversion(db, version, &entry->version);
+ }
+ if (close_version)
+ dns_db_closeversion(db, &version, ISC_FALSE);
+ /* Set DB node. */
+ if (node != NULL) {
+ INSIST(db != NULL);
+ dns_db_attachnode(db, node, &entry->node);
+ }
+
+ /*
+ * Set list of the corresponding rdatasets, if given.
+ * To minimize the overhead and memory consumption, we'll do this for
+ * positive cache only, in which case the DB node is non NULL.
+ * We do not want to cache incomplete information, so give up the
+ * entire entry when a memory shortage happen during the process.
+ */
+ if (node != NULL) {
+ dns_rdataset_t *ardataset, *crdataset;
+
+ entry->foundname = isc_mem_get(acache->mctx,
+ sizeof(*entry->foundname));
+
+ if (entry->foundname == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto fail;
+ }
+ dns_name_init(entry->foundname, NULL);
+ result = dns_name_dup(fname, acache->mctx,
+ entry->foundname);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+
+ for (ardataset = ISC_LIST_HEAD(fname->list);
+ ardataset != NULL;
+ ardataset = ISC_LIST_NEXT(ardataset, link)) {
+ crdataset = isc_mem_get(acache->mctx,
+ sizeof(*crdataset));
+ if (crdataset == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto fail;
+ }
+
+ dns_rdataset_init(crdataset);
+ dns_rdataset_clone(ardataset, crdataset);
+ ISC_LIST_APPEND(entry->foundname->list, crdataset,
+ link);
+ }
+ }
+
+ odbent = NULL;
+ result = finddbent(acache, entry->origdb, &odbent);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ if (db != NULL) {
+ rdbent = NULL;
+ result = finddbent(acache, db, &rdbent);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ }
+
+ ISC_LIST_APPEND(acache->entries, entry, link);
+ ISC_LIST_APPEND(odbent->originlist, entry, olink);
+ if (rdbent != NULL)
+ ISC_LIST_APPEND(rdbent->referlist, entry, rlink);
+
+ /*
+ * The additional cache needs an implicit reference to entries in its
+ * link.
+ */
+ dns_acache_attachentry(entry, &dummy_entry);
+
+ ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
+ isc_rwlocktype_write);
+
+ acache->stats.adds++;
+ UNLOCK(&acache->lock);
+
+ return (ISC_R_SUCCESS);
+
+ fail:
+ clear_entry(acache, entry);
+
+ ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
+ isc_rwlocktype_write);
+ UNLOCK(&acache->lock);
+
+ return (result);
+}
+
+void
+dns_acache_cancelentry(dns_acacheentry_t *entry) {
+ dns_acache_t *acache = entry->acache;
+
+ REQUIRE(DNS_ACACHEENTRY_VALID(entry));
+ INSIST(DNS_ACACHE_VALID(acache));
+
+ LOCK(&acache->lock);
+ ACACHE_LOCK(&acache->entrylocks[entry->locknum], isc_rwlocktype_write);
+
+ /*
+ * Release dependencies stored in this entry as much as possible.
+ * The main link cannot be released, since the acache object has
+ * a reference to this entry; the empty entry will be released in
+ * the next cleaning action.
+ */
+ unlink_dbentries(acache, entry);
+ clear_entry(entry->acache, entry);
+
+ entry->callback = NULL;
+ entry->cbarg = NULL;
+
+ ACACHE_UNLOCK(&acache->entrylocks[entry->locknum],
+ isc_rwlocktype_write);
+ UNLOCK(&acache->lock);
+}
+
+void
+dns_acache_attachentry(dns_acacheentry_t *source,
+ dns_acacheentry_t **targetp)
+{
+ REQUIRE(DNS_ACACHEENTRY_VALID(source));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ isc_refcount_increment(&source->references, NULL);
+
+ *targetp = source;
+}
+
+void
+dns_acache_detachentry(dns_acacheentry_t **entryp) {
+ dns_acacheentry_t *entry;
+ unsigned int refs;
+
+ REQUIRE(entryp != NULL && DNS_ACACHEENTRY_VALID(*entryp));
+ entry = *entryp;
+
+ isc_refcount_decrement(&entry->references, &refs);
+
+ /*
+ * If there are no references to the entry, the entry must have been
+ * unlinked and can be destroyed safely.
+ */
+ if (refs == 0) {
+ INSIST(!ISC_LINK_LINKED(entry, link));
+ (*entryp)->acache->stats.deleted++;
+ destroy_entry(entry);
+ }
+
+ *entryp = NULL;
+}
+
+void
+dns_acache_setcleaninginterval(dns_acache_t *acache, unsigned int t) {
+ isc_interval_t interval;
+ isc_result_t result;
+
+ REQUIRE(DNS_ACACHE_VALID(acache));
+
+ ATRACE("dns_acache_setcleaninginterval");
+
+ LOCK(&acache->lock);
+
+ /*
+ * It may be the case that the acache has already shut down.
+ * If so, it has no timer. (Not sure if this can really happen.)
+ */
+ if (acache->cleaner.cleaning_timer == NULL)
+ goto unlock;
+
+ acache->cleaner.cleaning_interval = t;
+
+ if (t == 0) {
+ result = isc_timer_reset(acache->cleaner.cleaning_timer,
+ isc_timertype_inactive,
+ NULL, NULL, ISC_TRUE);
+ } else {
+ isc_interval_set(&interval, acache->cleaner.cleaning_interval,
+ 0);
+ result = isc_timer_reset(acache->cleaner.cleaning_timer,
+ isc_timertype_ticker,
+ NULL, &interval, ISC_FALSE);
+ }
+ if (result != ISC_R_SUCCESS)
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_ACACHE, ISC_LOG_WARNING,
+ "could not set acache cleaning interval: %s",
+ isc_result_totext(result));
+ else
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_ACACHE, ISC_LOG_NOTICE,
+ "acache %p cleaning interval set to %d.",
+ acache, t);
+
+ unlock:
+ UNLOCK(&acache->lock);
+}
+
+/*
+ * This function was derived from cache.c:dns_cache_setcachesize(). See the
+ * function for more details about the logic.
+ */
+void
+dns_acache_setcachesize(dns_acache_t *acache, isc_uint32_t size) {
+ isc_uint32_t lowater;
+ isc_uint32_t hiwater;
+
+ REQUIRE(DNS_ACACHE_VALID(acache));
+
+ if (size != 0 && size < DNS_ACACHE_MINSIZE)
+ size = DNS_ACACHE_MINSIZE;
+
+ hiwater = size - (size >> 3);
+ lowater = size - (size >> 2);
+
+ if (size == 0 || hiwater == 0 || lowater == 0)
+ isc_mem_setwater(acache->mctx, water, acache, 0, 0);
+ else
+ isc_mem_setwater(acache->mctx, water, acache,
+ hiwater, lowater);
+}
diff --git a/lib/dns/acl.c b/lib/dns/acl.c
new file mode 100644
index 0000000..ed4aab9
--- /dev/null
+++ b/lib/dns/acl.c
@@ -0,0 +1,623 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: acl.c,v 1.50 2008/09/26 23:47:06 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/mem.h>
+#include <isc/once.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/acl.h>
+#include <dns/iptable.h>
+
+/*
+ * Create a new ACL, including an IP table and an array with room
+ * for 'n' ACL elements. The elements are uninitialized and the
+ * length is 0.
+ */
+isc_result_t
+dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target) {
+ isc_result_t result;
+ dns_acl_t *acl;
+
+ /*
+ * Work around silly limitation of isc_mem_get().
+ */
+ if (n == 0)
+ n = 1;
+
+ acl = isc_mem_get(mctx, sizeof(*acl));
+ if (acl == NULL)
+ return (ISC_R_NOMEMORY);
+ acl->mctx = mctx;
+ acl->name = NULL;
+
+ result = isc_refcount_init(&acl->refcount, 1);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, acl, sizeof(*acl));
+ return (result);
+ }
+
+ result = dns_iptable_create(mctx, &acl->iptable);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, acl, sizeof(*acl));
+ return (result);
+ }
+
+ acl->elements = NULL;
+ acl->alloc = 0;
+ acl->length = 0;
+ acl->has_negatives = ISC_FALSE;
+
+ ISC_LINK_INIT(acl, nextincache);
+ /*
+ * Must set magic early because we use dns_acl_detach() to clean up.
+ */
+ acl->magic = DNS_ACL_MAGIC;
+
+ acl->elements = isc_mem_get(mctx, n * sizeof(dns_aclelement_t));
+ if (acl->elements == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ acl->alloc = n;
+ memset(acl->elements, 0, n * sizeof(dns_aclelement_t));
+ *target = acl;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ dns_acl_detach(&acl);
+ return (result);
+}
+
+/*
+ * Create a new ACL and initialize it with the value "any" or "none",
+ * depending on the value of the "neg" parameter.
+ * "any" is a positive iptable entry with bit length 0.
+ * "none" is the same as "!any".
+ */
+static isc_result_t
+dns_acl_anyornone(isc_mem_t *mctx, isc_boolean_t neg, dns_acl_t **target) {
+ isc_result_t result;
+ dns_acl_t *acl = NULL;
+ result = dns_acl_create(mctx, 0, &acl);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_iptable_addprefix(acl->iptable, NULL, 0, ISC_TF(!neg));
+ if (result != ISC_R_SUCCESS) {
+ dns_acl_detach(&acl);
+ return (result);
+ }
+
+ *target = acl;
+ return (result);
+}
+
+/*
+ * Create a new ACL that matches everything.
+ */
+isc_result_t
+dns_acl_any(isc_mem_t *mctx, dns_acl_t **target) {
+ return (dns_acl_anyornone(mctx, ISC_FALSE, target));
+}
+
+/*
+ * Create a new ACL that matches nothing.
+ */
+isc_result_t
+dns_acl_none(isc_mem_t *mctx, dns_acl_t **target) {
+ return (dns_acl_anyornone(mctx, ISC_TRUE, target));
+}
+
+/*
+ * If pos is ISC_TRUE, test whether acl is set to "{ any; }"
+ * If pos is ISC_FALSE, test whether acl is set to "{ none; }"
+ */
+static isc_boolean_t
+dns_acl_isanyornone(dns_acl_t *acl, isc_boolean_t pos)
+{
+ /* Should never happen but let's be safe */
+ if (acl == NULL ||
+ acl->iptable == NULL ||
+ acl->iptable->radix == NULL ||
+ acl->iptable->radix->head == NULL ||
+ acl->iptable->radix->head->prefix == NULL)
+ return (ISC_FALSE);
+
+ if (acl->length != 0 || acl->node_count != 1)
+ return (ISC_FALSE);
+
+ if (acl->iptable->radix->head->prefix->bitlen == 0 &&
+ acl->iptable->radix->head->data[0] != NULL &&
+ acl->iptable->radix->head->data[0] ==
+ acl->iptable->radix->head->data[1] &&
+ *(isc_boolean_t *) (acl->iptable->radix->head->data[0]) == pos)
+ return (ISC_TRUE);
+
+ return (ISC_FALSE); /* All others */
+}
+
+/*
+ * Test whether acl is set to "{ any; }"
+ */
+isc_boolean_t
+dns_acl_isany(dns_acl_t *acl)
+{
+ return (dns_acl_isanyornone(acl, ISC_TRUE));
+}
+
+/*
+ * Test whether acl is set to "{ none; }"
+ */
+isc_boolean_t
+dns_acl_isnone(dns_acl_t *acl)
+{
+ return (dns_acl_isanyornone(acl, ISC_FALSE));
+}
+
+/*
+ * Determine whether a given address or signer matches a given ACL.
+ * For a match with a positive ACL element or iptable radix entry,
+ * return with a positive value in match; for a match with a negated ACL
+ * element or radix entry, return with a negative value in match.
+ */
+isc_result_t
+dns_acl_match(const isc_netaddr_t *reqaddr,
+ const dns_name_t *reqsigner,
+ const dns_acl_t *acl,
+ const dns_aclenv_t *env,
+ int *match,
+ const dns_aclelement_t **matchelt)
+{
+ isc_uint16_t bitlen, family;
+ isc_prefix_t pfx;
+ isc_radix_node_t *node;
+ const isc_netaddr_t *addr;
+ isc_netaddr_t v4addr;
+ isc_result_t result;
+ int match_num = -1;
+ unsigned int i;
+
+ REQUIRE(reqaddr != NULL);
+ REQUIRE(matchelt == NULL || *matchelt == NULL);
+
+ if (env == NULL || env->match_mapped == ISC_FALSE ||
+ reqaddr->family != AF_INET6 ||
+ !IN6_IS_ADDR_V4MAPPED(&reqaddr->type.in6))
+ addr = reqaddr;
+ else {
+ isc_netaddr_fromv4mapped(&v4addr, reqaddr);
+ addr = &v4addr;
+ }
+
+ /* Always match with host addresses. */
+ family = addr->family;
+ bitlen = family == AF_INET6 ? 128 : 32;
+ NETADDR_TO_PREFIX_T(addr, pfx, bitlen);
+
+ /* Assume no match. */
+ *match = 0;
+
+ /* Search radix. */
+ result = isc_radix_search(acl->iptable->radix, &node, &pfx);
+
+ /* Found a match. */
+ if (result == ISC_R_SUCCESS && node != NULL) {
+ match_num = node->node_num[ISC_IS6(family)];
+ if (*(isc_boolean_t *) node->data[ISC_IS6(family)] == ISC_TRUE)
+ *match = match_num;
+ else
+ *match = -match_num;
+ }
+
+ /* Now search non-radix elements for a match with a lower node_num. */
+ for (i = 0; i < acl->length; i++) {
+ dns_aclelement_t *e = &acl->elements[i];
+
+ /* Already found a better match? */
+ if (match_num != -1 && match_num < e->node_num) {
+ isc_refcount_destroy(&pfx.refcount);
+ return (ISC_R_SUCCESS);
+ }
+
+ if (dns_aclelement_match(reqaddr, reqsigner,
+ e, env, matchelt)) {
+ if (match_num == -1 || e->node_num < match_num) {
+ if (e->negative == ISC_TRUE)
+ *match = -e->node_num;
+ else
+ *match = e->node_num;
+ }
+ isc_refcount_destroy(&pfx.refcount);
+ return (ISC_R_SUCCESS);
+ }
+ }
+
+ isc_refcount_destroy(&pfx.refcount);
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Merge the contents of one ACL into another. Call dns_iptable_merge()
+ * for the IP tables, then concatenate the element arrays.
+ *
+ * If pos is set to false, then the nested ACL is to be negated. This
+ * means reverse the sense of each *positive* element or IP table node,
+ * but leave negatives alone, so as to prevent a double-negative causing
+ * an unexpected postive match in the parent ACL.
+ */
+isc_result_t
+dns_acl_merge(dns_acl_t *dest, dns_acl_t *source, isc_boolean_t pos)
+{
+ isc_result_t result;
+ unsigned int newalloc, nelem, i;
+ int max_node = 0, nodes;
+
+ /* Resize the element array if needed. */
+ if (dest->length + source->length > dest->alloc) {
+ void *newmem;
+
+ newalloc = dest->alloc + source->alloc;
+ if (newalloc < 4)
+ newalloc = 4;
+
+ newmem = isc_mem_get(dest->mctx,
+ newalloc * sizeof(dns_aclelement_t));
+ if (newmem == NULL)
+ return (ISC_R_NOMEMORY);
+
+ /* Copy in the original elements */
+ memcpy(newmem, dest->elements,
+ dest->length * sizeof(dns_aclelement_t));
+
+ /* Release the memory for the old elements array */
+ isc_mem_put(dest->mctx, dest->elements,
+ dest->alloc * sizeof(dns_aclelement_t));
+ dest->elements = newmem;
+ dest->alloc = newalloc;
+ }
+
+ /*
+ * Now copy in the new elements, increasing their node_num
+ * values so as to keep the new ACL consistent. If we're
+ * negating, then negate positive elements, but keep negative
+ * elements the same for security reasons.
+ */
+ nelem = dest->length;
+ dest->length += source->length;
+ for (i = 0; i < source->length; i++) {
+ if (source->elements[i].node_num > max_node)
+ max_node = source->elements[i].node_num;
+
+ /* Copy type. */
+ dest->elements[nelem + i].type = source->elements[i].type;
+
+ /* Adjust node numbering. */
+ dest->elements[nelem + i].node_num =
+ source->elements[i].node_num + dest->node_count;
+
+ /* Duplicate nested acl. */
+ if (source->elements[i].type == dns_aclelementtype_nestedacl &&
+ source->elements[i].nestedacl != NULL)
+ dns_acl_attach(source->elements[i].nestedacl,
+ &dest->elements[nelem + i].nestedacl);
+
+ /* Duplicate key name. */
+ if (source->elements[i].type == dns_aclelementtype_keyname) {
+ dns_name_init(&dest->elements[nelem+i].keyname, NULL);
+ result = dns_name_dup(&source->elements[i].keyname,
+ dest->mctx,
+ &dest->elements[nelem+i].keyname);
+ if (result != ISC_R_SUCCESS)
+ return result;
+ }
+
+ /* reverse sense of positives if this is a negative acl */
+ if (!pos && source->elements[i].negative == ISC_FALSE) {
+ dest->elements[nelem + i].negative = ISC_TRUE;
+ } else {
+ dest->elements[nelem + i].negative =
+ source->elements[i].negative;
+ }
+ }
+
+
+ /*
+ * Merge the iptables. Make sure the destination ACL's
+ * node_count value is set correctly afterward.
+ */
+ nodes = max_node + dest->node_count;
+ result = dns_iptable_merge(dest->iptable, source->iptable, pos);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (nodes > dest->node_count)
+ dest->node_count = nodes;
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Like dns_acl_match, but matches against the single ACL element 'e'
+ * rather than a complete ACL, and returns ISC_TRUE iff it matched.
+ *
+ * To determine whether the match was prositive or negative, the
+ * caller should examine e->negative. Since the element 'e' may be
+ * a reference to a named ACL or a nested ACL, a matching element
+ * returned through 'matchelt' is not necessarily 'e' itself.
+ */
+isc_boolean_t
+dns_aclelement_match(const isc_netaddr_t *reqaddr,
+ const dns_name_t *reqsigner,
+ const dns_aclelement_t *e,
+ const dns_aclenv_t *env,
+ const dns_aclelement_t **matchelt)
+{
+ dns_acl_t *inner = NULL;
+ int indirectmatch;
+ isc_result_t result;
+
+ switch (e->type) {
+ case dns_aclelementtype_keyname:
+ if (reqsigner != NULL &&
+ dns_name_equal(reqsigner, &e->keyname)) {
+ if (matchelt != NULL)
+ *matchelt = e;
+ return (ISC_TRUE);
+ } else {
+ return (ISC_FALSE);
+ }
+
+ case dns_aclelementtype_nestedacl:
+ inner = e->nestedacl;
+ break;
+
+ case dns_aclelementtype_localhost:
+ if (env == NULL || env->localhost == NULL)
+ return (ISC_FALSE);
+ inner = env->localhost;
+ break;
+
+ case dns_aclelementtype_localnets:
+ if (env == NULL || env->localnets == NULL)
+ return (ISC_FALSE);
+ inner = env->localnets;
+ break;
+
+ default:
+ /* Should be impossible. */
+ INSIST(0);
+ }
+
+ result = dns_acl_match(reqaddr, reqsigner, inner, env,
+ &indirectmatch, matchelt);
+ INSIST(result == ISC_R_SUCCESS);
+
+ /*
+ * Treat negative matches in indirect ACLs as "no match".
+ * That way, a negated indirect ACL will never become a
+ * surprise positive match through double negation.
+ * XXXDCL this should be documented.
+ */
+
+ if (indirectmatch > 0) {
+ if (matchelt != NULL)
+ *matchelt = e;
+ return (ISC_TRUE);
+ }
+
+ /*
+ * A negative indirect match may have set *matchelt, but we don't
+ * want it set when we return.
+ */
+
+ if (matchelt != NULL)
+ *matchelt = NULL;
+
+ return (ISC_FALSE);
+}
+
+void
+dns_acl_attach(dns_acl_t *source, dns_acl_t **target) {
+ REQUIRE(DNS_ACL_VALID(source));
+ isc_refcount_increment(&source->refcount, NULL);
+ *target = source;
+}
+
+static void
+destroy(dns_acl_t *dacl) {
+ unsigned int i;
+ for (i = 0; i < dacl->length; i++) {
+ dns_aclelement_t *de = &dacl->elements[i];
+ if (de->type == dns_aclelementtype_keyname) {
+ dns_name_free(&de->keyname, dacl->mctx);
+ } else if (de->type == dns_aclelementtype_nestedacl) {
+ dns_acl_detach(&de->nestedacl);
+ }
+ }
+ if (dacl->elements != NULL)
+ isc_mem_put(dacl->mctx, dacl->elements,
+ dacl->alloc * sizeof(dns_aclelement_t));
+ if (dacl->name != NULL)
+ isc_mem_free(dacl->mctx, dacl->name);
+ if (dacl->iptable != NULL)
+ dns_iptable_detach(&dacl->iptable);
+ isc_refcount_destroy(&dacl->refcount);
+ dacl->magic = 0;
+ isc_mem_put(dacl->mctx, dacl, sizeof(*dacl));
+}
+
+void
+dns_acl_detach(dns_acl_t **aclp) {
+ dns_acl_t *acl = *aclp;
+ unsigned int refs;
+ REQUIRE(DNS_ACL_VALID(acl));
+ isc_refcount_decrement(&acl->refcount, &refs);
+ if (refs == 0)
+ destroy(acl);
+ *aclp = NULL;
+}
+
+
+static isc_once_t insecure_prefix_once = ISC_ONCE_INIT;
+static isc_mutex_t insecure_prefix_lock;
+static isc_boolean_t insecure_prefix_found;
+
+static void
+initialize_action(void) {
+ RUNTIME_CHECK(isc_mutex_init(&insecure_prefix_lock) == ISC_R_SUCCESS);
+}
+
+/*
+ * Called via isc_radix_walk() to find IP table nodes that are
+ * insecure.
+ */
+static void
+is_insecure(isc_prefix_t *prefix, void **data) {
+ isc_boolean_t secure;
+ int bitlen, family;
+
+ bitlen = prefix->bitlen;
+ family = prefix->family;
+
+ /* Negated entries are always secure. */
+ secure = * (isc_boolean_t *)data[ISC_IS6(family)];
+ if (!secure) {
+ return;
+ }
+
+ /* If loopback prefix found, return */
+ switch (family) {
+ case AF_INET:
+ if (bitlen == 32 &&
+ htonl(prefix->add.sin.s_addr) == INADDR_LOOPBACK)
+ return;
+ break;
+ case AF_INET6:
+ if (bitlen == 128 && IN6_IS_ADDR_LOOPBACK(&prefix->add.sin6))
+ return;
+ break;
+ default:
+ break;
+ }
+
+ /* Non-negated, non-loopback */
+ insecure_prefix_found = ISC_TRUE; /* LOCKED */
+ return;
+}
+
+/*
+ * Return ISC_TRUE iff the acl 'a' is considered insecure, that is,
+ * if it contains IP addresses other than those of the local host.
+ * This is intended for applications such as printing warning
+ * messages for suspect ACLs; it is not intended for making access
+ * control decisions. We make no guarantee that an ACL for which
+ * this function returns ISC_FALSE is safe.
+ */
+isc_boolean_t
+dns_acl_isinsecure(const dns_acl_t *a) {
+ unsigned int i;
+ isc_boolean_t insecure;
+
+ RUNTIME_CHECK(isc_once_do(&insecure_prefix_once,
+ initialize_action) == ISC_R_SUCCESS);
+
+ /*
+ * Walk radix tree to find out if there are any non-negated,
+ * non-loopback prefixes.
+ */
+ LOCK(&insecure_prefix_lock);
+ insecure_prefix_found = ISC_FALSE;
+ isc_radix_process(a->iptable->radix, is_insecure);
+ insecure = insecure_prefix_found;
+ UNLOCK(&insecure_prefix_lock);
+ if (insecure)
+ return(ISC_TRUE);
+
+ /* Now check non-radix elements */
+ for (i = 0; i < a->length; i++) {
+ dns_aclelement_t *e = &a->elements[i];
+
+ /* A negated match can never be insecure. */
+ if (e->negative)
+ continue;
+
+ switch (e->type) {
+ case dns_aclelementtype_keyname:
+ case dns_aclelementtype_localhost:
+ continue;
+
+ case dns_aclelementtype_nestedacl:
+ if (dns_acl_isinsecure(e->nestedacl))
+ return (ISC_TRUE);
+ continue;
+
+ case dns_aclelementtype_localnets:
+ return (ISC_TRUE);
+
+ default:
+ INSIST(0);
+ return (ISC_TRUE);
+ }
+ }
+
+ /* No insecure elements were found. */
+ return (ISC_FALSE);
+}
+
+/*
+ * Initialize ACL environment, setting up localhost and localnets ACLs
+ */
+isc_result_t
+dns_aclenv_init(isc_mem_t *mctx, dns_aclenv_t *env) {
+ isc_result_t result;
+ env->localhost = NULL;
+ env->localnets = NULL;
+ result = dns_acl_create(mctx, 0, &env->localhost);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_nothing;
+ result = dns_acl_create(mctx, 0, &env->localnets);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_localhost;
+ env->match_mapped = ISC_FALSE;
+ return (ISC_R_SUCCESS);
+
+ cleanup_localhost:
+ dns_acl_detach(&env->localhost);
+ cleanup_nothing:
+ return (result);
+}
+
+void
+dns_aclenv_copy(dns_aclenv_t *t, dns_aclenv_t *s) {
+ dns_acl_detach(&t->localhost);
+ dns_acl_attach(s->localhost, &t->localhost);
+ dns_acl_detach(&t->localnets);
+ dns_acl_attach(s->localnets, &t->localnets);
+ t->match_mapped = s->match_mapped;
+}
+
+void
+dns_aclenv_destroy(dns_aclenv_t *env) {
+ dns_acl_detach(&env->localhost);
+ dns_acl_detach(&env->localnets);
+}
diff --git a/lib/dns/adb.c b/lib/dns/adb.c
new file mode 100644
index 0000000..a41515c
--- /dev/null
+++ b/lib/dns/adb.c
@@ -0,0 +1,3621 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: adb.c,v 1.243 2008/10/17 03:23:13 marka Exp $ */
+
+/*! \file
+ *
+ * \note
+ * In finds, if task == NULL, no events will be generated, and no events
+ * have been sent. If task != NULL but taskaction == NULL, an event has been
+ * posted but not yet freed. If neither are NULL, no event was posted.
+ *
+ */
+
+#include <config.h>
+
+#include <limits.h>
+
+#include <isc/mutexblock.h>
+#include <isc/netaddr.h>
+#include <isc/random.h>
+#include <isc/string.h> /* Required for HP/UX (and others?) */
+#include <isc/task.h>
+#include <isc/util.h>
+
+#include <dns/adb.h>
+#include <dns/db.h>
+#include <dns/events.h>
+#include <dns/log.h>
+#include <dns/rdata.h>
+#include <dns/rdataset.h>
+#include <dns/rdatastruct.h>
+#include <dns/rdatatype.h>
+#include <dns/resolver.h>
+#include <dns/result.h>
+#include <dns/stats.h>
+
+#define DNS_ADB_MAGIC ISC_MAGIC('D', 'a', 'd', 'b')
+#define DNS_ADB_VALID(x) ISC_MAGIC_VALID(x, DNS_ADB_MAGIC)
+#define DNS_ADBNAME_MAGIC ISC_MAGIC('a', 'd', 'b', 'N')
+#define DNS_ADBNAME_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBNAME_MAGIC)
+#define DNS_ADBNAMEHOOK_MAGIC ISC_MAGIC('a', 'd', 'N', 'H')
+#define DNS_ADBNAMEHOOK_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBNAMEHOOK_MAGIC)
+#define DNS_ADBLAMEINFO_MAGIC ISC_MAGIC('a', 'd', 'b', 'Z')
+#define DNS_ADBLAMEINFO_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBLAMEINFO_MAGIC)
+#define DNS_ADBENTRY_MAGIC ISC_MAGIC('a', 'd', 'b', 'E')
+#define DNS_ADBENTRY_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBENTRY_MAGIC)
+#define DNS_ADBFETCH_MAGIC ISC_MAGIC('a', 'd', 'F', '4')
+#define DNS_ADBFETCH_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBFETCH_MAGIC)
+#define DNS_ADBFETCH6_MAGIC ISC_MAGIC('a', 'd', 'F', '6')
+#define DNS_ADBFETCH6_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBFETCH6_MAGIC)
+
+/*!
+ * The number of buckets needs to be a prime (for good hashing).
+ *
+ * XXXRTH How many buckets do we need?
+ */
+#define NBUCKETS 1009 /*%< how many buckets for names/addrs */
+
+/*!
+ * For type 3 negative cache entries, we will remember that the address is
+ * broken for this long. XXXMLG This is also used for actual addresses, too.
+ * The intent is to keep us from constantly asking about A/AAAA records
+ * if the zone has extremely low TTLs.
+ */
+#define ADB_CACHE_MINIMUM 10 /*%< seconds */
+#define ADB_CACHE_MAXIMUM 86400 /*%< seconds (86400 = 24 hours) */
+#define ADB_ENTRY_WINDOW 1800 /*%< seconds */
+
+/*%
+ * The period in seconds after which an ADB name entry is regarded as stale
+ * and forced to be cleaned up.
+ * TODO: This should probably be configurable at run-time.
+ */
+#ifndef ADB_STALE_MARGIN
+#define ADB_STALE_MARGIN 1800
+#endif
+
+#define FREE_ITEMS 64 /*%< free count for memory pools */
+#define FILL_COUNT 16 /*%< fill count for memory pools */
+
+#define DNS_ADB_INVALIDBUCKET (-1) /*%< invalid bucket address */
+
+#define DNS_ADB_MINADBSIZE (1024*1024) /*%< 1 Megabyte */
+
+typedef ISC_LIST(dns_adbname_t) dns_adbnamelist_t;
+typedef struct dns_adbnamehook dns_adbnamehook_t;
+typedef ISC_LIST(dns_adbnamehook_t) dns_adbnamehooklist_t;
+typedef struct dns_adblameinfo dns_adblameinfo_t;
+typedef ISC_LIST(dns_adbentry_t) dns_adbentrylist_t;
+typedef struct dns_adbfetch dns_adbfetch_t;
+typedef struct dns_adbfetch6 dns_adbfetch6_t;
+
+/*% dns adb structure */
+struct dns_adb {
+ unsigned int magic;
+
+ isc_mutex_t lock;
+ isc_mutex_t reflock; /*%< Covers irefcnt, erefcnt */
+ isc_mutex_t overmemlock; /*%< Covers overmem */
+ isc_mem_t *mctx;
+ dns_view_t *view;
+
+ isc_taskmgr_t *taskmgr;
+ isc_task_t *task;
+ isc_boolean_t overmem;
+
+ isc_interval_t tick_interval;
+ int next_cleanbucket;
+
+ unsigned int irefcnt;
+ unsigned int erefcnt;
+
+ isc_mutex_t mplock;
+ isc_mempool_t *nmp; /*%< dns_adbname_t */
+ isc_mempool_t *nhmp; /*%< dns_adbnamehook_t */
+ isc_mempool_t *limp; /*%< dns_adblameinfo_t */
+ isc_mempool_t *emp; /*%< dns_adbentry_t */
+ isc_mempool_t *ahmp; /*%< dns_adbfind_t */
+ isc_mempool_t *aimp; /*%< dns_adbaddrinfo_t */
+ isc_mempool_t *afmp; /*%< dns_adbfetch_t */
+
+ /*!
+ * Bucketized locks and lists for names.
+ *
+ * XXXRTH Have a per-bucket structure that contains all of these?
+ */
+ dns_adbnamelist_t names[NBUCKETS];
+ /*% See dns_adbnamelist_t */
+ isc_mutex_t namelocks[NBUCKETS];
+ /*% See dns_adbnamelist_t */
+ isc_boolean_t name_sd[NBUCKETS];
+ /*% See dns_adbnamelist_t */
+ unsigned int name_refcnt[NBUCKETS];
+
+ /*!
+ * Bucketized locks for entries.
+ *
+ * XXXRTH Have a per-bucket structure that contains all of these?
+ */
+ dns_adbentrylist_t entries[NBUCKETS];
+ isc_mutex_t entrylocks[NBUCKETS];
+ isc_boolean_t entry_sd[NBUCKETS]; /*%< shutting down */
+ unsigned int entry_refcnt[NBUCKETS];
+
+ isc_event_t cevent;
+ isc_boolean_t cevent_sent;
+ isc_boolean_t shutting_down;
+ isc_eventlist_t whenshutdown;
+};
+
+/*
+ * XXXMLG Document these structures.
+ */
+
+/*% dns_adbname structure */
+struct dns_adbname {
+ unsigned int magic;
+ dns_name_t name;
+ dns_adb_t *adb;
+ unsigned int partial_result;
+ unsigned int flags;
+ int lock_bucket;
+ dns_name_t target;
+ isc_stdtime_t expire_target;
+ isc_stdtime_t expire_v4;
+ isc_stdtime_t expire_v6;
+ unsigned int chains;
+ dns_adbnamehooklist_t v4;
+ dns_adbnamehooklist_t v6;
+ dns_adbfetch_t *fetch_a;
+ dns_adbfetch_t *fetch_aaaa;
+ unsigned int fetch_err;
+ unsigned int fetch6_err;
+ dns_adbfindlist_t finds;
+ /* for LRU-based management */
+ isc_stdtime_t last_used;
+
+ ISC_LINK(dns_adbname_t) plink;
+};
+
+/*% The adbfetch structure */
+struct dns_adbfetch {
+ unsigned int magic;
+ dns_adbnamehook_t *namehook;
+ dns_adbentry_t *entry;
+ dns_fetch_t *fetch;
+ dns_rdataset_t rdataset;
+};
+
+/*%
+ * This is a small widget that dangles off a dns_adbname_t. It contains a
+ * pointer to the address information about this host, and a link to the next
+ * namehook that will contain the next address this host has.
+ */
+struct dns_adbnamehook {
+ unsigned int magic;
+ dns_adbentry_t *entry;
+ ISC_LINK(dns_adbnamehook_t) plink;
+};
+
+/*%
+ * This is a small widget that holds qname-specific information about an
+ * address. Currently limited to lameness, but could just as easily be
+ * extended to other types of information about zones.
+ */
+struct dns_adblameinfo {
+ unsigned int magic;
+
+ dns_name_t qname;
+ dns_rdatatype_t qtype;
+ isc_stdtime_t lame_timer;
+
+ ISC_LINK(dns_adblameinfo_t) plink;
+};
+
+/*%
+ * An address entry. It holds quite a bit of information about addresses,
+ * including edns state (in "flags"), rtt, and of course the address of
+ * the host.
+ */
+struct dns_adbentry {
+ unsigned int magic;
+
+ int lock_bucket;
+ unsigned int refcnt;
+
+ unsigned int flags;
+ unsigned int srtt;
+ isc_sockaddr_t sockaddr;
+
+ isc_stdtime_t expires;
+ /*%<
+ * A nonzero 'expires' field indicates that the entry should
+ * persist until that time. This allows entries found
+ * using dns_adb_findaddrinfo() to persist for a limited time
+ * even though they are not necessarily associated with a
+ * name.
+ */
+
+ ISC_LIST(dns_adblameinfo_t) lameinfo;
+ ISC_LINK(dns_adbentry_t) plink;
+};
+
+/*
+ * Internal functions (and prototypes).
+ */
+static inline dns_adbname_t *new_adbname(dns_adb_t *, dns_name_t *);
+static inline void free_adbname(dns_adb_t *, dns_adbname_t **);
+static inline dns_adbnamehook_t *new_adbnamehook(dns_adb_t *,
+ dns_adbentry_t *);
+static inline void free_adbnamehook(dns_adb_t *, dns_adbnamehook_t **);
+static inline dns_adblameinfo_t *new_adblameinfo(dns_adb_t *, dns_name_t *,
+ dns_rdatatype_t);
+static inline void free_adblameinfo(dns_adb_t *, dns_adblameinfo_t **);
+static inline dns_adbentry_t *new_adbentry(dns_adb_t *);
+static inline void free_adbentry(dns_adb_t *, dns_adbentry_t **);
+static inline dns_adbfind_t *new_adbfind(dns_adb_t *);
+static inline isc_boolean_t free_adbfind(dns_adb_t *, dns_adbfind_t **);
+static inline dns_adbaddrinfo_t *new_adbaddrinfo(dns_adb_t *, dns_adbentry_t *,
+ in_port_t);
+static inline dns_adbfetch_t *new_adbfetch(dns_adb_t *);
+static inline void free_adbfetch(dns_adb_t *, dns_adbfetch_t **);
+static inline dns_adbname_t *find_name_and_lock(dns_adb_t *, dns_name_t *,
+ unsigned int, int *);
+static inline dns_adbentry_t *find_entry_and_lock(dns_adb_t *,
+ isc_sockaddr_t *, int *);
+static void dump_adb(dns_adb_t *, FILE *, isc_boolean_t debug, isc_stdtime_t);
+static void print_dns_name(FILE *, dns_name_t *);
+static void print_namehook_list(FILE *, const char *legend,
+ dns_adbnamehooklist_t *list,
+ isc_boolean_t debug,
+ isc_stdtime_t now);
+static void print_find_list(FILE *, dns_adbname_t *);
+static void print_fetch_list(FILE *, dns_adbname_t *);
+static inline isc_boolean_t dec_adb_irefcnt(dns_adb_t *);
+static inline void inc_adb_irefcnt(dns_adb_t *);
+static inline void inc_adb_erefcnt(dns_adb_t *);
+static inline void inc_entry_refcnt(dns_adb_t *, dns_adbentry_t *,
+ isc_boolean_t);
+static inline isc_boolean_t dec_entry_refcnt(dns_adb_t *, dns_adbentry_t *,
+ isc_boolean_t);
+static inline void violate_locking_hierarchy(isc_mutex_t *, isc_mutex_t *);
+static isc_boolean_t clean_namehooks(dns_adb_t *, dns_adbnamehooklist_t *,
+ isc_boolean_t);
+static void clean_target(dns_adb_t *, dns_name_t *);
+static void clean_finds_at_name(dns_adbname_t *, isc_eventtype_t,
+ unsigned int);
+static isc_boolean_t check_expire_namehooks(dns_adbname_t *, isc_stdtime_t);
+static void cancel_fetches_at_name(dns_adbname_t *);
+static isc_result_t dbfind_name(dns_adbname_t *, isc_stdtime_t,
+ dns_rdatatype_t);
+static isc_result_t fetch_name(dns_adbname_t *, isc_boolean_t,
+ dns_rdatatype_t);
+static inline void check_exit(dns_adb_t *);
+static void destroy(dns_adb_t *);
+static isc_boolean_t shutdown_names(dns_adb_t *);
+static isc_boolean_t shutdown_entries(dns_adb_t *);
+static inline void link_name(dns_adb_t *, int, dns_adbname_t *);
+static inline isc_boolean_t unlink_name(dns_adb_t *, dns_adbname_t *);
+static inline void link_entry(dns_adb_t *, int, dns_adbentry_t *);
+static inline isc_boolean_t unlink_entry(dns_adb_t *, dns_adbentry_t *);
+static isc_boolean_t kill_name(dns_adbname_t **, isc_eventtype_t,
+ isc_boolean_t);
+static void water(void *, int);
+static void dump_entry(FILE *, dns_adbentry_t *, isc_boolean_t, isc_stdtime_t);
+
+/*
+ * MUST NOT overlap DNS_ADBFIND_* flags!
+ */
+#define FIND_EVENT_SENT 0x40000000
+#define FIND_EVENT_FREED 0x80000000
+#define FIND_EVENTSENT(h) (((h)->flags & FIND_EVENT_SENT) != 0)
+#define FIND_EVENTFREED(h) (((h)->flags & FIND_EVENT_FREED) != 0)
+
+#define NAME_NEEDS_POKE 0x80000000
+#define NAME_IS_DEAD 0x40000000
+#define NAME_HINT_OK DNS_ADBFIND_HINTOK
+#define NAME_GLUE_OK DNS_ADBFIND_GLUEOK
+#define NAME_STARTATZONE DNS_ADBFIND_STARTATZONE
+#define NAME_DEAD(n) (((n)->flags & NAME_IS_DEAD) != 0)
+#define NAME_NEEDSPOKE(n) (((n)->flags & NAME_NEEDS_POKE) != 0)
+#define NAME_GLUEOK(n) (((n)->flags & NAME_GLUE_OK) != 0)
+#define NAME_HINTOK(n) (((n)->flags & NAME_HINT_OK) != 0)
+
+/*
+ * To the name, address classes are all that really exist. If it has a
+ * V6 address it doesn't care if it came from a AAAA query.
+ */
+#define NAME_HAS_V4(n) (!ISC_LIST_EMPTY((n)->v4))
+#define NAME_HAS_V6(n) (!ISC_LIST_EMPTY((n)->v6))
+#define NAME_HAS_ADDRS(n) (NAME_HAS_V4(n) || NAME_HAS_V6(n))
+
+/*
+ * Fetches are broken out into A and AAAA types. In some cases,
+ * however, it makes more sense to test for a particular class of fetches,
+ * like V4 or V6 above.
+ * Note: since we have removed the support of A6 in adb, FETCH_A and FETCH_AAAA
+ * are now equal to FETCH_V4 and FETCH_V6, respectively.
+ */
+#define NAME_FETCH_A(n) ((n)->fetch_a != NULL)
+#define NAME_FETCH_AAAA(n) ((n)->fetch_aaaa != NULL)
+#define NAME_FETCH_V4(n) (NAME_FETCH_A(n))
+#define NAME_FETCH_V6(n) (NAME_FETCH_AAAA(n))
+#define NAME_FETCH(n) (NAME_FETCH_V4(n) || NAME_FETCH_V6(n))
+
+/*
+ * Find options and tests to see if there are addresses on the list.
+ */
+#define FIND_WANTEVENT(fn) (((fn)->options & DNS_ADBFIND_WANTEVENT) != 0)
+#define FIND_WANTEMPTYEVENT(fn) (((fn)->options & DNS_ADBFIND_EMPTYEVENT) != 0)
+#define FIND_AVOIDFETCHES(fn) (((fn)->options & DNS_ADBFIND_AVOIDFETCHES) \
+ != 0)
+#define FIND_STARTATZONE(fn) (((fn)->options & DNS_ADBFIND_STARTATZONE) \
+ != 0)
+#define FIND_HINTOK(fn) (((fn)->options & DNS_ADBFIND_HINTOK) != 0)
+#define FIND_GLUEOK(fn) (((fn)->options & DNS_ADBFIND_GLUEOK) != 0)
+#define FIND_HAS_ADDRS(fn) (!ISC_LIST_EMPTY((fn)->list))
+#define FIND_RETURNLAME(fn) (((fn)->options & DNS_ADBFIND_RETURNLAME) != 0)
+
+/*
+ * These are currently used on simple unsigned ints, so they are
+ * not really associated with any particular type.
+ */
+#define WANT_INET(x) (((x) & DNS_ADBFIND_INET) != 0)
+#define WANT_INET6(x) (((x) & DNS_ADBFIND_INET6) != 0)
+
+#define EXPIRE_OK(exp, now) ((exp == INT_MAX) || (exp < now))
+
+/*
+ * Find out if the flags on a name (nf) indicate if it is a hint or
+ * glue, and compare this to the appropriate bits set in o, to see if
+ * this is ok.
+ */
+#define GLUE_OK(nf, o) (!NAME_GLUEOK(nf) || (((o) & DNS_ADBFIND_GLUEOK) != 0))
+#define HINT_OK(nf, o) (!NAME_HINTOK(nf) || (((o) & DNS_ADBFIND_HINTOK) != 0))
+#define GLUEHINT_OK(nf, o) (GLUE_OK(nf, o) || HINT_OK(nf, o))
+#define STARTATZONE_MATCHES(nf, o) (((nf)->flags & NAME_STARTATZONE) == \
+ ((o) & DNS_ADBFIND_STARTATZONE))
+
+#define ENTER_LEVEL ISC_LOG_DEBUG(50)
+#define EXIT_LEVEL ENTER_LEVEL
+#define CLEAN_LEVEL ISC_LOG_DEBUG(100)
+#define DEF_LEVEL ISC_LOG_DEBUG(5)
+#define NCACHE_LEVEL ISC_LOG_DEBUG(20)
+
+#define NCACHE_RESULT(r) ((r) == DNS_R_NCACHENXDOMAIN || \
+ (r) == DNS_R_NCACHENXRRSET)
+#define AUTH_NX(r) ((r) == DNS_R_NXDOMAIN || \
+ (r) == DNS_R_NXRRSET)
+#define NXDOMAIN_RESULT(r) ((r) == DNS_R_NXDOMAIN || \
+ (r) == DNS_R_NCACHENXDOMAIN)
+#define NXRRSET_RESULT(r) ((r) == DNS_R_NCACHENXRRSET || \
+ (r) == DNS_R_NXRRSET || \
+ (r) == DNS_R_HINTNXRRSET)
+
+/*
+ * Error state rankings.
+ */
+
+#define FIND_ERR_SUCCESS 0 /* highest rank */
+#define FIND_ERR_CANCELED 1
+#define FIND_ERR_FAILURE 2
+#define FIND_ERR_NXDOMAIN 3
+#define FIND_ERR_NXRRSET 4
+#define FIND_ERR_UNEXPECTED 5
+#define FIND_ERR_NOTFOUND 6
+#define FIND_ERR_MAX 7
+
+static const char *errnames[] = {
+ "success",
+ "canceled",
+ "failure",
+ "nxdomain",
+ "nxrrset",
+ "unexpected",
+ "not_found"
+};
+
+#define NEWERR(old, new) (ISC_MIN((old), (new)))
+
+static isc_result_t find_err_map[FIND_ERR_MAX] = {
+ ISC_R_SUCCESS,
+ ISC_R_CANCELED,
+ ISC_R_FAILURE,
+ DNS_R_NXDOMAIN,
+ DNS_R_NXRRSET,
+ ISC_R_UNEXPECTED,
+ ISC_R_NOTFOUND /* not YET found */
+};
+
+static void
+DP(int level, const char *format, ...) ISC_FORMAT_PRINTF(2, 3);
+
+static void
+DP(int level, const char *format, ...) {
+ va_list args;
+
+ va_start(args, format);
+ isc_log_vwrite(dns_lctx,
+ DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ADB,
+ level, format, args);
+ va_end(args);
+}
+
+/*%
+ * Increment resolver-related statistics counters.
+ */
+static inline void
+inc_stats(dns_adb_t *adb, dns_statscounter_t counter) {
+ if (adb->view->resstats != NULL)
+ dns_generalstats_increment(adb->view->resstats, counter);
+}
+
+static inline dns_ttl_t
+ttlclamp(dns_ttl_t ttl) {
+ if (ttl < ADB_CACHE_MINIMUM)
+ ttl = ADB_CACHE_MINIMUM;
+ if (ttl > ADB_CACHE_MAXIMUM)
+ ttl = ADB_CACHE_MAXIMUM;
+
+ return (ttl);
+}
+
+/*
+ * Requires the adbname bucket be locked and that no entry buckets be locked.
+ *
+ * This code handles A and AAAA rdatasets only.
+ */
+static isc_result_t
+import_rdataset(dns_adbname_t *adbname, dns_rdataset_t *rdataset,
+ isc_stdtime_t now)
+{
+ isc_result_t result;
+ dns_adb_t *adb;
+ dns_adbnamehook_t *nh;
+ dns_adbnamehook_t *anh;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ struct in_addr ina;
+ struct in6_addr in6a;
+ isc_sockaddr_t sockaddr;
+ dns_adbentry_t *foundentry; /* NO CLEAN UP! */
+ int addr_bucket;
+ isc_boolean_t new_addresses_added;
+ dns_rdatatype_t rdtype;
+ unsigned int findoptions;
+ dns_adbnamehooklist_t *hookhead;
+
+ INSIST(DNS_ADBNAME_VALID(adbname));
+ adb = adbname->adb;
+ INSIST(DNS_ADB_VALID(adb));
+
+ rdtype = rdataset->type;
+ INSIST((rdtype == dns_rdatatype_a) || (rdtype == dns_rdatatype_aaaa));
+ if (rdtype == dns_rdatatype_a)
+ findoptions = DNS_ADBFIND_INET;
+ else
+ findoptions = DNS_ADBFIND_INET6;
+
+ addr_bucket = DNS_ADB_INVALIDBUCKET;
+ new_addresses_added = ISC_FALSE;
+
+ nh = NULL;
+ result = dns_rdataset_first(rdataset);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(rdataset, &rdata);
+ if (rdtype == dns_rdatatype_a) {
+ INSIST(rdata.length == 4);
+ memcpy(&ina.s_addr, rdata.data, 4);
+ isc_sockaddr_fromin(&sockaddr, &ina, 0);
+ hookhead = &adbname->v4;
+ } else {
+ INSIST(rdata.length == 16);
+ memcpy(in6a.s6_addr, rdata.data, 16);
+ isc_sockaddr_fromin6(&sockaddr, &in6a, 0);
+ hookhead = &adbname->v6;
+ }
+
+ INSIST(nh == NULL);
+ nh = new_adbnamehook(adb, NULL);
+ if (nh == NULL) {
+ adbname->partial_result |= findoptions;
+ result = ISC_R_NOMEMORY;
+ goto fail;
+ }
+
+ foundentry = find_entry_and_lock(adb, &sockaddr, &addr_bucket);
+ if (foundentry == NULL) {
+ dns_adbentry_t *entry;
+
+ entry = new_adbentry(adb);
+ if (entry == NULL) {
+ adbname->partial_result |= findoptions;
+ result = ISC_R_NOMEMORY;
+ goto fail;
+ }
+
+ entry->sockaddr = sockaddr;
+ entry->refcnt = 1;
+
+ nh->entry = entry;
+
+ link_entry(adb, addr_bucket, entry);
+ } else {
+ for (anh = ISC_LIST_HEAD(*hookhead);
+ anh != NULL;
+ anh = ISC_LIST_NEXT(anh, plink))
+ if (anh->entry == foundentry)
+ break;
+ if (anh == NULL) {
+ foundentry->refcnt++;
+ nh->entry = foundentry;
+ } else
+ free_adbnamehook(adb, &nh);
+ }
+
+ new_addresses_added = ISC_TRUE;
+ if (nh != NULL)
+ ISC_LIST_APPEND(*hookhead, nh, plink);
+ nh = NULL;
+ result = dns_rdataset_next(rdataset);
+ }
+
+ fail:
+ if (nh != NULL)
+ free_adbnamehook(adb, &nh);
+
+ if (addr_bucket != DNS_ADB_INVALIDBUCKET)
+ UNLOCK(&adb->entrylocks[addr_bucket]);
+
+ if (rdataset->trust == dns_trust_glue ||
+ rdataset->trust == dns_trust_additional)
+ rdataset->ttl = ADB_CACHE_MINIMUM;
+ else
+ rdataset->ttl = ttlclamp(rdataset->ttl);
+
+ if (rdtype == dns_rdatatype_a) {
+ DP(NCACHE_LEVEL, "expire_v4 set to MIN(%u,%u) import_rdataset",
+ adbname->expire_v4, now + rdataset->ttl);
+ adbname->expire_v4 = ISC_MIN(adbname->expire_v4,
+ now + rdataset->ttl);
+ } else {
+ DP(NCACHE_LEVEL, "expire_v6 set to MIN(%u,%u) import_rdataset",
+ adbname->expire_v6, now + rdataset->ttl);
+ adbname->expire_v6 = ISC_MIN(adbname->expire_v6,
+ now + rdataset->ttl);
+ }
+
+ if (new_addresses_added) {
+ /*
+ * Lie a little here. This is more or less so code that cares
+ * can find out if any new information was added or not.
+ */
+ return (ISC_R_SUCCESS);
+ }
+
+ return (result);
+}
+
+/*
+ * Requires the name's bucket be locked.
+ */
+static isc_boolean_t
+kill_name(dns_adbname_t **n, isc_eventtype_t ev, isc_boolean_t is_purge) {
+ dns_adbname_t *name;
+ isc_boolean_t result = ISC_FALSE;
+ isc_boolean_t result4, result6;
+ dns_adb_t *adb;
+
+ INSIST(n != NULL);
+ name = *n;
+ *n = NULL;
+ INSIST(DNS_ADBNAME_VALID(name));
+ adb = name->adb;
+ INSIST(DNS_ADB_VALID(adb));
+
+ DP(DEF_LEVEL, "killing name %p", name);
+
+ /*
+ * If we're dead already, just check to see if we should go
+ * away now or not.
+ */
+ if (NAME_DEAD(name) && !NAME_FETCH(name)) {
+ result = unlink_name(adb, name);
+ free_adbname(adb, &name);
+ if (result)
+ result = dec_adb_irefcnt(adb);
+ return (result);
+ }
+
+ /*
+ * Clean up the name's various lists. These two are destructive
+ * in that they will always empty the list.
+ */
+ clean_finds_at_name(name, ev, DNS_ADBFIND_ADDRESSMASK);
+ result4 = clean_namehooks(adb, &name->v4, is_purge);
+ result6 = clean_namehooks(adb, &name->v6, is_purge);
+ clean_target(adb, &name->target);
+ result = ISC_TF(result4 || result6);
+
+ /*
+ * If fetches are running, cancel them. If none are running, we can
+ * just kill the name here.
+ */
+ if (!NAME_FETCH(name)) {
+ INSIST(result == ISC_FALSE);
+ result = unlink_name(adb, name);
+ free_adbname(adb, &name);
+ if (result)
+ result = dec_adb_irefcnt(adb);
+ } else {
+ name->flags |= NAME_IS_DEAD;
+ cancel_fetches_at_name(name);
+ }
+ return (result);
+}
+
+/*
+ * Requires the name's bucket be locked and no entry buckets be locked.
+ */
+static isc_boolean_t
+check_expire_namehooks(dns_adbname_t *name, isc_stdtime_t now) {
+ dns_adb_t *adb;
+ isc_boolean_t result4 = ISC_FALSE;
+ isc_boolean_t result6 = ISC_FALSE;
+
+ INSIST(DNS_ADBNAME_VALID(name));
+ adb = name->adb;
+ INSIST(DNS_ADB_VALID(adb));
+
+ /*
+ * Check to see if we need to remove the v4 addresses
+ */
+ if (!NAME_FETCH_V4(name) && EXPIRE_OK(name->expire_v4, now)) {
+ if (NAME_HAS_V4(name)) {
+ DP(DEF_LEVEL, "expiring v4 for name %p", name);
+ result4 = clean_namehooks(adb, &name->v4, ISC_FALSE);
+ name->partial_result &= ~DNS_ADBFIND_INET;
+ }
+ name->expire_v4 = INT_MAX;
+ name->fetch_err = FIND_ERR_UNEXPECTED;
+ }
+
+ /*
+ * Check to see if we need to remove the v6 addresses
+ */
+ if (!NAME_FETCH_V6(name) && EXPIRE_OK(name->expire_v6, now)) {
+ if (NAME_HAS_V6(name)) {
+ DP(DEF_LEVEL, "expiring v6 for name %p", name);
+ result6 = clean_namehooks(adb, &name->v6, ISC_FALSE);
+ name->partial_result &= ~DNS_ADBFIND_INET6;
+ }
+ name->expire_v6 = INT_MAX;
+ name->fetch6_err = FIND_ERR_UNEXPECTED;
+ }
+
+ /*
+ * Check to see if we need to remove the alias target.
+ */
+ if (EXPIRE_OK(name->expire_target, now)) {
+ clean_target(adb, &name->target);
+ name->expire_target = INT_MAX;
+ }
+ return (ISC_TF(result4 || result6));
+}
+
+/*
+ * Requires the name's bucket be locked.
+ */
+static inline void
+link_name(dns_adb_t *adb, int bucket, dns_adbname_t *name) {
+ INSIST(name->lock_bucket == DNS_ADB_INVALIDBUCKET);
+
+ ISC_LIST_PREPEND(adb->names[bucket], name, plink);
+ name->lock_bucket = bucket;
+ adb->name_refcnt[bucket]++;
+}
+
+/*
+ * Requires the name's bucket be locked.
+ */
+static inline isc_boolean_t
+unlink_name(dns_adb_t *adb, dns_adbname_t *name) {
+ int bucket;
+ isc_boolean_t result = ISC_FALSE;
+
+ bucket = name->lock_bucket;
+ INSIST(bucket != DNS_ADB_INVALIDBUCKET);
+
+ ISC_LIST_UNLINK(adb->names[bucket], name, plink);
+ name->lock_bucket = DNS_ADB_INVALIDBUCKET;
+ INSIST(adb->name_refcnt[bucket] > 0);
+ adb->name_refcnt[bucket]--;
+ if (adb->name_sd[bucket] && adb->name_refcnt[bucket] == 0)
+ result = ISC_TRUE;
+ return (result);
+}
+
+/*
+ * Requires the entry's bucket be locked.
+ */
+static inline void
+link_entry(dns_adb_t *adb, int bucket, dns_adbentry_t *entry) {
+ ISC_LIST_PREPEND(adb->entries[bucket], entry, plink);
+ entry->lock_bucket = bucket;
+ adb->entry_refcnt[bucket]++;
+}
+
+/*
+ * Requires the entry's bucket be locked.
+ */
+static inline isc_boolean_t
+unlink_entry(dns_adb_t *adb, dns_adbentry_t *entry) {
+ int bucket;
+ isc_boolean_t result = ISC_FALSE;
+
+ bucket = entry->lock_bucket;
+ INSIST(bucket != DNS_ADB_INVALIDBUCKET);
+
+ ISC_LIST_UNLINK(adb->entries[bucket], entry, plink);
+ entry->lock_bucket = DNS_ADB_INVALIDBUCKET;
+ INSIST(adb->entry_refcnt[bucket] > 0);
+ adb->entry_refcnt[bucket]--;
+ if (adb->entry_sd[bucket] && adb->entry_refcnt[bucket] == 0)
+ result = ISC_TRUE;
+ return (result);
+}
+
+static inline void
+violate_locking_hierarchy(isc_mutex_t *have, isc_mutex_t *want) {
+ if (isc_mutex_trylock(want) != ISC_R_SUCCESS) {
+ UNLOCK(have);
+ LOCK(want);
+ LOCK(have);
+ }
+}
+
+/*
+ * The ADB _MUST_ be locked before calling. Also, exit conditions must be
+ * checked after calling this function.
+ */
+static isc_boolean_t
+shutdown_names(dns_adb_t *adb) {
+ int bucket;
+ isc_boolean_t result = ISC_FALSE;
+ dns_adbname_t *name;
+ dns_adbname_t *next_name;
+
+ for (bucket = 0; bucket < NBUCKETS; bucket++) {
+ LOCK(&adb->namelocks[bucket]);
+ adb->name_sd[bucket] = ISC_TRUE;
+
+ name = ISC_LIST_HEAD(adb->names[bucket]);
+ if (name == NULL) {
+ /*
+ * This bucket has no names. We must decrement the
+ * irefcnt ourselves, since it will not be
+ * automatically triggered by a name being unlinked.
+ */
+ INSIST(result == ISC_FALSE);
+ result = dec_adb_irefcnt(adb);
+ } else {
+ /*
+ * Run through the list. For each name, clean up finds
+ * found there, and cancel any fetches running. When
+ * all the fetches are canceled, the name will destroy
+ * itself.
+ */
+ while (name != NULL) {
+ next_name = ISC_LIST_NEXT(name, plink);
+ INSIST(result == ISC_FALSE);
+ result = kill_name(&name,
+ DNS_EVENT_ADBSHUTDOWN,
+ ISC_FALSE);
+ name = next_name;
+ }
+ }
+
+ UNLOCK(&adb->namelocks[bucket]);
+ }
+ return (result);
+}
+
+/*
+ * The ADB _MUST_ be locked before calling. Also, exit conditions must be
+ * checked after calling this function.
+ */
+static isc_boolean_t
+shutdown_entries(dns_adb_t *adb) {
+ int bucket;
+ isc_boolean_t result = ISC_FALSE;
+ dns_adbentry_t *entry;
+ dns_adbentry_t *next_entry;
+
+ for (bucket = 0; bucket < NBUCKETS; bucket++) {
+ LOCK(&adb->entrylocks[bucket]);
+ adb->entry_sd[bucket] = ISC_TRUE;
+
+ entry = ISC_LIST_HEAD(adb->entries[bucket]);
+ if (entry == NULL) {
+ /*
+ * This bucket has no entries. We must decrement the
+ * irefcnt ourselves, since it will not be
+ * automatically triggered by an entry being unlinked.
+ */
+ result = dec_adb_irefcnt(adb);
+ } else {
+ /*
+ * Run through the list. Cleanup any entries not
+ * associated with names, and which are not in use.
+ */
+ while (entry != NULL) {
+ next_entry = ISC_LIST_NEXT(entry, plink);
+ if (entry->refcnt == 0 &&
+ entry->expires != 0) {
+ result = unlink_entry(adb, entry);
+ free_adbentry(adb, &entry);
+ if (result)
+ result = dec_adb_irefcnt(adb);
+ }
+ entry = next_entry;
+ }
+ }
+
+ UNLOCK(&adb->entrylocks[bucket]);
+ }
+ return (result);
+}
+
+/*
+ * Name bucket must be locked
+ */
+static void
+cancel_fetches_at_name(dns_adbname_t *name) {
+ if (NAME_FETCH_A(name))
+ dns_resolver_cancelfetch(name->fetch_a->fetch);
+
+ if (NAME_FETCH_AAAA(name))
+ dns_resolver_cancelfetch(name->fetch_aaaa->fetch);
+}
+
+/*
+ * Assumes the name bucket is locked.
+ */
+static isc_boolean_t
+clean_namehooks(dns_adb_t *adb, dns_adbnamehooklist_t *namehooks,
+ isc_boolean_t is_purge)
+{
+ dns_adbentry_t *entry;
+ dns_adbnamehook_t *namehook;
+ int addr_bucket;
+ isc_boolean_t result = ISC_FALSE;
+
+ addr_bucket = DNS_ADB_INVALIDBUCKET;
+ namehook = ISC_LIST_HEAD(*namehooks);
+ while (namehook != NULL) {
+ INSIST(DNS_ADBNAMEHOOK_VALID(namehook));
+
+ /*
+ * Clean up the entry if needed.
+ */
+ entry = namehook->entry;
+ if (entry != NULL) {
+ INSIST(DNS_ADBENTRY_VALID(entry));
+
+ if (addr_bucket != entry->lock_bucket) {
+ if (addr_bucket != DNS_ADB_INVALIDBUCKET)
+ UNLOCK(&adb->entrylocks[addr_bucket]);
+ addr_bucket = entry->lock_bucket;
+ LOCK(&adb->entrylocks[addr_bucket]);
+ }
+
+ /*
+ * If we are in an overmem situation, force expiration
+ * so that # of names and # of entries are well
+ * balanced.
+ */
+ if (is_purge)
+ entry->expires = 0;
+ result = dec_entry_refcnt(adb, entry, ISC_FALSE);
+ }
+
+ /*
+ * Free the namehook
+ */
+ namehook->entry = NULL;
+ ISC_LIST_UNLINK(*namehooks, namehook, plink);
+ free_adbnamehook(adb, &namehook);
+
+ namehook = ISC_LIST_HEAD(*namehooks);
+ }
+
+ if (addr_bucket != DNS_ADB_INVALIDBUCKET)
+ UNLOCK(&adb->entrylocks[addr_bucket]);
+ return (result);
+}
+
+static void
+clean_target(dns_adb_t *adb, dns_name_t *target) {
+ if (dns_name_countlabels(target) > 0) {
+ dns_name_free(target, adb->mctx);
+ dns_name_init(target, NULL);
+ }
+}
+
+static isc_result_t
+set_target(dns_adb_t *adb, dns_name_t *name, dns_name_t *fname,
+ dns_rdataset_t *rdataset, dns_name_t *target)
+{
+ isc_result_t result;
+ dns_namereln_t namereln;
+ unsigned int nlabels;
+ int order;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_fixedname_t fixed1, fixed2;
+ dns_name_t *prefix, *new_target;
+
+ REQUIRE(dns_name_countlabels(target) == 0);
+
+ if (rdataset->type == dns_rdatatype_cname) {
+ dns_rdata_cname_t cname;
+
+ /*
+ * Copy the CNAME's target into the target name.
+ */
+ result = dns_rdataset_first(rdataset);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ dns_rdataset_current(rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &cname, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = dns_name_dup(&cname.cname, adb->mctx, target);
+ dns_rdata_freestruct(&cname);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ } else {
+ dns_rdata_dname_t dname;
+
+ INSIST(rdataset->type == dns_rdatatype_dname);
+ namereln = dns_name_fullcompare(name, fname, &order, &nlabels);
+ INSIST(namereln == dns_namereln_subdomain);
+ /*
+ * Get the target name of the DNAME.
+ */
+ result = dns_rdataset_first(rdataset);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ dns_rdataset_current(rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &dname, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ /*
+ * Construct the new target name.
+ */
+ dns_fixedname_init(&fixed1);
+ prefix = dns_fixedname_name(&fixed1);
+ dns_fixedname_init(&fixed2);
+ new_target = dns_fixedname_name(&fixed2);
+ dns_name_split(name, nlabels, prefix, NULL);
+ result = dns_name_concatenate(prefix, &dname.dname, new_target,
+ NULL);
+ dns_rdata_freestruct(&dname);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = dns_name_dup(new_target, adb->mctx, target);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Assumes nothing is locked, since this is called by the client.
+ */
+static void
+event_free(isc_event_t *event) {
+ dns_adbfind_t *find;
+
+ INSIST(event != NULL);
+ find = event->ev_destroy_arg;
+ INSIST(DNS_ADBFIND_VALID(find));
+
+ LOCK(&find->lock);
+ find->flags |= FIND_EVENT_FREED;
+ event->ev_destroy_arg = NULL;
+ UNLOCK(&find->lock);
+}
+
+/*
+ * Assumes the name bucket is locked.
+ */
+static void
+clean_finds_at_name(dns_adbname_t *name, isc_eventtype_t evtype,
+ unsigned int addrs)
+{
+ isc_event_t *ev;
+ isc_task_t *task;
+ dns_adbfind_t *find;
+ dns_adbfind_t *next_find;
+ isc_boolean_t process;
+ unsigned int wanted, notify;
+
+ DP(ENTER_LEVEL,
+ "ENTER clean_finds_at_name, name %p, evtype %08x, addrs %08x",
+ name, evtype, addrs);
+
+ find = ISC_LIST_HEAD(name->finds);
+ while (find != NULL) {
+ LOCK(&find->lock);
+ next_find = ISC_LIST_NEXT(find, plink);
+
+ process = ISC_FALSE;
+ wanted = find->flags & DNS_ADBFIND_ADDRESSMASK;
+ notify = wanted & addrs;
+
+ switch (evtype) {
+ case DNS_EVENT_ADBMOREADDRESSES:
+ DP(ISC_LOG_DEBUG(3), "DNS_EVENT_ADBMOREADDRESSES");
+ if ((notify) != 0) {
+ find->flags &= ~addrs;
+ process = ISC_TRUE;
+ }
+ break;
+ case DNS_EVENT_ADBNOMOREADDRESSES:
+ DP(ISC_LOG_DEBUG(3), "DNS_EVENT_ADBNOMOREADDRESSES");
+ find->flags &= ~addrs;
+ wanted = find->flags & DNS_ADBFIND_ADDRESSMASK;
+ if (wanted == 0)
+ process = ISC_TRUE;
+ break;
+ default:
+ find->flags &= ~addrs;
+ process = ISC_TRUE;
+ }
+
+ if (process) {
+ DP(DEF_LEVEL, "cfan: processing find %p", find);
+ /*
+ * Unlink the find from the name, letting the caller
+ * call dns_adb_destroyfind() on it to clean it up
+ * later.
+ */
+ ISC_LIST_UNLINK(name->finds, find, plink);
+ find->adbname = NULL;
+ find->name_bucket = DNS_ADB_INVALIDBUCKET;
+
+ INSIST(!FIND_EVENTSENT(find));
+
+ ev = &find->event;
+ task = ev->ev_sender;
+ ev->ev_sender = find;
+ find->result_v4 = find_err_map[name->fetch_err];
+ find->result_v6 = find_err_map[name->fetch6_err];
+ ev->ev_type = evtype;
+ ev->ev_destroy = event_free;
+ ev->ev_destroy_arg = find;
+
+ DP(DEF_LEVEL,
+ "sending event %p to task %p for find %p",
+ ev, task, find);
+
+ isc_task_sendanddetach(&task, (isc_event_t **)&ev);
+ } else {
+ DP(DEF_LEVEL, "cfan: skipping find %p", find);
+ }
+
+ UNLOCK(&find->lock);
+ find = next_find;
+ }
+
+ DP(ENTER_LEVEL, "EXIT clean_finds_at_name, name %p", name);
+}
+
+static inline void
+check_exit(dns_adb_t *adb) {
+ isc_event_t *event;
+ /*
+ * The caller must be holding the adb lock.
+ */
+ if (adb->shutting_down) {
+ /*
+ * If there aren't any external references either, we're
+ * done. Send the control event to initiate shutdown.
+ */
+ INSIST(!adb->cevent_sent); /* Sanity check. */
+ event = &adb->cevent;
+ isc_task_send(adb->task, &event);
+ adb->cevent_sent = ISC_TRUE;
+ }
+}
+
+static inline isc_boolean_t
+dec_adb_irefcnt(dns_adb_t *adb) {
+ isc_event_t *event;
+ isc_task_t *etask;
+ isc_boolean_t result = ISC_FALSE;
+
+ LOCK(&adb->reflock);
+
+ INSIST(adb->irefcnt > 0);
+ adb->irefcnt--;
+
+ if (adb->irefcnt == 0) {
+ event = ISC_LIST_HEAD(adb->whenshutdown);
+ while (event != NULL) {
+ ISC_LIST_UNLINK(adb->whenshutdown, event, ev_link);
+ etask = event->ev_sender;
+ event->ev_sender = adb;
+ isc_task_sendanddetach(&etask, &event);
+ event = ISC_LIST_HEAD(adb->whenshutdown);
+ }
+ }
+
+ if (adb->irefcnt == 0 && adb->erefcnt == 0)
+ result = ISC_TRUE;
+ UNLOCK(&adb->reflock);
+ return (result);
+}
+
+static inline void
+inc_adb_irefcnt(dns_adb_t *adb) {
+ LOCK(&adb->reflock);
+ adb->irefcnt++;
+ UNLOCK(&adb->reflock);
+}
+
+static inline void
+inc_adb_erefcnt(dns_adb_t *adb) {
+ LOCK(&adb->reflock);
+ adb->erefcnt++;
+ UNLOCK(&adb->reflock);
+}
+
+static inline void
+inc_entry_refcnt(dns_adb_t *adb, dns_adbentry_t *entry, isc_boolean_t lock) {
+ int bucket;
+
+ bucket = entry->lock_bucket;
+
+ if (lock)
+ LOCK(&adb->entrylocks[bucket]);
+
+ entry->refcnt++;
+
+ if (lock)
+ UNLOCK(&adb->entrylocks[bucket]);
+}
+
+static inline isc_boolean_t
+dec_entry_refcnt(dns_adb_t *adb, dns_adbentry_t *entry, isc_boolean_t lock) {
+ int bucket;
+ isc_boolean_t destroy_entry;
+ isc_boolean_t result = ISC_FALSE;
+
+ bucket = entry->lock_bucket;
+
+ if (lock)
+ LOCK(&adb->entrylocks[bucket]);
+
+ INSIST(entry->refcnt > 0);
+ entry->refcnt--;
+
+ destroy_entry = ISC_FALSE;
+ if (entry->refcnt == 0 &&
+ (adb->entry_sd[bucket] || entry->expires == 0)) {
+ destroy_entry = ISC_TRUE;
+ result = unlink_entry(adb, entry);
+ }
+
+ if (lock)
+ UNLOCK(&adb->entrylocks[bucket]);
+
+ if (!destroy_entry)
+ return (result);
+
+ entry->lock_bucket = DNS_ADB_INVALIDBUCKET;
+
+ free_adbentry(adb, &entry);
+ if (result)
+ result = dec_adb_irefcnt(adb);
+
+ return (result);
+}
+
+static inline dns_adbname_t *
+new_adbname(dns_adb_t *adb, dns_name_t *dnsname) {
+ dns_adbname_t *name;
+
+ name = isc_mempool_get(adb->nmp);
+ if (name == NULL)
+ return (NULL);
+
+ dns_name_init(&name->name, NULL);
+ if (dns_name_dup(dnsname, adb->mctx, &name->name) != ISC_R_SUCCESS) {
+ isc_mempool_put(adb->nmp, name);
+ return (NULL);
+ }
+ dns_name_init(&name->target, NULL);
+ name->magic = DNS_ADBNAME_MAGIC;
+ name->adb = adb;
+ name->partial_result = 0;
+ name->flags = 0;
+ name->expire_v4 = INT_MAX;
+ name->expire_v6 = INT_MAX;
+ name->expire_target = INT_MAX;
+ name->chains = 0;
+ name->lock_bucket = DNS_ADB_INVALIDBUCKET;
+ ISC_LIST_INIT(name->v4);
+ ISC_LIST_INIT(name->v6);
+ name->fetch_a = NULL;
+ name->fetch_aaaa = NULL;
+ name->fetch_err = FIND_ERR_UNEXPECTED;
+ name->fetch6_err = FIND_ERR_UNEXPECTED;
+ ISC_LIST_INIT(name->finds);
+ ISC_LINK_INIT(name, plink);
+
+ return (name);
+}
+
+static inline void
+free_adbname(dns_adb_t *adb, dns_adbname_t **name) {
+ dns_adbname_t *n;
+
+ INSIST(name != NULL && DNS_ADBNAME_VALID(*name));
+ n = *name;
+ *name = NULL;
+
+ INSIST(!NAME_HAS_V4(n));
+ INSIST(!NAME_HAS_V6(n));
+ INSIST(!NAME_FETCH(n));
+ INSIST(ISC_LIST_EMPTY(n->finds));
+ INSIST(!ISC_LINK_LINKED(n, plink));
+ INSIST(n->lock_bucket == DNS_ADB_INVALIDBUCKET);
+ INSIST(n->adb == adb);
+
+ n->magic = 0;
+ dns_name_free(&n->name, adb->mctx);
+
+ isc_mempool_put(adb->nmp, n);
+}
+
+static inline dns_adbnamehook_t *
+new_adbnamehook(dns_adb_t *adb, dns_adbentry_t *entry) {
+ dns_adbnamehook_t *nh;
+
+ nh = isc_mempool_get(adb->nhmp);
+ if (nh == NULL)
+ return (NULL);
+
+ nh->magic = DNS_ADBNAMEHOOK_MAGIC;
+ nh->entry = entry;
+ ISC_LINK_INIT(nh, plink);
+
+ return (nh);
+}
+
+static inline void
+free_adbnamehook(dns_adb_t *adb, dns_adbnamehook_t **namehook) {
+ dns_adbnamehook_t *nh;
+
+ INSIST(namehook != NULL && DNS_ADBNAMEHOOK_VALID(*namehook));
+ nh = *namehook;
+ *namehook = NULL;
+
+ INSIST(nh->entry == NULL);
+ INSIST(!ISC_LINK_LINKED(nh, plink));
+
+ nh->magic = 0;
+ isc_mempool_put(adb->nhmp, nh);
+}
+
+static inline dns_adblameinfo_t *
+new_adblameinfo(dns_adb_t *adb, dns_name_t *qname, dns_rdatatype_t qtype) {
+ dns_adblameinfo_t *li;
+
+ li = isc_mempool_get(adb->limp);
+ if (li == NULL)
+ return (NULL);
+
+ dns_name_init(&li->qname, NULL);
+ if (dns_name_dup(qname, adb->mctx, &li->qname) != ISC_R_SUCCESS) {
+ isc_mempool_put(adb->limp, li);
+ return (NULL);
+ }
+ li->magic = DNS_ADBLAMEINFO_MAGIC;
+ li->lame_timer = 0;
+ li->qtype = qtype;
+ ISC_LINK_INIT(li, plink);
+
+ return (li);
+}
+
+static inline void
+free_adblameinfo(dns_adb_t *adb, dns_adblameinfo_t **lameinfo) {
+ dns_adblameinfo_t *li;
+
+ INSIST(lameinfo != NULL && DNS_ADBLAMEINFO_VALID(*lameinfo));
+ li = *lameinfo;
+ *lameinfo = NULL;
+
+ INSIST(!ISC_LINK_LINKED(li, plink));
+
+ dns_name_free(&li->qname, adb->mctx);
+
+ li->magic = 0;
+
+ isc_mempool_put(adb->limp, li);
+}
+
+static inline dns_adbentry_t *
+new_adbentry(dns_adb_t *adb) {
+ dns_adbentry_t *e;
+ isc_uint32_t r;
+
+ e = isc_mempool_get(adb->emp);
+ if (e == NULL)
+ return (NULL);
+
+ e->magic = DNS_ADBENTRY_MAGIC;
+ e->lock_bucket = DNS_ADB_INVALIDBUCKET;
+ e->refcnt = 0;
+ e->flags = 0;
+ isc_random_get(&r);
+ e->srtt = (r & 0x1f) + 1;
+ e->expires = 0;
+ ISC_LIST_INIT(e->lameinfo);
+ ISC_LINK_INIT(e, plink);
+
+ return (e);
+}
+
+static inline void
+free_adbentry(dns_adb_t *adb, dns_adbentry_t **entry) {
+ dns_adbentry_t *e;
+ dns_adblameinfo_t *li;
+
+ INSIST(entry != NULL && DNS_ADBENTRY_VALID(*entry));
+ e = *entry;
+ *entry = NULL;
+
+ INSIST(e->lock_bucket == DNS_ADB_INVALIDBUCKET);
+ INSIST(e->refcnt == 0);
+ INSIST(!ISC_LINK_LINKED(e, plink));
+
+ e->magic = 0;
+
+ li = ISC_LIST_HEAD(e->lameinfo);
+ while (li != NULL) {
+ ISC_LIST_UNLINK(e->lameinfo, li, plink);
+ free_adblameinfo(adb, &li);
+ li = ISC_LIST_HEAD(e->lameinfo);
+ }
+
+ isc_mempool_put(adb->emp, e);
+}
+
+static inline dns_adbfind_t *
+new_adbfind(dns_adb_t *adb) {
+ dns_adbfind_t *h;
+ isc_result_t result;
+
+ h = isc_mempool_get(adb->ahmp);
+ if (h == NULL)
+ return (NULL);
+
+ /*
+ * Public members.
+ */
+ h->magic = 0;
+ h->adb = adb;
+ h->partial_result = 0;
+ h->options = 0;
+ h->flags = 0;
+ h->result_v4 = ISC_R_UNEXPECTED;
+ h->result_v6 = ISC_R_UNEXPECTED;
+ ISC_LINK_INIT(h, publink);
+ ISC_LINK_INIT(h, plink);
+ ISC_LIST_INIT(h->list);
+ h->adbname = NULL;
+ h->name_bucket = DNS_ADB_INVALIDBUCKET;
+
+ /*
+ * private members
+ */
+ result = isc_mutex_init(&h->lock);
+ if (result != ISC_R_SUCCESS) {
+ isc_mempool_put(adb->ahmp, h);
+ return (NULL);
+ }
+
+ ISC_EVENT_INIT(&h->event, sizeof(isc_event_t), 0, 0, 0, NULL, NULL,
+ NULL, NULL, h);
+
+ inc_adb_irefcnt(adb);
+ h->magic = DNS_ADBFIND_MAGIC;
+ return (h);
+}
+
+static inline dns_adbfetch_t *
+new_adbfetch(dns_adb_t *adb) {
+ dns_adbfetch_t *f;
+
+ f = isc_mempool_get(adb->afmp);
+ if (f == NULL)
+ return (NULL);
+
+ f->magic = 0;
+ f->namehook = NULL;
+ f->entry = NULL;
+ f->fetch = NULL;
+
+ f->namehook = new_adbnamehook(adb, NULL);
+ if (f->namehook == NULL)
+ goto err;
+
+ f->entry = new_adbentry(adb);
+ if (f->entry == NULL)
+ goto err;
+
+ dns_rdataset_init(&f->rdataset);
+
+ f->magic = DNS_ADBFETCH_MAGIC;
+
+ return (f);
+
+ err:
+ if (f->namehook != NULL)
+ free_adbnamehook(adb, &f->namehook);
+ if (f->entry != NULL)
+ free_adbentry(adb, &f->entry);
+ isc_mempool_put(adb->afmp, f);
+ return (NULL);
+}
+
+static inline void
+free_adbfetch(dns_adb_t *adb, dns_adbfetch_t **fetch) {
+ dns_adbfetch_t *f;
+
+ INSIST(fetch != NULL && DNS_ADBFETCH_VALID(*fetch));
+ f = *fetch;
+ *fetch = NULL;
+
+ f->magic = 0;
+
+ if (f->namehook != NULL)
+ free_adbnamehook(adb, &f->namehook);
+ if (f->entry != NULL)
+ free_adbentry(adb, &f->entry);
+
+ if (dns_rdataset_isassociated(&f->rdataset))
+ dns_rdataset_disassociate(&f->rdataset);
+
+ isc_mempool_put(adb->afmp, f);
+}
+
+static inline isc_boolean_t
+free_adbfind(dns_adb_t *adb, dns_adbfind_t **findp) {
+ dns_adbfind_t *find;
+
+ INSIST(findp != NULL && DNS_ADBFIND_VALID(*findp));
+ find = *findp;
+ *findp = NULL;
+
+ INSIST(!FIND_HAS_ADDRS(find));
+ INSIST(!ISC_LINK_LINKED(find, publink));
+ INSIST(!ISC_LINK_LINKED(find, plink));
+ INSIST(find->name_bucket == DNS_ADB_INVALIDBUCKET);
+ INSIST(find->adbname == NULL);
+
+ find->magic = 0;
+
+ DESTROYLOCK(&find->lock);
+ isc_mempool_put(adb->ahmp, find);
+ return (dec_adb_irefcnt(adb));
+}
+
+/*
+ * Copy bits from the entry into the newly allocated addrinfo. The entry
+ * must be locked, and the reference count must be bumped up by one
+ * if this function returns a valid pointer.
+ */
+static inline dns_adbaddrinfo_t *
+new_adbaddrinfo(dns_adb_t *adb, dns_adbentry_t *entry, in_port_t port) {
+ dns_adbaddrinfo_t *ai;
+
+ ai = isc_mempool_get(adb->aimp);
+ if (ai == NULL)
+ return (NULL);
+
+ ai->magic = DNS_ADBADDRINFO_MAGIC;
+ ai->sockaddr = entry->sockaddr;
+ isc_sockaddr_setport(&ai->sockaddr, port);
+ ai->srtt = entry->srtt;
+ ai->flags = entry->flags;
+ ai->entry = entry;
+ ISC_LINK_INIT(ai, publink);
+
+ return (ai);
+}
+
+static inline void
+free_adbaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **ainfo) {
+ dns_adbaddrinfo_t *ai;
+
+ INSIST(ainfo != NULL && DNS_ADBADDRINFO_VALID(*ainfo));
+ ai = *ainfo;
+ *ainfo = NULL;
+
+ INSIST(ai->entry == NULL);
+ INSIST(!ISC_LINK_LINKED(ai, publink));
+
+ ai->magic = 0;
+
+ isc_mempool_put(adb->aimp, ai);
+}
+
+/*
+ * Search for the name. NOTE: The bucket is kept locked on both
+ * success and failure, so it must always be unlocked by the caller!
+ *
+ * On the first call to this function, *bucketp must be set to
+ * DNS_ADB_INVALIDBUCKET.
+ */
+static inline dns_adbname_t *
+find_name_and_lock(dns_adb_t *adb, dns_name_t *name,
+ unsigned int options, int *bucketp)
+{
+ dns_adbname_t *adbname;
+ int bucket;
+
+ bucket = dns_name_fullhash(name, ISC_FALSE) % NBUCKETS;
+
+ if (*bucketp == DNS_ADB_INVALIDBUCKET) {
+ LOCK(&adb->namelocks[bucket]);
+ *bucketp = bucket;
+ } else if (*bucketp != bucket) {
+ UNLOCK(&adb->namelocks[*bucketp]);
+ LOCK(&adb->namelocks[bucket]);
+ *bucketp = bucket;
+ }
+
+ adbname = ISC_LIST_HEAD(adb->names[bucket]);
+ while (adbname != NULL) {
+ if (!NAME_DEAD(adbname)) {
+ if (dns_name_equal(name, &adbname->name)
+ && GLUEHINT_OK(adbname, options)
+ && STARTATZONE_MATCHES(adbname, options))
+ return (adbname);
+ }
+ adbname = ISC_LIST_NEXT(adbname, plink);
+ }
+
+ return (NULL);
+}
+
+/*
+ * Search for the address. NOTE: The bucket is kept locked on both
+ * success and failure, so it must always be unlocked by the caller.
+ *
+ * On the first call to this function, *bucketp must be set to
+ * DNS_ADB_INVALIDBUCKET. This will cause a lock to occur. On
+ * later calls (within the same "lock path") it can be left alone, so
+ * if this function is called multiple times locking is only done if
+ * the bucket changes.
+ */
+static inline dns_adbentry_t *
+find_entry_and_lock(dns_adb_t *adb, isc_sockaddr_t *addr, int *bucketp) {
+ dns_adbentry_t *entry;
+ int bucket;
+
+ bucket = isc_sockaddr_hash(addr, ISC_TRUE) % NBUCKETS;
+
+ if (*bucketp == DNS_ADB_INVALIDBUCKET) {
+ LOCK(&adb->entrylocks[bucket]);
+ *bucketp = bucket;
+ } else if (*bucketp != bucket) {
+ UNLOCK(&adb->entrylocks[*bucketp]);
+ LOCK(&adb->entrylocks[bucket]);
+ *bucketp = bucket;
+ }
+
+ entry = ISC_LIST_HEAD(adb->entries[bucket]);
+ while (entry != NULL) {
+ if (isc_sockaddr_equal(addr, &entry->sockaddr))
+ return (entry);
+ entry = ISC_LIST_NEXT(entry, plink);
+ }
+
+ return (NULL);
+}
+
+/*
+ * Entry bucket MUST be locked!
+ */
+static isc_boolean_t
+entry_is_lame(dns_adb_t *adb, dns_adbentry_t *entry, dns_name_t *qname,
+ dns_rdatatype_t qtype, isc_stdtime_t now)
+{
+ dns_adblameinfo_t *li, *next_li;
+ isc_boolean_t is_bad;
+
+ is_bad = ISC_FALSE;
+
+ li = ISC_LIST_HEAD(entry->lameinfo);
+ if (li == NULL)
+ return (ISC_FALSE);
+ while (li != NULL) {
+ next_li = ISC_LIST_NEXT(li, plink);
+
+ /*
+ * Has the entry expired?
+ */
+ if (li->lame_timer < now) {
+ ISC_LIST_UNLINK(entry->lameinfo, li, plink);
+ free_adblameinfo(adb, &li);
+ }
+
+ /*
+ * Order tests from least to most expensive.
+ *
+ * We do not break out of the main loop here as
+ * we use the loop for house keeping.
+ */
+ if (li != NULL && !is_bad && li->qtype == qtype &&
+ dns_name_equal(qname, &li->qname))
+ is_bad = ISC_TRUE;
+
+ li = next_li;
+ }
+
+ return (is_bad);
+}
+
+static void
+copy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, dns_name_t *qname,
+ dns_rdatatype_t qtype, dns_adbname_t *name,
+ isc_stdtime_t now)
+{
+ dns_adbnamehook_t *namehook;
+ dns_adbaddrinfo_t *addrinfo;
+ dns_adbentry_t *entry;
+ int bucket;
+
+ bucket = DNS_ADB_INVALIDBUCKET;
+
+ if (find->options & DNS_ADBFIND_INET) {
+ namehook = ISC_LIST_HEAD(name->v4);
+ while (namehook != NULL) {
+ entry = namehook->entry;
+ bucket = entry->lock_bucket;
+ LOCK(&adb->entrylocks[bucket]);
+
+ if (!FIND_RETURNLAME(find)
+ && entry_is_lame(adb, entry, qname, qtype, now)) {
+ find->options |= DNS_ADBFIND_LAMEPRUNED;
+ goto nextv4;
+ }
+ addrinfo = new_adbaddrinfo(adb, entry, find->port);
+ if (addrinfo == NULL) {
+ find->partial_result |= DNS_ADBFIND_INET;
+ goto out;
+ }
+ /*
+ * Found a valid entry. Add it to the find's list.
+ */
+ inc_entry_refcnt(adb, entry, ISC_FALSE);
+ ISC_LIST_APPEND(find->list, addrinfo, publink);
+ addrinfo = NULL;
+ nextv4:
+ UNLOCK(&adb->entrylocks[bucket]);
+ bucket = DNS_ADB_INVALIDBUCKET;
+ namehook = ISC_LIST_NEXT(namehook, plink);
+ }
+ }
+
+ if (find->options & DNS_ADBFIND_INET6) {
+ namehook = ISC_LIST_HEAD(name->v6);
+ while (namehook != NULL) {
+ entry = namehook->entry;
+ bucket = entry->lock_bucket;
+ LOCK(&adb->entrylocks[bucket]);
+
+ if (!FIND_RETURNLAME(find)
+ && entry_is_lame(adb, entry, qname, qtype, now)) {
+ find->options |= DNS_ADBFIND_LAMEPRUNED;
+ goto nextv6;
+ }
+ addrinfo = new_adbaddrinfo(adb, entry, find->port);
+ if (addrinfo == NULL) {
+ find->partial_result |= DNS_ADBFIND_INET6;
+ goto out;
+ }
+ /*
+ * Found a valid entry. Add it to the find's list.
+ */
+ inc_entry_refcnt(adb, entry, ISC_FALSE);
+ ISC_LIST_APPEND(find->list, addrinfo, publink);
+ addrinfo = NULL;
+ nextv6:
+ UNLOCK(&adb->entrylocks[bucket]);
+ bucket = DNS_ADB_INVALIDBUCKET;
+ namehook = ISC_LIST_NEXT(namehook, plink);
+ }
+ }
+
+ out:
+ if (bucket != DNS_ADB_INVALIDBUCKET)
+ UNLOCK(&adb->entrylocks[bucket]);
+}
+
+static void
+shutdown_task(isc_task_t *task, isc_event_t *ev) {
+ dns_adb_t *adb;
+
+ UNUSED(task);
+
+ adb = ev->ev_arg;
+ INSIST(DNS_ADB_VALID(adb));
+
+ isc_event_free(&ev);
+ /*
+ * Wait for lock around check_exit() call to be released.
+ */
+ LOCK(&adb->lock);
+ UNLOCK(&adb->lock);
+ destroy(adb);
+}
+
+/*
+ * Name bucket must be locked; adb may be locked; no other locks held.
+ */
+static isc_boolean_t
+check_expire_name(dns_adbname_t **namep, isc_stdtime_t now) {
+ dns_adbname_t *name;
+ isc_boolean_t result = ISC_FALSE;
+
+ INSIST(namep != NULL && DNS_ADBNAME_VALID(*namep));
+ name = *namep;
+
+ if (NAME_HAS_V4(name) || NAME_HAS_V6(name))
+ return (result);
+ if (NAME_FETCH(name))
+ return (result);
+ if (!EXPIRE_OK(name->expire_v4, now))
+ return (result);
+ if (!EXPIRE_OK(name->expire_v6, now))
+ return (result);
+ if (!EXPIRE_OK(name->expire_target, now))
+ return (result);
+
+ /*
+ * The name is empty. Delete it.
+ */
+ result = kill_name(&name, DNS_EVENT_ADBEXPIRED, ISC_FALSE);
+ *namep = NULL;
+
+ /*
+ * Our caller, or one of its callers, will be calling check_exit() at
+ * some point, so we don't need to do it here.
+ */
+ return (result);
+}
+
+/*%
+ * Examine the tail entry of the LRU list to see if it expires or is stale
+ * (unused for some period); if so, the name entry will be freed. If the ADB
+ * is in the overmem condition, the tail and the next to tail entries
+ * will be unconditionally removed (unless they have an outstanding fetch).
+ * We don't care about a race on 'overmem' at the risk of causing some
+ * collateral damage or a small delay in starting cleanup, so we don't bother
+ * to lock ADB (if it's not locked).
+ *
+ * Name bucket must be locked; adb may be locked; no other locks held.
+ */
+static void
+check_stale_name(dns_adb_t *adb, int bucket, isc_stdtime_t now) {
+ int victims, max_victims;
+ isc_boolean_t result;
+ dns_adbname_t *victim, *next_victim;
+ isc_boolean_t overmem = adb->overmem;
+ int scans = 0;
+
+ INSIST(bucket != DNS_ADB_INVALIDBUCKET);
+
+ max_victims = overmem ? 2 : 1;
+
+ /*
+ * We limit the number of scanned entries to 10 (arbitrary choice)
+ * in order to avoid examining too many entries when there are many
+ * tail entries that have fetches (this should be rare, but could
+ * happen).
+ */
+ victim = ISC_LIST_TAIL(adb->names[bucket]);
+ for (victims = 0;
+ victim != NULL && victims < max_victims && scans < 10;
+ victim = next_victim) {
+ scans++;
+ next_victim = ISC_LIST_PREV(victim, plink);
+
+ /*
+ * If the victim is already dead, it simply waits for some
+ * final events. Ignore it.
+ */
+ if (NAME_DEAD(victim))
+ goto next;
+
+ result = check_expire_name(&victim, now);
+ if (victim == NULL) {
+ victims++;
+ goto next;
+ }
+
+ if (!NAME_FETCH(victim) &&
+ (overmem || victim->last_used + ADB_STALE_MARGIN <= now)) {
+ RUNTIME_CHECK(kill_name(&victim,
+ DNS_EVENT_ADBCANCELED,
+ ISC_TRUE) ==
+ ISC_FALSE);
+ victims++;
+ }
+
+ next:
+ if (!overmem)
+ break;
+ }
+}
+
+/*
+ * Entry bucket must be locked; adb may be locked; no other locks held.
+ */
+static isc_boolean_t
+check_expire_entry(dns_adb_t *adb, dns_adbentry_t **entryp, isc_stdtime_t now)
+{
+ dns_adbentry_t *entry;
+ isc_boolean_t result = ISC_FALSE;
+
+ INSIST(entryp != NULL && DNS_ADBENTRY_VALID(*entryp));
+ entry = *entryp;
+
+ if (entry->refcnt != 0)
+ return (result);
+
+ if (entry->expires == 0 || entry->expires > now)
+ return (result);
+
+ /*
+ * The entry is not in use. Delete it.
+ */
+ DP(DEF_LEVEL, "killing entry %p", entry);
+ INSIST(ISC_LINK_LINKED(entry, plink));
+ result = unlink_entry(adb, entry);
+ free_adbentry(adb, &entry);
+ if (result)
+ dec_adb_irefcnt(adb);
+ *entryp = NULL;
+ return (result);
+}
+
+/*
+ * ADB must be locked, and no other locks held.
+ */
+static isc_boolean_t
+cleanup_names(dns_adb_t *adb, int bucket, isc_stdtime_t now) {
+ dns_adbname_t *name;
+ dns_adbname_t *next_name;
+ isc_boolean_t result = ISC_FALSE;
+
+ DP(CLEAN_LEVEL, "cleaning name bucket %d", bucket);
+
+ LOCK(&adb->namelocks[bucket]);
+ if (adb->name_sd[bucket]) {
+ UNLOCK(&adb->namelocks[bucket]);
+ return (result);
+ }
+
+ name = ISC_LIST_HEAD(adb->names[bucket]);
+ while (name != NULL) {
+ next_name = ISC_LIST_NEXT(name, plink);
+ INSIST(result == ISC_FALSE);
+ result = check_expire_namehooks(name, now);
+ if (!result)
+ result = check_expire_name(&name, now);
+ name = next_name;
+ }
+ UNLOCK(&adb->namelocks[bucket]);
+ return (result);
+}
+
+/*
+ * ADB must be locked, and no other locks held.
+ */
+static isc_boolean_t
+cleanup_entries(dns_adb_t *adb, int bucket, isc_stdtime_t now) {
+ dns_adbentry_t *entry, *next_entry;
+ isc_boolean_t result = ISC_FALSE;
+
+ DP(CLEAN_LEVEL, "cleaning entry bucket %d", bucket);
+
+ LOCK(&adb->entrylocks[bucket]);
+ entry = ISC_LIST_HEAD(adb->entries[bucket]);
+ while (entry != NULL) {
+ next_entry = ISC_LIST_NEXT(entry, plink);
+ INSIST(result == ISC_FALSE);
+ result = check_expire_entry(adb, &entry, now);
+ entry = next_entry;
+ }
+ UNLOCK(&adb->entrylocks[bucket]);
+ return (result);
+}
+
+static void
+destroy(dns_adb_t *adb) {
+ adb->magic = 0;
+
+ isc_task_detach(&adb->task);
+
+ isc_mempool_destroy(&adb->nmp);
+ isc_mempool_destroy(&adb->nhmp);
+ isc_mempool_destroy(&adb->limp);
+ isc_mempool_destroy(&adb->emp);
+ isc_mempool_destroy(&adb->ahmp);
+ isc_mempool_destroy(&adb->aimp);
+ isc_mempool_destroy(&adb->afmp);
+
+ DESTROYMUTEXBLOCK(adb->entrylocks, NBUCKETS);
+ DESTROYMUTEXBLOCK(adb->namelocks, NBUCKETS);
+
+ DESTROYLOCK(&adb->reflock);
+ DESTROYLOCK(&adb->lock);
+ DESTROYLOCK(&adb->mplock);
+ DESTROYLOCK(&adb->overmemlock);
+
+ isc_mem_putanddetach(&adb->mctx, adb, sizeof(dns_adb_t));
+}
+
+
+/*
+ * Public functions.
+ */
+
+isc_result_t
+dns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_timermgr_t *timermgr,
+ isc_taskmgr_t *taskmgr, dns_adb_t **newadb)
+{
+ dns_adb_t *adb;
+ isc_result_t result;
+ int i;
+
+ REQUIRE(mem != NULL);
+ REQUIRE(view != NULL);
+ REQUIRE(timermgr != NULL); /* this is actually unused */
+ REQUIRE(taskmgr != NULL);
+ REQUIRE(newadb != NULL && *newadb == NULL);
+
+ UNUSED(timermgr);
+
+ adb = isc_mem_get(mem, sizeof(dns_adb_t));
+ if (adb == NULL)
+ return (ISC_R_NOMEMORY);
+
+ /*
+ * Initialize things here that cannot fail, and especially things
+ * that must be NULL for the error return to work properly.
+ */
+ adb->magic = 0;
+ adb->erefcnt = 1;
+ adb->irefcnt = 0;
+ adb->nmp = NULL;
+ adb->nhmp = NULL;
+ adb->limp = NULL;
+ adb->emp = NULL;
+ adb->ahmp = NULL;
+ adb->aimp = NULL;
+ adb->afmp = NULL;
+ adb->task = NULL;
+ adb->mctx = NULL;
+ adb->view = view;
+ adb->taskmgr = taskmgr;
+ adb->next_cleanbucket = 0;
+ ISC_EVENT_INIT(&adb->cevent, sizeof(adb->cevent), 0, NULL,
+ DNS_EVENT_ADBCONTROL, shutdown_task, adb,
+ adb, NULL, NULL);
+ adb->cevent_sent = ISC_FALSE;
+ adb->shutting_down = ISC_FALSE;
+ adb->overmem = ISC_FALSE;
+ ISC_LIST_INIT(adb->whenshutdown);
+
+ isc_mem_attach(mem, &adb->mctx);
+
+ result = isc_mutex_init(&adb->lock);
+ if (result != ISC_R_SUCCESS)
+ goto fail0b;
+
+ result = isc_mutex_init(&adb->mplock);
+ if (result != ISC_R_SUCCESS)
+ goto fail0c;
+
+ result = isc_mutex_init(&adb->reflock);
+ if (result != ISC_R_SUCCESS)
+ goto fail0d;
+
+ result = isc_mutex_init(&adb->overmemlock);
+ if (result != ISC_R_SUCCESS)
+ goto fail0e;
+
+ /*
+ * Initialize the bucket locks for names and elements.
+ * May as well initialize the list heads, too.
+ */
+ result = isc_mutexblock_init(adb->namelocks, NBUCKETS);
+ if (result != ISC_R_SUCCESS)
+ goto fail1;
+ for (i = 0; i < NBUCKETS; i++) {
+ ISC_LIST_INIT(adb->names[i]);
+ adb->name_sd[i] = ISC_FALSE;
+ adb->name_refcnt[i] = 0;
+ adb->irefcnt++;
+ }
+ for (i = 0; i < NBUCKETS; i++) {
+ ISC_LIST_INIT(adb->entries[i]);
+ adb->entry_sd[i] = ISC_FALSE;
+ adb->entry_refcnt[i] = 0;
+ adb->irefcnt++;
+ }
+ result = isc_mutexblock_init(adb->entrylocks, NBUCKETS);
+ if (result != ISC_R_SUCCESS)
+ goto fail2;
+
+ /*
+ * Memory pools
+ */
+#define MPINIT(t, p, n) do { \
+ result = isc_mempool_create(mem, sizeof(t), &(p)); \
+ if (result != ISC_R_SUCCESS) \
+ goto fail3; \
+ isc_mempool_setfreemax((p), FREE_ITEMS); \
+ isc_mempool_setfillcount((p), FILL_COUNT); \
+ isc_mempool_setname((p), n); \
+ isc_mempool_associatelock((p), &adb->mplock); \
+} while (0)
+
+ MPINIT(dns_adbname_t, adb->nmp, "adbname");
+ MPINIT(dns_adbnamehook_t, adb->nhmp, "adbnamehook");
+ MPINIT(dns_adblameinfo_t, adb->limp, "adblameinfo");
+ MPINIT(dns_adbentry_t, adb->emp, "adbentry");
+ MPINIT(dns_adbfind_t, adb->ahmp, "adbfind");
+ MPINIT(dns_adbaddrinfo_t, adb->aimp, "adbaddrinfo");
+ MPINIT(dns_adbfetch_t, adb->afmp, "adbfetch");
+
+#undef MPINIT
+
+ /*
+ * Allocate an internal task.
+ */
+ result = isc_task_create(adb->taskmgr, 0, &adb->task);
+ if (result != ISC_R_SUCCESS)
+ goto fail3;
+ isc_task_setname(adb->task, "ADB", adb);
+
+ /*
+ * Normal return.
+ */
+ adb->magic = DNS_ADB_MAGIC;
+ *newadb = adb;
+ return (ISC_R_SUCCESS);
+
+ fail3:
+ if (adb->task != NULL)
+ isc_task_detach(&adb->task);
+
+ /* clean up entrylocks */
+ DESTROYMUTEXBLOCK(adb->entrylocks, NBUCKETS);
+
+ fail2: /* clean up namelocks */
+ DESTROYMUTEXBLOCK(adb->namelocks, NBUCKETS);
+
+ fail1: /* clean up only allocated memory */
+ if (adb->nmp != NULL)
+ isc_mempool_destroy(&adb->nmp);
+ if (adb->nhmp != NULL)
+ isc_mempool_destroy(&adb->nhmp);
+ if (adb->limp != NULL)
+ isc_mempool_destroy(&adb->limp);
+ if (adb->emp != NULL)
+ isc_mempool_destroy(&adb->emp);
+ if (adb->ahmp != NULL)
+ isc_mempool_destroy(&adb->ahmp);
+ if (adb->aimp != NULL)
+ isc_mempool_destroy(&adb->aimp);
+ if (adb->afmp != NULL)
+ isc_mempool_destroy(&adb->afmp);
+
+ DESTROYLOCK(&adb->overmemlock);
+ fail0e:
+ DESTROYLOCK(&adb->reflock);
+ fail0d:
+ DESTROYLOCK(&adb->mplock);
+ fail0c:
+ DESTROYLOCK(&adb->lock);
+ fail0b:
+ isc_mem_putanddetach(&adb->mctx, adb, sizeof(dns_adb_t));
+
+ return (result);
+}
+
+void
+dns_adb_attach(dns_adb_t *adb, dns_adb_t **adbx) {
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(adbx != NULL && *adbx == NULL);
+
+ inc_adb_erefcnt(adb);
+ *adbx = adb;
+}
+
+void
+dns_adb_detach(dns_adb_t **adbx) {
+ dns_adb_t *adb;
+ isc_boolean_t need_exit_check;
+
+ REQUIRE(adbx != NULL && DNS_ADB_VALID(*adbx));
+
+ adb = *adbx;
+ *adbx = NULL;
+
+ INSIST(adb->erefcnt > 0);
+
+ LOCK(&adb->reflock);
+ adb->erefcnt--;
+ need_exit_check = ISC_TF(adb->erefcnt == 0 && adb->irefcnt == 0);
+ UNLOCK(&adb->reflock);
+
+ if (need_exit_check) {
+ LOCK(&adb->lock);
+ INSIST(adb->shutting_down);
+ check_exit(adb);
+ UNLOCK(&adb->lock);
+ }
+}
+
+void
+dns_adb_whenshutdown(dns_adb_t *adb, isc_task_t *task, isc_event_t **eventp) {
+ isc_task_t *clone;
+ isc_event_t *event;
+ isc_boolean_t zeroirefcnt = ISC_FALSE;
+
+ /*
+ * Send '*eventp' to 'task' when 'adb' has shutdown.
+ */
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(eventp != NULL);
+
+ event = *eventp;
+ *eventp = NULL;
+
+ LOCK(&adb->lock);
+
+ LOCK(&adb->reflock);
+ zeroirefcnt = ISC_TF(adb->irefcnt == 0);
+
+ if (adb->shutting_down && zeroirefcnt &&
+ isc_mempool_getallocated(adb->ahmp) == 0) {
+ /*
+ * We're already shutdown. Send the event.
+ */
+ event->ev_sender = adb;
+ isc_task_send(task, &event);
+ } else {
+ clone = NULL;
+ isc_task_attach(task, &clone);
+ event->ev_sender = clone;
+ ISC_LIST_APPEND(adb->whenshutdown, event, ev_link);
+ }
+
+ UNLOCK(&adb->reflock);
+ UNLOCK(&adb->lock);
+}
+
+void
+dns_adb_shutdown(dns_adb_t *adb) {
+ isc_boolean_t need_check_exit;
+
+ /*
+ * Shutdown 'adb'.
+ */
+
+ LOCK(&adb->lock);
+
+ if (!adb->shutting_down) {
+ adb->shutting_down = ISC_TRUE;
+ isc_mem_setwater(adb->mctx, water, adb, 0, 0);
+ need_check_exit = shutdown_names(adb);
+ if (!need_check_exit)
+ need_check_exit = shutdown_entries(adb);
+ if (need_check_exit)
+ check_exit(adb);
+ }
+
+ UNLOCK(&adb->lock);
+}
+
+isc_result_t
+dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action,
+ void *arg, dns_name_t *name, dns_name_t *qname,
+ dns_rdatatype_t qtype, unsigned int options,
+ isc_stdtime_t now, dns_name_t *target,
+ in_port_t port, dns_adbfind_t **findp)
+{
+ dns_adbfind_t *find;
+ dns_adbname_t *adbname;
+ int bucket;
+ isc_boolean_t want_event, start_at_zone, alias, have_address;
+ isc_result_t result;
+ unsigned int wanted_addresses;
+ unsigned int wanted_fetches;
+ unsigned int query_pending;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ if (task != NULL) {
+ REQUIRE(action != NULL);
+ }
+ REQUIRE(name != NULL);
+ REQUIRE(qname != NULL);
+ REQUIRE(findp != NULL && *findp == NULL);
+ REQUIRE(target == NULL || dns_name_hasbuffer(target));
+
+ REQUIRE((options & DNS_ADBFIND_ADDRESSMASK) != 0);
+
+ result = ISC_R_UNEXPECTED;
+ wanted_addresses = (options & DNS_ADBFIND_ADDRESSMASK);
+ wanted_fetches = 0;
+ query_pending = 0;
+ want_event = ISC_FALSE;
+ start_at_zone = ISC_FALSE;
+ alias = ISC_FALSE;
+
+ if (now == 0)
+ isc_stdtime_get(&now);
+
+ /*
+ * XXXMLG Move this comment somewhere else!
+ *
+ * Look up the name in our internal database.
+ *
+ * Possibilities: Note that these are not always exclusive.
+ *
+ * No name found. In this case, allocate a new name header and
+ * an initial namehook or two. If any of these allocations
+ * fail, clean up and return ISC_R_NOMEMORY.
+ *
+ * Name found, valid addresses present. Allocate one addrinfo
+ * structure for each found and append it to the linked list
+ * of addresses for this header.
+ *
+ * Name found, queries pending. In this case, if a task was
+ * passed in, allocate a job id, attach it to the name's job
+ * list and remember to tell the caller that there will be
+ * more info coming later.
+ */
+
+ find = new_adbfind(adb);
+ if (find == NULL)
+ return (ISC_R_NOMEMORY);
+
+ find->port = port;
+
+ /*
+ * Remember what types of addresses we are interested in.
+ */
+ find->options = options;
+ find->flags |= wanted_addresses;
+ if (FIND_WANTEVENT(find)) {
+ REQUIRE(task != NULL);
+ }
+
+ /*
+ * Try to see if we know anything about this name at all.
+ */
+ bucket = DNS_ADB_INVALIDBUCKET;
+ adbname = find_name_and_lock(adb, name, find->options, &bucket);
+ if (adb->name_sd[bucket]) {
+ DP(DEF_LEVEL,
+ "dns_adb_createfind: returning ISC_R_SHUTTINGDOWN");
+ RUNTIME_CHECK(free_adbfind(adb, &find) == ISC_FALSE);
+ result = ISC_R_SHUTTINGDOWN;
+ goto out;
+ }
+
+ /*
+ * Nothing found. Allocate a new adbname structure for this name.
+ */
+ if (adbname == NULL) {
+ /*
+ * See if there is any stale name at the end of list, and purge
+ * it if so.
+ */
+ check_stale_name(adb, bucket, now);
+
+ adbname = new_adbname(adb, name);
+ if (adbname == NULL) {
+ RUNTIME_CHECK(free_adbfind(adb, &find) == ISC_FALSE);
+ result = ISC_R_NOMEMORY;
+ goto out;
+ }
+ link_name(adb, bucket, adbname);
+ if (FIND_HINTOK(find))
+ adbname->flags |= NAME_HINT_OK;
+ if (FIND_GLUEOK(find))
+ adbname->flags |= NAME_GLUE_OK;
+ if (FIND_STARTATZONE(find))
+ adbname->flags |= NAME_STARTATZONE;
+ } else {
+ /* Move this name forward in the LRU list */
+ ISC_LIST_UNLINK(adb->names[bucket], adbname, plink);
+ ISC_LIST_PREPEND(adb->names[bucket], adbname, plink);
+ }
+ adbname->last_used = now;
+
+ /*
+ * Expire old entries, etc.
+ */
+ RUNTIME_CHECK(check_expire_namehooks(adbname, now) == ISC_FALSE);
+
+ /*
+ * Do we know that the name is an alias?
+ */
+ if (!EXPIRE_OK(adbname->expire_target, now)) {
+ /*
+ * Yes, it is.
+ */
+ DP(DEF_LEVEL,
+ "dns_adb_createfind: name %p is an alias (cached)",
+ adbname);
+ alias = ISC_TRUE;
+ goto post_copy;
+ }
+
+ /*
+ * Try to populate the name from the database and/or
+ * start fetches. First try looking for an A record
+ * in the database.
+ */
+ if (!NAME_HAS_V4(adbname) && EXPIRE_OK(adbname->expire_v4, now)
+ && WANT_INET(wanted_addresses)) {
+ result = dbfind_name(adbname, now, dns_rdatatype_a);
+ if (result == ISC_R_SUCCESS) {
+ DP(DEF_LEVEL,
+ "dns_adb_createfind: found A for name %p in db",
+ adbname);
+ goto v6;
+ }
+
+ /*
+ * Did we get a CNAME or DNAME?
+ */
+ if (result == DNS_R_ALIAS) {
+ DP(DEF_LEVEL,
+ "dns_adb_createfind: name %p is an alias",
+ adbname);
+ alias = ISC_TRUE;
+ goto post_copy;
+ }
+
+ /*
+ * If the name doesn't exist at all, don't bother with
+ * v6 queries; they won't work.
+ *
+ * If the name does exist but we didn't get our data, go
+ * ahead and try AAAA.
+ *
+ * If the result is neither of these, try a fetch for A.
+ */
+ if (NXDOMAIN_RESULT(result))
+ goto fetch;
+ else if (NXRRSET_RESULT(result))
+ goto v6;
+
+ if (!NAME_FETCH_V4(adbname))
+ wanted_fetches |= DNS_ADBFIND_INET;
+ }
+
+ v6:
+ if (!NAME_HAS_V6(adbname) && EXPIRE_OK(adbname->expire_v6, now)
+ && WANT_INET6(wanted_addresses)) {
+ result = dbfind_name(adbname, now, dns_rdatatype_aaaa);
+ if (result == ISC_R_SUCCESS) {
+ DP(DEF_LEVEL,
+ "dns_adb_createfind: found AAAA for name %p",
+ adbname);
+ goto fetch;
+ }
+
+ /*
+ * Did we get a CNAME or DNAME?
+ */
+ if (result == DNS_R_ALIAS) {
+ DP(DEF_LEVEL,
+ "dns_adb_createfind: name %p is an alias",
+ adbname);
+ alias = ISC_TRUE;
+ goto post_copy;
+ }
+
+ /*
+ * Listen to negative cache hints, and don't start
+ * another query.
+ */
+ if (NCACHE_RESULT(result) || AUTH_NX(result))
+ goto fetch;
+
+ if (!NAME_FETCH_V6(adbname))
+ wanted_fetches |= DNS_ADBFIND_INET6;
+ }
+
+ fetch:
+ if ((WANT_INET(wanted_addresses) && NAME_HAS_V4(adbname)) ||
+ (WANT_INET6(wanted_addresses) && NAME_HAS_V6(adbname)))
+ have_address = ISC_TRUE;
+ else
+ have_address = ISC_FALSE;
+ if (wanted_fetches != 0 &&
+ ! (FIND_AVOIDFETCHES(find) && have_address)) {
+ /*
+ * We're missing at least one address family. Either the
+ * caller hasn't instructed us to avoid fetches, or we don't
+ * know anything about any of the address families that would
+ * be acceptable so we have to launch fetches.
+ */
+
+ if (FIND_STARTATZONE(find))
+ start_at_zone = ISC_TRUE;
+
+ /*
+ * Start V4.
+ */
+ if (WANT_INET(wanted_fetches) &&
+ fetch_name(adbname, start_at_zone,
+ dns_rdatatype_a) == ISC_R_SUCCESS) {
+ DP(DEF_LEVEL,
+ "dns_adb_createfind: started A fetch for name %p",
+ adbname);
+ }
+
+ /*
+ * Start V6.
+ */
+ if (WANT_INET6(wanted_fetches) &&
+ fetch_name(adbname, start_at_zone,
+ dns_rdatatype_aaaa) == ISC_R_SUCCESS) {
+ DP(DEF_LEVEL,
+ "dns_adb_createfind: "
+ "started AAAA fetch for name %p",
+ adbname);
+ }
+ }
+
+ /*
+ * Run through the name and copy out the bits we are
+ * interested in.
+ */
+ copy_namehook_lists(adb, find, qname, qtype, adbname, now);
+
+ post_copy:
+ if (NAME_FETCH_V4(adbname))
+ query_pending |= DNS_ADBFIND_INET;
+ if (NAME_FETCH_V6(adbname))
+ query_pending |= DNS_ADBFIND_INET6;
+
+ /*
+ * Attach to the name's query list if there are queries
+ * already running, and we have been asked to.
+ */
+ want_event = ISC_TRUE;
+ if (!FIND_WANTEVENT(find))
+ want_event = ISC_FALSE;
+ if (FIND_WANTEMPTYEVENT(find) && FIND_HAS_ADDRS(find))
+ want_event = ISC_FALSE;
+ if ((wanted_addresses & query_pending) == 0)
+ want_event = ISC_FALSE;
+ if (alias)
+ want_event = ISC_FALSE;
+ if (want_event) {
+ find->adbname = adbname;
+ find->name_bucket = bucket;
+ ISC_LIST_APPEND(adbname->finds, find, plink);
+ find->query_pending = (query_pending & wanted_addresses);
+ find->flags &= ~DNS_ADBFIND_ADDRESSMASK;
+ find->flags |= (find->query_pending & DNS_ADBFIND_ADDRESSMASK);
+ DP(DEF_LEVEL, "createfind: attaching find %p to adbname %p",
+ find, adbname);
+ } else {
+ /*
+ * Remove the flag so the caller knows there will never
+ * be an event, and set internal flags to fake that
+ * the event was sent and freed, so dns_adb_destroyfind() will
+ * do the right thing.
+ */
+ find->query_pending = (query_pending & wanted_addresses);
+ find->options &= ~DNS_ADBFIND_WANTEVENT;
+ find->flags |= (FIND_EVENT_SENT | FIND_EVENT_FREED);
+ find->flags &= ~DNS_ADBFIND_ADDRESSMASK;
+ }
+
+ find->partial_result |= (adbname->partial_result & wanted_addresses);
+ if (alias) {
+ if (target != NULL) {
+ result = dns_name_copy(&adbname->target, target, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ }
+ result = DNS_R_ALIAS;
+ } else
+ result = ISC_R_SUCCESS;
+
+ /*
+ * Copy out error flags from the name structure into the find.
+ */
+ find->result_v4 = find_err_map[adbname->fetch_err];
+ find->result_v6 = find_err_map[adbname->fetch6_err];
+
+ out:
+ if (find != NULL) {
+ *findp = find;
+
+ if (want_event) {
+ isc_task_t *taskp;
+
+ INSIST((find->flags & DNS_ADBFIND_ADDRESSMASK) != 0);
+ taskp = NULL;
+ isc_task_attach(task, &taskp);
+ find->event.ev_sender = taskp;
+ find->event.ev_action = action;
+ find->event.ev_arg = arg;
+ }
+ }
+
+ UNLOCK(&adb->namelocks[bucket]);
+
+ return (result);
+}
+
+void
+dns_adb_destroyfind(dns_adbfind_t **findp) {
+ dns_adbfind_t *find;
+ dns_adbentry_t *entry;
+ dns_adbaddrinfo_t *ai;
+ int bucket;
+ dns_adb_t *adb;
+
+ REQUIRE(findp != NULL && DNS_ADBFIND_VALID(*findp));
+ find = *findp;
+ *findp = NULL;
+
+ LOCK(&find->lock);
+
+ DP(DEF_LEVEL, "dns_adb_destroyfind on find %p", find);
+
+ adb = find->adb;
+ REQUIRE(DNS_ADB_VALID(adb));
+
+ REQUIRE(FIND_EVENTFREED(find));
+
+ bucket = find->name_bucket;
+ INSIST(bucket == DNS_ADB_INVALIDBUCKET);
+
+ UNLOCK(&find->lock);
+
+ /*
+ * The find doesn't exist on any list, and nothing is locked.
+ * Return the find to the memory pool, and decrement the adb's
+ * reference count.
+ */
+ ai = ISC_LIST_HEAD(find->list);
+ while (ai != NULL) {
+ ISC_LIST_UNLINK(find->list, ai, publink);
+ entry = ai->entry;
+ ai->entry = NULL;
+ INSIST(DNS_ADBENTRY_VALID(entry));
+ RUNTIME_CHECK(dec_entry_refcnt(adb, entry, ISC_TRUE) ==
+ ISC_FALSE);
+ free_adbaddrinfo(adb, &ai);
+ ai = ISC_LIST_HEAD(find->list);
+ }
+
+ /*
+ * WARNING: The find is freed with the adb locked. This is done
+ * to avoid a race condition where we free the find, some other
+ * thread tests to see if it should be destroyed, detects it should
+ * be, destroys it, and then we try to lock it for our check, but the
+ * lock is destroyed.
+ */
+ LOCK(&adb->lock);
+ if (free_adbfind(adb, &find))
+ check_exit(adb);
+ UNLOCK(&adb->lock);
+}
+
+void
+dns_adb_cancelfind(dns_adbfind_t *find) {
+ isc_event_t *ev;
+ isc_task_t *task;
+ dns_adb_t *adb;
+ int bucket;
+ int unlock_bucket;
+
+ LOCK(&find->lock);
+
+ DP(DEF_LEVEL, "dns_adb_cancelfind on find %p", find);
+
+ adb = find->adb;
+ REQUIRE(DNS_ADB_VALID(adb));
+
+ REQUIRE(!FIND_EVENTFREED(find));
+ REQUIRE(FIND_WANTEVENT(find));
+
+ bucket = find->name_bucket;
+ if (bucket == DNS_ADB_INVALIDBUCKET)
+ goto cleanup;
+
+ /*
+ * We need to get the adbname's lock to unlink the find.
+ */
+ unlock_bucket = bucket;
+ violate_locking_hierarchy(&find->lock, &adb->namelocks[unlock_bucket]);
+ bucket = find->name_bucket;
+ if (bucket != DNS_ADB_INVALIDBUCKET) {
+ ISC_LIST_UNLINK(find->adbname->finds, find, plink);
+ find->adbname = NULL;
+ find->name_bucket = DNS_ADB_INVALIDBUCKET;
+ }
+ UNLOCK(&adb->namelocks[unlock_bucket]);
+ bucket = DNS_ADB_INVALIDBUCKET;
+
+ cleanup:
+
+ if (!FIND_EVENTSENT(find)) {
+ ev = &find->event;
+ task = ev->ev_sender;
+ ev->ev_sender = find;
+ ev->ev_type = DNS_EVENT_ADBCANCELED;
+ ev->ev_destroy = event_free;
+ ev->ev_destroy_arg = find;
+ find->result_v4 = ISC_R_CANCELED;
+ find->result_v6 = ISC_R_CANCELED;
+
+ DP(DEF_LEVEL, "sending event %p to task %p for find %p",
+ ev, task, find);
+
+ isc_task_sendanddetach(&task, (isc_event_t **)&ev);
+ }
+
+ UNLOCK(&find->lock);
+}
+
+void
+dns_adb_dump(dns_adb_t *adb, FILE *f) {
+ int i;
+ isc_stdtime_t now;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(f != NULL);
+
+ /*
+ * Lock the adb itself, lock all the name buckets, then lock all
+ * the entry buckets. This should put the adb into a state where
+ * nothing can change, so we can iterate through everything and
+ * print at our leisure.
+ */
+
+ LOCK(&adb->lock);
+ isc_stdtime_get(&now);
+
+ for (i = 0; i < NBUCKETS; i++)
+ RUNTIME_CHECK(cleanup_names(adb, i, now) == ISC_FALSE);
+ for (i = 0; i < NBUCKETS; i++)
+ RUNTIME_CHECK(cleanup_entries(adb, i, now) == ISC_FALSE);
+
+ dump_adb(adb, f, ISC_FALSE, now);
+ UNLOCK(&adb->lock);
+}
+
+static void
+dump_ttl(FILE *f, const char *legend, isc_stdtime_t value, isc_stdtime_t now) {
+ if (value == INT_MAX)
+ return;
+ fprintf(f, " [%s TTL %d]", legend, value - now);
+}
+
+static void
+dump_adb(dns_adb_t *adb, FILE *f, isc_boolean_t debug, isc_stdtime_t now) {
+ int i;
+ dns_adbname_t *name;
+ dns_adbentry_t *entry;
+
+ fprintf(f, ";\n; Address database dump\n;\n");
+ if (debug)
+ fprintf(f, "; addr %p, erefcnt %u, irefcnt %u, finds out %u\n",
+ adb, adb->erefcnt, adb->irefcnt,
+ isc_mempool_getallocated(adb->nhmp));
+
+ for (i = 0; i < NBUCKETS; i++)
+ LOCK(&adb->namelocks[i]);
+ for (i = 0; i < NBUCKETS; i++)
+ LOCK(&adb->entrylocks[i]);
+
+ /*
+ * Dump the names
+ */
+ for (i = 0; i < NBUCKETS; i++) {
+ name = ISC_LIST_HEAD(adb->names[i]);
+ if (name == NULL)
+ continue;
+ if (debug)
+ fprintf(f, "; bucket %d\n", i);
+ for (;
+ name != NULL;
+ name = ISC_LIST_NEXT(name, plink))
+ {
+ if (debug)
+ fprintf(f, "; name %p (flags %08x)\n",
+ name, name->flags);
+
+ fprintf(f, "; ");
+ print_dns_name(f, &name->name);
+ if (dns_name_countlabels(&name->target) > 0) {
+ fprintf(f, " alias ");
+ print_dns_name(f, &name->target);
+ }
+
+ dump_ttl(f, "v4", name->expire_v4, now);
+ dump_ttl(f, "v6", name->expire_v6, now);
+ dump_ttl(f, "target", name->expire_target, now);
+
+ fprintf(f, " [v4 %s] [v6 %s]",
+ errnames[name->fetch_err],
+ errnames[name->fetch6_err]);
+
+ fprintf(f, "\n");
+
+ print_namehook_list(f, "v4", &name->v4, debug, now);
+ print_namehook_list(f, "v6", &name->v6, debug, now);
+
+ if (debug)
+ print_fetch_list(f, name);
+ if (debug)
+ print_find_list(f, name);
+
+ }
+ }
+
+ fprintf(f, ";\n; Unassociated entries\n;\n");
+
+ for (i = 0; i < NBUCKETS; i++) {
+ entry = ISC_LIST_HEAD(adb->entries[i]);
+ while (entry != NULL) {
+ if (entry->refcnt == 0)
+ dump_entry(f, entry, debug, now);
+ entry = ISC_LIST_NEXT(entry, plink);
+ }
+ }
+
+ /*
+ * Unlock everything
+ */
+ for (i = 0; i < NBUCKETS; i++)
+ UNLOCK(&adb->entrylocks[i]);
+ for (i = 0; i < NBUCKETS; i++)
+ UNLOCK(&adb->namelocks[i]);
+}
+
+static void
+dump_entry(FILE *f, dns_adbentry_t *entry, isc_boolean_t debug,
+ isc_stdtime_t now)
+{
+ char addrbuf[ISC_NETADDR_FORMATSIZE];
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
+ isc_netaddr_t netaddr;
+ dns_adblameinfo_t *li;
+
+ isc_netaddr_fromsockaddr(&netaddr, &entry->sockaddr);
+ isc_netaddr_format(&netaddr, addrbuf, sizeof(addrbuf));
+
+ if (debug)
+ fprintf(f, ";\t%p: refcnt %u\n", entry, entry->refcnt);
+
+ fprintf(f, ";\t%s [srtt %u] [flags %08x]",
+ addrbuf, entry->srtt, entry->flags);
+ if (entry->expires != 0)
+ fprintf(f, " [ttl %d]", entry->expires - now);
+ fprintf(f, "\n");
+ for (li = ISC_LIST_HEAD(entry->lameinfo);
+ li != NULL;
+ li = ISC_LIST_NEXT(li, plink)) {
+ fprintf(f, ";\t\t");
+ print_dns_name(f, &li->qname);
+ dns_rdatatype_format(li->qtype, typebuf, sizeof(typebuf));
+ fprintf(f, " %s [lame TTL %d]\n", typebuf,
+ li->lame_timer - now);
+ }
+}
+
+void
+dns_adb_dumpfind(dns_adbfind_t *find, FILE *f) {
+ char tmp[512];
+ const char *tmpp;
+ dns_adbaddrinfo_t *ai;
+ isc_sockaddr_t *sa;
+
+ /*
+ * Not used currently, in the API Just In Case we
+ * want to dump out the name and/or entries too.
+ */
+
+ LOCK(&find->lock);
+
+ fprintf(f, ";Find %p\n", find);
+ fprintf(f, ";\tqpending %08x partial %08x options %08x flags %08x\n",
+ find->query_pending, find->partial_result,
+ find->options, find->flags);
+ fprintf(f, ";\tname_bucket %d, name %p, event sender %p\n",
+ find->name_bucket, find->adbname, find->event.ev_sender);
+
+ ai = ISC_LIST_HEAD(find->list);
+ if (ai != NULL)
+ fprintf(f, "\tAddresses:\n");
+ while (ai != NULL) {
+ sa = &ai->sockaddr;
+ switch (sa->type.sa.sa_family) {
+ case AF_INET:
+ tmpp = inet_ntop(AF_INET, &sa->type.sin.sin_addr,
+ tmp, sizeof(tmp));
+ break;
+ case AF_INET6:
+ tmpp = inet_ntop(AF_INET6, &sa->type.sin6.sin6_addr,
+ tmp, sizeof(tmp));
+ break;
+ default:
+ tmpp = "UnkFamily";
+ }
+
+ if (tmpp == NULL)
+ tmpp = "BadAddress";
+
+ fprintf(f, "\t\tentry %p, flags %08x"
+ " srtt %u addr %s\n",
+ ai->entry, ai->flags, ai->srtt, tmpp);
+
+ ai = ISC_LIST_NEXT(ai, publink);
+ }
+
+ UNLOCK(&find->lock);
+}
+
+static void
+print_dns_name(FILE *f, dns_name_t *name) {
+ char buf[DNS_NAME_FORMATSIZE];
+
+ INSIST(f != NULL);
+
+ dns_name_format(name, buf, sizeof(buf));
+ fprintf(f, "%s", buf);
+}
+
+static void
+print_namehook_list(FILE *f, const char *legend, dns_adbnamehooklist_t *list,
+ isc_boolean_t debug, isc_stdtime_t now)
+{
+ dns_adbnamehook_t *nh;
+
+ for (nh = ISC_LIST_HEAD(*list);
+ nh != NULL;
+ nh = ISC_LIST_NEXT(nh, plink))
+ {
+ if (debug)
+ fprintf(f, ";\tHook(%s) %p\n", legend, nh);
+ dump_entry(f, nh->entry, debug, now);
+ }
+}
+
+static inline void
+print_fetch(FILE *f, dns_adbfetch_t *ft, const char *type) {
+ fprintf(f, "\t\tFetch(%s): %p -> { nh %p, entry %p, fetch %p }\n",
+ type, ft, ft->namehook, ft->entry, ft->fetch);
+}
+
+static void
+print_fetch_list(FILE *f, dns_adbname_t *n) {
+ if (NAME_FETCH_A(n))
+ print_fetch(f, n->fetch_a, "A");
+ if (NAME_FETCH_AAAA(n))
+ print_fetch(f, n->fetch_aaaa, "AAAA");
+}
+
+static void
+print_find_list(FILE *f, dns_adbname_t *name) {
+ dns_adbfind_t *find;
+
+ find = ISC_LIST_HEAD(name->finds);
+ while (find != NULL) {
+ dns_adb_dumpfind(find, f);
+ find = ISC_LIST_NEXT(find, plink);
+ }
+}
+
+static isc_result_t
+dbfind_name(dns_adbname_t *adbname, isc_stdtime_t now, dns_rdatatype_t rdtype)
+{
+ isc_result_t result;
+ dns_rdataset_t rdataset;
+ dns_adb_t *adb;
+ dns_fixedname_t foundname;
+ dns_name_t *fname;
+
+ INSIST(DNS_ADBNAME_VALID(adbname));
+ adb = adbname->adb;
+ INSIST(DNS_ADB_VALID(adb));
+ INSIST(rdtype == dns_rdatatype_a || rdtype == dns_rdatatype_aaaa);
+
+ dns_fixedname_init(&foundname);
+ fname = dns_fixedname_name(&foundname);
+ dns_rdataset_init(&rdataset);
+
+ if (rdtype == dns_rdatatype_a)
+ adbname->fetch_err = FIND_ERR_UNEXPECTED;
+ else
+ adbname->fetch6_err = FIND_ERR_UNEXPECTED;
+
+ result = dns_view_find(adb->view, &adbname->name, rdtype, now,
+ NAME_GLUEOK(adbname) ? DNS_DBFIND_GLUEOK : 0,
+ ISC_TF(NAME_HINTOK(adbname)),
+ NULL, NULL, fname, &rdataset, NULL);
+
+ /* XXXVIX this switch statement is too sparse to gen a jump table. */
+ switch (result) {
+ case DNS_R_GLUE:
+ case DNS_R_HINT:
+ case ISC_R_SUCCESS:
+ /*
+ * Found in the database. Even if we can't copy out
+ * any information, return success, or else a fetch
+ * will be made, which will only make things worse.
+ */
+ if (rdtype == dns_rdatatype_a)
+ adbname->fetch_err = FIND_ERR_SUCCESS;
+ else
+ adbname->fetch6_err = FIND_ERR_SUCCESS;
+ result = import_rdataset(adbname, &rdataset, now);
+ break;
+ case DNS_R_NXDOMAIN:
+ case DNS_R_NXRRSET:
+ /*
+ * We're authoritative and the data doesn't exist.
+ * Make up a negative cache entry so we don't ask again
+ * for a while.
+ *
+ * XXXRTH What time should we use? I'm putting in 30 seconds
+ * for now.
+ */
+ if (rdtype == dns_rdatatype_a) {
+ adbname->expire_v4 = now + 30;
+ DP(NCACHE_LEVEL,
+ "adb name %p: Caching auth negative entry for A",
+ adbname);
+ if (result == DNS_R_NXDOMAIN)
+ adbname->fetch_err = FIND_ERR_NXDOMAIN;
+ else
+ adbname->fetch_err = FIND_ERR_NXRRSET;
+ } else {
+ DP(NCACHE_LEVEL,
+ "adb name %p: Caching auth negative entry for AAAA",
+ adbname);
+ adbname->expire_v6 = now + 30;
+ if (result == DNS_R_NXDOMAIN)
+ adbname->fetch6_err = FIND_ERR_NXDOMAIN;
+ else
+ adbname->fetch6_err = FIND_ERR_NXRRSET;
+ }
+ break;
+ case DNS_R_NCACHENXDOMAIN:
+ case DNS_R_NCACHENXRRSET:
+ /*
+ * We found a negative cache entry. Pull the TTL from it
+ * so we won't ask again for a while.
+ */
+ rdataset.ttl = ttlclamp(rdataset.ttl);
+ if (rdtype == dns_rdatatype_a) {
+ adbname->expire_v4 = rdataset.ttl + now;
+ if (result == DNS_R_NCACHENXDOMAIN)
+ adbname->fetch_err = FIND_ERR_NXDOMAIN;
+ else
+ adbname->fetch_err = FIND_ERR_NXRRSET;
+ DP(NCACHE_LEVEL,
+ "adb name %p: Caching negative entry for A (ttl %u)",
+ adbname, rdataset.ttl);
+ } else {
+ DP(NCACHE_LEVEL,
+ "adb name %p: Caching negative entry for AAAA (ttl %u)",
+ adbname, rdataset.ttl);
+ adbname->expire_v6 = rdataset.ttl + now;
+ if (result == DNS_R_NCACHENXDOMAIN)
+ adbname->fetch6_err = FIND_ERR_NXDOMAIN;
+ else
+ adbname->fetch6_err = FIND_ERR_NXRRSET;
+ }
+ break;
+ case DNS_R_CNAME:
+ case DNS_R_DNAME:
+ /*
+ * Clear the hint and glue flags, so this will match
+ * more often.
+ */
+ adbname->flags &= ~(DNS_ADBFIND_GLUEOK | DNS_ADBFIND_HINTOK);
+
+ rdataset.ttl = ttlclamp(rdataset.ttl);
+ clean_target(adb, &adbname->target);
+ adbname->expire_target = INT_MAX;
+ result = set_target(adb, &adbname->name, fname, &rdataset,
+ &adbname->target);
+ if (result == ISC_R_SUCCESS) {
+ result = DNS_R_ALIAS;
+ DP(NCACHE_LEVEL,
+ "adb name %p: caching alias target",
+ adbname);
+ adbname->expire_target = rdataset.ttl + now;
+ }
+ if (rdtype == dns_rdatatype_a)
+ adbname->fetch_err = FIND_ERR_SUCCESS;
+ else
+ adbname->fetch6_err = FIND_ERR_SUCCESS;
+ break;
+ }
+
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+
+ return (result);
+}
+
+static void
+fetch_callback(isc_task_t *task, isc_event_t *ev) {
+ dns_fetchevent_t *dev;
+ dns_adbname_t *name;
+ dns_adb_t *adb;
+ dns_adbfetch_t *fetch;
+ int bucket;
+ isc_eventtype_t ev_status;
+ isc_stdtime_t now;
+ isc_result_t result;
+ unsigned int address_type;
+ isc_boolean_t want_check_exit = ISC_FALSE;
+
+ UNUSED(task);
+
+ INSIST(ev->ev_type == DNS_EVENT_FETCHDONE);
+ dev = (dns_fetchevent_t *)ev;
+ name = ev->ev_arg;
+ INSIST(DNS_ADBNAME_VALID(name));
+ adb = name->adb;
+ INSIST(DNS_ADB_VALID(adb));
+
+ bucket = name->lock_bucket;
+ LOCK(&adb->namelocks[bucket]);
+
+ INSIST(NAME_FETCH_A(name) || NAME_FETCH_AAAA(name));
+ address_type = 0;
+ if (NAME_FETCH_A(name) && (name->fetch_a->fetch == dev->fetch)) {
+ address_type = DNS_ADBFIND_INET;
+ fetch = name->fetch_a;
+ name->fetch_a = NULL;
+ } else if (NAME_FETCH_AAAA(name)
+ && (name->fetch_aaaa->fetch == dev->fetch)) {
+ address_type = DNS_ADBFIND_INET6;
+ fetch = name->fetch_aaaa;
+ name->fetch_aaaa = NULL;
+ } else
+ fetch = NULL;
+
+ INSIST(address_type != 0 && fetch != NULL);
+
+ dns_resolver_destroyfetch(&fetch->fetch);
+ dev->fetch = NULL;
+
+ ev_status = DNS_EVENT_ADBNOMOREADDRESSES;
+
+ /*
+ * Cleanup things we don't care about.
+ */
+ if (dev->node != NULL)
+ dns_db_detachnode(dev->db, &dev->node);
+ if (dev->db != NULL)
+ dns_db_detach(&dev->db);
+
+ /*
+ * If this name is marked as dead, clean up, throwing away
+ * potentially good data.
+ */
+ if (NAME_DEAD(name)) {
+ free_adbfetch(adb, &fetch);
+ isc_event_free(&ev);
+
+ want_check_exit = kill_name(&name, DNS_EVENT_ADBCANCELED,
+ ISC_FALSE);
+
+ UNLOCK(&adb->namelocks[bucket]);
+
+ if (want_check_exit) {
+ LOCK(&adb->lock);
+ check_exit(adb);
+ UNLOCK(&adb->lock);
+ }
+
+ return;
+ }
+
+ isc_stdtime_get(&now);
+
+ /*
+ * If we got a negative cache response, remember it.
+ */
+ if (NCACHE_RESULT(dev->result)) {
+ dev->rdataset->ttl = ttlclamp(dev->rdataset->ttl);
+ if (address_type == DNS_ADBFIND_INET) {
+ DP(NCACHE_LEVEL, "adb fetch name %p: "
+ "caching negative entry for A (ttl %u)",
+ name, dev->rdataset->ttl);
+ name->expire_v4 = ISC_MIN(name->expire_v4,
+ dev->rdataset->ttl + now);
+ if (dev->result == DNS_R_NCACHENXDOMAIN)
+ name->fetch_err = FIND_ERR_NXDOMAIN;
+ else
+ name->fetch_err = FIND_ERR_NXRRSET;
+ inc_stats(adb, dns_resstatscounter_gluefetchv4fail);
+ } else {
+ DP(NCACHE_LEVEL, "adb fetch name %p: "
+ "caching negative entry for AAAA (ttl %u)",
+ name, dev->rdataset->ttl);
+ name->expire_v6 = ISC_MIN(name->expire_v6,
+ dev->rdataset->ttl + now);
+ if (dev->result == DNS_R_NCACHENXDOMAIN)
+ name->fetch6_err = FIND_ERR_NXDOMAIN;
+ else
+ name->fetch6_err = FIND_ERR_NXRRSET;
+ inc_stats(adb, dns_resstatscounter_gluefetchv6fail);
+ }
+ goto out;
+ }
+
+ /*
+ * Handle CNAME/DNAME.
+ */
+ if (dev->result == DNS_R_CNAME || dev->result == DNS_R_DNAME) {
+ dev->rdataset->ttl = ttlclamp(dev->rdataset->ttl);
+ clean_target(adb, &name->target);
+ name->expire_target = INT_MAX;
+ result = set_target(adb, &name->name,
+ dns_fixedname_name(&dev->foundname),
+ dev->rdataset,
+ &name->target);
+ if (result == ISC_R_SUCCESS) {
+ DP(NCACHE_LEVEL,
+ "adb fetch name %p: caching alias target",
+ name);
+ name->expire_target = dev->rdataset->ttl + now;
+ }
+ goto check_result;
+ }
+
+ /*
+ * Did we get back junk? If so, and there are no more fetches
+ * sitting out there, tell all the finds about it.
+ */
+ if (dev->result != ISC_R_SUCCESS) {
+ char buf[DNS_NAME_FORMATSIZE];
+
+ dns_name_format(&name->name, buf, sizeof(buf));
+ DP(DEF_LEVEL, "adb: fetch of '%s' %s failed: %s",
+ buf, address_type == DNS_ADBFIND_INET ? "A" : "AAAA",
+ dns_result_totext(dev->result));
+ /* XXXMLG Don't pound on bad servers. */
+ if (address_type == DNS_ADBFIND_INET) {
+ name->expire_v4 = ISC_MIN(name->expire_v4, now + 300);
+ name->fetch_err = FIND_ERR_FAILURE;
+ inc_stats(adb, dns_resstatscounter_gluefetchv4fail);
+ } else {
+ name->expire_v6 = ISC_MIN(name->expire_v6, now + 300);
+ name->fetch6_err = FIND_ERR_FAILURE;
+ inc_stats(adb, dns_resstatscounter_gluefetchv6fail);
+ }
+ goto out;
+ }
+
+ /*
+ * We got something potentially useful.
+ */
+ result = import_rdataset(name, &fetch->rdataset, now);
+
+ check_result:
+ if (result == ISC_R_SUCCESS) {
+ ev_status = DNS_EVENT_ADBMOREADDRESSES;
+ if (address_type == DNS_ADBFIND_INET)
+ name->fetch_err = FIND_ERR_SUCCESS;
+ else
+ name->fetch6_err = FIND_ERR_SUCCESS;
+ }
+
+ out:
+ free_adbfetch(adb, &fetch);
+ isc_event_free(&ev);
+
+ clean_finds_at_name(name, ev_status, address_type);
+
+ UNLOCK(&adb->namelocks[bucket]);
+}
+
+static isc_result_t
+fetch_name(dns_adbname_t *adbname,
+ isc_boolean_t start_at_zone,
+ dns_rdatatype_t type)
+{
+ isc_result_t result;
+ dns_adbfetch_t *fetch = NULL;
+ dns_adb_t *adb;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ dns_rdataset_t rdataset;
+ dns_rdataset_t *nameservers;
+ unsigned int options;
+
+ INSIST(DNS_ADBNAME_VALID(adbname));
+ adb = adbname->adb;
+ INSIST(DNS_ADB_VALID(adb));
+
+ INSIST((type == dns_rdatatype_a && !NAME_FETCH_V4(adbname)) ||
+ (type == dns_rdatatype_aaaa && !NAME_FETCH_V6(adbname)));
+
+ adbname->fetch_err = FIND_ERR_NOTFOUND;
+
+ name = NULL;
+ nameservers = NULL;
+ dns_rdataset_init(&rdataset);
+
+ options = DNS_FETCHOPT_NOVALIDATE;
+ if (start_at_zone) {
+ DP(ENTER_LEVEL,
+ "fetch_name: starting at zone for name %p",
+ adbname);
+ dns_fixedname_init(&fixed);
+ name = dns_fixedname_name(&fixed);
+ result = dns_view_findzonecut2(adb->view, &adbname->name, name,
+ 0, 0, ISC_TRUE, ISC_FALSE,
+ &rdataset, NULL);
+ if (result != ISC_R_SUCCESS && result != DNS_R_HINT)
+ goto cleanup;
+ nameservers = &rdataset;
+ options |= DNS_FETCHOPT_UNSHARED;
+ }
+
+ fetch = new_adbfetch(adb);
+ if (fetch == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ result = dns_resolver_createfetch(adb->view->resolver, &adbname->name,
+ type, name, nameservers, NULL,
+ options, adb->task, fetch_callback,
+ adbname, &fetch->rdataset, NULL,
+ &fetch->fetch);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ if (type == dns_rdatatype_a) {
+ adbname->fetch_a = fetch;
+ inc_stats(adb, dns_resstatscounter_gluefetchv4);
+ } else {
+ adbname->fetch_aaaa = fetch;
+ inc_stats(adb, dns_resstatscounter_gluefetchv6);
+ }
+ fetch = NULL; /* Keep us from cleaning this up below. */
+
+ cleanup:
+ if (fetch != NULL)
+ free_adbfetch(adb, &fetch);
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+
+ return (result);
+}
+
+/*
+ * XXXMLG Needs to take a find argument and an address info, no zone or adb,
+ * since these can be extracted from the find itself.
+ */
+isc_result_t
+dns_adb_marklame(dns_adb_t *adb, dns_adbaddrinfo_t *addr, dns_name_t *qname,
+ dns_rdatatype_t qtype, isc_stdtime_t expire_time)
+{
+ dns_adblameinfo_t *li;
+ int bucket;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+ REQUIRE(qname != NULL);
+
+ bucket = addr->entry->lock_bucket;
+ LOCK(&adb->entrylocks[bucket]);
+ li = ISC_LIST_HEAD(addr->entry->lameinfo);
+ while (li != NULL &&
+ (li->qtype != qtype || !dns_name_equal(qname, &li->qname)))
+ li = ISC_LIST_NEXT(li, plink);
+ if (li != NULL) {
+ if (expire_time > li->lame_timer)
+ li->lame_timer = expire_time;
+ goto unlock;
+ }
+ li = new_adblameinfo(adb, qname, qtype);
+ if (li == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto unlock;
+ }
+
+ li->lame_timer = expire_time;
+
+ ISC_LIST_PREPEND(addr->entry->lameinfo, li, plink);
+ unlock:
+ UNLOCK(&adb->entrylocks[bucket]);
+
+ return (result);
+}
+
+void
+dns_adb_adjustsrtt(dns_adb_t *adb, dns_adbaddrinfo_t *addr,
+ unsigned int rtt, unsigned int factor)
+{
+ int bucket;
+ unsigned int new_srtt;
+ isc_stdtime_t now;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+ REQUIRE(factor <= 10);
+
+ bucket = addr->entry->lock_bucket;
+ LOCK(&adb->entrylocks[bucket]);
+
+ if (factor == DNS_ADB_RTTADJAGE)
+ new_srtt = addr->entry->srtt * 98 / 100;
+ else
+ new_srtt = (addr->entry->srtt / 10 * factor)
+ + (rtt / 10 * (10 - factor));
+
+ addr->entry->srtt = new_srtt;
+ addr->srtt = new_srtt;
+
+ isc_stdtime_get(&now);
+ addr->entry->expires = now + ADB_ENTRY_WINDOW;
+
+ UNLOCK(&adb->entrylocks[bucket]);
+}
+
+void
+dns_adb_changeflags(dns_adb_t *adb, dns_adbaddrinfo_t *addr,
+ unsigned int bits, unsigned int mask)
+{
+ int bucket;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+
+ bucket = addr->entry->lock_bucket;
+ LOCK(&adb->entrylocks[bucket]);
+
+ addr->entry->flags = (addr->entry->flags & ~mask) | (bits & mask);
+ /*
+ * Note that we do not update the other bits in addr->flags with
+ * the most recent values from addr->entry->flags.
+ */
+ addr->flags = (addr->flags & ~mask) | (bits & mask);
+
+ UNLOCK(&adb->entrylocks[bucket]);
+}
+
+isc_result_t
+dns_adb_findaddrinfo(dns_adb_t *adb, isc_sockaddr_t *sa,
+ dns_adbaddrinfo_t **addrp, isc_stdtime_t now)
+{
+ int bucket;
+ dns_adbentry_t *entry;
+ dns_adbaddrinfo_t *addr;
+ isc_result_t result;
+ in_port_t port;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(addrp != NULL && *addrp == NULL);
+
+ UNUSED(now);
+
+ result = ISC_R_SUCCESS;
+ bucket = DNS_ADB_INVALIDBUCKET;
+ entry = find_entry_and_lock(adb, sa, &bucket);
+ if (adb->entry_sd[bucket]) {
+ result = ISC_R_SHUTTINGDOWN;
+ goto unlock;
+ }
+ if (entry == NULL) {
+ /*
+ * We don't know anything about this address.
+ */
+ entry = new_adbentry(adb);
+ if (entry == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto unlock;
+ }
+ entry->sockaddr = *sa;
+ link_entry(adb, bucket, entry);
+ DP(ENTER_LEVEL, "findaddrinfo: new entry %p", entry);
+ } else
+ DP(ENTER_LEVEL, "findaddrinfo: found entry %p", entry);
+
+ port = isc_sockaddr_getport(sa);
+ addr = new_adbaddrinfo(adb, entry, port);
+ if (addr == NULL) {
+ result = ISC_R_NOMEMORY;
+ } else {
+ inc_entry_refcnt(adb, entry, ISC_FALSE);
+ *addrp = addr;
+ }
+
+ unlock:
+ UNLOCK(&adb->entrylocks[bucket]);
+
+ return (result);
+}
+
+void
+dns_adb_freeaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **addrp) {
+ dns_adbaddrinfo_t *addr;
+ dns_adbentry_t *entry;
+ int bucket;
+ isc_stdtime_t now;
+ isc_boolean_t want_check_exit = ISC_FALSE;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(addrp != NULL);
+ addr = *addrp;
+ REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+ entry = addr->entry;
+ REQUIRE(DNS_ADBENTRY_VALID(entry));
+
+ isc_stdtime_get(&now);
+
+ *addrp = NULL;
+
+ bucket = addr->entry->lock_bucket;
+ LOCK(&adb->entrylocks[bucket]);
+
+ entry->expires = now + ADB_ENTRY_WINDOW;
+
+ want_check_exit = dec_entry_refcnt(adb, entry, ISC_FALSE);
+
+ UNLOCK(&adb->entrylocks[bucket]);
+
+ addr->entry = NULL;
+ free_adbaddrinfo(adb, &addr);
+
+ if (want_check_exit) {
+ LOCK(&adb->lock);
+ check_exit(adb);
+ UNLOCK(&adb->lock);
+ }
+}
+
+void
+dns_adb_flush(dns_adb_t *adb) {
+ unsigned int i;
+
+ INSIST(DNS_ADB_VALID(adb));
+
+ LOCK(&adb->lock);
+
+ /*
+ * Call our cleanup routines.
+ */
+ for (i = 0; i < NBUCKETS; i++)
+ RUNTIME_CHECK(cleanup_names(adb, i, INT_MAX) == ISC_FALSE);
+ for (i = 0; i < NBUCKETS; i++)
+ RUNTIME_CHECK(cleanup_entries(adb, i, INT_MAX) == ISC_FALSE);
+
+#ifdef DUMP_ADB_AFTER_CLEANING
+ dump_adb(adb, stdout, ISC_TRUE, INT_MAX);
+#endif
+
+ UNLOCK(&adb->lock);
+}
+
+void
+dns_adb_flushname(dns_adb_t *adb, dns_name_t *name) {
+ dns_adbname_t *adbname;
+ dns_adbname_t *nextname;
+ int bucket;
+
+ INSIST(DNS_ADB_VALID(adb));
+
+ LOCK(&adb->lock);
+ bucket = dns_name_hash(name, ISC_FALSE) % NBUCKETS;
+ LOCK(&adb->namelocks[bucket]);
+ adbname = ISC_LIST_HEAD(adb->names[bucket]);
+ while (adbname != NULL) {
+ nextname = ISC_LIST_NEXT(adbname, plink);
+ if (!NAME_DEAD(adbname) &&
+ dns_name_equal(name, &adbname->name)) {
+ RUNTIME_CHECK(kill_name(&adbname,
+ DNS_EVENT_ADBCANCELED,
+ ISC_TRUE) ==
+ ISC_FALSE);
+ }
+ adbname = nextname;
+ }
+ UNLOCK(&adb->namelocks[bucket]);
+ UNLOCK(&adb->lock);
+}
+
+static void
+water(void *arg, int mark) {
+ dns_adb_t *adb = arg;
+ isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER);
+
+ REQUIRE(DNS_ADB_VALID(adb));
+
+ DP(ISC_LOG_DEBUG(1),
+ "adb reached %s water mark", overmem ? "high" : "low");
+
+ /*
+ * We can't use adb->lock as there is potential for water
+ * to be called when adb->lock is held.
+ */
+ LOCK(&adb->overmemlock);
+ if (adb->overmem != overmem) {
+ adb->overmem = overmem;
+ isc_mem_waterack(adb->mctx, mark);
+ }
+ UNLOCK(&adb->overmemlock);
+}
+
+void
+dns_adb_setadbsize(dns_adb_t *adb, isc_uint32_t size) {
+ isc_uint32_t hiwater;
+ isc_uint32_t lowater;
+
+ INSIST(DNS_ADB_VALID(adb));
+
+ if (size != 0 && size < DNS_ADB_MINADBSIZE)
+ size = DNS_ADB_MINADBSIZE;
+
+ hiwater = size - (size >> 3); /* Approximately 7/8ths. */
+ lowater = size - (size >> 2); /* Approximately 3/4ths. */
+
+ if (size == 0 || hiwater == 0 || lowater == 0)
+ isc_mem_setwater(adb->mctx, water, adb, 0, 0);
+ else
+ isc_mem_setwater(adb->mctx, water, adb, hiwater, lowater);
+}
diff --git a/lib/dns/api b/lib/dns/api
new file mode 100644
index 0000000..fbbf923
--- /dev/null
+++ b/lib/dns/api
@@ -0,0 +1,3 @@
+LIBINTERFACE = 50
+LIBREVISION = 3
+LIBAGE = 0
diff --git a/lib/dns/byaddr.c b/lib/dns/byaddr.c
new file mode 100644
index 0000000..234d6b2
--- /dev/null
+++ b/lib/dns/byaddr.c
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: byaddr.c,v 1.39 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/mem.h>
+#include <isc/netaddr.h>
+#include <isc/print.h>
+#include <isc/string.h> /* Required for HP/UX (and others?) */
+#include <isc/task.h>
+#include <isc/util.h>
+
+#include <dns/byaddr.h>
+#include <dns/db.h>
+#include <dns/events.h>
+#include <dns/lookup.h>
+#include <dns/rdata.h>
+#include <dns/rdataset.h>
+#include <dns/rdatastruct.h>
+#include <dns/resolver.h>
+#include <dns/result.h>
+#include <dns/view.h>
+
+/*
+ * XXXRTH We could use a static event...
+ */
+
+struct dns_byaddr {
+ /* Unlocked. */
+ unsigned int magic;
+ isc_mem_t * mctx;
+ isc_mutex_t lock;
+ dns_fixedname_t name;
+ /* Locked by lock. */
+ unsigned int options;
+ dns_lookup_t * lookup;
+ isc_task_t * task;
+ dns_byaddrevent_t * event;
+ isc_boolean_t canceled;
+};
+
+#define BYADDR_MAGIC ISC_MAGIC('B', 'y', 'A', 'd')
+#define VALID_BYADDR(b) ISC_MAGIC_VALID(b, BYADDR_MAGIC)
+
+#define MAX_RESTARTS 16
+
+static char hex_digits[] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+};
+
+isc_result_t
+dns_byaddr_createptrname(isc_netaddr_t *address, isc_boolean_t nibble,
+ dns_name_t *name)
+{
+ /*
+ * We dropped bitstring labels, so all lookups will use nibbles.
+ */
+ UNUSED(nibble);
+
+ return (dns_byaddr_createptrname2(address,
+ DNS_BYADDROPT_IPV6INT, name));
+}
+
+isc_result_t
+dns_byaddr_createptrname2(isc_netaddr_t *address, unsigned int options,
+ dns_name_t *name)
+{
+ char textname[128];
+ unsigned char *bytes;
+ int i;
+ char *cp;
+ isc_buffer_t buffer;
+ unsigned int len;
+
+ REQUIRE(address != NULL);
+
+ /*
+ * We create the text representation and then convert to a
+ * dns_name_t. This is not maximally efficient, but it keeps all
+ * of the knowledge of wire format in the dns_name_ routines.
+ */
+
+ bytes = (unsigned char *)(&address->type);
+ if (address->family == AF_INET) {
+ (void)snprintf(textname, sizeof(textname),
+ "%u.%u.%u.%u.in-addr.arpa.",
+ (bytes[3] & 0xff),
+ (bytes[2] & 0xff),
+ (bytes[1] & 0xff),
+ (bytes[0] & 0xff));
+ } else if (address->family == AF_INET6) {
+ cp = textname;
+ for (i = 15; i >= 0; i--) {
+ *cp++ = hex_digits[bytes[i] & 0x0f];
+ *cp++ = '.';
+ *cp++ = hex_digits[(bytes[i] >> 4) & 0x0f];
+ *cp++ = '.';
+ }
+ if ((options & DNS_BYADDROPT_IPV6INT) != 0)
+ strcpy(cp, "ip6.int.");
+ else
+ strcpy(cp, "ip6.arpa.");
+ } else
+ return (ISC_R_NOTIMPLEMENTED);
+
+ len = (unsigned int)strlen(textname);
+ isc_buffer_init(&buffer, textname, len);
+ isc_buffer_add(&buffer, len);
+ return (dns_name_fromtext(name, &buffer, dns_rootname,
+ ISC_FALSE, NULL));
+}
+
+static inline isc_result_t
+copy_ptr_targets(dns_byaddr_t *byaddr, dns_rdataset_t *rdataset) {
+ isc_result_t result;
+ dns_name_t *name;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ /*
+ * The caller must be holding the byaddr's lock.
+ */
+
+ result = dns_rdataset_first(rdataset);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdata_ptr_t ptr;
+ dns_rdataset_current(rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &ptr, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ name = isc_mem_get(byaddr->mctx, sizeof(*name));
+ if (name == NULL) {
+ dns_rdata_freestruct(&ptr);
+ return (ISC_R_NOMEMORY);
+ }
+ dns_name_init(name, NULL);
+ result = dns_name_dup(&ptr.ptr, byaddr->mctx, name);
+ dns_rdata_freestruct(&ptr);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(byaddr->mctx, name, sizeof(*name));
+ return (ISC_R_NOMEMORY);
+ }
+ ISC_LIST_APPEND(byaddr->event->names, name, link);
+ dns_rdata_reset(&rdata);
+ result = dns_rdataset_next(rdataset);
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+
+ return (result);
+}
+
+static void
+lookup_done(isc_task_t *task, isc_event_t *event) {
+ dns_byaddr_t *byaddr = event->ev_arg;
+ dns_lookupevent_t *levent;
+ isc_result_t result;
+
+ REQUIRE(event->ev_type == DNS_EVENT_LOOKUPDONE);
+ REQUIRE(VALID_BYADDR(byaddr));
+ REQUIRE(byaddr->task == task);
+
+ UNUSED(task);
+
+ levent = (dns_lookupevent_t *)event;
+
+ if (levent->result == ISC_R_SUCCESS) {
+ result = copy_ptr_targets(byaddr, levent->rdataset);
+ byaddr->event->result = result;
+ } else
+ byaddr->event->result = levent->result;
+ isc_event_free(&event);
+ isc_task_sendanddetach(&byaddr->task, (isc_event_t **)&byaddr->event);
+}
+
+static void
+bevent_destroy(isc_event_t *event) {
+ dns_byaddrevent_t *bevent;
+ dns_name_t *name, *next_name;
+ isc_mem_t *mctx;
+
+ REQUIRE(event->ev_type == DNS_EVENT_BYADDRDONE);
+ mctx = event->ev_destroy_arg;
+ bevent = (dns_byaddrevent_t *)event;
+
+ for (name = ISC_LIST_HEAD(bevent->names);
+ name != NULL;
+ name = next_name) {
+ next_name = ISC_LIST_NEXT(name, link);
+ ISC_LIST_UNLINK(bevent->names, name, link);
+ dns_name_free(name, mctx);
+ isc_mem_put(mctx, name, sizeof(*name));
+ }
+ isc_mem_put(mctx, event, event->ev_size);
+}
+
+isc_result_t
+dns_byaddr_create(isc_mem_t *mctx, isc_netaddr_t *address, dns_view_t *view,
+ unsigned int options, isc_task_t *task,
+ isc_taskaction_t action, void *arg, dns_byaddr_t **byaddrp)
+{
+ isc_result_t result;
+ dns_byaddr_t *byaddr;
+ isc_event_t *ievent;
+
+ byaddr = isc_mem_get(mctx, sizeof(*byaddr));
+ if (byaddr == NULL)
+ return (ISC_R_NOMEMORY);
+ byaddr->mctx = mctx;
+ byaddr->options = options;
+
+ byaddr->event = isc_mem_get(mctx, sizeof(*byaddr->event));
+ if (byaddr->event == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_byaddr;
+ }
+ ISC_EVENT_INIT(byaddr->event, sizeof(*byaddr->event), 0, NULL,
+ DNS_EVENT_BYADDRDONE, action, arg, byaddr,
+ bevent_destroy, mctx);
+ byaddr->event->result = ISC_R_FAILURE;
+ ISC_LIST_INIT(byaddr->event->names);
+
+ byaddr->task = NULL;
+ isc_task_attach(task, &byaddr->task);
+
+ result = isc_mutex_init(&byaddr->lock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_event;
+
+ dns_fixedname_init(&byaddr->name);
+
+ result = dns_byaddr_createptrname2(address, options,
+ dns_fixedname_name(&byaddr->name));
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_lock;
+
+ byaddr->lookup = NULL;
+ result = dns_lookup_create(mctx, dns_fixedname_name(&byaddr->name),
+ dns_rdatatype_ptr, view, 0, task,
+ lookup_done, byaddr, &byaddr->lookup);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_lock;
+
+ byaddr->canceled = ISC_FALSE;
+ byaddr->magic = BYADDR_MAGIC;
+
+ *byaddrp = byaddr;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup_lock:
+ DESTROYLOCK(&byaddr->lock);
+
+ cleanup_event:
+ ievent = (isc_event_t *)byaddr->event;
+ isc_event_free(&ievent);
+ byaddr->event = NULL;
+
+ isc_task_detach(&byaddr->task);
+
+ cleanup_byaddr:
+ isc_mem_put(mctx, byaddr, sizeof(*byaddr));
+
+ return (result);
+}
+
+void
+dns_byaddr_cancel(dns_byaddr_t *byaddr) {
+ REQUIRE(VALID_BYADDR(byaddr));
+
+ LOCK(&byaddr->lock);
+
+ if (!byaddr->canceled) {
+ byaddr->canceled = ISC_TRUE;
+ if (byaddr->lookup != NULL)
+ dns_lookup_cancel(byaddr->lookup);
+ }
+
+ UNLOCK(&byaddr->lock);
+}
+
+void
+dns_byaddr_destroy(dns_byaddr_t **byaddrp) {
+ dns_byaddr_t *byaddr;
+
+ REQUIRE(byaddrp != NULL);
+ byaddr = *byaddrp;
+ REQUIRE(VALID_BYADDR(byaddr));
+ REQUIRE(byaddr->event == NULL);
+ REQUIRE(byaddr->task == NULL);
+ dns_lookup_destroy(&byaddr->lookup);
+
+ DESTROYLOCK(&byaddr->lock);
+ byaddr->magic = 0;
+ isc_mem_put(byaddr->mctx, byaddr, sizeof(*byaddr));
+
+ *byaddrp = NULL;
+}
diff --git a/lib/dns/cache.c b/lib/dns/cache.c
new file mode 100644
index 0000000..1b8c388
--- /dev/null
+++ b/lib/dns/cache.c
@@ -0,0 +1,1089 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: cache.c,v 1.80 2008/09/24 02:46:22 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/mem.h>
+#include <isc/string.h>
+#include <isc/task.h>
+#include <isc/time.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+#include <dns/cache.h>
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/events.h>
+#include <dns/lib.h>
+#include <dns/log.h>
+#include <dns/masterdump.h>
+#include <dns/rdata.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/result.h>
+
+#define CACHE_MAGIC ISC_MAGIC('$', '$', '$', '$')
+#define VALID_CACHE(cache) ISC_MAGIC_VALID(cache, CACHE_MAGIC)
+
+/*!
+ * Control incremental cleaning.
+ * DNS_CACHE_MINSIZE is how many bytes is the floor for dns_cache_setcachesize().
+ * See also DNS_CACHE_CLEANERINCREMENT
+ */
+#define DNS_CACHE_MINSIZE 2097152 /*%< Bytes. 2097152 = 2 MB */
+/*!
+ * Control incremental cleaning.
+ * CLEANERINCREMENT is how many nodes are examined in one pass.
+ * See also DNS_CACHE_MINSIZE
+ */
+#define DNS_CACHE_CLEANERINCREMENT 1000U /*%< Number of nodes. */
+
+/***
+ *** Types
+ ***/
+
+/*
+ * A cache_cleaner_t encapsulsates the state of the periodic
+ * cache cleaning.
+ */
+
+typedef struct cache_cleaner cache_cleaner_t;
+
+typedef enum {
+ cleaner_s_idle, /*%< Waiting for cleaning-interval to expire. */
+ cleaner_s_busy, /*%< Currently cleaning. */
+ cleaner_s_done /*%< Freed enough memory after being overmem. */
+} cleaner_state_t;
+
+/*
+ * Convenience macros for comprehensive assertion checking.
+ */
+#define CLEANER_IDLE(c) ((c)->state == cleaner_s_idle && \
+ (c)->resched_event != NULL)
+#define CLEANER_BUSY(c) ((c)->state == cleaner_s_busy && \
+ (c)->iterator != NULL && \
+ (c)->resched_event == NULL)
+
+/*%
+ * Accesses to a cache cleaner object are synchronized through
+ * task/event serialization, or locked from the cache object.
+ */
+struct cache_cleaner {
+ isc_mutex_t lock;
+ /*%<
+ * Locks overmem_event, overmem. Note: never allocate memory
+ * while holding this lock - that could lead to deadlock since
+ * the lock is take by water() which is called from the memory
+ * allocator.
+ */
+
+ dns_cache_t *cache;
+ isc_task_t *task;
+ unsigned int cleaning_interval; /*% The cleaning-interval from
+ named.conf, in seconds. */
+ isc_timer_t *cleaning_timer;
+ isc_event_t *resched_event; /*% Sent by cleaner task to
+ itself to reschedule */
+ isc_event_t *overmem_event;
+
+ dns_dbiterator_t *iterator;
+ unsigned int increment; /*% Number of names to
+ clean in one increment */
+ cleaner_state_t state; /*% Idle/Busy. */
+ isc_boolean_t overmem; /*% The cache is in an overmem state. */
+ isc_boolean_t replaceiterator;
+};
+
+/*%
+ * The actual cache object.
+ */
+
+struct dns_cache {
+ /* Unlocked. */
+ unsigned int magic;
+ isc_mutex_t lock;
+ isc_mutex_t filelock;
+ isc_mem_t *mctx;
+
+ /* Locked by 'lock'. */
+ int references;
+ int live_tasks;
+ dns_rdataclass_t rdclass;
+ dns_db_t *db;
+ cache_cleaner_t cleaner;
+ char *db_type;
+ int db_argc;
+ char **db_argv;
+
+ /* Locked by 'filelock'. */
+ char *filename;
+ /* Access to the on-disk cache file is also locked by 'filelock'. */
+};
+
+/***
+ *** Functions
+ ***/
+
+static isc_result_t
+cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
+ isc_timermgr_t *timermgr, cache_cleaner_t *cleaner);
+
+static void
+cleaning_timer_action(isc_task_t *task, isc_event_t *event);
+
+static void
+incremental_cleaning_action(isc_task_t *task, isc_event_t *event);
+
+static void
+cleaner_shutdown_action(isc_task_t *task, isc_event_t *event);
+
+static void
+overmem_cleaning_action(isc_task_t *task, isc_event_t *event);
+
+static inline isc_result_t
+cache_create_db(dns_cache_t *cache, dns_db_t **db) {
+ return (dns_db_create(cache->mctx, cache->db_type, dns_rootname,
+ dns_dbtype_cache, cache->rdclass,
+ cache->db_argc, cache->db_argv, db));
+}
+
+isc_result_t
+dns_cache_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
+ isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
+ const char *db_type, unsigned int db_argc, char **db_argv,
+ dns_cache_t **cachep)
+{
+ isc_result_t result;
+ dns_cache_t *cache;
+ int i;
+
+ REQUIRE(cachep != NULL);
+ REQUIRE(*cachep == NULL);
+ REQUIRE(mctx != NULL);
+
+ cache = isc_mem_get(mctx, sizeof(*cache));
+ if (cache == NULL)
+ return (ISC_R_NOMEMORY);
+
+ cache->mctx = NULL;
+ isc_mem_attach(mctx, &cache->mctx);
+
+ result = isc_mutex_init(&cache->lock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_mem;
+
+ result = isc_mutex_init(&cache->filelock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_lock;
+
+ cache->references = 1;
+ cache->live_tasks = 0;
+ cache->rdclass = rdclass;
+
+ cache->db_type = isc_mem_strdup(mctx, db_type);
+ if (cache->db_type == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_filelock;
+ }
+
+ cache->db_argc = db_argc;
+ if (cache->db_argc == 0)
+ cache->db_argv = NULL;
+ else {
+ cache->db_argv = isc_mem_get(mctx,
+ cache->db_argc * sizeof(char *));
+ if (cache->db_argv == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_dbtype;
+ }
+ for (i = 0; i < cache->db_argc; i++)
+ cache->db_argv[i] = NULL;
+ for (i = 0; i < cache->db_argc; i++) {
+ cache->db_argv[i] = isc_mem_strdup(mctx, db_argv[i]);
+ if (cache->db_argv[i] == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_dbargv;
+ }
+ }
+ }
+
+ cache->db = NULL;
+ result = cache_create_db(cache, &cache->db);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_dbargv;
+
+ cache->filename = NULL;
+
+ cache->magic = CACHE_MAGIC;
+
+ /*
+ * RBT-type cache DB has its own mechanism of cache cleaning and doesn't
+ * need the control of the generic cleaner.
+ */
+ if (strcmp(db_type, "rbt") == 0)
+ result = cache_cleaner_init(cache, NULL, NULL, &cache->cleaner);
+ else {
+ result = cache_cleaner_init(cache, taskmgr, timermgr,
+ &cache->cleaner);
+ }
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_db;
+
+ *cachep = cache;
+ return (ISC_R_SUCCESS);
+
+ cleanup_db:
+ dns_db_detach(&cache->db);
+ cleanup_dbargv:
+ for (i = 0; i < cache->db_argc; i++)
+ if (cache->db_argv[i] != NULL)
+ isc_mem_free(mctx, cache->db_argv[i]);
+ if (cache->db_argv != NULL)
+ isc_mem_put(mctx, cache->db_argv,
+ cache->db_argc * sizeof(char *));
+ cleanup_dbtype:
+ isc_mem_free(mctx, cache->db_type);
+ cleanup_filelock:
+ DESTROYLOCK(&cache->filelock);
+ cleanup_lock:
+ DESTROYLOCK(&cache->lock);
+ cleanup_mem:
+ isc_mem_put(mctx, cache, sizeof(*cache));
+ isc_mem_detach(&mctx);
+ return (result);
+}
+
+static void
+cache_free(dns_cache_t *cache) {
+ isc_mem_t *mctx;
+ int i;
+
+ REQUIRE(VALID_CACHE(cache));
+ REQUIRE(cache->references == 0);
+
+ isc_mem_setwater(cache->mctx, NULL, NULL, 0, 0);
+
+ if (cache->cleaner.task != NULL)
+ isc_task_detach(&cache->cleaner.task);
+
+ if (cache->cleaner.overmem_event != NULL)
+ isc_event_free(&cache->cleaner.overmem_event);
+
+ if (cache->cleaner.resched_event != NULL)
+ isc_event_free(&cache->cleaner.resched_event);
+
+ if (cache->cleaner.iterator != NULL)
+ dns_dbiterator_destroy(&cache->cleaner.iterator);
+
+ DESTROYLOCK(&cache->cleaner.lock);
+
+ if (cache->filename) {
+ isc_mem_free(cache->mctx, cache->filename);
+ cache->filename = NULL;
+ }
+
+ if (cache->db != NULL)
+ dns_db_detach(&cache->db);
+
+ if (cache->db_argv != NULL) {
+ for (i = 0; i < cache->db_argc; i++)
+ if (cache->db_argv[i] != NULL)
+ isc_mem_free(cache->mctx, cache->db_argv[i]);
+ isc_mem_put(cache->mctx, cache->db_argv,
+ cache->db_argc * sizeof(char *));
+ }
+
+ if (cache->db_type != NULL)
+ isc_mem_free(cache->mctx, cache->db_type);
+
+ DESTROYLOCK(&cache->lock);
+ DESTROYLOCK(&cache->filelock);
+ cache->magic = 0;
+ mctx = cache->mctx;
+ isc_mem_put(cache->mctx, cache, sizeof(*cache));
+ isc_mem_detach(&mctx);
+}
+
+
+void
+dns_cache_attach(dns_cache_t *cache, dns_cache_t **targetp) {
+
+ REQUIRE(VALID_CACHE(cache));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ LOCK(&cache->lock);
+ cache->references++;
+ UNLOCK(&cache->lock);
+
+ *targetp = cache;
+}
+
+void
+dns_cache_detach(dns_cache_t **cachep) {
+ dns_cache_t *cache;
+ isc_boolean_t free_cache = ISC_FALSE;
+
+ REQUIRE(cachep != NULL);
+ cache = *cachep;
+ REQUIRE(VALID_CACHE(cache));
+
+ LOCK(&cache->lock);
+ REQUIRE(cache->references > 0);
+ cache->references--;
+ if (cache->references == 0) {
+ cache->cleaner.overmem = ISC_FALSE;
+ free_cache = ISC_TRUE;
+ }
+
+ *cachep = NULL;
+
+ if (free_cache) {
+ /*
+ * When the cache is shut down, dump it to a file if one is
+ * specified.
+ */
+ isc_result_t result = dns_cache_dump(cache);
+ if (result != ISC_R_SUCCESS)
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
+ "error dumping cache: %s ",
+ isc_result_totext(result));
+
+ /*
+ * If the cleaner task exists, let it free the cache.
+ */
+ if (cache->live_tasks > 0) {
+ isc_task_shutdown(cache->cleaner.task);
+ free_cache = ISC_FALSE;
+ }
+ }
+
+ UNLOCK(&cache->lock);
+
+ if (free_cache)
+ cache_free(cache);
+}
+
+void
+dns_cache_attachdb(dns_cache_t *cache, dns_db_t **dbp) {
+ REQUIRE(VALID_CACHE(cache));
+ REQUIRE(dbp != NULL && *dbp == NULL);
+ REQUIRE(cache->db != NULL);
+
+ LOCK(&cache->lock);
+ dns_db_attach(cache->db, dbp);
+ UNLOCK(&cache->lock);
+
+}
+
+isc_result_t
+dns_cache_setfilename(dns_cache_t *cache, const char *filename) {
+ char *newname;
+
+ REQUIRE(VALID_CACHE(cache));
+ REQUIRE(filename != NULL);
+
+ newname = isc_mem_strdup(cache->mctx, filename);
+ if (newname == NULL)
+ return (ISC_R_NOMEMORY);
+
+ LOCK(&cache->filelock);
+ if (cache->filename)
+ isc_mem_free(cache->mctx, cache->filename);
+ cache->filename = newname;
+ UNLOCK(&cache->filelock);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_cache_load(dns_cache_t *cache) {
+ isc_result_t result;
+
+ REQUIRE(VALID_CACHE(cache));
+
+ if (cache->filename == NULL)
+ return (ISC_R_SUCCESS);
+
+ LOCK(&cache->filelock);
+ result = dns_db_load(cache->db, cache->filename);
+ UNLOCK(&cache->filelock);
+
+ return (result);
+}
+
+isc_result_t
+dns_cache_dump(dns_cache_t *cache) {
+ isc_result_t result;
+
+ REQUIRE(VALID_CACHE(cache));
+
+ if (cache->filename == NULL)
+ return (ISC_R_SUCCESS);
+
+ LOCK(&cache->filelock);
+ result = dns_master_dump(cache->mctx, cache->db, NULL,
+ &dns_master_style_cache, cache->filename);
+ UNLOCK(&cache->filelock);
+
+ return (result);
+}
+
+void
+dns_cache_setcleaninginterval(dns_cache_t *cache, unsigned int t) {
+ isc_interval_t interval;
+ isc_result_t result;
+
+ LOCK(&cache->lock);
+
+ /*
+ * It may be the case that the cache has already shut down.
+ * If so, it has no timer.
+ */
+ if (cache->cleaner.cleaning_timer == NULL)
+ goto unlock;
+
+ cache->cleaner.cleaning_interval = t;
+
+ if (t == 0) {
+ result = isc_timer_reset(cache->cleaner.cleaning_timer,
+ isc_timertype_inactive,
+ NULL, NULL, ISC_TRUE);
+ } else {
+ isc_interval_set(&interval, cache->cleaner.cleaning_interval,
+ 0);
+ result = isc_timer_reset(cache->cleaner.cleaning_timer,
+ isc_timertype_ticker,
+ NULL, &interval, ISC_FALSE);
+ }
+ if (result != ISC_R_SUCCESS)
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
+ "could not set cache cleaning interval: %s",
+ isc_result_totext(result));
+
+ unlock:
+ UNLOCK(&cache->lock);
+}
+
+/*
+ * Initialize the cache cleaner object at *cleaner.
+ * Space for the object must be allocated by the caller.
+ */
+
+static isc_result_t
+cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
+ isc_timermgr_t *timermgr, cache_cleaner_t *cleaner)
+{
+ isc_result_t result;
+
+ result = isc_mutex_init(&cleaner->lock);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+
+ cleaner->increment = DNS_CACHE_CLEANERINCREMENT;
+ cleaner->state = cleaner_s_idle;
+ cleaner->cache = cache;
+ cleaner->iterator = NULL;
+ cleaner->overmem = ISC_FALSE;
+ cleaner->replaceiterator = ISC_FALSE;
+
+ cleaner->task = NULL;
+ cleaner->cleaning_timer = NULL;
+ cleaner->resched_event = NULL;
+ cleaner->overmem_event = NULL;
+
+ result = dns_db_createiterator(cleaner->cache->db, ISC_FALSE,
+ &cleaner->iterator);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ if (taskmgr != NULL && timermgr != NULL) {
+ result = isc_task_create(taskmgr, 1, &cleaner->task);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_task_create() failed: %s",
+ dns_result_totext(result));
+ result = ISC_R_UNEXPECTED;
+ goto cleanup;
+ }
+ cleaner->cache->live_tasks++;
+ isc_task_setname(cleaner->task, "cachecleaner", cleaner);
+
+ result = isc_task_onshutdown(cleaner->task,
+ cleaner_shutdown_action, cache);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "cache cleaner: "
+ "isc_task_onshutdown() failed: %s",
+ dns_result_totext(result));
+ goto cleanup;
+ }
+
+ cleaner->cleaning_interval = 0; /* Initially turned off. */
+ result = isc_timer_create(timermgr, isc_timertype_inactive,
+ NULL, NULL, cleaner->task,
+ cleaning_timer_action, cleaner,
+ &cleaner->cleaning_timer);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_timer_create() failed: %s",
+ dns_result_totext(result));
+ result = ISC_R_UNEXPECTED;
+ goto cleanup;
+ }
+
+ cleaner->resched_event =
+ isc_event_allocate(cache->mctx, cleaner,
+ DNS_EVENT_CACHECLEAN,
+ incremental_cleaning_action,
+ cleaner, sizeof(isc_event_t));
+ if (cleaner->resched_event == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ cleaner->overmem_event =
+ isc_event_allocate(cache->mctx, cleaner,
+ DNS_EVENT_CACHEOVERMEM,
+ overmem_cleaning_action,
+ cleaner, sizeof(isc_event_t));
+ if (cleaner->overmem_event == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ }
+
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (cleaner->overmem_event != NULL)
+ isc_event_free(&cleaner->overmem_event);
+ if (cleaner->resched_event != NULL)
+ isc_event_free(&cleaner->resched_event);
+ if (cleaner->cleaning_timer != NULL)
+ isc_timer_detach(&cleaner->cleaning_timer);
+ if (cleaner->task != NULL)
+ isc_task_detach(&cleaner->task);
+ if (cleaner->iterator != NULL)
+ dns_dbiterator_destroy(&cleaner->iterator);
+ DESTROYLOCK(&cleaner->lock);
+ fail:
+ return (result);
+}
+
+static void
+begin_cleaning(cache_cleaner_t *cleaner) {
+ isc_result_t result = ISC_R_SUCCESS;
+
+ REQUIRE(CLEANER_IDLE(cleaner));
+
+ /*
+ * Create an iterator, if it does not already exist, and
+ * position it at the beginning of the cache.
+ */
+ if (cleaner->iterator == NULL)
+ result = dns_db_createiterator(cleaner->cache->db, ISC_FALSE,
+ &cleaner->iterator);
+ if (result != ISC_R_SUCCESS)
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
+ "cache cleaner could not create "
+ "iterator: %s", isc_result_totext(result));
+ else {
+ dns_dbiterator_setcleanmode(cleaner->iterator, ISC_TRUE);
+ result = dns_dbiterator_first(cleaner->iterator);
+ }
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * If the result is ISC_R_NOMORE, the database is empty,
+ * so there is nothing to be cleaned.
+ */
+ if (result != ISC_R_NOMORE && cleaner->iterator != NULL) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "cache cleaner: "
+ "dns_dbiterator_first() failed: %s",
+ dns_result_totext(result));
+ dns_dbiterator_destroy(&cleaner->iterator);
+ } else if (cleaner->iterator != NULL) {
+ result = dns_dbiterator_pause(cleaner->iterator);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ }
+ } else {
+ /*
+ * Pause the iterator to free its lock.
+ */
+ result = dns_dbiterator_pause(cleaner->iterator);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1),
+ "begin cache cleaning, mem inuse %lu",
+ (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
+ cleaner->state = cleaner_s_busy;
+ isc_task_send(cleaner->task, &cleaner->resched_event);
+ }
+
+ return;
+}
+
+static void
+end_cleaning(cache_cleaner_t *cleaner, isc_event_t *event) {
+ isc_result_t result;
+
+ REQUIRE(CLEANER_BUSY(cleaner));
+ REQUIRE(event != NULL);
+
+ result = dns_dbiterator_pause(cleaner->iterator);
+ if (result != ISC_R_SUCCESS)
+ dns_dbiterator_destroy(&cleaner->iterator);
+
+ dns_cache_setcleaninginterval(cleaner->cache,
+ cleaner->cleaning_interval);
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
+ ISC_LOG_DEBUG(1), "end cache cleaning, mem inuse %lu",
+ (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
+
+ cleaner->state = cleaner_s_idle;
+ cleaner->resched_event = event;
+}
+
+/*
+ * This is run once for every cache-cleaning-interval as defined in named.conf.
+ */
+static void
+cleaning_timer_action(isc_task_t *task, isc_event_t *event) {
+ cache_cleaner_t *cleaner = event->ev_arg;
+
+ UNUSED(task);
+
+ INSIST(task == cleaner->task);
+ INSIST(event->ev_type == ISC_TIMEREVENT_TICK);
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
+ ISC_LOG_DEBUG(1), "cache cleaning timer fired, "
+ "cleaner state = %d", cleaner->state);
+
+ if (cleaner->state == cleaner_s_idle)
+ begin_cleaning(cleaner);
+
+ isc_event_free(&event);
+}
+
+/*
+ * This is called when the cache either surpasses its upper limit
+ * or shrinks beyond its lower limit.
+ */
+static void
+overmem_cleaning_action(isc_task_t *task, isc_event_t *event) {
+ cache_cleaner_t *cleaner = event->ev_arg;
+ isc_boolean_t want_cleaning = ISC_FALSE;
+
+ UNUSED(task);
+
+ INSIST(task == cleaner->task);
+ INSIST(event->ev_type == DNS_EVENT_CACHEOVERMEM);
+ INSIST(cleaner->overmem_event == NULL);
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
+ ISC_LOG_DEBUG(1), "overmem_cleaning_action called, "
+ "overmem = %d, state = %d", cleaner->overmem,
+ cleaner->state);
+
+ LOCK(&cleaner->lock);
+
+ if (cleaner->overmem) {
+ if (cleaner->state == cleaner_s_idle)
+ want_cleaning = ISC_TRUE;
+ } else {
+ if (cleaner->state == cleaner_s_busy)
+ /*
+ * end_cleaning() can't be called here because
+ * then both cleaner->overmem_event and
+ * cleaner->resched_event will point to this
+ * event. Set the state to done, and then
+ * when the incremental_cleaning_action() event
+ * is posted, it will handle the end_cleaning.
+ */
+ cleaner->state = cleaner_s_done;
+ }
+
+ cleaner->overmem_event = event;
+
+ UNLOCK(&cleaner->lock);
+
+ if (want_cleaning)
+ begin_cleaning(cleaner);
+}
+
+/*
+ * Do incremental cleaning.
+ */
+static void
+incremental_cleaning_action(isc_task_t *task, isc_event_t *event) {
+ cache_cleaner_t *cleaner = event->ev_arg;
+ isc_result_t result;
+ unsigned int n_names;
+ isc_time_t start;
+
+ UNUSED(task);
+
+ INSIST(task == cleaner->task);
+ INSIST(event->ev_type == DNS_EVENT_CACHECLEAN);
+
+ if (cleaner->state == cleaner_s_done) {
+ cleaner->state = cleaner_s_busy;
+ end_cleaning(cleaner, event);
+ LOCK(&cleaner->cache->lock);
+ LOCK(&cleaner->lock);
+ if (cleaner->replaceiterator) {
+ dns_dbiterator_destroy(&cleaner->iterator);
+ (void) dns_db_createiterator(cleaner->cache->db,
+ ISC_FALSE,
+ &cleaner->iterator);
+ cleaner->replaceiterator = ISC_FALSE;
+ }
+ UNLOCK(&cleaner->lock);
+ UNLOCK(&cleaner->cache->lock);
+ return;
+ }
+
+ INSIST(CLEANER_BUSY(cleaner));
+
+ n_names = cleaner->increment;
+
+ REQUIRE(DNS_DBITERATOR_VALID(cleaner->iterator));
+
+ isc_time_now(&start);
+ while (n_names-- > 0) {
+ dns_dbnode_t *node = NULL;
+
+ result = dns_dbiterator_current(cleaner->iterator, &node,
+ NULL);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "cache cleaner: dns_dbiterator_current() "
+ "failed: %s", dns_result_totext(result));
+
+ end_cleaning(cleaner, event);
+ return;
+ }
+
+ /*
+ * The node was not needed, but was required by
+ * dns_dbiterator_current(). Give up its reference.
+ */
+ dns_db_detachnode(cleaner->cache->db, &node);
+
+ /*
+ * Step to the next node.
+ */
+ result = dns_dbiterator_next(cleaner->iterator);
+
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * Either the end was reached (ISC_R_NOMORE) or
+ * some error was signaled. If the cache is still
+ * overmem and no error was encountered,
+ * keep trying to clean it, otherwise stop cleaning.
+ */
+ if (result != ISC_R_NOMORE)
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "cache cleaner: "
+ "dns_dbiterator_next() "
+ "failed: %s",
+ dns_result_totext(result));
+ else if (cleaner->overmem) {
+ result = dns_dbiterator_first(cleaner->
+ iterator);
+ if (result == ISC_R_SUCCESS) {
+ isc_log_write(dns_lctx,
+ DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_CACHE,
+ ISC_LOG_DEBUG(1),
+ "cache cleaner: "
+ "still overmem, "
+ "reset and try again");
+ continue;
+ }
+ }
+
+ end_cleaning(cleaner, event);
+ return;
+ }
+ }
+
+ /*
+ * We have successfully performed a cleaning increment but have
+ * not gone through the entire cache. Free the iterator locks
+ * and reschedule another batch. If it fails, just try to continue
+ * anyway.
+ */
+ result = dns_dbiterator_pause(cleaner->iterator);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
+ ISC_LOG_DEBUG(1), "cache cleaner: checked %u nodes, "
+ "mem inuse %lu, sleeping", cleaner->increment,
+ (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
+
+ isc_task_send(task, &event);
+ INSIST(CLEANER_BUSY(cleaner));
+ return;
+}
+
+/*
+ * Do immediate cleaning.
+ */
+isc_result_t
+dns_cache_clean(dns_cache_t *cache, isc_stdtime_t now) {
+ isc_result_t result;
+ dns_dbiterator_t *iterator = NULL;
+
+ REQUIRE(VALID_CACHE(cache));
+
+ result = dns_db_createiterator(cache->db, 0, &iterator);
+ if (result != ISC_R_SUCCESS)
+ return result;
+
+ result = dns_dbiterator_first(iterator);
+
+ while (result == ISC_R_SUCCESS) {
+ dns_dbnode_t *node = NULL;
+ result = dns_dbiterator_current(iterator, &node,
+ (dns_name_t *)NULL);
+ if (result != ISC_R_SUCCESS)
+ break;
+
+ /*
+ * Check TTLs, mark expired rdatasets stale.
+ */
+ result = dns_db_expirenode(cache->db, node, now);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "cache cleaner: dns_db_expirenode() "
+ "failed: %s",
+ dns_result_totext(result));
+ /*
+ * Continue anyway.
+ */
+ }
+
+ /*
+ * This is where the actual freeing takes place.
+ */
+ dns_db_detachnode(cache->db, &node);
+
+ result = dns_dbiterator_next(iterator);
+ }
+
+ dns_dbiterator_destroy(&iterator);
+
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+
+ return (result);
+}
+
+static void
+water(void *arg, int mark) {
+ dns_cache_t *cache = arg;
+ isc_boolean_t overmem = ISC_TF(mark == ISC_MEM_HIWATER);
+
+ REQUIRE(VALID_CACHE(cache));
+
+ LOCK(&cache->cleaner.lock);
+
+ if (overmem != cache->cleaner.overmem) {
+ dns_db_overmem(cache->db, overmem);
+ cache->cleaner.overmem = overmem;
+ isc_mem_waterack(cache->mctx, mark);
+ }
+
+ if (cache->cleaner.overmem_event != NULL)
+ isc_task_send(cache->cleaner.task,
+ &cache->cleaner.overmem_event);
+
+ UNLOCK(&cache->cleaner.lock);
+}
+
+void
+dns_cache_setcachesize(dns_cache_t *cache, isc_uint32_t size) {
+ isc_uint32_t lowater;
+ isc_uint32_t hiwater;
+
+ REQUIRE(VALID_CACHE(cache));
+
+ /*
+ * Impose a minumum cache size; pathological things happen if there
+ * is too little room.
+ */
+ if (size != 0 && size < DNS_CACHE_MINSIZE)
+ size = DNS_CACHE_MINSIZE;
+
+ hiwater = size - (size >> 3); /* Approximately 7/8ths. */
+ lowater = size - (size >> 2); /* Approximately 3/4ths. */
+
+ /*
+ * If the cache was overmem and cleaning, but now with the new limits
+ * it is no longer in an overmem condition, then the next
+ * isc_mem_put for cache memory will do the right thing and trigger
+ * water().
+ */
+
+ if (size == 0 || hiwater == 0 || lowater == 0)
+ /*
+ * Disable cache memory limiting.
+ */
+ isc_mem_setwater(cache->mctx, water, cache, 0, 0);
+ else
+ /*
+ * Establish new cache memory limits (either for the first
+ * time, or replacing other limits).
+ */
+ isc_mem_setwater(cache->mctx, water, cache, hiwater, lowater);
+}
+
+/*
+ * The cleaner task is shutting down; do the necessary cleanup.
+ */
+static void
+cleaner_shutdown_action(isc_task_t *task, isc_event_t *event) {
+ dns_cache_t *cache = event->ev_arg;
+ isc_boolean_t should_free = ISC_FALSE;
+
+ UNUSED(task);
+
+ INSIST(task == cache->cleaner.task);
+ INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
+
+ if (CLEANER_BUSY(&cache->cleaner))
+ end_cleaning(&cache->cleaner, event);
+ else
+ isc_event_free(&event);
+
+ LOCK(&cache->lock);
+
+ cache->live_tasks--;
+ INSIST(cache->live_tasks == 0);
+
+ if (cache->references == 0)
+ should_free = ISC_TRUE;
+
+ /*
+ * By detaching the timer in the context of its task,
+ * we are guaranteed that there will be no further timer
+ * events.
+ */
+ if (cache->cleaner.cleaning_timer != NULL)
+ isc_timer_detach(&cache->cleaner.cleaning_timer);
+
+ /* Make sure we don't reschedule anymore. */
+ (void)isc_task_purge(task, NULL, DNS_EVENT_CACHECLEAN, NULL);
+
+ UNLOCK(&cache->lock);
+
+ if (should_free)
+ cache_free(cache);
+}
+
+isc_result_t
+dns_cache_flush(dns_cache_t *cache) {
+ dns_db_t *db = NULL;
+ isc_result_t result;
+
+ result = cache_create_db(cache, &db);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ LOCK(&cache->lock);
+ LOCK(&cache->cleaner.lock);
+ if (cache->cleaner.state == cleaner_s_idle) {
+ if (cache->cleaner.iterator != NULL)
+ dns_dbiterator_destroy(&cache->cleaner.iterator);
+ (void) dns_db_createiterator(db, ISC_FALSE,
+ &cache->cleaner.iterator);
+ } else {
+ if (cache->cleaner.state == cleaner_s_busy)
+ cache->cleaner.state = cleaner_s_done;
+ cache->cleaner.replaceiterator = ISC_TRUE;
+ }
+ dns_db_detach(&cache->db);
+ cache->db = db;
+ UNLOCK(&cache->cleaner.lock);
+ UNLOCK(&cache->lock);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_cache_flushname(dns_cache_t *cache, dns_name_t *name) {
+ isc_result_t result;
+ dns_rdatasetiter_t *iter = NULL;
+ dns_dbnode_t *node = NULL;
+ dns_db_t *db = NULL;
+
+ LOCK(&cache->lock);
+ if (cache->db != NULL)
+ dns_db_attach(cache->db, &db);
+ UNLOCK(&cache->lock);
+ if (db == NULL)
+ return (ISC_R_SUCCESS);
+ result = dns_db_findnode(cache->db, name, ISC_FALSE, &node);
+ if (result == ISC_R_NOTFOUND) {
+ result = ISC_R_SUCCESS;
+ goto cleanup_db;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_db;
+
+ result = dns_db_allrdatasets(cache->db, node, NULL,
+ (isc_stdtime_t)0, &iter);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_node;
+
+ for (result = dns_rdatasetiter_first(iter);
+ result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(iter))
+ {
+ dns_rdataset_t rdataset;
+ dns_rdataset_init(&rdataset);
+
+ dns_rdatasetiter_current(iter, &rdataset);
+ result = dns_db_deleterdataset(cache->db, node, NULL,
+ rdataset.type, rdataset.covers);
+ dns_rdataset_disassociate(&rdataset);
+ if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED)
+ break;
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+
+ dns_rdatasetiter_destroy(&iter);
+
+ cleanup_node:
+ dns_db_detachnode(cache->db, &node);
+
+ cleanup_db:
+ dns_db_detach(&db);
+ return (result);
+}
diff --git a/lib/dns/callbacks.c b/lib/dns/callbacks.c
new file mode 100644
index 0000000..928f37d
--- /dev/null
+++ b/lib/dns/callbacks.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: callbacks.c,v 1.17 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/util.h>
+
+#include <dns/callbacks.h>
+#include <dns/log.h>
+
+static void
+stdio_error_warn_callback(dns_rdatacallbacks_t *, const char *, ...)
+ ISC_FORMAT_PRINTF(2, 3);
+
+static void
+isclog_error_callback(dns_rdatacallbacks_t *callbacks, const char *fmt, ...)
+ ISC_FORMAT_PRINTF(2, 3);
+
+static void
+isclog_warn_callback(dns_rdatacallbacks_t *callbacks, const char *fmt, ...)
+ ISC_FORMAT_PRINTF(2, 3);
+
+/*
+ * Private
+ */
+
+static void
+stdio_error_warn_callback(dns_rdatacallbacks_t *callbacks,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ UNUSED(callbacks);
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+}
+
+static void
+isclog_error_callback(dns_rdatacallbacks_t *callbacks, const char *fmt, ...) {
+ va_list ap;
+
+ UNUSED(callbacks);
+
+ va_start(ap, fmt);
+ isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTER, /* XXX */
+ ISC_LOG_ERROR, fmt, ap);
+ va_end(ap);
+}
+
+static void
+isclog_warn_callback(dns_rdatacallbacks_t *callbacks, const char *fmt, ...) {
+ va_list ap;
+
+ UNUSED(callbacks);
+
+ va_start(ap, fmt);
+
+ isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTER, /* XXX */
+ ISC_LOG_WARNING, fmt, ap);
+ va_end(ap);
+}
+
+static void
+dns_rdatacallbacks_initcommon(dns_rdatacallbacks_t *callbacks) {
+ REQUIRE(callbacks != NULL);
+
+ callbacks->add = NULL;
+ callbacks->add_private = NULL;
+ callbacks->error_private = NULL;
+ callbacks->warn_private = NULL;
+}
+
+/*
+ * Public.
+ */
+
+void
+dns_rdatacallbacks_init(dns_rdatacallbacks_t *callbacks) {
+ dns_rdatacallbacks_initcommon(callbacks);
+ callbacks->error = isclog_error_callback;
+ callbacks->warn = isclog_warn_callback;
+}
+
+void
+dns_rdatacallbacks_init_stdio(dns_rdatacallbacks_t *callbacks) {
+ dns_rdatacallbacks_initcommon(callbacks);
+ callbacks->error = stdio_error_warn_callback;
+ callbacks->warn = stdio_error_warn_callback;
+}
+
diff --git a/lib/dns/compress.c b/lib/dns/compress.c
new file mode 100644
index 0000000..11473ee
--- /dev/null
+++ b/lib/dns/compress.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: compress.c,v 1.59 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file */
+
+#define DNS_NAME_USEINLINE 1
+
+#include <config.h>
+
+#include <isc/mem.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/compress.h>
+#include <dns/fixedname.h>
+#include <dns/rbt.h>
+#include <dns/result.h>
+
+#define CCTX_MAGIC ISC_MAGIC('C', 'C', 'T', 'X')
+#define VALID_CCTX(x) ISC_MAGIC_VALID(x, CCTX_MAGIC)
+
+#define DCTX_MAGIC ISC_MAGIC('D', 'C', 'T', 'X')
+#define VALID_DCTX(x) ISC_MAGIC_VALID(x, DCTX_MAGIC)
+
+/***
+ *** Compression
+ ***/
+
+isc_result_t
+dns_compress_init(dns_compress_t *cctx, int edns, isc_mem_t *mctx) {
+ unsigned int i;
+
+ REQUIRE(cctx != NULL);
+ REQUIRE(mctx != NULL); /* See: rdataset.c:towiresorted(). */
+
+ cctx->allowed = 0;
+ cctx->edns = edns;
+ for (i = 0; i < DNS_COMPRESS_TABLESIZE; i++)
+ cctx->table[i] = NULL;
+ cctx->mctx = mctx;
+ cctx->count = 0;
+ cctx->magic = CCTX_MAGIC;
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_compress_invalidate(dns_compress_t *cctx) {
+ dns_compressnode_t *node;
+ unsigned int i;
+
+ REQUIRE(VALID_CCTX(cctx));
+
+ cctx->magic = 0;
+ for (i = 0; i < DNS_COMPRESS_TABLESIZE; i++) {
+ while (cctx->table[i] != NULL) {
+ node = cctx->table[i];
+ cctx->table[i] = cctx->table[i]->next;
+ if (node->count < DNS_COMPRESS_INITIALNODES)
+ continue;
+ isc_mem_put(cctx->mctx, node, sizeof(*node));
+ }
+ }
+ cctx->allowed = 0;
+ cctx->edns = -1;
+}
+
+void
+dns_compress_setmethods(dns_compress_t *cctx, unsigned int allowed) {
+ REQUIRE(VALID_CCTX(cctx));
+
+ cctx->allowed &= ~DNS_COMPRESS_ALL;
+ cctx->allowed |= (allowed & DNS_COMPRESS_ALL);
+}
+
+unsigned int
+dns_compress_getmethods(dns_compress_t *cctx) {
+ REQUIRE(VALID_CCTX(cctx));
+ return (cctx->allowed & DNS_COMPRESS_ALL);
+}
+
+void
+dns_compress_setsensitive(dns_compress_t *cctx, isc_boolean_t sensitive) {
+ REQUIRE(VALID_CCTX(cctx));
+
+ if (sensitive)
+ cctx->allowed |= DNS_COMPRESS_CASESENSITIVE;
+ else
+ cctx->allowed &= ~DNS_COMPRESS_CASESENSITIVE;
+}
+
+isc_boolean_t
+dns_compress_getsensitive(dns_compress_t *cctx) {
+ REQUIRE(VALID_CCTX(cctx));
+
+ return (ISC_TF((cctx->allowed & DNS_COMPRESS_CASESENSITIVE) != 0));
+}
+
+int
+dns_compress_getedns(dns_compress_t *cctx) {
+ REQUIRE(VALID_CCTX(cctx));
+ return (cctx->edns);
+}
+
+#define NODENAME(node, name) \
+do { \
+ (name)->length = (node)->r.length; \
+ (name)->labels = (node)->labels; \
+ (name)->ndata = (node)->r.base; \
+ (name)->attributes = DNS_NAMEATTR_ABSOLUTE; \
+} while (0)
+
+/*
+ * Find the longest match of name in the table.
+ * If match is found return ISC_TRUE. prefix, suffix and offset are updated.
+ * If no match is found return ISC_FALSE.
+ */
+isc_boolean_t
+dns_compress_findglobal(dns_compress_t *cctx, const dns_name_t *name,
+ dns_name_t *prefix, isc_uint16_t *offset)
+{
+ dns_name_t tname, nname;
+ dns_compressnode_t *node = NULL;
+ unsigned int labels, hash, n;
+
+ REQUIRE(VALID_CCTX(cctx));
+ REQUIRE(dns_name_isabsolute(name) == ISC_TRUE);
+ REQUIRE(offset != NULL);
+
+ if (cctx->count == 0)
+ return (ISC_FALSE);
+
+ labels = dns_name_countlabels(name);
+ INSIST(labels > 0);
+
+ dns_name_init(&tname, NULL);
+ dns_name_init(&nname, NULL);
+
+ for (n = 0; n < labels - 1; n++) {
+ dns_name_getlabelsequence(name, n, labels - n, &tname);
+ hash = dns_name_hash(&tname, ISC_FALSE) %
+ DNS_COMPRESS_TABLESIZE;
+ for (node = cctx->table[hash]; node != NULL; node = node->next)
+ {
+ NODENAME(node, &nname);
+ if ((cctx->allowed & DNS_COMPRESS_CASESENSITIVE) != 0) {
+ if (dns_name_caseequal(&nname, &tname))
+ break;
+ } else {
+ if (dns_name_equal(&nname, &tname))
+ break;
+ }
+ }
+ if (node != NULL)
+ break;
+ }
+
+ /*
+ * If node == NULL, we found no match at all.
+ */
+ if (node == NULL)
+ return (ISC_FALSE);
+
+ if (n == 0)
+ dns_name_reset(prefix);
+ else
+ dns_name_getlabelsequence(name, 0, n, prefix);
+
+ *offset = node->offset;
+ return (ISC_TRUE);
+}
+
+static inline unsigned int
+name_length(const dns_name_t *name) {
+ isc_region_t r;
+ dns_name_toregion(name, &r);
+ return (r.length);
+}
+
+void
+dns_compress_add(dns_compress_t *cctx, const dns_name_t *name,
+ const dns_name_t *prefix, isc_uint16_t offset)
+{
+ dns_name_t tname;
+ unsigned int start;
+ unsigned int n;
+ unsigned int count;
+ unsigned int hash;
+ dns_compressnode_t *node;
+ unsigned int length;
+ unsigned int tlength;
+ isc_uint16_t toffset;
+
+ REQUIRE(VALID_CCTX(cctx));
+ REQUIRE(dns_name_isabsolute(name));
+
+ dns_name_init(&tname, NULL);
+
+ n = dns_name_countlabels(name);
+ count = dns_name_countlabels(prefix);
+ if (dns_name_isabsolute(prefix))
+ count--;
+ start = 0;
+ length = name_length(name);
+ while (count > 0) {
+ if (offset >= 0x4000)
+ break;
+ dns_name_getlabelsequence(name, start, n, &tname);
+ hash = dns_name_hash(&tname, ISC_FALSE) %
+ DNS_COMPRESS_TABLESIZE;
+ tlength = name_length(&tname);
+ toffset = (isc_uint16_t)(offset + (length - tlength));
+ /*
+ * Create a new node and add it.
+ */
+ if (cctx->count < DNS_COMPRESS_INITIALNODES)
+ node = &cctx->initialnodes[cctx->count];
+ else {
+ node = isc_mem_get(cctx->mctx,
+ sizeof(dns_compressnode_t));
+ if (node == NULL)
+ return;
+ }
+ node->count = cctx->count++;
+ node->offset = toffset;
+ dns_name_toregion(&tname, &node->r);
+ node->labels = (isc_uint8_t)dns_name_countlabels(&tname);
+ node->next = cctx->table[hash];
+ cctx->table[hash] = node;
+ start++;
+ n--;
+ count--;
+ }
+}
+
+void
+dns_compress_rollback(dns_compress_t *cctx, isc_uint16_t offset) {
+ unsigned int i;
+ dns_compressnode_t *node;
+
+ REQUIRE(VALID_CCTX(cctx));
+
+ for (i = 0; i < DNS_COMPRESS_TABLESIZE; i++) {
+ node = cctx->table[i];
+ /*
+ * This relies on nodes with greater offsets being
+ * closer to the beginning of the list, and the
+ * items with the greatest offsets being at the end
+ * of the initialnodes[] array.
+ */
+ while (node != NULL && node->offset >= offset) {
+ cctx->table[i] = node->next;
+ if (node->count >= DNS_COMPRESS_INITIALNODES)
+ isc_mem_put(cctx->mctx, node, sizeof(*node));
+ cctx->count--;
+ node = cctx->table[i];
+ }
+ }
+}
+
+/***
+ *** Decompression
+ ***/
+
+void
+dns_decompress_init(dns_decompress_t *dctx, int edns,
+ dns_decompresstype_t type) {
+
+ REQUIRE(dctx != NULL);
+ REQUIRE(edns >= -1 && edns <= 255);
+
+ dctx->allowed = DNS_COMPRESS_NONE;
+ dctx->edns = edns;
+ dctx->type = type;
+ dctx->magic = DCTX_MAGIC;
+}
+
+void
+dns_decompress_invalidate(dns_decompress_t *dctx) {
+
+ REQUIRE(VALID_DCTX(dctx));
+
+ dctx->magic = 0;
+}
+
+void
+dns_decompress_setmethods(dns_decompress_t *dctx, unsigned int allowed) {
+
+ REQUIRE(VALID_DCTX(dctx));
+
+ switch (dctx->type) {
+ case DNS_DECOMPRESS_ANY:
+ dctx->allowed = DNS_COMPRESS_ALL;
+ break;
+ case DNS_DECOMPRESS_NONE:
+ dctx->allowed = DNS_COMPRESS_NONE;
+ break;
+ case DNS_DECOMPRESS_STRICT:
+ dctx->allowed = allowed;
+ break;
+ }
+}
+
+unsigned int
+dns_decompress_getmethods(dns_decompress_t *dctx) {
+
+ REQUIRE(VALID_DCTX(dctx));
+
+ return (dctx->allowed);
+}
+
+int
+dns_decompress_edns(dns_decompress_t *dctx) {
+
+ REQUIRE(VALID_DCTX(dctx));
+
+ return (dctx->edns);
+}
+
+dns_decompresstype_t
+dns_decompress_type(dns_decompress_t *dctx) {
+
+ REQUIRE(VALID_DCTX(dctx));
+
+ return (dctx->type);
+}
diff --git a/lib/dns/db.c b/lib/dns/db.c
new file mode 100644
index 0000000..a4c2864
--- /dev/null
+++ b/lib/dns/db.c
@@ -0,0 +1,926 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001, 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: db.c,v 1.88 2008/09/24 02:46:22 marka Exp $ */
+
+/*! \file */
+
+/***
+ *** Imports
+ ***/
+
+#include <config.h>
+
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/once.h>
+#include <isc/rwlock.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/callbacks.h>
+#include <dns/db.h>
+#include <dns/log.h>
+#include <dns/master.h>
+#include <dns/rdata.h>
+#include <dns/rdataset.h>
+#include <dns/result.h>
+
+/***
+ *** Private Types
+ ***/
+
+struct dns_dbimplementation {
+ const char * name;
+ dns_dbcreatefunc_t create;
+ isc_mem_t * mctx;
+ void * driverarg;
+ ISC_LINK(dns_dbimplementation_t) link;
+};
+
+/***
+ *** Supported DB Implementations Registry
+ ***/
+
+/*
+ * Built in database implementations are registered here.
+ */
+
+#include "rbtdb.h"
+#include "rbtdb64.h"
+
+static ISC_LIST(dns_dbimplementation_t) implementations;
+static isc_rwlock_t implock;
+static isc_once_t once = ISC_ONCE_INIT;
+
+static dns_dbimplementation_t rbtimp;
+static dns_dbimplementation_t rbt64imp;
+
+static void
+initialize(void) {
+ RUNTIME_CHECK(isc_rwlock_init(&implock, 0, 0) == ISC_R_SUCCESS);
+
+ rbtimp.name = "rbt";
+ rbtimp.create = dns_rbtdb_create;
+ rbtimp.mctx = NULL;
+ rbtimp.driverarg = NULL;
+ ISC_LINK_INIT(&rbtimp, link);
+
+ rbt64imp.name = "rbt64";
+ rbt64imp.create = dns_rbtdb64_create;
+ rbt64imp.mctx = NULL;
+ rbt64imp.driverarg = NULL;
+ ISC_LINK_INIT(&rbt64imp, link);
+
+ ISC_LIST_INIT(implementations);
+ ISC_LIST_APPEND(implementations, &rbtimp, link);
+ ISC_LIST_APPEND(implementations, &rbt64imp, link);
+}
+
+static inline dns_dbimplementation_t *
+impfind(const char *name) {
+ dns_dbimplementation_t *imp;
+
+ for (imp = ISC_LIST_HEAD(implementations);
+ imp != NULL;
+ imp = ISC_LIST_NEXT(imp, link))
+ if (strcasecmp(name, imp->name) == 0)
+ return (imp);
+ return (NULL);
+}
+
+
+/***
+ *** Basic DB Methods
+ ***/
+
+isc_result_t
+dns_db_create(isc_mem_t *mctx, const char *db_type, dns_name_t *origin,
+ dns_dbtype_t type, dns_rdataclass_t rdclass,
+ unsigned int argc, char *argv[], dns_db_t **dbp)
+{
+ dns_dbimplementation_t *impinfo;
+
+ RUNTIME_CHECK(isc_once_do(&once, initialize) == ISC_R_SUCCESS);
+
+ /*
+ * Create a new database using implementation 'db_type'.
+ */
+
+ REQUIRE(dbp != NULL && *dbp == NULL);
+ REQUIRE(dns_name_isabsolute(origin));
+
+ RWLOCK(&implock, isc_rwlocktype_read);
+ impinfo = impfind(db_type);
+ if (impinfo != NULL) {
+ isc_result_t result;
+ result = ((impinfo->create)(mctx, origin, type,
+ rdclass, argc, argv,
+ impinfo->driverarg, dbp));
+ RWUNLOCK(&implock, isc_rwlocktype_read);
+ return (result);
+ }
+
+ RWUNLOCK(&implock, isc_rwlocktype_read);
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DB, ISC_LOG_ERROR,
+ "unsupported database type '%s'", db_type);
+
+ return (ISC_R_NOTFOUND);
+}
+
+void
+dns_db_attach(dns_db_t *source, dns_db_t **targetp) {
+
+ /*
+ * Attach *targetp to source.
+ */
+
+ REQUIRE(DNS_DB_VALID(source));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ (source->methods->attach)(source, targetp);
+
+ ENSURE(*targetp == source);
+}
+
+void
+dns_db_detach(dns_db_t **dbp) {
+
+ /*
+ * Detach *dbp from its database.
+ */
+
+ REQUIRE(dbp != NULL);
+ REQUIRE(DNS_DB_VALID(*dbp));
+
+ ((*dbp)->methods->detach)(dbp);
+
+ ENSURE(*dbp == NULL);
+}
+
+isc_result_t
+dns_db_ondestroy(dns_db_t *db, isc_task_t *task, isc_event_t **eventp)
+{
+ REQUIRE(DNS_DB_VALID(db));
+
+ return (isc_ondestroy_register(&db->ondest, task, eventp));
+}
+
+
+isc_boolean_t
+dns_db_iscache(dns_db_t *db) {
+
+ /*
+ * Does 'db' have cache semantics?
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+
+ if ((db->attributes & DNS_DBATTR_CACHE) != 0)
+ return (ISC_TRUE);
+
+ return (ISC_FALSE);
+}
+
+isc_boolean_t
+dns_db_iszone(dns_db_t *db) {
+
+ /*
+ * Does 'db' have zone semantics?
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+
+ if ((db->attributes & (DNS_DBATTR_CACHE|DNS_DBATTR_STUB)) == 0)
+ return (ISC_TRUE);
+
+ return (ISC_FALSE);
+}
+
+isc_boolean_t
+dns_db_isstub(dns_db_t *db) {
+
+ /*
+ * Does 'db' have stub semantics?
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+
+ if ((db->attributes & DNS_DBATTR_STUB) != 0)
+ return (ISC_TRUE);
+
+ return (ISC_FALSE);
+}
+
+isc_boolean_t
+dns_db_isdnssec(dns_db_t *db) {
+
+ /*
+ * Is 'db' secure or partially secure?
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0);
+
+ if (db->methods->isdnssec != NULL)
+ return ((db->methods->isdnssec)(db));
+ return ((db->methods->issecure)(db));
+}
+
+isc_boolean_t
+dns_db_issecure(dns_db_t *db) {
+
+ /*
+ * Is 'db' secure?
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0);
+
+ return ((db->methods->issecure)(db));
+}
+
+isc_boolean_t
+dns_db_ispersistent(dns_db_t *db) {
+
+ /*
+ * Is 'db' persistent?
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+
+ return ((db->methods->ispersistent)(db));
+}
+
+dns_name_t *
+dns_db_origin(dns_db_t *db) {
+ /*
+ * The origin of the database.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+
+ return (&db->origin);
+}
+
+dns_rdataclass_t
+dns_db_class(dns_db_t *db) {
+ /*
+ * The class of the database.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+
+ return (db->rdclass);
+}
+
+isc_result_t
+dns_db_beginload(dns_db_t *db, dns_addrdatasetfunc_t *addp,
+ dns_dbload_t **dbloadp) {
+ /*
+ * Begin loading 'db'.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(addp != NULL && *addp == NULL);
+ REQUIRE(dbloadp != NULL && *dbloadp == NULL);
+
+ return ((db->methods->beginload)(db, addp, dbloadp));
+}
+
+isc_result_t
+dns_db_endload(dns_db_t *db, dns_dbload_t **dbloadp) {
+ /*
+ * Finish loading 'db'.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(dbloadp != NULL && *dbloadp != NULL);
+
+ return ((db->methods->endload)(db, dbloadp));
+}
+
+isc_result_t
+dns_db_load(dns_db_t *db, const char *filename) {
+ return (dns_db_load2(db, filename, dns_masterformat_text));
+}
+
+isc_result_t
+dns_db_load2(dns_db_t *db, const char *filename, dns_masterformat_t format) {
+ isc_result_t result, eresult;
+ dns_rdatacallbacks_t callbacks;
+ unsigned int options = 0;
+
+ /*
+ * Load master file 'filename' into 'db'.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+
+ if ((db->attributes & DNS_DBATTR_CACHE) != 0)
+ options |= DNS_MASTER_AGETTL;
+
+ dns_rdatacallbacks_init(&callbacks);
+
+ result = dns_db_beginload(db, &callbacks.add, &callbacks.add_private);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = dns_master_loadfile2(filename, &db->origin, &db->origin,
+ db->rdclass, options,
+ &callbacks, db->mctx, format);
+ eresult = dns_db_endload(db, &callbacks.add_private);
+ /*
+ * We always call dns_db_endload(), but we only want to return its
+ * result if dns_master_loadfile() succeeded. If dns_master_loadfile()
+ * failed, we want to return the result code it gave us.
+ */
+ if (eresult != ISC_R_SUCCESS &&
+ (result == ISC_R_SUCCESS || result == DNS_R_SEENINCLUDE))
+ result = eresult;
+
+ return (result);
+}
+
+isc_result_t
+dns_db_dump(dns_db_t *db, dns_dbversion_t *version, const char *filename) {
+ return ((db->methods->dump)(db, version, filename,
+ dns_masterformat_text));
+}
+
+isc_result_t
+dns_db_dump2(dns_db_t *db, dns_dbversion_t *version, const char *filename,
+ dns_masterformat_t masterformat) {
+ /*
+ * Dump 'db' into master file 'filename' in the 'masterformat' format.
+ * XXXJT: is it okay to modify the interface to the existing "dump"
+ * method?
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+
+ return ((db->methods->dump)(db, version, filename, masterformat));
+}
+
+/***
+ *** Version Methods
+ ***/
+
+void
+dns_db_currentversion(dns_db_t *db, dns_dbversion_t **versionp) {
+
+ /*
+ * Open the current version for reading.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0);
+ REQUIRE(versionp != NULL && *versionp == NULL);
+
+ (db->methods->currentversion)(db, versionp);
+}
+
+isc_result_t
+dns_db_newversion(dns_db_t *db, dns_dbversion_t **versionp) {
+
+ /*
+ * Open a new version for reading and writing.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0);
+ REQUIRE(versionp != NULL && *versionp == NULL);
+
+ return ((db->methods->newversion)(db, versionp));
+}
+
+void
+dns_db_attachversion(dns_db_t *db, dns_dbversion_t *source,
+ dns_dbversion_t **targetp)
+{
+ /*
+ * Attach '*targetp' to 'source'.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0);
+ REQUIRE(source != NULL);
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ (db->methods->attachversion)(db, source, targetp);
+
+ ENSURE(*targetp != NULL);
+}
+
+void
+dns_db_closeversion(dns_db_t *db, dns_dbversion_t **versionp,
+ isc_boolean_t commit)
+{
+
+ /*
+ * Close version '*versionp'.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0);
+ REQUIRE(versionp != NULL && *versionp != NULL);
+
+ (db->methods->closeversion)(db, versionp, commit);
+
+ ENSURE(*versionp == NULL);
+}
+
+/***
+ *** Node Methods
+ ***/
+
+isc_result_t
+dns_db_findnode(dns_db_t *db, dns_name_t *name,
+ isc_boolean_t create, dns_dbnode_t **nodep)
+{
+
+ /*
+ * Find the node with name 'name'.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(nodep != NULL && *nodep == NULL);
+
+ return ((db->methods->findnode)(db, name, create, nodep));
+}
+
+isc_result_t
+dns_db_findnsec3node(dns_db_t *db, dns_name_t *name,
+ isc_boolean_t create, dns_dbnode_t **nodep)
+{
+
+ /*
+ * Find the node with name 'name'.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(nodep != NULL && *nodep == NULL);
+
+ return ((db->methods->findnsec3node)(db, name, create, nodep));
+}
+
+isc_result_t
+dns_db_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
+ dns_rdatatype_t type, unsigned int options, isc_stdtime_t now,
+ dns_dbnode_t **nodep, dns_name_t *foundname,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+
+ /*
+ * Find the best match for 'name' and 'type' in version 'version'
+ * of 'db'.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(type != dns_rdatatype_rrsig);
+ REQUIRE(nodep == NULL || (nodep != NULL && *nodep == NULL));
+ REQUIRE(dns_name_hasbuffer(foundname));
+ REQUIRE(rdataset == NULL ||
+ (DNS_RDATASET_VALID(rdataset) &&
+ ! dns_rdataset_isassociated(rdataset)));
+ REQUIRE(sigrdataset == NULL ||
+ (DNS_RDATASET_VALID(sigrdataset) &&
+ ! dns_rdataset_isassociated(sigrdataset)));
+
+ return ((db->methods->find)(db, name, version, type, options, now,
+ nodep, foundname, rdataset, sigrdataset));
+}
+
+isc_result_t
+dns_db_findzonecut(dns_db_t *db, dns_name_t *name,
+ unsigned int options, isc_stdtime_t now,
+ dns_dbnode_t **nodep, dns_name_t *foundname,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+ /*
+ * Find the deepest known zonecut which encloses 'name' in 'db'.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE((db->attributes & DNS_DBATTR_CACHE) != 0);
+ REQUIRE(nodep == NULL || (nodep != NULL && *nodep == NULL));
+ REQUIRE(dns_name_hasbuffer(foundname));
+ REQUIRE(sigrdataset == NULL ||
+ (DNS_RDATASET_VALID(sigrdataset) &&
+ ! dns_rdataset_isassociated(sigrdataset)));
+
+ return ((db->methods->findzonecut)(db, name, options, now, nodep,
+ foundname, rdataset, sigrdataset));
+}
+
+void
+dns_db_attachnode(dns_db_t *db, dns_dbnode_t *source, dns_dbnode_t **targetp) {
+
+ /*
+ * Attach *targetp to source.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(source != NULL);
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ (db->methods->attachnode)(db, source, targetp);
+}
+
+void
+dns_db_detachnode(dns_db_t *db, dns_dbnode_t **nodep) {
+
+ /*
+ * Detach *nodep from its node.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(nodep != NULL && *nodep != NULL);
+
+ (db->methods->detachnode)(db, nodep);
+
+ ENSURE(*nodep == NULL);
+}
+
+void
+dns_db_transfernode(dns_db_t *db, dns_dbnode_t **sourcep,
+ dns_dbnode_t **targetp)
+{
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+ /*
+ * This doesn't check the implementation magic. If we find that
+ * we need such checks in future then this will be done in the
+ * method.
+ */
+ REQUIRE(sourcep != NULL && *sourcep != NULL);
+
+ UNUSED(db);
+
+ if (db->methods->transfernode == NULL) {
+ *targetp = *sourcep;
+ *sourcep = NULL;
+ } else
+ (db->methods->transfernode)(db, sourcep, targetp);
+
+ ENSURE(*sourcep == NULL);
+}
+
+isc_result_t
+dns_db_expirenode(dns_db_t *db, dns_dbnode_t *node, isc_stdtime_t now) {
+
+ /*
+ * Mark as stale all records at 'node' which expire at or before 'now'.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE((db->attributes & DNS_DBATTR_CACHE) != 0);
+ REQUIRE(node != NULL);
+
+ return ((db->methods->expirenode)(db, node, now));
+}
+
+void
+dns_db_printnode(dns_db_t *db, dns_dbnode_t *node, FILE *out) {
+ /*
+ * Print a textual representation of the contents of the node to
+ * 'out'.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(node != NULL);
+
+ (db->methods->printnode)(db, node, out);
+}
+
+/***
+ *** DB Iterator Creation
+ ***/
+
+isc_result_t
+dns_db_createiterator(dns_db_t *db, unsigned int flags,
+ dns_dbiterator_t **iteratorp)
+{
+ /*
+ * Create an iterator for version 'version' of 'db'.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(iteratorp != NULL && *iteratorp == NULL);
+
+ return (db->methods->createiterator(db, flags, iteratorp));
+}
+
+/***
+ *** Rdataset Methods
+ ***/
+
+isc_result_t
+dns_db_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ dns_rdatatype_t type, dns_rdatatype_t covers,
+ isc_stdtime_t now, dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset)
+{
+ /*
+ * Search for an rdataset of type 'type' at 'node' that are in version
+ * 'version' of 'db'. If found, make 'rdataset' refer to it.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(node != NULL);
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(! dns_rdataset_isassociated(rdataset));
+ REQUIRE(covers == 0 || type == dns_rdatatype_rrsig);
+ REQUIRE(type != dns_rdatatype_any);
+ REQUIRE(sigrdataset == NULL ||
+ (DNS_RDATASET_VALID(sigrdataset) &&
+ ! dns_rdataset_isassociated(sigrdataset)));
+
+ return ((db->methods->findrdataset)(db, node, version, type, covers,
+ now, rdataset, sigrdataset));
+}
+
+isc_result_t
+dns_db_allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ isc_stdtime_t now, dns_rdatasetiter_t **iteratorp)
+{
+ /*
+ * Make '*iteratorp' an rdataset iteratator for all rdatasets at
+ * 'node' in version 'version' of 'db'.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(iteratorp != NULL && *iteratorp == NULL);
+
+ return ((db->methods->allrdatasets)(db, node, version, now,
+ iteratorp));
+}
+
+isc_result_t
+dns_db_addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ isc_stdtime_t now, dns_rdataset_t *rdataset,
+ unsigned int options, dns_rdataset_t *addedrdataset)
+{
+ /*
+ * Add 'rdataset' to 'node' in version 'version' of 'db'.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(node != NULL);
+ REQUIRE(((db->attributes & DNS_DBATTR_CACHE) == 0 && version != NULL)||
+ ((db->attributes & DNS_DBATTR_CACHE) != 0 &&
+ version == NULL && (options & DNS_DBADD_MERGE) == 0));
+ REQUIRE((options & DNS_DBADD_EXACT) == 0 ||
+ (options & DNS_DBADD_MERGE) != 0);
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(dns_rdataset_isassociated(rdataset));
+ REQUIRE(rdataset->rdclass == db->rdclass);
+ REQUIRE(addedrdataset == NULL ||
+ (DNS_RDATASET_VALID(addedrdataset) &&
+ ! dns_rdataset_isassociated(addedrdataset)));
+
+ return ((db->methods->addrdataset)(db, node, version, now, rdataset,
+ options, addedrdataset));
+}
+
+isc_result_t
+dns_db_subtractrdataset(dns_db_t *db, dns_dbnode_t *node,
+ dns_dbversion_t *version, dns_rdataset_t *rdataset,
+ unsigned int options, dns_rdataset_t *newrdataset)
+{
+ /*
+ * Remove any rdata in 'rdataset' from 'node' in version 'version' of
+ * 'db'.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(node != NULL);
+ REQUIRE((db->attributes & DNS_DBATTR_CACHE) == 0 && version != NULL);
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(dns_rdataset_isassociated(rdataset));
+ REQUIRE(rdataset->rdclass == db->rdclass);
+ REQUIRE(newrdataset == NULL ||
+ (DNS_RDATASET_VALID(newrdataset) &&
+ ! dns_rdataset_isassociated(newrdataset)));
+
+ return ((db->methods->subtractrdataset)(db, node, version, rdataset,
+ options, newrdataset));
+}
+
+isc_result_t
+dns_db_deleterdataset(dns_db_t *db, dns_dbnode_t *node,
+ dns_dbversion_t *version, dns_rdatatype_t type,
+ dns_rdatatype_t covers)
+{
+ /*
+ * Make it so that no rdataset of type 'type' exists at 'node' in
+ * version version 'version' of 'db'.
+ */
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(node != NULL);
+ REQUIRE(((db->attributes & DNS_DBATTR_CACHE) == 0 && version != NULL)||
+ ((db->attributes & DNS_DBATTR_CACHE) != 0 && version == NULL));
+
+ return ((db->methods->deleterdataset)(db, node, version,
+ type, covers));
+}
+
+void
+dns_db_overmem(dns_db_t *db, isc_boolean_t overmem) {
+
+ REQUIRE(DNS_DB_VALID(db));
+
+ (db->methods->overmem)(db, overmem);
+}
+
+isc_result_t
+dns_db_getsoaserial(dns_db_t *db, dns_dbversion_t *ver, isc_uint32_t *serialp)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_buffer_t buffer;
+
+ REQUIRE(dns_db_iszone(db) || dns_db_isstub(db));
+
+ result = dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ dns_rdataset_init(&rdataset);
+ result = dns_db_findrdataset(db, node, ver, dns_rdatatype_soa, 0,
+ (isc_stdtime_t)0, &rdataset, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto freenode;
+
+ result = dns_rdataset_first(&rdataset);
+ if (result != ISC_R_SUCCESS)
+ goto freerdataset;
+ dns_rdataset_current(&rdataset, &rdata);
+ result = dns_rdataset_next(&rdataset);
+ INSIST(result == ISC_R_NOMORE);
+
+ INSIST(rdata.length > 20);
+ isc_buffer_init(&buffer, rdata.data, rdata.length);
+ isc_buffer_add(&buffer, rdata.length);
+ isc_buffer_forward(&buffer, rdata.length - 20);
+ *serialp = isc_buffer_getuint32(&buffer);
+
+ result = ISC_R_SUCCESS;
+
+ freerdataset:
+ dns_rdataset_disassociate(&rdataset);
+
+ freenode:
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+unsigned int
+dns_db_nodecount(dns_db_t *db) {
+ REQUIRE(DNS_DB_VALID(db));
+
+ return ((db->methods->nodecount)(db));
+}
+
+void
+dns_db_settask(dns_db_t *db, isc_task_t *task) {
+ REQUIRE(DNS_DB_VALID(db));
+
+ (db->methods->settask)(db, task);
+}
+
+isc_result_t
+dns_db_register(const char *name, dns_dbcreatefunc_t create, void *driverarg,
+ isc_mem_t *mctx, dns_dbimplementation_t **dbimp)
+{
+ dns_dbimplementation_t *imp;
+
+ REQUIRE(name != NULL);
+ REQUIRE(dbimp != NULL && *dbimp == NULL);
+
+ RUNTIME_CHECK(isc_once_do(&once, initialize) == ISC_R_SUCCESS);
+
+ RWLOCK(&implock, isc_rwlocktype_write);
+ imp = impfind(name);
+ if (imp != NULL) {
+ RWUNLOCK(&implock, isc_rwlocktype_write);
+ return (ISC_R_EXISTS);
+ }
+
+ imp = isc_mem_get(mctx, sizeof(dns_dbimplementation_t));
+ if (imp == NULL) {
+ RWUNLOCK(&implock, isc_rwlocktype_write);
+ return (ISC_R_NOMEMORY);
+ }
+ imp->name = name;
+ imp->create = create;
+ imp->mctx = NULL;
+ imp->driverarg = driverarg;
+ isc_mem_attach(mctx, &imp->mctx);
+ ISC_LINK_INIT(imp, link);
+ ISC_LIST_APPEND(implementations, imp, link);
+ RWUNLOCK(&implock, isc_rwlocktype_write);
+
+ *dbimp = imp;
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_db_unregister(dns_dbimplementation_t **dbimp) {
+ dns_dbimplementation_t *imp;
+ isc_mem_t *mctx;
+
+ REQUIRE(dbimp != NULL && *dbimp != NULL);
+
+ RUNTIME_CHECK(isc_once_do(&once, initialize) == ISC_R_SUCCESS);
+
+ imp = *dbimp;
+ RWLOCK(&implock, isc_rwlocktype_write);
+ ISC_LIST_UNLINK(implementations, imp, link);
+ mctx = imp->mctx;
+ isc_mem_put(mctx, imp, sizeof(dns_dbimplementation_t));
+ isc_mem_detach(&mctx);
+ RWUNLOCK(&implock, isc_rwlocktype_write);
+}
+
+isc_result_t
+dns_db_getoriginnode(dns_db_t *db, dns_dbnode_t **nodep) {
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(dns_db_iszone(db) == ISC_TRUE);
+ REQUIRE(nodep != NULL && *nodep == NULL);
+
+ if (db->methods->getoriginnode != NULL)
+ return ((db->methods->getoriginnode)(db, nodep));
+
+ return (ISC_R_NOTFOUND);
+}
+
+dns_stats_t *
+dns_db_getrrsetstats(dns_db_t *db) {
+ REQUIRE(DNS_DB_VALID(db));
+
+ if (db->methods->getrrsetstats != NULL)
+ return ((db->methods->getrrsetstats)(db));
+
+ return (NULL);
+}
+
+isc_result_t
+dns_db_getnsec3parameters(dns_db_t *db, dns_dbversion_t *version,
+ dns_hash_t *hash, isc_uint8_t *flags,
+ isc_uint16_t *iterations,
+ unsigned char *salt, size_t *salt_length)
+{
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(dns_db_iszone(db) == ISC_TRUE);
+
+ if (db->methods->getnsec3parameters != NULL)
+ return ((db->methods->getnsec3parameters)(db, version, hash,
+ flags, iterations,
+ salt, salt_length));
+
+ return (ISC_R_NOTFOUND);
+}
+
+isc_result_t
+dns_db_setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset,
+ isc_stdtime_t resign)
+{
+ if (db->methods->setsigningtime != NULL)
+ return ((db->methods->setsigningtime)(db, rdataset, resign));
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+isc_result_t
+dns_db_getsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, dns_name_t *name)
+{
+ if (db->methods->getsigningtime != NULL)
+ return ((db->methods->getsigningtime)(db, rdataset, name));
+ return (ISC_R_NOTFOUND);
+}
+
+void
+dns_db_resigned(dns_db_t *db, dns_rdataset_t *rdataset, dns_dbversion_t *version)
+{
+ if (db->methods->resigned != NULL)
+ (db->methods->resigned)(db, rdataset, version);
+}
diff --git a/lib/dns/dbiterator.c b/lib/dns/dbiterator.c
new file mode 100644
index 0000000..8981e49
--- /dev/null
+++ b/lib/dns/dbiterator.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: dbiterator.c,v 1.18 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/util.h>
+
+#include <dns/dbiterator.h>
+#include <dns/name.h>
+
+void
+dns_dbiterator_destroy(dns_dbiterator_t **iteratorp) {
+ /*
+ * Destroy '*iteratorp'.
+ */
+
+ REQUIRE(iteratorp != NULL);
+ REQUIRE(DNS_DBITERATOR_VALID(*iteratorp));
+
+ (*iteratorp)->methods->destroy(iteratorp);
+
+ ENSURE(*iteratorp == NULL);
+}
+
+isc_result_t
+dns_dbiterator_first(dns_dbiterator_t *iterator) {
+ /*
+ * Move the node cursor to the first node in the database (if any).
+ */
+
+ REQUIRE(DNS_DBITERATOR_VALID(iterator));
+
+ return (iterator->methods->first(iterator));
+}
+
+isc_result_t
+dns_dbiterator_last(dns_dbiterator_t *iterator) {
+ /*
+ * Move the node cursor to the first node in the database (if any).
+ */
+
+ REQUIRE(DNS_DBITERATOR_VALID(iterator));
+
+ return (iterator->methods->last(iterator));
+}
+
+isc_result_t
+dns_dbiterator_seek(dns_dbiterator_t *iterator, dns_name_t *name) {
+ /*
+ * Move the node cursor to the node with name 'name'.
+ */
+
+ REQUIRE(DNS_DBITERATOR_VALID(iterator));
+
+ return (iterator->methods->seek(iterator, name));
+}
+
+isc_result_t
+dns_dbiterator_prev(dns_dbiterator_t *iterator) {
+ /*
+ * Move the node cursor to the previous node in the database (if any).
+ */
+
+ REQUIRE(DNS_DBITERATOR_VALID(iterator));
+
+ return (iterator->methods->prev(iterator));
+}
+
+isc_result_t
+dns_dbiterator_next(dns_dbiterator_t *iterator) {
+ /*
+ * Move the node cursor to the next node in the database (if any).
+ */
+
+ REQUIRE(DNS_DBITERATOR_VALID(iterator));
+
+ return (iterator->methods->next(iterator));
+}
+
+isc_result_t
+dns_dbiterator_current(dns_dbiterator_t *iterator, dns_dbnode_t **nodep,
+ dns_name_t *name)
+{
+ /*
+ * Return the current node.
+ */
+
+ REQUIRE(DNS_DBITERATOR_VALID(iterator));
+ REQUIRE(nodep != NULL && *nodep == NULL);
+ REQUIRE(name == NULL || dns_name_hasbuffer(name));
+
+ return (iterator->methods->current(iterator, nodep, name));
+}
+
+isc_result_t
+dns_dbiterator_pause(dns_dbiterator_t *iterator) {
+ /*
+ * Pause iteration.
+ */
+
+ REQUIRE(DNS_DBITERATOR_VALID(iterator));
+
+ return (iterator->methods->pause(iterator));
+}
+
+isc_result_t
+dns_dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name) {
+
+ /*
+ * Return the origin to which returned node names are relative.
+ */
+
+ REQUIRE(DNS_DBITERATOR_VALID(iterator));
+ REQUIRE(iterator->relative_names);
+ REQUIRE(dns_name_hasbuffer(name));
+
+ return (iterator->methods->origin(iterator, name));
+}
+
+void
+dns_dbiterator_setcleanmode(dns_dbiterator_t *iterator, isc_boolean_t mode) {
+ REQUIRE(DNS_DBITERATOR_VALID(iterator));
+
+ iterator->cleaning = mode;
+}
diff --git a/lib/dns/dbtable.c b/lib/dns/dbtable.c
new file mode 100644
index 0000000..57bbfc1
--- /dev/null
+++ b/lib/dns/dbtable.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/*
+ * $Id: dbtable.c,v 1.33 2007/06/19 23:47:16 tbox Exp $
+ */
+
+/*! \file
+ * \author
+ * Principal Author: DCL
+ */
+
+#include <config.h>
+
+#include <isc/mem.h>
+#include <isc/rwlock.h>
+#include <isc/util.h>
+
+#include <dns/dbtable.h>
+#include <dns/db.h>
+#include <dns/rbt.h>
+#include <dns/result.h>
+
+struct dns_dbtable {
+ /* Unlocked. */
+ unsigned int magic;
+ isc_mem_t * mctx;
+ dns_rdataclass_t rdclass;
+ isc_mutex_t lock;
+ isc_rwlock_t tree_lock;
+ /* Locked by lock. */
+ unsigned int references;
+ /* Locked by tree_lock. */
+ dns_rbt_t * rbt;
+ dns_db_t * default_db;
+};
+
+#define DBTABLE_MAGIC ISC_MAGIC('D', 'B', '-', '-')
+#define VALID_DBTABLE(dbtable) ISC_MAGIC_VALID(dbtable, DBTABLE_MAGIC)
+
+static void
+dbdetach(void *data, void *arg) {
+ dns_db_t *db = data;
+
+ UNUSED(arg);
+
+ dns_db_detach(&db);
+}
+
+isc_result_t
+dns_dbtable_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
+ dns_dbtable_t **dbtablep)
+{
+ dns_dbtable_t *dbtable;
+ isc_result_t result;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(dbtablep != NULL && *dbtablep == NULL);
+
+ dbtable = (dns_dbtable_t *)isc_mem_get(mctx, sizeof(*dbtable));
+ if (dbtable == NULL)
+ return (ISC_R_NOMEMORY);
+
+ dbtable->rbt = NULL;
+ result = dns_rbt_create(mctx, dbdetach, NULL, &dbtable->rbt);
+ if (result != ISC_R_SUCCESS)
+ goto clean1;
+
+ result = isc_mutex_init(&dbtable->lock);
+ if (result != ISC_R_SUCCESS)
+ goto clean2;
+
+ result = isc_rwlock_init(&dbtable->tree_lock, 0, 0);
+ if (result != ISC_R_SUCCESS)
+ goto clean3;
+
+ dbtable->default_db = NULL;
+ dbtable->mctx = mctx;
+ dbtable->rdclass = rdclass;
+ dbtable->magic = DBTABLE_MAGIC;
+ dbtable->references = 1;
+
+ *dbtablep = dbtable;
+
+ return (ISC_R_SUCCESS);
+
+ clean3:
+ DESTROYLOCK(&dbtable->lock);
+
+ clean2:
+ dns_rbt_destroy(&dbtable->rbt);
+
+ clean1:
+ isc_mem_put(mctx, dbtable, sizeof(*dbtable));
+
+ return (result);
+}
+
+static inline void
+dbtable_free(dns_dbtable_t *dbtable) {
+ /*
+ * Caller must ensure that it is safe to call.
+ */
+
+ RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
+
+ if (dbtable->default_db != NULL)
+ dns_db_detach(&dbtable->default_db);
+
+ dns_rbt_destroy(&dbtable->rbt);
+
+ RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
+
+ isc_rwlock_destroy(&dbtable->tree_lock);
+
+ dbtable->magic = 0;
+
+ isc_mem_put(dbtable->mctx, dbtable, sizeof(*dbtable));
+}
+
+void
+dns_dbtable_attach(dns_dbtable_t *source, dns_dbtable_t **targetp) {
+ REQUIRE(VALID_DBTABLE(source));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ LOCK(&source->lock);
+
+ INSIST(source->references > 0);
+ source->references++;
+ INSIST(source->references != 0);
+
+ UNLOCK(&source->lock);
+
+ *targetp = source;
+}
+
+void
+dns_dbtable_detach(dns_dbtable_t **dbtablep) {
+ dns_dbtable_t *dbtable;
+ isc_boolean_t free_dbtable = ISC_FALSE;
+
+ REQUIRE(dbtablep != NULL);
+ dbtable = *dbtablep;
+ REQUIRE(VALID_DBTABLE(dbtable));
+
+ LOCK(&dbtable->lock);
+
+ INSIST(dbtable->references > 0);
+ dbtable->references--;
+ if (dbtable->references == 0)
+ free_dbtable = ISC_TRUE;
+
+ UNLOCK(&dbtable->lock);
+
+ if (free_dbtable)
+ dbtable_free(dbtable);
+
+ *dbtablep = NULL;
+}
+
+isc_result_t
+dns_dbtable_add(dns_dbtable_t *dbtable, dns_db_t *db) {
+ isc_result_t result;
+ dns_db_t *clone;
+
+ REQUIRE(VALID_DBTABLE(dbtable));
+ REQUIRE(dns_db_class(db) == dbtable->rdclass);
+
+ clone = NULL;
+ dns_db_attach(db, &clone);
+
+ RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
+ result = dns_rbt_addname(dbtable->rbt, dns_db_origin(clone), clone);
+ RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
+
+ return (result);
+}
+
+void
+dns_dbtable_remove(dns_dbtable_t *dbtable, dns_db_t *db) {
+ dns_db_t *stored_data = NULL;
+ isc_result_t result;
+ dns_name_t *name;
+
+ REQUIRE(VALID_DBTABLE(dbtable));
+
+ name = dns_db_origin(db);
+
+ /*
+ * There is a requirement that the association of name with db
+ * be verified. With the current rbt.c this is expensive to do,
+ * because effectively two find operations are being done, but
+ * deletion is relatively infrequent.
+ * XXXDCL ... this could be cheaper now with dns_rbt_deletenode.
+ */
+
+ RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
+
+ result = dns_rbt_findname(dbtable->rbt, name, 0, NULL,
+ (void **) (void *)&stored_data);
+
+ if (result == ISC_R_SUCCESS) {
+ INSIST(stored_data == db);
+
+ (void)dns_rbt_deletename(dbtable->rbt, name, ISC_FALSE);
+ }
+
+ RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
+}
+
+void
+dns_dbtable_adddefault(dns_dbtable_t *dbtable, dns_db_t *db) {
+ REQUIRE(VALID_DBTABLE(dbtable));
+ REQUIRE(dbtable->default_db == NULL);
+ REQUIRE(dns_name_compare(dns_db_origin(db), dns_rootname) == 0);
+
+ RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
+
+ dbtable->default_db = NULL;
+ dns_db_attach(db, &dbtable->default_db);
+
+ RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
+}
+
+void
+dns_dbtable_getdefault(dns_dbtable_t *dbtable, dns_db_t **dbp) {
+ REQUIRE(VALID_DBTABLE(dbtable));
+ REQUIRE(dbp != NULL && *dbp == NULL);
+
+ RWLOCK(&dbtable->tree_lock, isc_rwlocktype_read);
+
+ dns_db_attach(dbtable->default_db, dbp);
+
+ RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_read);
+}
+
+void
+dns_dbtable_removedefault(dns_dbtable_t *dbtable) {
+ REQUIRE(VALID_DBTABLE(dbtable));
+
+ RWLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
+
+ dns_db_detach(&dbtable->default_db);
+
+ RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_write);
+}
+
+isc_result_t
+dns_dbtable_find(dns_dbtable_t *dbtable, dns_name_t *name,
+ unsigned int options, dns_db_t **dbp)
+{
+ dns_db_t *stored_data = NULL;
+ isc_result_t result;
+ unsigned int rbtoptions = 0;
+
+ REQUIRE(dbp != NULL && *dbp == NULL);
+
+ if ((options & DNS_DBTABLEFIND_NOEXACT) != 0)
+ rbtoptions |= DNS_RBTFIND_NOEXACT;
+
+ RWLOCK(&dbtable->tree_lock, isc_rwlocktype_read);
+
+ result = dns_rbt_findname(dbtable->rbt, name, rbtoptions, NULL,
+ (void **) (void *)&stored_data);
+
+ if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
+ dns_db_attach(stored_data, dbp);
+ else if (dbtable->default_db != NULL) {
+ dns_db_attach(dbtable->default_db, dbp);
+ result = DNS_R_PARTIALMATCH;
+ } else
+ result = ISC_R_NOTFOUND;
+
+ RWUNLOCK(&dbtable->tree_lock, isc_rwlocktype_read);
+
+ return (result);
+}
diff --git a/lib/dns/diff.c b/lib/dns/diff.c
new file mode 100644
index 0000000..850ca91
--- /dev/null
+++ b/lib/dns/diff.c
@@ -0,0 +1,638 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: diff.c,v 1.18 2008/09/24 02:46:22 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdlib.h>
+
+#include <isc/buffer.h>
+#include <isc/file.h>
+#include <isc/mem.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/diff.h>
+#include <dns/log.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatastruct.h>
+#include <dns/rdatatype.h>
+#include <dns/result.h>
+
+#define CHECK(op) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+
+#define DIFF_COMMON_LOGARGS \
+ dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_DIFF
+
+static dns_rdatatype_t
+rdata_covers(dns_rdata_t *rdata) {
+ return (rdata->type == dns_rdatatype_rrsig ?
+ dns_rdata_covers(rdata) : 0);
+}
+
+isc_result_t
+dns_difftuple_create(isc_mem_t *mctx,
+ dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl,
+ dns_rdata_t *rdata, dns_difftuple_t **tp)
+{
+ dns_difftuple_t *t;
+ unsigned int size;
+ unsigned char *datap;
+
+ REQUIRE(tp != NULL && *tp == NULL);
+
+ /*
+ * Create a new tuple. The variable-size wire-format name data and
+ * rdata immediately follow the dns_difftuple_t structure
+ * in memory.
+ */
+ size = sizeof(*t) + name->length + rdata->length;
+ t = isc_mem_allocate(mctx, size);
+ if (t == NULL)
+ return (ISC_R_NOMEMORY);
+ t->mctx = mctx;
+ t->op = op;
+
+ datap = (unsigned char *)(t + 1);
+
+ memcpy(datap, name->ndata, name->length);
+ dns_name_init(&t->name, NULL);
+ dns_name_clone(name, &t->name);
+ t->name.ndata = datap;
+ datap += name->length;
+
+ t->ttl = ttl;
+
+ memcpy(datap, rdata->data, rdata->length);
+ dns_rdata_init(&t->rdata);
+ dns_rdata_clone(rdata, &t->rdata);
+ t->rdata.data = datap;
+ datap += rdata->length;
+
+ ISC_LINK_INIT(&t->rdata, link);
+ ISC_LINK_INIT(t, link);
+ t->magic = DNS_DIFFTUPLE_MAGIC;
+
+ INSIST(datap == (unsigned char *)t + size);
+
+ *tp = t;
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_difftuple_free(dns_difftuple_t **tp) {
+ dns_difftuple_t *t = *tp;
+ REQUIRE(DNS_DIFFTUPLE_VALID(t));
+ dns_name_invalidate(&t->name);
+ t->magic = 0;
+ isc_mem_free(t->mctx, t);
+ *tp = NULL;
+}
+
+isc_result_t
+dns_difftuple_copy(dns_difftuple_t *orig, dns_difftuple_t **copyp) {
+ return (dns_difftuple_create(orig->mctx, orig->op, &orig->name,
+ orig->ttl, &orig->rdata, copyp));
+}
+
+void
+dns_diff_init(isc_mem_t *mctx, dns_diff_t *diff) {
+ diff->mctx = mctx;
+ diff->resign = 0;
+ ISC_LIST_INIT(diff->tuples);
+ diff->magic = DNS_DIFF_MAGIC;
+}
+
+void
+dns_diff_clear(dns_diff_t *diff) {
+ dns_difftuple_t *t;
+ REQUIRE(DNS_DIFF_VALID(diff));
+ while ((t = ISC_LIST_HEAD(diff->tuples)) != NULL) {
+ ISC_LIST_UNLINK(diff->tuples, t, link);
+ dns_difftuple_free(&t);
+ }
+ ENSURE(ISC_LIST_EMPTY(diff->tuples));
+}
+
+void
+dns_diff_append(dns_diff_t *diff, dns_difftuple_t **tuplep)
+{
+ ISC_LIST_APPEND(diff->tuples, *tuplep, link);
+ *tuplep = NULL;
+}
+
+/* XXX this is O(N) */
+
+void
+dns_diff_appendminimal(dns_diff_t *diff, dns_difftuple_t **tuplep)
+{
+ dns_difftuple_t *ot, *next_ot;
+
+ REQUIRE(DNS_DIFF_VALID(diff));
+ REQUIRE(DNS_DIFFTUPLE_VALID(*tuplep));
+
+ /*
+ * Look for an existing tuple with the same owner name,
+ * rdata, and TTL. If we are doing an addition and find a
+ * deletion or vice versa, remove both the old and the
+ * new tuple since they cancel each other out (assuming
+ * that we never delete nonexistent data or add existing
+ * data).
+ *
+ * If we find an old update of the same kind as
+ * the one we are doing, there must be a programming
+ * error. We report it but try to continue anyway.
+ */
+ for (ot = ISC_LIST_HEAD(diff->tuples); ot != NULL;
+ ot = next_ot)
+ {
+ next_ot = ISC_LIST_NEXT(ot, link);
+ if (dns_name_equal(&ot->name, &(*tuplep)->name) &&
+ dns_rdata_compare(&ot->rdata, &(*tuplep)->rdata) == 0 &&
+ ot->ttl == (*tuplep)->ttl)
+ {
+ ISC_LIST_UNLINK(diff->tuples, ot, link);
+ if ((*tuplep)->op == ot->op) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "unexpected non-minimal diff");
+ } else {
+ dns_difftuple_free(tuplep);
+ }
+ dns_difftuple_free(&ot);
+ break;
+ }
+ }
+
+ if (*tuplep != NULL) {
+ ISC_LIST_APPEND(diff->tuples, *tuplep, link);
+ *tuplep = NULL;
+ }
+
+ ENSURE(*tuplep == NULL);
+}
+
+static isc_stdtime_t
+setresign(dns_rdataset_t *modified, isc_uint32_t delta) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_rrsig_t sig;
+ isc_stdtime_t when;
+ isc_result_t result;
+
+ result = dns_rdataset_first(modified);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_rdataset_current(modified, &rdata);
+ (void)dns_rdata_tostruct(&rdata, &sig, NULL);
+ if ((rdata.flags & DNS_RDATA_OFFLINE) != 0)
+ when = 0;
+ else
+ when = sig.timeexpire - delta;
+ dns_rdata_reset(&rdata);
+
+ result = dns_rdataset_next(modified);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdataset_current(modified, &rdata);
+ (void)dns_rdata_tostruct(&rdata, &sig, NULL);
+ if ((rdata.flags & DNS_RDATA_OFFLINE) != 0) {
+ goto next_rr;
+ }
+ if (when == 0 || sig.timeexpire - delta < when)
+ when = sig.timeexpire - delta;
+ next_rr:
+ dns_rdata_reset(&rdata);
+ result = dns_rdataset_next(modified);
+ }
+ INSIST(result == ISC_R_NOMORE);
+ return (when);
+}
+
+static isc_result_t
+diff_apply(dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver,
+ isc_boolean_t warn)
+{
+ dns_difftuple_t *t;
+ dns_dbnode_t *node = NULL;
+ isc_result_t result;
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
+ char classbuf[DNS_RDATACLASS_FORMATSIZE];
+
+ REQUIRE(DNS_DIFF_VALID(diff));
+ REQUIRE(DNS_DB_VALID(db));
+
+ t = ISC_LIST_HEAD(diff->tuples);
+ while (t != NULL) {
+ dns_name_t *name;
+
+ INSIST(node == NULL);
+ name = &t->name;
+ /*
+ * Find the node.
+ * We create the node if it does not exist.
+ * This will cause an empty node to be created if the diff
+ * contains a deletion of an RR at a nonexistent name,
+ * but such diffs should never be created in the first
+ * place.
+ */
+
+ while (t != NULL && dns_name_equal(&t->name, name)) {
+ dns_rdatatype_t type, covers;
+ dns_diffop_t op;
+ dns_rdatalist_t rdl;
+ dns_rdataset_t rds;
+ dns_rdataset_t ardataset;
+ dns_rdataset_t *modified = NULL;
+ isc_boolean_t offline;
+
+ op = t->op;
+ type = t->rdata.type;
+ covers = rdata_covers(&t->rdata);
+
+ /*
+ * Collect a contiguous set of updates with
+ * the same operation (add/delete) and RR type
+ * into a single rdatalist so that the
+ * database rrset merging/subtraction code
+ * can work more efficiently than if each
+ * RR were merged into / subtracted from
+ * the database separately.
+ *
+ * This is done by linking rdata structures from the
+ * diff into "rdatalist". This uses the rdata link
+ * field, not the diff link field, so the structure
+ * of the diff itself is not affected.
+ */
+
+ rdl.type = type;
+ rdl.covers = covers;
+ rdl.rdclass = t->rdata.rdclass;
+ rdl.ttl = t->ttl;
+ ISC_LIST_INIT(rdl.rdata);
+ ISC_LINK_INIT(&rdl, link);
+
+ node = NULL;
+ if (type != dns_rdatatype_nsec3 &&
+ covers != dns_rdatatype_nsec3)
+ CHECK(dns_db_findnode(db, name, ISC_TRUE,
+ &node));
+ else
+ CHECK(dns_db_findnsec3node(db, name, ISC_TRUE,
+ &node));
+
+ offline = ISC_FALSE;
+ while (t != NULL &&
+ dns_name_equal(&t->name, name) &&
+ t->op == op &&
+ t->rdata.type == type &&
+ rdata_covers(&t->rdata) == covers)
+ {
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ dns_rdatatype_format(t->rdata.type, typebuf,
+ sizeof(typebuf));
+ dns_rdataclass_format(t->rdata.rdclass,
+ classbuf,
+ sizeof(classbuf));
+ if (t->ttl != rdl.ttl && warn)
+ isc_log_write(DIFF_COMMON_LOGARGS,
+ ISC_LOG_WARNING,
+ "'%s/%s/%s': TTL differs in "
+ "rdataset, adjusting "
+ "%lu -> %lu",
+ namebuf, typebuf, classbuf,
+ (unsigned long) t->ttl,
+ (unsigned long) rdl.ttl);
+ if (t->rdata.flags & DNS_RDATA_OFFLINE)
+ offline = ISC_TRUE;
+ ISC_LIST_APPEND(rdl.rdata, &t->rdata, link);
+ t = ISC_LIST_NEXT(t, link);
+ }
+
+ /*
+ * Convert the rdatalist into a rdataset.
+ */
+ dns_rdataset_init(&rds);
+ CHECK(dns_rdatalist_tordataset(&rdl, &rds));
+ if (rds.type == dns_rdatatype_rrsig)
+ switch (op) {
+ case DNS_DIFFOP_ADDRESIGN:
+ case DNS_DIFFOP_DELRESIGN:
+ modified = &ardataset;
+ dns_rdataset_init(modified);
+ break;
+ default:
+ break;
+ }
+ rds.trust = dns_trust_ultimate;
+
+ /*
+ * Merge the rdataset into the database.
+ */
+ switch (op) {
+ case DNS_DIFFOP_ADD:
+ case DNS_DIFFOP_ADDRESIGN:
+ result = dns_db_addrdataset(db, node, ver,
+ 0, &rds,
+ DNS_DBADD_MERGE|
+ DNS_DBADD_EXACT|
+ DNS_DBADD_EXACTTTL,
+ modified);
+ break;
+ case DNS_DIFFOP_DEL:
+ case DNS_DIFFOP_DELRESIGN:
+ result = dns_db_subtractrdataset(db, node, ver,
+ &rds,
+ DNS_DBSUB_EXACT,
+ modified);
+ break;
+ default:
+ INSIST(0);
+ }
+
+ if (result == ISC_R_SUCCESS) {
+ if (modified != NULL) {
+ isc_stdtime_t resign;
+ resign = setresign(modified,
+ diff->resign);
+ dns_db_setsigningtime(db, modified,
+ resign);
+ }
+ } else if (result == DNS_R_UNCHANGED) {
+ /*
+ * This will not happen when executing a
+ * dynamic update, because that code will
+ * generate strictly minimal diffs.
+ * It may happen when receiving an IXFR
+ * from a server that is not as careful.
+ * Issue a warning and continue.
+ */
+ if (warn)
+ isc_log_write(DIFF_COMMON_LOGARGS,
+ ISC_LOG_WARNING,
+ "update with no effect");
+ } else if (result == DNS_R_NXRRSET) {
+ /*
+ * OK.
+ */
+ } else {
+ if (modified != NULL &&
+ dns_rdataset_isassociated(modified))
+ dns_rdataset_disassociate(modified);
+ CHECK(result);
+ }
+ dns_db_detachnode(db, &node);
+ if (modified != NULL &&
+ dns_rdataset_isassociated(modified))
+ dns_rdataset_disassociate(modified);
+ }
+ }
+ return (ISC_R_SUCCESS);
+
+ failure:
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+isc_result_t
+dns_diff_apply(dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver) {
+ return (diff_apply(diff, db, ver, ISC_TRUE));
+}
+
+isc_result_t
+dns_diff_applysilently(dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver) {
+ return (diff_apply(diff, db, ver, ISC_FALSE));
+}
+
+/* XXX this duplicates lots of code in diff_apply(). */
+
+isc_result_t
+dns_diff_load(dns_diff_t *diff, dns_addrdatasetfunc_t addfunc,
+ void *add_private)
+{
+ dns_difftuple_t *t;
+ isc_result_t result;
+
+ REQUIRE(DNS_DIFF_VALID(diff));
+
+ t = ISC_LIST_HEAD(diff->tuples);
+ while (t != NULL) {
+ dns_name_t *name;
+
+ name = &t->name;
+ while (t != NULL && dns_name_equal(&t->name, name)) {
+ dns_rdatatype_t type, covers;
+ dns_diffop_t op;
+ dns_rdatalist_t rdl;
+ dns_rdataset_t rds;
+
+ op = t->op;
+ type = t->rdata.type;
+ covers = rdata_covers(&t->rdata);
+
+ rdl.type = type;
+ rdl.covers = covers;
+ rdl.rdclass = t->rdata.rdclass;
+ rdl.ttl = t->ttl;
+ ISC_LIST_INIT(rdl.rdata);
+ ISC_LINK_INIT(&rdl, link);
+
+ while (t != NULL && dns_name_equal(&t->name, name) &&
+ t->op == op && t->rdata.type == type &&
+ rdata_covers(&t->rdata) == covers)
+ {
+ ISC_LIST_APPEND(rdl.rdata, &t->rdata, link);
+ t = ISC_LIST_NEXT(t, link);
+ }
+
+ /*
+ * Convert the rdatalist into a rdataset.
+ */
+ dns_rdataset_init(&rds);
+ CHECK(dns_rdatalist_tordataset(&rdl, &rds));
+ rds.trust = dns_trust_ultimate;
+
+ INSIST(op == DNS_DIFFOP_ADD);
+ result = (*addfunc)(add_private, name, &rds);
+ if (result == DNS_R_UNCHANGED) {
+ isc_log_write(DIFF_COMMON_LOGARGS,
+ ISC_LOG_WARNING,
+ "update with no effect");
+ } else if (result == ISC_R_SUCCESS ||
+ result == DNS_R_NXRRSET) {
+ /*
+ * OK.
+ */
+ } else {
+ CHECK(result);
+ }
+ }
+ }
+ result = ISC_R_SUCCESS;
+ failure:
+ return (result);
+}
+
+/*
+ * XXX uses qsort(); a merge sort would be more natural for lists,
+ * and perhaps safer wrt thread stack overflow.
+ */
+isc_result_t
+dns_diff_sort(dns_diff_t *diff, dns_diff_compare_func *compare) {
+ unsigned int length = 0;
+ unsigned int i;
+ dns_difftuple_t **v;
+ dns_difftuple_t *p;
+ REQUIRE(DNS_DIFF_VALID(diff));
+
+ for (p = ISC_LIST_HEAD(diff->tuples);
+ p != NULL;
+ p = ISC_LIST_NEXT(p, link))
+ length++;
+ if (length == 0)
+ return (ISC_R_SUCCESS);
+ v = isc_mem_get(diff->mctx, length * sizeof(dns_difftuple_t *));
+ if (v == NULL)
+ return (ISC_R_NOMEMORY);
+ i = 0;
+ for (i = 0; i < length; i++) {
+ p = ISC_LIST_HEAD(diff->tuples);
+ v[i] = p;
+ ISC_LIST_UNLINK(diff->tuples, p, link);
+ }
+ INSIST(ISC_LIST_HEAD(diff->tuples) == NULL);
+ qsort(v, length, sizeof(v[0]), compare);
+ for (i = 0; i < length; i++) {
+ ISC_LIST_APPEND(diff->tuples, v[i], link);
+ }
+ isc_mem_put(diff->mctx, v, length * sizeof(dns_difftuple_t *));
+ return (ISC_R_SUCCESS);
+}
+
+
+/*
+ * Create an rdataset containing the single RR of the given
+ * tuple. The caller must allocate the the rdata, rdataset and
+ * an rdatalist structure for it to refer to.
+ */
+
+static isc_result_t
+diff_tuple_tordataset(dns_difftuple_t *t, dns_rdata_t *rdata,
+ dns_rdatalist_t *rdl, dns_rdataset_t *rds)
+{
+ REQUIRE(DNS_DIFFTUPLE_VALID(t));
+ REQUIRE(rdl != NULL);
+ REQUIRE(rds != NULL);
+
+ rdl->type = t->rdata.type;
+ rdl->rdclass = t->rdata.rdclass;
+ rdl->ttl = t->ttl;
+ ISC_LIST_INIT(rdl->rdata);
+ ISC_LINK_INIT(rdl, link);
+ dns_rdataset_init(rds);
+ ISC_LINK_INIT(rdata, link);
+ dns_rdata_clone(&t->rdata, rdata);
+ ISC_LIST_APPEND(rdl->rdata, rdata, link);
+ return (dns_rdatalist_tordataset(rdl, rds));
+}
+
+isc_result_t
+dns_diff_print(dns_diff_t *diff, FILE *file) {
+ isc_result_t result;
+ dns_difftuple_t *t;
+ char *mem = NULL;
+ unsigned int size = 2048;
+ const char *op = NULL;
+
+ REQUIRE(DNS_DIFF_VALID(diff));
+
+ mem = isc_mem_get(diff->mctx, size);
+ if (mem == NULL)
+ return (ISC_R_NOMEMORY);
+
+ for (t = ISC_LIST_HEAD(diff->tuples); t != NULL;
+ t = ISC_LIST_NEXT(t, link))
+ {
+ isc_buffer_t buf;
+ isc_region_t r;
+
+ dns_rdatalist_t rdl;
+ dns_rdataset_t rds;
+ dns_rdata_t rd = DNS_RDATA_INIT;
+
+ result = diff_tuple_tordataset(t, &rd, &rdl, &rds);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "diff_tuple_tordataset failed: %s",
+ dns_result_totext(result));
+ result = ISC_R_UNEXPECTED;
+ goto cleanup;
+ }
+ again:
+ isc_buffer_init(&buf, mem, size);
+ result = dns_rdataset_totext(&rds, &t->name,
+ ISC_FALSE, ISC_FALSE, &buf);
+
+ if (result == ISC_R_NOSPACE) {
+ isc_mem_put(diff->mctx, mem, size);
+ size += 1024;
+ mem = isc_mem_get(diff->mctx, size);
+ if (mem == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ goto again;
+ }
+
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ /*
+ * Get rid of final newline.
+ */
+ INSIST(buf.used >= 1 &&
+ ((char *) buf.base)[buf.used-1] == '\n');
+ buf.used--;
+
+ isc_buffer_usedregion(&buf, &r);
+ switch (t->op) {
+ case DNS_DIFFOP_EXISTS: op = "exists"; break;
+ case DNS_DIFFOP_ADD: op = "add"; break;
+ case DNS_DIFFOP_DEL: op = "del"; break;
+ case DNS_DIFFOP_ADDRESIGN: op = "add re-sign"; break;
+ case DNS_DIFFOP_DELRESIGN: op = "del re-sign"; break;
+ }
+ if (file != NULL)
+ fprintf(file, "%s %.*s\n", op, (int) r.length,
+ (char *) r.base);
+ else
+ isc_log_write(DIFF_COMMON_LOGARGS, ISC_LOG_DEBUG(7),
+ "%s %.*s", op, (int) r.length,
+ (char *) r.base);
+ }
+ result = ISC_R_SUCCESS;
+ cleanup:
+ if (mem != NULL)
+ isc_mem_put(diff->mctx, mem, size);
+ return (result);
+}
diff --git a/lib/dns/dispatch.c b/lib/dns/dispatch.c
new file mode 100644
index 0000000..9284389
--- /dev/null
+++ b/lib/dns/dispatch.c
@@ -0,0 +1,3445 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: dispatch.c,v 1.155.12.2 2008/12/10 23:48:13 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <isc/entropy.h>
+#include <isc/mem.h>
+#include <isc/mutex.h>
+#include <isc/portset.h>
+#include <isc/print.h>
+#include <isc/random.h>
+#include <isc/string.h>
+#include <isc/task.h>
+#include <isc/time.h>
+#include <isc/util.h>
+
+#include <dns/acl.h>
+#include <dns/dispatch.h>
+#include <dns/events.h>
+#include <dns/log.h>
+#include <dns/message.h>
+#include <dns/portlist.h>
+#include <dns/stats.h>
+#include <dns/tcpmsg.h>
+#include <dns/types.h>
+
+typedef ISC_LIST(dns_dispentry_t) dns_displist_t;
+
+typedef struct dispsocket dispsocket_t;
+typedef ISC_LIST(dispsocket_t) dispsocketlist_t;
+
+typedef struct dispportentry dispportentry_t;
+typedef ISC_LIST(dispportentry_t) dispportlist_t;
+
+/* ARC4 Random generator state */
+typedef struct arc4ctx {
+ isc_uint8_t i;
+ isc_uint8_t j;
+ isc_uint8_t s[256];
+ int count;
+ isc_entropy_t *entropy; /*%< entropy source for ARC4 */
+ isc_mutex_t *lock;
+} arc4ctx_t;
+
+typedef struct dns_qid {
+ unsigned int magic;
+ unsigned int qid_nbuckets; /*%< hash table size */
+ unsigned int qid_increment; /*%< id increment on collision */
+ isc_mutex_t lock;
+ dns_displist_t *qid_table; /*%< the table itself */
+ dispsocketlist_t *sock_table; /*%< socket table */
+} dns_qid_t;
+
+struct dns_dispatchmgr {
+ /* Unlocked. */
+ unsigned int magic;
+ isc_mem_t *mctx;
+ dns_acl_t *blackhole;
+ dns_portlist_t *portlist;
+ dns_stats_t *stats;
+ isc_entropy_t *entropy; /*%< entropy source */
+
+ /* Locked by "lock". */
+ isc_mutex_t lock;
+ unsigned int state;
+ ISC_LIST(dns_dispatch_t) list;
+
+ /* Locked by arc4_lock. */
+ isc_mutex_t arc4_lock;
+ arc4ctx_t arc4ctx; /*%< ARC4 context for QID */
+
+ /* locked by buffer lock */
+ dns_qid_t *qid;
+ isc_mutex_t buffer_lock;
+ unsigned int buffers; /*%< allocated buffers */
+ unsigned int buffersize; /*%< size of each buffer */
+ unsigned int maxbuffers; /*%< max buffers */
+
+ /* Locked internally. */
+ isc_mutex_t pool_lock;
+ isc_mempool_t *epool; /*%< memory pool for events */
+ isc_mempool_t *rpool; /*%< memory pool for replies */
+ isc_mempool_t *dpool; /*%< dispatch allocations */
+ isc_mempool_t *bpool; /*%< memory pool for buffers */
+ isc_mempool_t *spool; /*%< memory pool for dispsocs */
+
+ /*%
+ * Locked by qid->lock if qid exists; otherwise, can be used without
+ * being locked.
+ * Memory footprint considerations: this is a simple implementation of
+ * available ports, i.e., an ordered array of the actual port numbers.
+ * This will require about 256KB of memory in the worst case (128KB for
+ * each of IPv4 and IPv6). We could reduce it by representing it as a
+ * more sophisticated way such as a list (or array) of ranges that are
+ * searched to identify a specific port. Our decision here is the saved
+ * memory isn't worth the implementation complexity, considering the
+ * fact that the whole BIND9 process (which is mainly named) already
+ * requires a pretty large memory footprint. We may, however, have to
+ * revisit the decision when we want to use it as a separate module for
+ * an environment where memory requirement is severer.
+ */
+ in_port_t *v4ports; /*%< available ports for IPv4 */
+ unsigned int nv4ports; /*%< # of available ports for IPv4 */
+ in_port_t *v6ports; /*%< available ports for IPv4 */
+ unsigned int nv6ports; /*%< # of available ports for IPv4 */
+};
+
+#define MGR_SHUTTINGDOWN 0x00000001U
+#define MGR_IS_SHUTTINGDOWN(l) (((l)->state & MGR_SHUTTINGDOWN) != 0)
+
+#define IS_PRIVATE(d) (((d)->attributes & DNS_DISPATCHATTR_PRIVATE) != 0)
+
+struct dns_dispentry {
+ unsigned int magic;
+ dns_dispatch_t *disp;
+ dns_messageid_t id;
+ in_port_t port;
+ unsigned int bucket;
+ isc_sockaddr_t host;
+ isc_task_t *task;
+ isc_taskaction_t action;
+ void *arg;
+ isc_boolean_t item_out;
+ dispsocket_t *dispsocket;
+ ISC_LIST(dns_dispatchevent_t) items;
+ ISC_LINK(dns_dispentry_t) link;
+};
+
+/*%
+ * Maximum number of dispatch sockets that can be pooled for reuse. The
+ * appropriate value may vary, but experiments have shown a busy caching server
+ * may need more than 1000 sockets concurrently opened. The maximum allowable
+ * number of dispatch sockets (per manager) will be set to the double of this
+ * value.
+ */
+#ifndef DNS_DISPATCH_POOLSOCKS
+#define DNS_DISPATCH_POOLSOCKS 2048
+#endif
+
+/*%
+ * Quota to control the number of dispatch sockets. If a dispatch has more
+ * than the quota of sockets, new queries will purge oldest ones, so that
+ * a massive number of outstanding queries won't prevent subsequent queries
+ * (especially if the older ones take longer time and result in timeout).
+ */
+#ifndef DNS_DISPATCH_SOCKSQUOTA
+#define DNS_DISPATCH_SOCKSQUOTA 3072
+#endif
+
+struct dispsocket {
+ unsigned int magic;
+ isc_socket_t *socket;
+ dns_dispatch_t *disp;
+ isc_sockaddr_t host;
+ in_port_t localport; /* XXX: should be removed later */
+ dispportentry_t *portentry;
+ dns_dispentry_t *resp;
+ isc_task_t *task;
+ ISC_LINK(dispsocket_t) link;
+ unsigned int bucket;
+ ISC_LINK(dispsocket_t) blink;
+};
+
+/*%
+ * A port table entry. We remember every port we first open in a table with a
+ * reference counter so that we can 'reuse' the same port (with different
+ * destination addresses) using the SO_REUSEADDR socket option.
+ */
+struct dispportentry {
+ in_port_t port;
+ unsigned int refs;
+ ISC_LINK(struct dispportentry) link;
+};
+
+#ifndef DNS_DISPATCH_PORTTABLESIZE
+#define DNS_DISPATCH_PORTTABLESIZE 1024
+#endif
+
+#define INVALID_BUCKET (0xffffdead)
+
+/*%
+ * Number of tasks for each dispatch that use separate sockets for different
+ * transactions. This must be a power of 2 as it will divide 32 bit numbers
+ * to get an uniformly random tasks selection. See get_dispsocket().
+ */
+#define MAX_INTERNAL_TASKS 64
+
+struct dns_dispatch {
+ /* Unlocked. */
+ unsigned int magic; /*%< magic */
+ dns_dispatchmgr_t *mgr; /*%< dispatch manager */
+ int ntasks;
+ /*%
+ * internal task buckets. We use multiple tasks to distribute various
+ * socket events well when using separate dispatch sockets. We use the
+ * 1st task (task[0]) for internal control events.
+ */
+ isc_task_t *task[MAX_INTERNAL_TASKS];
+ isc_socket_t *socket; /*%< isc socket attached to */
+ isc_sockaddr_t local; /*%< local address */
+ in_port_t localport; /*%< local UDP port */
+ unsigned int maxrequests; /*%< max requests */
+ isc_event_t *ctlevent;
+
+ /*% Locked by mgr->lock. */
+ ISC_LINK(dns_dispatch_t) link;
+
+ /* Locked by "lock". */
+ isc_mutex_t lock; /*%< locks all below */
+ isc_sockettype_t socktype;
+ unsigned int attributes;
+ unsigned int refcount; /*%< number of users */
+ dns_dispatchevent_t *failsafe_ev; /*%< failsafe cancel event */
+ unsigned int shutting_down : 1,
+ shutdown_out : 1,
+ connected : 1,
+ tcpmsg_valid : 1,
+ recv_pending : 1; /*%< is a recv() pending? */
+ isc_result_t shutdown_why;
+ ISC_LIST(dispsocket_t) activesockets;
+ ISC_LIST(dispsocket_t) inactivesockets;
+ unsigned int nsockets;
+ unsigned int requests; /*%< how many requests we have */
+ unsigned int tcpbuffers; /*%< allocated buffers */
+ dns_tcpmsg_t tcpmsg; /*%< for tcp streams */
+ dns_qid_t *qid;
+ arc4ctx_t arc4ctx; /*%< for QID/UDP port num */
+ dispportlist_t *port_table; /*%< hold ports 'owned' by us */
+ isc_mempool_t *portpool; /*%< port table entries */
+};
+
+#define QID_MAGIC ISC_MAGIC('Q', 'i', 'd', ' ')
+#define VALID_QID(e) ISC_MAGIC_VALID((e), QID_MAGIC)
+
+#define RESPONSE_MAGIC ISC_MAGIC('D', 'r', 's', 'p')
+#define VALID_RESPONSE(e) ISC_MAGIC_VALID((e), RESPONSE_MAGIC)
+
+#define DISPSOCK_MAGIC ISC_MAGIC('D', 's', 'o', 'c')
+#define VALID_DISPSOCK(e) ISC_MAGIC_VALID((e), DISPSOCK_MAGIC)
+
+#define DISPATCH_MAGIC ISC_MAGIC('D', 'i', 's', 'p')
+#define VALID_DISPATCH(e) ISC_MAGIC_VALID((e), DISPATCH_MAGIC)
+
+#define DNS_DISPATCHMGR_MAGIC ISC_MAGIC('D', 'M', 'g', 'r')
+#define VALID_DISPATCHMGR(e) ISC_MAGIC_VALID((e), DNS_DISPATCHMGR_MAGIC)
+
+#define DNS_QID(disp) ((disp)->socktype == isc_sockettype_tcp) ? \
+ (disp)->qid : (disp)->mgr->qid
+#define DISP_ARC4CTX(disp) ((disp)->socktype == isc_sockettype_udp) ? \
+ (&(disp)->arc4ctx) : (&(disp)->mgr->arc4ctx)
+
+/*%
+ * Locking a query port buffer is a bit tricky. We access the buffer without
+ * locking until qid is created. Technically, there is a possibility of race
+ * between the creation of qid and access to the port buffer; in practice,
+ * however, this should be safe because qid isn't created until the first
+ * dispatch is created and there should be no contending situation until then.
+ */
+#define PORTBUFLOCK(mgr) if ((mgr)->qid != NULL) LOCK(&((mgr)->qid->lock))
+#define PORTBUFUNLOCK(mgr) if ((mgr)->qid != NULL) UNLOCK((&(mgr)->qid->lock))
+
+/*
+ * Statics.
+ */
+static dns_dispentry_t *entry_search(dns_qid_t *, isc_sockaddr_t *,
+ dns_messageid_t, in_port_t, unsigned int);
+static isc_boolean_t destroy_disp_ok(dns_dispatch_t *);
+static void destroy_disp(isc_task_t *task, isc_event_t *event);
+static void destroy_dispsocket(dns_dispatch_t *, dispsocket_t **);
+static void deactivate_dispsocket(dns_dispatch_t *, dispsocket_t *);
+static void udp_exrecv(isc_task_t *, isc_event_t *);
+static void udp_shrecv(isc_task_t *, isc_event_t *);
+static void udp_recv(isc_event_t *, dns_dispatch_t *, dispsocket_t *);
+static void tcp_recv(isc_task_t *, isc_event_t *);
+static isc_result_t startrecv(dns_dispatch_t *, dispsocket_t *);
+static isc_uint32_t dns_hash(dns_qid_t *, isc_sockaddr_t *, dns_messageid_t,
+ in_port_t);
+static void free_buffer(dns_dispatch_t *disp, void *buf, unsigned int len);
+static void *allocate_udp_buffer(dns_dispatch_t *disp);
+static inline void free_event(dns_dispatch_t *disp, dns_dispatchevent_t *ev);
+static inline dns_dispatchevent_t *allocate_event(dns_dispatch_t *disp);
+static void do_cancel(dns_dispatch_t *disp);
+static dns_dispentry_t *linear_first(dns_qid_t *disp);
+static dns_dispentry_t *linear_next(dns_qid_t *disp,
+ dns_dispentry_t *resp);
+static void dispatch_free(dns_dispatch_t **dispp);
+static isc_result_t get_udpsocket(dns_dispatchmgr_t *mgr,
+ dns_dispatch_t *disp,
+ isc_socketmgr_t *sockmgr,
+ isc_sockaddr_t *localaddr,
+ isc_socket_t **sockp);
+static isc_result_t dispatch_createudp(dns_dispatchmgr_t *mgr,
+ isc_socketmgr_t *sockmgr,
+ isc_taskmgr_t *taskmgr,
+ isc_sockaddr_t *localaddr,
+ unsigned int maxrequests,
+ unsigned int attributes,
+ dns_dispatch_t **dispp);
+static isc_boolean_t destroy_mgr_ok(dns_dispatchmgr_t *mgr);
+static void destroy_mgr(dns_dispatchmgr_t **mgrp);
+static isc_result_t qid_allocate(dns_dispatchmgr_t *mgr, unsigned int buckets,
+ unsigned int increment, dns_qid_t **qidp,
+ isc_boolean_t needaddrtable);
+static void qid_destroy(isc_mem_t *mctx, dns_qid_t **qidp);
+static isc_result_t open_socket(isc_socketmgr_t *mgr, isc_sockaddr_t *local,
+ unsigned int options, isc_socket_t **sockp);
+static isc_boolean_t portavailable(dns_dispatchmgr_t *mgr, isc_socket_t *sock,
+ isc_sockaddr_t *sockaddrp);
+
+#define LVL(x) ISC_LOG_DEBUG(x)
+
+static void
+mgr_log(dns_dispatchmgr_t *mgr, int level, const char *fmt, ...)
+ ISC_FORMAT_PRINTF(3, 4);
+
+static void
+mgr_log(dns_dispatchmgr_t *mgr, int level, const char *fmt, ...) {
+ char msgbuf[2048];
+ va_list ap;
+
+ if (! isc_log_wouldlog(dns_lctx, level))
+ return;
+
+ va_start(ap, fmt);
+ vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
+ va_end(ap);
+
+ isc_log_write(dns_lctx,
+ DNS_LOGCATEGORY_DISPATCH, DNS_LOGMODULE_DISPATCH,
+ level, "dispatchmgr %p: %s", mgr, msgbuf);
+}
+
+static void
+dispatch_log(dns_dispatch_t *disp, int level, const char *fmt, ...)
+ ISC_FORMAT_PRINTF(3, 4);
+
+static void
+dispatch_log(dns_dispatch_t *disp, int level, const char *fmt, ...) {
+ char msgbuf[2048];
+ va_list ap;
+
+ if (! isc_log_wouldlog(dns_lctx, level))
+ return;
+
+ va_start(ap, fmt);
+ vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
+ va_end(ap);
+
+ isc_log_write(dns_lctx,
+ DNS_LOGCATEGORY_DISPATCH, DNS_LOGMODULE_DISPATCH,
+ level, "dispatch %p: %s", disp, msgbuf);
+}
+
+static void
+request_log(dns_dispatch_t *disp, dns_dispentry_t *resp,
+ int level, const char *fmt, ...)
+ ISC_FORMAT_PRINTF(4, 5);
+
+static void
+request_log(dns_dispatch_t *disp, dns_dispentry_t *resp,
+ int level, const char *fmt, ...)
+{
+ char msgbuf[2048];
+ char peerbuf[256];
+ va_list ap;
+
+ if (! isc_log_wouldlog(dns_lctx, level))
+ return;
+
+ va_start(ap, fmt);
+ vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
+ va_end(ap);
+
+ if (VALID_RESPONSE(resp)) {
+ isc_sockaddr_format(&resp->host, peerbuf, sizeof(peerbuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DISPATCH,
+ DNS_LOGMODULE_DISPATCH, level,
+ "dispatch %p response %p %s: %s", disp, resp,
+ peerbuf, msgbuf);
+ } else {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DISPATCH,
+ DNS_LOGMODULE_DISPATCH, level,
+ "dispatch %p req/resp %p: %s", disp, resp,
+ msgbuf);
+ }
+}
+
+/*%
+ * ARC4 random number generator derived from OpenBSD.
+ * Only dispatch_arc4random() and dispatch_arc4uniformrandom() are expected
+ * to be called from general dispatch routines; the rest of them are subroutines
+ * for these two.
+ *
+ * The original copyright follows:
+ * 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.
+ */
+static void
+dispatch_arc4init(arc4ctx_t *actx, isc_entropy_t *entropy, isc_mutex_t *lock) {
+ int n;
+ for (n = 0; n < 256; n++)
+ actx->s[n] = n;
+ actx->i = 0;
+ actx->j = 0;
+ actx->count = 0;
+ actx->entropy = entropy; /* don't have to attach */
+ actx->lock = lock;
+}
+
+static void
+dispatch_arc4addrandom(arc4ctx_t *actx, unsigned char *dat, int datlen) {
+ int n;
+ isc_uint8_t si;
+
+ actx->i--;
+ for (n = 0; n < 256; n++) {
+ actx->i = (actx->i + 1);
+ si = actx->s[actx->i];
+ actx->j = (actx->j + si + dat[n % datlen]);
+ actx->s[actx->i] = actx->s[actx->j];
+ actx->s[actx->j] = si;
+ }
+ actx->j = actx->i;
+}
+
+static inline isc_uint8_t
+dispatch_arc4get8(arc4ctx_t *actx) {
+ isc_uint8_t si, sj;
+
+ actx->i = (actx->i + 1);
+ si = actx->s[actx->i];
+ actx->j = (actx->j + si);
+ sj = actx->s[actx->j];
+ actx->s[actx->i] = sj;
+ actx->s[actx->j] = si;
+
+ return (actx->s[(si + sj) & 0xff]);
+}
+
+static inline isc_uint16_t
+dispatch_arc4get16(arc4ctx_t *actx) {
+ isc_uint16_t val;
+
+ val = dispatch_arc4get8(actx) << 8;
+ val |= dispatch_arc4get8(actx);
+
+ return (val);
+}
+
+static void
+dispatch_arc4stir(arc4ctx_t *actx) {
+ int i;
+ union {
+ unsigned char rnd[128];
+ isc_uint32_t rnd32[32];
+ } rnd;
+ isc_result_t result;
+
+ if (actx->entropy != NULL) {
+ /*
+ * We accept any quality of random data to avoid blocking.
+ */
+ result = isc_entropy_getdata(actx->entropy, rnd.rnd,
+ sizeof(rnd), NULL, 0);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ } else {
+ for (i = 0; i < 32; i++)
+ isc_random_get(&rnd.rnd32[i]);
+ }
+ dispatch_arc4addrandom(actx, rnd.rnd, sizeof(rnd.rnd));
+
+ /*
+ * Discard early keystream, as per recommendations in:
+ * http://www.wisdom.weizmann.ac.il/~itsik/RC4/Papers/Rc4_ksa.ps
+ */
+ for (i = 0; i < 256; i++)
+ (void)dispatch_arc4get8(actx);
+
+ /*
+ * Derived from OpenBSD's implementation. The rationale is not clear,
+ * but should be conservative enough in safety, and reasonably large
+ * for efficiency.
+ */
+ actx->count = 1600000;
+}
+
+static isc_uint16_t
+dispatch_arc4random(arc4ctx_t *actx) {
+ isc_uint16_t result;
+
+ if (actx->lock != NULL)
+ LOCK(actx->lock);
+
+ actx->count -= sizeof(isc_uint16_t);
+ if (actx->count <= 0)
+ dispatch_arc4stir(actx);
+ result = dispatch_arc4get16(actx);
+
+ if (actx->lock != NULL)
+ UNLOCK(actx->lock);
+
+ return (result);
+}
+
+static isc_uint16_t
+dispatch_arc4uniformrandom(arc4ctx_t *actx, isc_uint16_t upper_bound) {
+ isc_uint16_t min, r;
+
+ if (upper_bound < 2)
+ return (0);
+
+ /*
+ * Ensure the range of random numbers [min, 0xffff] be a multiple of
+ * upper_bound and contain at least a half of the 16 bit range.
+ */
+
+ if (upper_bound > 0x8000)
+ min = 1 + ~upper_bound; /* 0x8000 - upper_bound */
+ else
+ min = (isc_uint16_t)(0x10000 % (isc_uint32_t)upper_bound);
+
+ /*
+ * 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 = dispatch_arc4random(actx);
+ if (r >= min)
+ break;
+ }
+
+ return (r % upper_bound);
+}
+
+/*
+ * Return a hash of the destination and message id.
+ */
+static isc_uint32_t
+dns_hash(dns_qid_t *qid, isc_sockaddr_t *dest, dns_messageid_t id,
+ in_port_t port)
+{
+ unsigned int ret;
+
+ ret = isc_sockaddr_hash(dest, ISC_TRUE);
+ ret ^= (id << 16) | port;
+ ret %= qid->qid_nbuckets;
+
+ INSIST(ret < qid->qid_nbuckets);
+
+ return (ret);
+}
+
+/*
+ * Find the first entry in 'qid'. Returns NULL if there are no entries.
+ */
+static dns_dispentry_t *
+linear_first(dns_qid_t *qid) {
+ dns_dispentry_t *ret;
+ unsigned int bucket;
+
+ bucket = 0;
+
+ while (bucket < qid->qid_nbuckets) {
+ ret = ISC_LIST_HEAD(qid->qid_table[bucket]);
+ if (ret != NULL)
+ return (ret);
+ bucket++;
+ }
+
+ return (NULL);
+}
+
+/*
+ * Find the next entry after 'resp' in 'qid'. Return NULL if there are
+ * no more entries.
+ */
+static dns_dispentry_t *
+linear_next(dns_qid_t *qid, dns_dispentry_t *resp) {
+ dns_dispentry_t *ret;
+ unsigned int bucket;
+
+ ret = ISC_LIST_NEXT(resp, link);
+ if (ret != NULL)
+ return (ret);
+
+ bucket = resp->bucket;
+ bucket++;
+ while (bucket < qid->qid_nbuckets) {
+ ret = ISC_LIST_HEAD(qid->qid_table[bucket]);
+ if (ret != NULL)
+ return (ret);
+ bucket++;
+ }
+
+ return (NULL);
+}
+
+/*
+ * The dispatch must be locked.
+ */
+static isc_boolean_t
+destroy_disp_ok(dns_dispatch_t *disp)
+{
+ if (disp->refcount != 0)
+ return (ISC_FALSE);
+
+ if (disp->recv_pending != 0)
+ return (ISC_FALSE);
+
+ if (!ISC_LIST_EMPTY(disp->activesockets))
+ return (ISC_FALSE);
+
+ if (disp->shutting_down == 0)
+ return (ISC_FALSE);
+
+ return (ISC_TRUE);
+}
+
+/*
+ * Called when refcount reaches 0 (and safe to destroy).
+ *
+ * The dispatcher must not be locked.
+ * The manager must be locked.
+ */
+static void
+destroy_disp(isc_task_t *task, isc_event_t *event) {
+ dns_dispatch_t *disp;
+ dns_dispatchmgr_t *mgr;
+ isc_boolean_t killmgr;
+ dispsocket_t *dispsocket;
+ int i;
+
+ INSIST(event->ev_type == DNS_EVENT_DISPATCHCONTROL);
+
+ UNUSED(task);
+
+ disp = event->ev_arg;
+ mgr = disp->mgr;
+
+ LOCK(&mgr->lock);
+ ISC_LIST_UNLINK(mgr->list, disp, link);
+
+ dispatch_log(disp, LVL(90),
+ "shutting down; detaching from sock %p, task %p",
+ disp->socket, disp->task[0]); /* XXXX */
+
+ if (disp->socket != NULL)
+ isc_socket_detach(&disp->socket);
+ while ((dispsocket = ISC_LIST_HEAD(disp->inactivesockets)) != NULL) {
+ ISC_LIST_UNLINK(disp->inactivesockets, dispsocket, link);
+ destroy_dispsocket(disp, &dispsocket);
+ }
+ for (i = 0; i < disp->ntasks; i++)
+ isc_task_detach(&disp->task[i]);
+ isc_event_free(&event);
+
+ dispatch_free(&disp);
+
+ killmgr = destroy_mgr_ok(mgr);
+ UNLOCK(&mgr->lock);
+ if (killmgr)
+ destroy_mgr(&mgr);
+}
+
+/*%
+ * Manipulate port table per dispatch: find an entry for a given port number,
+ * create a new entry, and decrement a given entry with possible clean-up.
+ */
+static dispportentry_t *
+port_search(dns_dispatch_t *disp, in_port_t port) {
+ dispportentry_t *portentry;
+
+ REQUIRE(disp->port_table != NULL);
+
+ portentry = ISC_LIST_HEAD(disp->port_table[port %
+ DNS_DISPATCH_PORTTABLESIZE]);
+ while (portentry != NULL) {
+ if (portentry->port == port)
+ return (portentry);
+ portentry = ISC_LIST_NEXT(portentry, link);
+ }
+
+ return (NULL);
+}
+
+static dispportentry_t *
+new_portentry(dns_dispatch_t *disp, in_port_t port) {
+ dispportentry_t *portentry;
+
+ REQUIRE(disp->port_table != NULL);
+
+ portentry = isc_mempool_get(disp->portpool);
+ if (portentry == NULL)
+ return (portentry);
+
+ portentry->port = port;
+ portentry->refs = 0;
+ ISC_LINK_INIT(portentry, link);
+ ISC_LIST_APPEND(disp->port_table[port % DNS_DISPATCH_PORTTABLESIZE],
+ portentry, link);
+
+ return (portentry);
+}
+
+static void
+deref_portentry(dns_dispatch_t *disp, dispportentry_t **portentryp) {
+ dispportentry_t *portentry = *portentryp;
+
+ REQUIRE(disp->port_table != NULL);
+ REQUIRE(portentry != NULL && portentry->refs > 0);
+
+ portentry->refs--;
+ if (portentry->refs == 0) {
+ ISC_LIST_UNLINK(disp->port_table[portentry->port %
+ DNS_DISPATCH_PORTTABLESIZE],
+ portentry, link);
+ isc_mempool_put(disp->portpool, portentry);
+ }
+
+ *portentryp = NULL;
+}
+
+/*%
+ * Find a dispsocket for socket address 'dest', and port number 'port'.
+ * Return NULL if no such entry exists.
+ */
+static dispsocket_t *
+socket_search(dns_qid_t *qid, isc_sockaddr_t *dest, in_port_t port,
+ unsigned int bucket)
+{
+ dispsocket_t *dispsock;
+
+ REQUIRE(bucket < qid->qid_nbuckets);
+
+ dispsock = ISC_LIST_HEAD(qid->sock_table[bucket]);
+
+ while (dispsock != NULL) {
+ if (isc_sockaddr_equal(dest, &dispsock->host) &&
+ dispsock->portentry->port == port)
+ return (dispsock);
+ dispsock = ISC_LIST_NEXT(dispsock, blink);
+ }
+
+ return (NULL);
+}
+
+/*%
+ * Make a new socket for a single dispatch with a random port number.
+ * The caller must hold the disp->lock and qid->lock.
+ */
+static isc_result_t
+get_dispsocket(dns_dispatch_t *disp, isc_sockaddr_t *dest,
+ isc_socketmgr_t *sockmgr, dns_qid_t *qid,
+ dispsocket_t **dispsockp, in_port_t *portp)
+{
+ int i;
+ isc_uint32_t r;
+ dns_dispatchmgr_t *mgr = disp->mgr;
+ isc_socket_t *sock = NULL;
+ isc_result_t result = ISC_R_FAILURE;
+ in_port_t port;
+ isc_sockaddr_t localaddr;
+ unsigned int bucket = 0;
+ dispsocket_t *dispsock;
+ unsigned int nports;
+ in_port_t *ports;
+ unsigned int bindoptions = 0;
+ dispportentry_t *portentry = NULL;
+
+ if (isc_sockaddr_pf(&disp->local) == AF_INET) {
+ nports = disp->mgr->nv4ports;
+ ports = disp->mgr->v4ports;
+ } else {
+ nports = disp->mgr->nv6ports;
+ ports = disp->mgr->v6ports;
+ }
+ if (nports == 0)
+ return (ISC_R_ADDRNOTAVAIL);
+
+ dispsock = ISC_LIST_HEAD(disp->inactivesockets);
+ if (dispsock != NULL) {
+ ISC_LIST_UNLINK(disp->inactivesockets, dispsock, link);
+ sock = dispsock->socket;
+ dispsock->socket = NULL;
+ } else {
+ dispsock = isc_mempool_get(mgr->spool);
+ if (dispsock == NULL)
+ return (ISC_R_NOMEMORY);
+
+ disp->nsockets++;
+ dispsock->socket = NULL;
+ dispsock->disp = disp;
+ dispsock->resp = NULL;
+ dispsock->portentry = NULL;
+ isc_random_get(&r);
+ dispsock->task = NULL;
+ isc_task_attach(disp->task[r % disp->ntasks], &dispsock->task);
+ ISC_LINK_INIT(dispsock, link);
+ ISC_LINK_INIT(dispsock, blink);
+ dispsock->magic = DISPSOCK_MAGIC;
+ }
+
+ /*
+ * Pick up a random UDP port and open a new socket with it. Avoid
+ * choosing ports that share the same destination because it will be
+ * very likely to fail in bind(2) or connect(2).
+ */
+ localaddr = disp->local;
+ for (i = 0; i < 64; i++) {
+ port = ports[dispatch_arc4uniformrandom(DISP_ARC4CTX(disp),
+ nports)];
+ isc_sockaddr_setport(&localaddr, port);
+
+ bucket = dns_hash(qid, dest, 0, port);
+ if (socket_search(qid, dest, port, bucket) != NULL)
+ continue;
+ portentry = port_search(disp, port);
+ if (portentry != NULL)
+ bindoptions |= ISC_SOCKET_REUSEADDRESS;
+ result = open_socket(sockmgr, &localaddr, bindoptions, &sock);
+ if (result == ISC_R_SUCCESS) {
+ if (portentry == NULL) {
+ portentry = new_portentry(disp, port);
+ if (portentry == NULL) {
+ result = ISC_R_NOMEMORY;
+ break;
+ }
+ }
+ portentry->refs++;
+ break;
+ } else if (result != ISC_R_ADDRINUSE)
+ break;
+ }
+
+ if (result == ISC_R_SUCCESS) {
+ dispsock->socket = sock;
+ dispsock->host = *dest;
+ dispsock->portentry = portentry;
+ dispsock->bucket = bucket;
+ ISC_LIST_APPEND(qid->sock_table[bucket], dispsock, blink);
+ *dispsockp = dispsock;
+ *portp = port;
+ } else {
+ /*
+ * We could keep it in the inactive list, but since this should
+ * be an exceptional case and might be resource shortage, we'd
+ * rather destroy it.
+ */
+ if (sock != NULL)
+ isc_socket_detach(&sock);
+ destroy_dispsocket(disp, &dispsock);
+ }
+
+ return (result);
+}
+
+/*%
+ * Destroy a dedicated dispatch socket.
+ */
+static void
+destroy_dispsocket(dns_dispatch_t *disp, dispsocket_t **dispsockp) {
+ dispsocket_t *dispsock;
+ dns_qid_t *qid;
+
+ /*
+ * The dispatch must be locked.
+ */
+
+ REQUIRE(dispsockp != NULL && *dispsockp != NULL);
+ dispsock = *dispsockp;
+ REQUIRE(!ISC_LINK_LINKED(dispsock, link));
+
+ disp->nsockets--;
+ dispsock->magic = 0;
+ if (dispsock->portentry != NULL)
+ deref_portentry(disp, &dispsock->portentry);
+ if (dispsock->socket != NULL)
+ isc_socket_detach(&dispsock->socket);
+ if (ISC_LINK_LINKED(dispsock, blink)) {
+ qid = DNS_QID(disp);
+ LOCK(&qid->lock);
+ ISC_LIST_UNLINK(qid->sock_table[dispsock->bucket], dispsock,
+ blink);
+ UNLOCK(&qid->lock);
+ }
+ if (dispsock->task != NULL)
+ isc_task_detach(&dispsock->task);
+ isc_mempool_put(disp->mgr->spool, dispsock);
+
+ *dispsockp = NULL;
+}
+
+/*%
+ * Deactivate a dedicated dispatch socket. Move it to the inactive list for
+ * future reuse unless the total number of sockets are exceeding the maximum.
+ */
+static void
+deactivate_dispsocket(dns_dispatch_t *disp, dispsocket_t *dispsock) {
+ isc_result_t result;
+ dns_qid_t *qid;
+
+ /*
+ * The dispatch must be locked.
+ */
+ ISC_LIST_UNLINK(disp->activesockets, dispsock, link);
+ if (dispsock->resp != NULL) {
+ INSIST(dispsock->resp->dispsocket == dispsock);
+ dispsock->resp->dispsocket = NULL;
+ }
+
+ INSIST(dispsock->portentry != NULL);
+ deref_portentry(disp, &dispsock->portentry);
+
+ if (disp->nsockets > DNS_DISPATCH_POOLSOCKS)
+ destroy_dispsocket(disp, &dispsock);
+ else {
+ result = isc_socket_close(dispsock->socket);
+
+ qid = DNS_QID(disp);
+ LOCK(&qid->lock);
+ ISC_LIST_UNLINK(qid->sock_table[dispsock->bucket], dispsock,
+ blink);
+ UNLOCK(&qid->lock);
+
+ if (result == ISC_R_SUCCESS)
+ ISC_LIST_APPEND(disp->inactivesockets, dispsock, link);
+ else {
+ /*
+ * If the underlying system does not allow this
+ * optimization, destroy this temporary structure (and
+ * create a new one for a new transaction).
+ */
+ INSIST(result == ISC_R_NOTIMPLEMENTED);
+ destroy_dispsocket(disp, &dispsock);
+ }
+ }
+}
+
+/*
+ * Find an entry for query ID 'id', socket address 'dest', and port number
+ * 'port'.
+ * Return NULL if no such entry exists.
+ */
+static dns_dispentry_t *
+entry_search(dns_qid_t *qid, isc_sockaddr_t *dest, dns_messageid_t id,
+ in_port_t port, unsigned int bucket)
+{
+ dns_dispentry_t *res;
+
+ REQUIRE(bucket < qid->qid_nbuckets);
+
+ res = ISC_LIST_HEAD(qid->qid_table[bucket]);
+
+ while (res != NULL) {
+ if (res->id == id && isc_sockaddr_equal(dest, &res->host) &&
+ res->port == port) {
+ return (res);
+ }
+ res = ISC_LIST_NEXT(res, link);
+ }
+
+ return (NULL);
+}
+
+static void
+free_buffer(dns_dispatch_t *disp, void *buf, unsigned int len) {
+ INSIST(buf != NULL && len != 0);
+
+
+ switch (disp->socktype) {
+ case isc_sockettype_tcp:
+ INSIST(disp->tcpbuffers > 0);
+ disp->tcpbuffers--;
+ isc_mem_put(disp->mgr->mctx, buf, len);
+ break;
+ case isc_sockettype_udp:
+ LOCK(&disp->mgr->buffer_lock);
+ INSIST(disp->mgr->buffers > 0);
+ INSIST(len == disp->mgr->buffersize);
+ disp->mgr->buffers--;
+ isc_mempool_put(disp->mgr->bpool, buf);
+ UNLOCK(&disp->mgr->buffer_lock);
+ break;
+ default:
+ INSIST(0);
+ break;
+ }
+}
+
+static void *
+allocate_udp_buffer(dns_dispatch_t *disp) {
+ void *temp;
+
+ LOCK(&disp->mgr->buffer_lock);
+ temp = isc_mempool_get(disp->mgr->bpool);
+
+ if (temp != NULL)
+ disp->mgr->buffers++;
+ UNLOCK(&disp->mgr->buffer_lock);
+
+ return (temp);
+}
+
+static inline void
+free_event(dns_dispatch_t *disp, dns_dispatchevent_t *ev) {
+ if (disp->failsafe_ev == ev) {
+ INSIST(disp->shutdown_out == 1);
+ disp->shutdown_out = 0;
+
+ return;
+ }
+
+ isc_mempool_put(disp->mgr->epool, ev);
+}
+
+static inline dns_dispatchevent_t *
+allocate_event(dns_dispatch_t *disp) {
+ dns_dispatchevent_t *ev;
+
+ ev = isc_mempool_get(disp->mgr->epool);
+ if (ev == NULL)
+ return (NULL);
+ ISC_EVENT_INIT(ev, sizeof(*ev), 0, NULL, 0,
+ NULL, NULL, NULL, NULL, NULL);
+
+ return (ev);
+}
+
+static void
+udp_exrecv(isc_task_t *task, isc_event_t *ev) {
+ dispsocket_t *dispsock = ev->ev_arg;
+
+ UNUSED(task);
+
+ REQUIRE(VALID_DISPSOCK(dispsock));
+ udp_recv(ev, dispsock->disp, dispsock);
+}
+
+static void
+udp_shrecv(isc_task_t *task, isc_event_t *ev) {
+ dns_dispatch_t *disp = ev->ev_arg;
+
+ UNUSED(task);
+
+ REQUIRE(VALID_DISPATCH(disp));
+ udp_recv(ev, disp, NULL);
+}
+
+/*
+ * General flow:
+ *
+ * If I/O result == CANCELED or error, free the buffer.
+ *
+ * If query, free the buffer, restart.
+ *
+ * If response:
+ * Allocate event, fill in details.
+ * If cannot allocate, free buffer, restart.
+ * find target. If not found, free buffer, restart.
+ * if event queue is not empty, queue. else, send.
+ * restart.
+ */
+static void
+udp_recv(isc_event_t *ev_in, dns_dispatch_t *disp, dispsocket_t *dispsock) {
+ isc_socketevent_t *ev = (isc_socketevent_t *)ev_in;
+ dns_messageid_t id;
+ isc_result_t dres;
+ isc_buffer_t source;
+ unsigned int flags;
+ dns_dispentry_t *resp = NULL;
+ dns_dispatchevent_t *rev;
+ unsigned int bucket;
+ isc_boolean_t killit;
+ isc_boolean_t queue_response;
+ dns_dispatchmgr_t *mgr;
+ dns_qid_t *qid;
+ isc_netaddr_t netaddr;
+ int match;
+ int result;
+ isc_boolean_t qidlocked = ISC_FALSE;
+
+ LOCK(&disp->lock);
+
+ mgr = disp->mgr;
+ qid = mgr->qid;
+
+ dispatch_log(disp, LVL(90),
+ "got packet: requests %d, buffers %d, recvs %d",
+ disp->requests, disp->mgr->buffers, disp->recv_pending);
+
+ if (dispsock == NULL && ev->ev_type == ISC_SOCKEVENT_RECVDONE) {
+ /*
+ * Unless the receive event was imported from a listening
+ * interface, in which case the event type is
+ * DNS_EVENT_IMPORTRECVDONE, receive operation must be pending.
+ */
+ INSIST(disp->recv_pending != 0);
+ disp->recv_pending = 0;
+ }
+
+ if (dispsock != NULL &&
+ (ev->result == ISC_R_CANCELED || dispsock->resp == NULL)) {
+ /*
+ * dispsock->resp can be NULL if this transaction was canceled
+ * just after receiving a response. Since this socket is
+ * exclusively used and there should be at most one receive
+ * event the canceled event should have been no effect. So
+ * we can (and should) deactivate the socket right now.
+ */
+ deactivate_dispsocket(disp, dispsock);
+ dispsock = NULL;
+ }
+
+ if (disp->shutting_down) {
+ /*
+ * This dispatcher is shutting down.
+ */
+ free_buffer(disp, ev->region.base, ev->region.length);
+
+ isc_event_free(&ev_in);
+ ev = NULL;
+
+ killit = destroy_disp_ok(disp);
+ UNLOCK(&disp->lock);
+ if (killit)
+ isc_task_send(disp->task[0], &disp->ctlevent);
+
+ return;
+ }
+
+ if ((disp->attributes & DNS_DISPATCHATTR_EXCLUSIVE) != 0) {
+ if (dispsock != NULL) {
+ resp = dispsock->resp;
+ id = resp->id;
+ if (ev->result != ISC_R_SUCCESS) {
+ /*
+ * This is most likely a network error on a
+ * connected socket. It makes no sense to
+ * check the address or parse the packet, but it
+ * will help to return the error to the caller.
+ */
+ goto sendresponse;
+ }
+ } else {
+ free_buffer(disp, ev->region.base, ev->region.length);
+
+ UNLOCK(&disp->lock);
+ isc_event_free(&ev_in);
+ return;
+ }
+ } else if (ev->result != ISC_R_SUCCESS) {
+ free_buffer(disp, ev->region.base, ev->region.length);
+
+ if (ev->result != ISC_R_CANCELED)
+ dispatch_log(disp, ISC_LOG_ERROR,
+ "odd socket result in udp_recv(): %s",
+ isc_result_totext(ev->result));
+
+ UNLOCK(&disp->lock);
+ isc_event_free(&ev_in);
+ return;
+ }
+
+ /*
+ * If this is from a blackholed address, drop it.
+ */
+ isc_netaddr_fromsockaddr(&netaddr, &ev->address);
+ if (disp->mgr->blackhole != NULL &&
+ dns_acl_match(&netaddr, NULL, disp->mgr->blackhole,
+ NULL, &match, NULL) == ISC_R_SUCCESS &&
+ match > 0)
+ {
+ if (isc_log_wouldlog(dns_lctx, LVL(10))) {
+ char netaddrstr[ISC_NETADDR_FORMATSIZE];
+ isc_netaddr_format(&netaddr, netaddrstr,
+ sizeof(netaddrstr));
+ dispatch_log(disp, LVL(10),
+ "blackholed packet from %s",
+ netaddrstr);
+ }
+ free_buffer(disp, ev->region.base, ev->region.length);
+ goto restart;
+ }
+
+ /*
+ * Peek into the buffer to see what we can see.
+ */
+ isc_buffer_init(&source, ev->region.base, ev->region.length);
+ isc_buffer_add(&source, ev->n);
+ dres = dns_message_peekheader(&source, &id, &flags);
+ if (dres != ISC_R_SUCCESS) {
+ free_buffer(disp, ev->region.base, ev->region.length);
+ dispatch_log(disp, LVL(10), "got garbage packet");
+ goto restart;
+ }
+
+ dispatch_log(disp, LVL(92),
+ "got valid DNS message header, /QR %c, id %u",
+ ((flags & DNS_MESSAGEFLAG_QR) ? '1' : '0'), id);
+
+ /*
+ * Look at flags. If query, drop it. If response,
+ * look to see where it goes.
+ */
+ queue_response = ISC_FALSE;
+ if ((flags & DNS_MESSAGEFLAG_QR) == 0) {
+ /* query */
+ free_buffer(disp, ev->region.base, ev->region.length);
+ goto restart;
+ }
+
+ /*
+ * Search for the corresponding response. If we are using an exclusive
+ * socket, we've already identified it and we can skip the search; but
+ * the ID and the address must match the expected ones.
+ */
+ if (resp == NULL) {
+ bucket = dns_hash(qid, &ev->address, id, disp->localport);
+ LOCK(&qid->lock);
+ qidlocked = ISC_TRUE;
+ resp = entry_search(qid, &ev->address, id, disp->localport,
+ bucket);
+ dispatch_log(disp, LVL(90),
+ "search for response in bucket %d: %s",
+ bucket, (resp == NULL ? "not found" : "found"));
+
+ if (resp == NULL) {
+ dns_generalstats_increment(mgr->stats,
+ dns_resstatscounter_mismatch);
+ free_buffer(disp, ev->region.base, ev->region.length);
+ goto unlock;
+ }
+ } else if (resp->id != id || !isc_sockaddr_equal(&ev->address,
+ &resp->host)) {
+ dispatch_log(disp, LVL(90),
+ "response to an exclusive socket doesn't match");
+ dns_generalstats_increment(mgr->stats,
+ dns_resstatscounter_mismatch);
+ free_buffer(disp, ev->region.base, ev->region.length);
+ goto unlock;
+ }
+
+ /*
+ * Now that we have the original dispatch the query was sent
+ * from check that the address and port the response was
+ * sent to make sense.
+ */
+ if (disp != resp->disp) {
+ isc_sockaddr_t a1;
+ isc_sockaddr_t a2;
+
+ /*
+ * Check that the socket types and ports match.
+ */
+ if (disp->socktype != resp->disp->socktype ||
+ isc_sockaddr_getport(&disp->local) !=
+ isc_sockaddr_getport(&resp->disp->local)) {
+ free_buffer(disp, ev->region.base, ev->region.length);
+ goto unlock;
+ }
+
+ /*
+ * If both dispatches are bound to an address then fail as
+ * the addresses can't be equal (enforced by the IP stack).
+ *
+ * Note under Linux a packet can be sent out via IPv4 socket
+ * and the response be received via a IPv6 socket.
+ *
+ * Requests sent out via IPv6 should always come back in
+ * via IPv6.
+ */
+ if (isc_sockaddr_pf(&resp->disp->local) == PF_INET6 &&
+ isc_sockaddr_pf(&disp->local) != PF_INET6) {
+ free_buffer(disp, ev->region.base, ev->region.length);
+ goto unlock;
+ }
+ isc_sockaddr_anyofpf(&a1, isc_sockaddr_pf(&resp->disp->local));
+ isc_sockaddr_anyofpf(&a2, isc_sockaddr_pf(&disp->local));
+ if (!isc_sockaddr_eqaddr(&a1, &resp->disp->local) &&
+ !isc_sockaddr_eqaddr(&a2, &disp->local)) {
+ free_buffer(disp, ev->region.base, ev->region.length);
+ goto unlock;
+ }
+ }
+
+ sendresponse:
+ queue_response = resp->item_out;
+ rev = allocate_event(resp->disp);
+ if (rev == NULL) {
+ free_buffer(disp, ev->region.base, ev->region.length);
+ goto unlock;
+ }
+
+ /*
+ * At this point, rev contains the event we want to fill in, and
+ * resp contains the information on the place to send it to.
+ * Send the event off.
+ */
+ isc_buffer_init(&rev->buffer, ev->region.base, ev->region.length);
+ isc_buffer_add(&rev->buffer, ev->n);
+ rev->result = ev->result;
+ rev->id = id;
+ rev->addr = ev->address;
+ rev->pktinfo = ev->pktinfo;
+ rev->attributes = ev->attributes;
+ if (queue_response) {
+ ISC_LIST_APPEND(resp->items, rev, ev_link);
+ } else {
+ ISC_EVENT_INIT(rev, sizeof(*rev), 0, NULL,
+ DNS_EVENT_DISPATCH,
+ resp->action, resp->arg, resp, NULL, NULL);
+ request_log(disp, resp, LVL(90),
+ "[a] Sent event %p buffer %p len %d to task %p",
+ rev, rev->buffer.base, rev->buffer.length,
+ resp->task);
+ resp->item_out = ISC_TRUE;
+ isc_task_send(resp->task, ISC_EVENT_PTR(&rev));
+ }
+ unlock:
+ if (qidlocked)
+ UNLOCK(&qid->lock);
+
+ /*
+ * Restart recv() to get the next packet.
+ */
+ restart:
+ result = startrecv(disp, dispsock);
+ if (result != ISC_R_SUCCESS && dispsock != NULL) {
+ /*
+ * XXX: wired. There seems to be no recovery process other than
+ * deactivate this socket anyway (since we cannot start
+ * receiving, we won't be able to receive a cancel event
+ * from the user).
+ */
+ deactivate_dispsocket(disp, dispsock);
+ }
+ UNLOCK(&disp->lock);
+
+ isc_event_free(&ev_in);
+}
+
+/*
+ * General flow:
+ *
+ * If I/O result == CANCELED, EOF, or error, notify everyone as the
+ * various queues drain.
+ *
+ * If query, restart.
+ *
+ * If response:
+ * Allocate event, fill in details.
+ * If cannot allocate, restart.
+ * find target. If not found, restart.
+ * if event queue is not empty, queue. else, send.
+ * restart.
+ */
+static void
+tcp_recv(isc_task_t *task, isc_event_t *ev_in) {
+ dns_dispatch_t *disp = ev_in->ev_arg;
+ dns_tcpmsg_t *tcpmsg = &disp->tcpmsg;
+ dns_messageid_t id;
+ isc_result_t dres;
+ unsigned int flags;
+ dns_dispentry_t *resp;
+ dns_dispatchevent_t *rev;
+ unsigned int bucket;
+ isc_boolean_t killit;
+ isc_boolean_t queue_response;
+ dns_qid_t *qid;
+ int level;
+ char buf[ISC_SOCKADDR_FORMATSIZE];
+
+ UNUSED(task);
+
+ REQUIRE(VALID_DISPATCH(disp));
+
+ qid = disp->qid;
+
+ dispatch_log(disp, LVL(90),
+ "got TCP packet: requests %d, buffers %d, recvs %d",
+ disp->requests, disp->tcpbuffers, disp->recv_pending);
+
+ LOCK(&disp->lock);
+
+ INSIST(disp->recv_pending != 0);
+ disp->recv_pending = 0;
+
+ if (disp->refcount == 0) {
+ /*
+ * This dispatcher is shutting down. Force cancelation.
+ */
+ tcpmsg->result = ISC_R_CANCELED;
+ }
+
+ if (tcpmsg->result != ISC_R_SUCCESS) {
+ switch (tcpmsg->result) {
+ case ISC_R_CANCELED:
+ break;
+
+ case ISC_R_EOF:
+ dispatch_log(disp, LVL(90), "shutting down on EOF");
+ do_cancel(disp);
+ break;
+
+ case ISC_R_CONNECTIONRESET:
+ level = ISC_LOG_INFO;
+ goto logit;
+
+ default:
+ level = ISC_LOG_ERROR;
+ logit:
+ isc_sockaddr_format(&tcpmsg->address, buf, sizeof(buf));
+ dispatch_log(disp, level, "shutting down due to TCP "
+ "receive error: %s: %s", buf,
+ isc_result_totext(tcpmsg->result));
+ do_cancel(disp);
+ break;
+ }
+
+ /*
+ * The event is statically allocated in the tcpmsg
+ * structure, and destroy_disp() frees the tcpmsg, so we must
+ * free the event *before* calling destroy_disp().
+ */
+ isc_event_free(&ev_in);
+
+ disp->shutting_down = 1;
+ disp->shutdown_why = tcpmsg->result;
+
+ /*
+ * If the recv() was canceled pass the word on.
+ */
+ killit = destroy_disp_ok(disp);
+ UNLOCK(&disp->lock);
+ if (killit)
+ isc_task_send(disp->task[0], &disp->ctlevent);
+ return;
+ }
+
+ dispatch_log(disp, LVL(90), "result %d, length == %d, addr = %p",
+ tcpmsg->result,
+ tcpmsg->buffer.length, tcpmsg->buffer.base);
+
+ /*
+ * Peek into the buffer to see what we can see.
+ */
+ dres = dns_message_peekheader(&tcpmsg->buffer, &id, &flags);
+ if (dres != ISC_R_SUCCESS) {
+ dispatch_log(disp, LVL(10), "got garbage packet");
+ goto restart;
+ }
+
+ dispatch_log(disp, LVL(92),
+ "got valid DNS message header, /QR %c, id %u",
+ ((flags & DNS_MESSAGEFLAG_QR) ? '1' : '0'), id);
+
+ /*
+ * Allocate an event to send to the query or response client, and
+ * allocate a new buffer for our use.
+ */
+
+ /*
+ * Look at flags. If query, drop it. If response,
+ * look to see where it goes.
+ */
+ queue_response = ISC_FALSE;
+ if ((flags & DNS_MESSAGEFLAG_QR) == 0) {
+ /*
+ * Query.
+ */
+ goto restart;
+ }
+
+ /*
+ * Response.
+ */
+ bucket = dns_hash(qid, &tcpmsg->address, id, disp->localport);
+ LOCK(&qid->lock);
+ resp = entry_search(qid, &tcpmsg->address, id, disp->localport, bucket);
+ dispatch_log(disp, LVL(90),
+ "search for response in bucket %d: %s",
+ bucket, (resp == NULL ? "not found" : "found"));
+
+ if (resp == NULL)
+ goto unlock;
+ queue_response = resp->item_out;
+ rev = allocate_event(disp);
+ if (rev == NULL)
+ goto unlock;
+
+ /*
+ * At this point, rev contains the event we want to fill in, and
+ * resp contains the information on the place to send it to.
+ * Send the event off.
+ */
+ dns_tcpmsg_keepbuffer(tcpmsg, &rev->buffer);
+ disp->tcpbuffers++;
+ rev->result = ISC_R_SUCCESS;
+ rev->id = id;
+ rev->addr = tcpmsg->address;
+ if (queue_response) {
+ ISC_LIST_APPEND(resp->items, rev, ev_link);
+ } else {
+ ISC_EVENT_INIT(rev, sizeof(*rev), 0, NULL, DNS_EVENT_DISPATCH,
+ resp->action, resp->arg, resp, NULL, NULL);
+ request_log(disp, resp, LVL(90),
+ "[b] Sent event %p buffer %p len %d to task %p",
+ rev, rev->buffer.base, rev->buffer.length,
+ resp->task);
+ resp->item_out = ISC_TRUE;
+ isc_task_send(resp->task, ISC_EVENT_PTR(&rev));
+ }
+ unlock:
+ UNLOCK(&qid->lock);
+
+ /*
+ * Restart recv() to get the next packet.
+ */
+ restart:
+ (void)startrecv(disp, NULL);
+
+ UNLOCK(&disp->lock);
+
+ isc_event_free(&ev_in);
+}
+
+/*
+ * disp must be locked.
+ */
+static isc_result_t
+startrecv(dns_dispatch_t *disp, dispsocket_t *dispsock) {
+ isc_result_t res;
+ isc_region_t region;
+ isc_socket_t *socket;
+
+ if (disp->shutting_down == 1)
+ return (ISC_R_SUCCESS);
+
+ if ((disp->attributes & DNS_DISPATCHATTR_NOLISTEN) != 0)
+ return (ISC_R_SUCCESS);
+
+ if (disp->recv_pending != 0 && dispsock == NULL)
+ return (ISC_R_SUCCESS);
+
+ if (disp->mgr->buffers >= disp->mgr->maxbuffers)
+ return (ISC_R_NOMEMORY);
+
+ if ((disp->attributes & DNS_DISPATCHATTR_EXCLUSIVE) != 0 &&
+ dispsock == NULL)
+ return (ISC_R_SUCCESS);
+
+ if (dispsock != NULL)
+ socket = dispsock->socket;
+ else
+ socket = disp->socket;
+ INSIST(socket != NULL);
+
+ switch (disp->socktype) {
+ /*
+ * UDP reads are always maximal.
+ */
+ case isc_sockettype_udp:
+ region.length = disp->mgr->buffersize;
+ region.base = allocate_udp_buffer(disp);
+ if (region.base == NULL)
+ return (ISC_R_NOMEMORY);
+ if (dispsock != NULL) {
+ res = isc_socket_recv(socket, &region, 1,
+ dispsock->task, udp_exrecv,
+ dispsock);
+ if (res != ISC_R_SUCCESS) {
+ free_buffer(disp, region.base, region.length);
+ return (res);
+ }
+ } else {
+ res = isc_socket_recv(socket, &region, 1,
+ disp->task[0], udp_shrecv, disp);
+ if (res != ISC_R_SUCCESS) {
+ free_buffer(disp, region.base, region.length);
+ disp->shutdown_why = res;
+ disp->shutting_down = 1;
+ do_cancel(disp);
+ return (ISC_R_SUCCESS); /* recover by cancel */
+ }
+ INSIST(disp->recv_pending == 0);
+ disp->recv_pending = 1;
+ }
+ break;
+
+ case isc_sockettype_tcp:
+ res = dns_tcpmsg_readmessage(&disp->tcpmsg, disp->task[0],
+ tcp_recv, disp);
+ if (res != ISC_R_SUCCESS) {
+ disp->shutdown_why = res;
+ disp->shutting_down = 1;
+ do_cancel(disp);
+ return (ISC_R_SUCCESS); /* recover by cancel */
+ }
+ INSIST(disp->recv_pending == 0);
+ disp->recv_pending = 1;
+ break;
+ default:
+ INSIST(0);
+ break;
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Mgr must be locked when calling this function.
+ */
+static isc_boolean_t
+destroy_mgr_ok(dns_dispatchmgr_t *mgr) {
+ mgr_log(mgr, LVL(90),
+ "destroy_mgr_ok: shuttingdown=%d, listnonempty=%d, "
+ "epool=%d, rpool=%d, dpool=%d",
+ MGR_IS_SHUTTINGDOWN(mgr), !ISC_LIST_EMPTY(mgr->list),
+ isc_mempool_getallocated(mgr->epool),
+ isc_mempool_getallocated(mgr->rpool),
+ isc_mempool_getallocated(mgr->dpool));
+ if (!MGR_IS_SHUTTINGDOWN(mgr))
+ return (ISC_FALSE);
+ if (!ISC_LIST_EMPTY(mgr->list))
+ return (ISC_FALSE);
+ if (isc_mempool_getallocated(mgr->epool) != 0)
+ return (ISC_FALSE);
+ if (isc_mempool_getallocated(mgr->rpool) != 0)
+ return (ISC_FALSE);
+ if (isc_mempool_getallocated(mgr->dpool) != 0)
+ return (ISC_FALSE);
+
+ return (ISC_TRUE);
+}
+
+/*
+ * Mgr must be unlocked when calling this function.
+ */
+static void
+destroy_mgr(dns_dispatchmgr_t **mgrp) {
+ isc_mem_t *mctx;
+ dns_dispatchmgr_t *mgr;
+
+ mgr = *mgrp;
+ *mgrp = NULL;
+
+ mctx = mgr->mctx;
+
+ mgr->magic = 0;
+ mgr->mctx = NULL;
+ DESTROYLOCK(&mgr->lock);
+ mgr->state = 0;
+
+ DESTROYLOCK(&mgr->arc4_lock);
+
+ isc_mempool_destroy(&mgr->epool);
+ isc_mempool_destroy(&mgr->rpool);
+ isc_mempool_destroy(&mgr->dpool);
+ isc_mempool_destroy(&mgr->bpool);
+ isc_mempool_destroy(&mgr->spool);
+
+ DESTROYLOCK(&mgr->pool_lock);
+
+ if (mgr->entropy != NULL)
+ isc_entropy_detach(&mgr->entropy);
+ if (mgr->qid != NULL)
+ qid_destroy(mctx, &mgr->qid);
+
+ DESTROYLOCK(&mgr->buffer_lock);
+
+ if (mgr->blackhole != NULL)
+ dns_acl_detach(&mgr->blackhole);
+
+ if (mgr->stats != NULL)
+ dns_stats_detach(&mgr->stats);
+
+ if (mgr->v4ports != NULL) {
+ isc_mem_put(mctx, mgr->v4ports,
+ mgr->nv4ports * sizeof(in_port_t));
+ }
+ if (mgr->v6ports != NULL) {
+ isc_mem_put(mctx, mgr->v6ports,
+ mgr->nv6ports * sizeof(in_port_t));
+ }
+ isc_mem_put(mctx, mgr, sizeof(dns_dispatchmgr_t));
+ isc_mem_detach(&mctx);
+}
+
+static isc_result_t
+open_socket(isc_socketmgr_t *mgr, isc_sockaddr_t *local,
+ unsigned int options, isc_socket_t **sockp)
+{
+ isc_socket_t *sock;
+ isc_result_t result;
+
+ sock = *sockp;
+ if (sock == NULL) {
+ result = isc_socket_create(mgr, isc_sockaddr_pf(local),
+ isc_sockettype_udp, &sock);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ isc_socket_setname(sock, "dispatcher", NULL);
+ } else {
+ result = isc_socket_open(sock);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+#ifndef ISC_ALLOW_MAPPED
+ isc_socket_ipv6only(sock, ISC_TRUE);
+#endif
+ result = isc_socket_bind(sock, local, options);
+ if (result != ISC_R_SUCCESS) {
+ if (*sockp == NULL)
+ isc_socket_detach(&sock);
+ else
+ isc_socket_close(sock);
+ return (result);
+ }
+
+ *sockp = sock;
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Create a temporary port list to set the initial default set of dispatch
+ * ports: [1024, 65535]. This is almost meaningless as the application will
+ * normally set the ports explicitly, but is provided to fill some minor corner
+ * cases.
+ */
+static isc_result_t
+create_default_portset(isc_mem_t *mctx, isc_portset_t **portsetp) {
+ isc_result_t result;
+
+ result = isc_portset_create(mctx, portsetp);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ isc_portset_addrange(*portsetp, 1024, 65535);
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Publics.
+ */
+
+isc_result_t
+dns_dispatchmgr_create(isc_mem_t *mctx, isc_entropy_t *entropy,
+ dns_dispatchmgr_t **mgrp)
+{
+ dns_dispatchmgr_t *mgr;
+ isc_result_t result;
+ isc_portset_t *v4portset = NULL;
+ isc_portset_t *v6portset = NULL;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(mgrp != NULL && *mgrp == NULL);
+
+ mgr = isc_mem_get(mctx, sizeof(dns_dispatchmgr_t));
+ if (mgr == NULL)
+ return (ISC_R_NOMEMORY);
+
+ mgr->mctx = NULL;
+ isc_mem_attach(mctx, &mgr->mctx);
+
+ mgr->blackhole = NULL;
+ mgr->stats = NULL;
+
+ result = isc_mutex_init(&mgr->lock);
+ if (result != ISC_R_SUCCESS)
+ goto deallocate;
+
+ result = isc_mutex_init(&mgr->arc4_lock);
+ if (result != ISC_R_SUCCESS)
+ goto kill_lock;
+
+ result = isc_mutex_init(&mgr->buffer_lock);
+ if (result != ISC_R_SUCCESS)
+ goto kill_arc4_lock;
+
+ result = isc_mutex_init(&mgr->pool_lock);
+ if (result != ISC_R_SUCCESS)
+ goto kill_buffer_lock;
+
+ mgr->epool = NULL;
+ if (isc_mempool_create(mgr->mctx, sizeof(dns_dispatchevent_t),
+ &mgr->epool) != ISC_R_SUCCESS) {
+ result = ISC_R_NOMEMORY;
+ goto kill_pool_lock;
+ }
+
+ mgr->rpool = NULL;
+ if (isc_mempool_create(mgr->mctx, sizeof(dns_dispentry_t),
+ &mgr->rpool) != ISC_R_SUCCESS) {
+ result = ISC_R_NOMEMORY;
+ goto kill_epool;
+ }
+
+ mgr->dpool = NULL;
+ if (isc_mempool_create(mgr->mctx, sizeof(dns_dispatch_t),
+ &mgr->dpool) != ISC_R_SUCCESS) {
+ result = ISC_R_NOMEMORY;
+ goto kill_rpool;
+ }
+
+ isc_mempool_setname(mgr->epool, "dispmgr_epool");
+ isc_mempool_setfreemax(mgr->epool, 1024);
+ isc_mempool_associatelock(mgr->epool, &mgr->pool_lock);
+
+ isc_mempool_setname(mgr->rpool, "dispmgr_rpool");
+ isc_mempool_setfreemax(mgr->rpool, 1024);
+ isc_mempool_associatelock(mgr->rpool, &mgr->pool_lock);
+
+ isc_mempool_setname(mgr->dpool, "dispmgr_dpool");
+ isc_mempool_setfreemax(mgr->dpool, 1024);
+ isc_mempool_associatelock(mgr->dpool, &mgr->pool_lock);
+
+ mgr->buffers = 0;
+ mgr->buffersize = 0;
+ mgr->maxbuffers = 0;
+ mgr->bpool = NULL;
+ mgr->spool = NULL;
+ mgr->entropy = NULL;
+ mgr->qid = NULL;
+ mgr->state = 0;
+ ISC_LIST_INIT(mgr->list);
+ mgr->v4ports = NULL;
+ mgr->v6ports = NULL;
+ mgr->nv4ports = 0;
+ mgr->nv6ports = 0;
+ mgr->magic = DNS_DISPATCHMGR_MAGIC;
+
+ result = create_default_portset(mctx, &v4portset);
+ if (result == ISC_R_SUCCESS) {
+ result = create_default_portset(mctx, &v6portset);
+ if (result == ISC_R_SUCCESS) {
+ result = dns_dispatchmgr_setavailports(mgr,
+ v4portset,
+ v6portset);
+ }
+ }
+ if (v4portset != NULL)
+ isc_portset_destroy(mctx, &v4portset);
+ if (v6portset != NULL)
+ isc_portset_destroy(mctx, &v6portset);
+ if (result != ISC_R_SUCCESS)
+ goto kill_dpool;
+
+ if (entropy != NULL)
+ isc_entropy_attach(entropy, &mgr->entropy);
+
+ dispatch_arc4init(&mgr->arc4ctx, mgr->entropy, &mgr->arc4_lock);
+
+ *mgrp = mgr;
+ return (ISC_R_SUCCESS);
+
+ kill_dpool:
+ isc_mempool_destroy(&mgr->dpool);
+ kill_rpool:
+ isc_mempool_destroy(&mgr->rpool);
+ kill_epool:
+ isc_mempool_destroy(&mgr->epool);
+ kill_pool_lock:
+ DESTROYLOCK(&mgr->pool_lock);
+ kill_buffer_lock:
+ DESTROYLOCK(&mgr->buffer_lock);
+ kill_arc4_lock:
+ DESTROYLOCK(&mgr->arc4_lock);
+ kill_lock:
+ DESTROYLOCK(&mgr->lock);
+ deallocate:
+ isc_mem_put(mctx, mgr, sizeof(dns_dispatchmgr_t));
+ isc_mem_detach(&mctx);
+
+ return (result);
+}
+
+void
+dns_dispatchmgr_setblackhole(dns_dispatchmgr_t *mgr, dns_acl_t *blackhole) {
+ REQUIRE(VALID_DISPATCHMGR(mgr));
+ if (mgr->blackhole != NULL)
+ dns_acl_detach(&mgr->blackhole);
+ dns_acl_attach(blackhole, &mgr->blackhole);
+}
+
+dns_acl_t *
+dns_dispatchmgr_getblackhole(dns_dispatchmgr_t *mgr) {
+ REQUIRE(VALID_DISPATCHMGR(mgr));
+ return (mgr->blackhole);
+}
+
+void
+dns_dispatchmgr_setblackportlist(dns_dispatchmgr_t *mgr,
+ dns_portlist_t *portlist)
+{
+ REQUIRE(VALID_DISPATCHMGR(mgr));
+ UNUSED(portlist);
+
+ /* This function is deprecated: use dns_dispatchmgr_setavailports(). */
+ return;
+}
+
+dns_portlist_t *
+dns_dispatchmgr_getblackportlist(dns_dispatchmgr_t *mgr) {
+ REQUIRE(VALID_DISPATCHMGR(mgr));
+ return (NULL); /* this function is deprecated */
+}
+
+isc_result_t
+dns_dispatchmgr_setavailports(dns_dispatchmgr_t *mgr, isc_portset_t *v4portset,
+ isc_portset_t *v6portset)
+{
+ in_port_t *v4ports, *v6ports, p;
+ unsigned int nv4ports, nv6ports, i4, i6;
+
+ REQUIRE(VALID_DISPATCHMGR(mgr));
+
+ nv4ports = isc_portset_nports(v4portset);
+ nv6ports = isc_portset_nports(v6portset);
+
+ v4ports = NULL;
+ if (nv4ports != 0) {
+ v4ports = isc_mem_get(mgr->mctx, sizeof(in_port_t) * nv4ports);
+ if (v4ports == NULL)
+ return (ISC_R_NOMEMORY);
+ }
+ v6ports = NULL;
+ if (nv6ports != 0) {
+ v6ports = isc_mem_get(mgr->mctx, sizeof(in_port_t) * nv6ports);
+ if (v6ports == NULL) {
+ if (v4ports != NULL) {
+ isc_mem_put(mgr->mctx, v4ports,
+ sizeof(in_port_t) *
+ isc_portset_nports(v4portset));
+ }
+ return (ISC_R_NOMEMORY);
+ }
+ }
+
+ p = 0;
+ i4 = 0;
+ i6 = 0;
+ do {
+ if (isc_portset_isset(v4portset, p)) {
+ INSIST(i4 < nv4ports);
+ v4ports[i4++] = p;
+ }
+ if (isc_portset_isset(v6portset, p)) {
+ INSIST(i6 < nv6ports);
+ v6ports[i6++] = p;
+ }
+ } while (p++ < 65535);
+ INSIST(i4 == nv4ports && i6 == nv6ports);
+
+ PORTBUFLOCK(mgr);
+ if (mgr->v4ports != NULL) {
+ isc_mem_put(mgr->mctx, mgr->v4ports,
+ mgr->nv4ports * sizeof(in_port_t));
+ }
+ mgr->v4ports = v4ports;
+ mgr->nv4ports = nv4ports;
+
+ if (mgr->v6ports != NULL) {
+ isc_mem_put(mgr->mctx, mgr->v6ports,
+ mgr->nv6ports * sizeof(in_port_t));
+ }
+ mgr->v6ports = v6ports;
+ mgr->nv6ports = nv6ports;
+ PORTBUFUNLOCK(mgr);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+dns_dispatchmgr_setudp(dns_dispatchmgr_t *mgr,
+ unsigned int buffersize, unsigned int maxbuffers,
+ unsigned int maxrequests, unsigned int buckets,
+ unsigned int increment)
+{
+ isc_result_t result;
+
+ REQUIRE(VALID_DISPATCHMGR(mgr));
+ REQUIRE(buffersize >= 512 && buffersize < (64 * 1024));
+ REQUIRE(maxbuffers > 0);
+ REQUIRE(buckets < 2097169); /* next prime > 65536 * 32 */
+ REQUIRE(increment > buckets);
+
+ /*
+ * Keep some number of items around. This should be a config
+ * option. For now, keep 8, but later keep at least two even
+ * if the caller wants less. This allows us to ensure certain
+ * things, like an event can be "freed" and the next allocation
+ * will always succeed.
+ *
+ * Note that if limits are placed on anything here, we use one
+ * event internally, so the actual limit should be "wanted + 1."
+ *
+ * XXXMLG
+ */
+
+ if (maxbuffers < 8)
+ maxbuffers = 8;
+
+ LOCK(&mgr->buffer_lock);
+
+ /* Create or adjust buffer pool */
+ if (mgr->bpool != NULL) {
+ isc_mempool_setmaxalloc(mgr->bpool, maxbuffers);
+ mgr->maxbuffers = maxbuffers;
+ } else {
+ result = isc_mempool_create(mgr->mctx, buffersize, &mgr->bpool);
+ if (result != ISC_R_SUCCESS) {
+ UNLOCK(&mgr->buffer_lock);
+ return (result);
+ }
+ isc_mempool_setname(mgr->bpool, "dispmgr_bpool");
+ isc_mempool_setmaxalloc(mgr->bpool, maxbuffers);
+ isc_mempool_associatelock(mgr->bpool, &mgr->pool_lock);
+ }
+
+ /* Create or adjust socket pool */
+ if (mgr->spool != NULL) {
+ isc_mempool_setmaxalloc(mgr->spool, DNS_DISPATCH_POOLSOCKS * 2);
+ UNLOCK(&mgr->buffer_lock);
+ return (ISC_R_SUCCESS);
+ }
+ result = isc_mempool_create(mgr->mctx, sizeof(dispsocket_t),
+ &mgr->spool);
+ if (result != ISC_R_SUCCESS) {
+ UNLOCK(&mgr->buffer_lock);
+ goto cleanup;
+ }
+ isc_mempool_setname(mgr->spool, "dispmgr_spool");
+ isc_mempool_setmaxalloc(mgr->spool, maxrequests);
+ isc_mempool_associatelock(mgr->spool, &mgr->pool_lock);
+
+ result = qid_allocate(mgr, buckets, increment, &mgr->qid, ISC_TRUE);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ mgr->buffersize = buffersize;
+ mgr->maxbuffers = maxbuffers;
+ UNLOCK(&mgr->buffer_lock);
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ isc_mempool_destroy(&mgr->bpool);
+ if (mgr->spool != NULL)
+ isc_mempool_destroy(&mgr->spool);
+ UNLOCK(&mgr->buffer_lock);
+ return (result);
+}
+
+void
+dns_dispatchmgr_destroy(dns_dispatchmgr_t **mgrp) {
+ dns_dispatchmgr_t *mgr;
+ isc_boolean_t killit;
+
+ REQUIRE(mgrp != NULL);
+ REQUIRE(VALID_DISPATCHMGR(*mgrp));
+
+ mgr = *mgrp;
+ *mgrp = NULL;
+
+ LOCK(&mgr->lock);
+ mgr->state |= MGR_SHUTTINGDOWN;
+
+ killit = destroy_mgr_ok(mgr);
+ UNLOCK(&mgr->lock);
+
+ mgr_log(mgr, LVL(90), "destroy: killit=%d", killit);
+
+ if (killit)
+ destroy_mgr(&mgr);
+}
+
+void
+dns_dispatchmgr_setstats(dns_dispatchmgr_t *mgr, dns_stats_t *stats) {
+ REQUIRE(VALID_DISPATCHMGR(mgr));
+ REQUIRE(ISC_LIST_EMPTY(mgr->list));
+ REQUIRE(mgr->stats == NULL);
+
+ dns_stats_attach(stats, &mgr->stats);
+}
+
+static int
+port_cmp(const void *key, const void *ent) {
+ in_port_t p1 = *(const in_port_t *)key;
+ in_port_t p2 = *(const in_port_t *)ent;
+
+ if (p1 < p2)
+ return (-1);
+ else if (p1 == p2)
+ return (0);
+ else
+ return (1);
+}
+
+static isc_boolean_t
+portavailable(dns_dispatchmgr_t *mgr, isc_socket_t *sock,
+ isc_sockaddr_t *sockaddrp)
+{
+ isc_sockaddr_t sockaddr;
+ isc_result_t result;
+ in_port_t *ports, port;
+ unsigned int nports;
+ isc_boolean_t available = ISC_FALSE;
+
+ REQUIRE(sock != NULL || sockaddrp != NULL);
+
+ PORTBUFLOCK(mgr);
+ if (sock != NULL) {
+ sockaddrp = &sockaddr;
+ result = isc_socket_getsockname(sock, sockaddrp);
+ if (result != ISC_R_SUCCESS)
+ goto unlock;
+ }
+
+ if (isc_sockaddr_pf(sockaddrp) == AF_INET) {
+ ports = mgr->v4ports;
+ nports = mgr->nv4ports;
+ } else {
+ ports = mgr->v6ports;
+ nports = mgr->nv6ports;
+ }
+ if (ports == NULL)
+ goto unlock;
+
+ port = isc_sockaddr_getport(sockaddrp);
+ if (bsearch(&port, ports, nports, sizeof(in_port_t), port_cmp) != NULL)
+ available = ISC_TRUE;
+
+unlock:
+ PORTBUFUNLOCK(mgr);
+ return (available);
+}
+
+#define ATTRMATCH(_a1, _a2, _mask) (((_a1) & (_mask)) == ((_a2) & (_mask)))
+
+static isc_boolean_t
+local_addr_match(dns_dispatch_t *disp, isc_sockaddr_t *addr) {
+ isc_sockaddr_t sockaddr;
+ isc_result_t result;
+
+ REQUIRE(disp->socket != NULL);
+
+ if (addr == NULL)
+ return (ISC_TRUE);
+
+ /*
+ * Don't match wildcard ports unless the port is available in the
+ * current configuration.
+ */
+ if (isc_sockaddr_getport(addr) == 0 &&
+ isc_sockaddr_getport(&disp->local) == 0 &&
+ !portavailable(disp->mgr, disp->socket, NULL)) {
+ return (ISC_FALSE);
+ }
+
+ /*
+ * Check if we match the binding <address,port>.
+ * Wildcard ports match/fail here.
+ */
+ if (isc_sockaddr_equal(&disp->local, addr))
+ return (ISC_TRUE);
+ if (isc_sockaddr_getport(addr) == 0)
+ return (ISC_FALSE);
+
+ /*
+ * Check if we match a bound wildcard port <address,port>.
+ */
+ if (!isc_sockaddr_eqaddr(&disp->local, addr))
+ return (ISC_FALSE);
+ result = isc_socket_getsockname(disp->socket, &sockaddr);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_FALSE);
+
+ return (isc_sockaddr_equal(&sockaddr, addr));
+}
+
+/*
+ * Requires mgr be locked.
+ *
+ * No dispatcher can be locked by this thread when calling this function.
+ *
+ *
+ * NOTE:
+ * If a matching dispatcher is found, it is locked after this function
+ * returns, and must be unlocked by the caller.
+ */
+static isc_result_t
+dispatch_find(dns_dispatchmgr_t *mgr, isc_sockaddr_t *local,
+ unsigned int attributes, unsigned int mask,
+ dns_dispatch_t **dispp)
+{
+ dns_dispatch_t *disp;
+ isc_result_t result;
+
+ /*
+ * Make certain that we will not match a private or exclusive dispatch.
+ */
+ attributes &= ~(DNS_DISPATCHATTR_PRIVATE|DNS_DISPATCHATTR_EXCLUSIVE);
+ mask |= (DNS_DISPATCHATTR_PRIVATE|DNS_DISPATCHATTR_EXCLUSIVE);
+
+ disp = ISC_LIST_HEAD(mgr->list);
+ while (disp != NULL) {
+ LOCK(&disp->lock);
+ if ((disp->shutting_down == 0)
+ && ATTRMATCH(disp->attributes, attributes, mask)
+ && local_addr_match(disp, local))
+ break;
+ UNLOCK(&disp->lock);
+ disp = ISC_LIST_NEXT(disp, link);
+ }
+
+ if (disp == NULL) {
+ result = ISC_R_NOTFOUND;
+ goto out;
+ }
+
+ *dispp = disp;
+ result = ISC_R_SUCCESS;
+ out:
+
+ return (result);
+}
+
+static isc_result_t
+qid_allocate(dns_dispatchmgr_t *mgr, unsigned int buckets,
+ unsigned int increment, dns_qid_t **qidp,
+ isc_boolean_t needsocktable)
+{
+ dns_qid_t *qid;
+ unsigned int i;
+ isc_result_t result;
+
+ REQUIRE(VALID_DISPATCHMGR(mgr));
+ REQUIRE(buckets < 2097169); /* next prime > 65536 * 32 */
+ REQUIRE(increment > buckets);
+ REQUIRE(qidp != NULL && *qidp == NULL);
+
+ qid = isc_mem_get(mgr->mctx, sizeof(*qid));
+ if (qid == NULL)
+ return (ISC_R_NOMEMORY);
+
+ qid->qid_table = isc_mem_get(mgr->mctx,
+ buckets * sizeof(dns_displist_t));
+ if (qid->qid_table == NULL) {
+ isc_mem_put(mgr->mctx, qid, sizeof(*qid));
+ return (ISC_R_NOMEMORY);
+ }
+
+ qid->sock_table = NULL;
+ if (needsocktable) {
+ qid->sock_table = isc_mem_get(mgr->mctx, buckets *
+ sizeof(dispsocketlist_t));
+ if (qid->sock_table == NULL) {
+ isc_mem_put(mgr->mctx, qid, sizeof(*qid));
+ isc_mem_put(mgr->mctx, qid->qid_table,
+ buckets * sizeof(dns_displist_t));
+ return (ISC_R_NOMEMORY);
+ }
+ }
+
+ result = isc_mutex_init(&qid->lock);
+ if (result != ISC_R_SUCCESS) {
+ if (qid->sock_table != NULL) {
+ isc_mem_put(mgr->mctx, qid->sock_table,
+ buckets * sizeof(dispsocketlist_t));
+ }
+ isc_mem_put(mgr->mctx, qid->qid_table,
+ buckets * sizeof(dns_displist_t));
+ isc_mem_put(mgr->mctx, qid, sizeof(*qid));
+ return (result);
+ }
+
+ for (i = 0; i < buckets; i++) {
+ ISC_LIST_INIT(qid->qid_table[i]);
+ if (qid->sock_table != NULL)
+ ISC_LIST_INIT(qid->sock_table[i]);
+ }
+
+ qid->qid_nbuckets = buckets;
+ qid->qid_increment = increment;
+ qid->magic = QID_MAGIC;
+ *qidp = qid;
+ return (ISC_R_SUCCESS);
+}
+
+static void
+qid_destroy(isc_mem_t *mctx, dns_qid_t **qidp) {
+ dns_qid_t *qid;
+
+ REQUIRE(qidp != NULL);
+ qid = *qidp;
+
+ REQUIRE(VALID_QID(qid));
+
+ *qidp = NULL;
+ qid->magic = 0;
+ isc_mem_put(mctx, qid->qid_table,
+ qid->qid_nbuckets * sizeof(dns_displist_t));
+ if (qid->sock_table != NULL) {
+ isc_mem_put(mctx, qid->sock_table,
+ qid->qid_nbuckets * sizeof(dispsocketlist_t));
+ }
+ DESTROYLOCK(&qid->lock);
+ isc_mem_put(mctx, qid, sizeof(*qid));
+}
+
+/*
+ * Allocate and set important limits.
+ */
+static isc_result_t
+dispatch_allocate(dns_dispatchmgr_t *mgr, unsigned int maxrequests,
+ dns_dispatch_t **dispp)
+{
+ dns_dispatch_t *disp;
+ isc_result_t result;
+
+ REQUIRE(VALID_DISPATCHMGR(mgr));
+ REQUIRE(dispp != NULL && *dispp == NULL);
+
+ /*
+ * Set up the dispatcher, mostly. Don't bother setting some of
+ * the options that are controlled by tcp vs. udp, etc.
+ */
+
+ disp = isc_mempool_get(mgr->dpool);
+ if (disp == NULL)
+ return (ISC_R_NOMEMORY);
+
+ disp->magic = 0;
+ disp->mgr = mgr;
+ disp->maxrequests = maxrequests;
+ disp->attributes = 0;
+ ISC_LINK_INIT(disp, link);
+ disp->refcount = 1;
+ disp->recv_pending = 0;
+ memset(&disp->local, 0, sizeof(disp->local));
+ disp->localport = 0;
+ disp->shutting_down = 0;
+ disp->shutdown_out = 0;
+ disp->connected = 0;
+ disp->tcpmsg_valid = 0;
+ disp->shutdown_why = ISC_R_UNEXPECTED;
+ disp->requests = 0;
+ disp->tcpbuffers = 0;
+ disp->qid = NULL;
+ ISC_LIST_INIT(disp->activesockets);
+ ISC_LIST_INIT(disp->inactivesockets);
+ disp->nsockets = 0;
+ dispatch_arc4init(&disp->arc4ctx, mgr->entropy, NULL);
+ disp->port_table = NULL;
+ disp->portpool = NULL;
+
+ result = isc_mutex_init(&disp->lock);
+ if (result != ISC_R_SUCCESS)
+ goto deallocate;
+
+ disp->failsafe_ev = allocate_event(disp);
+ if (disp->failsafe_ev == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto kill_lock;
+ }
+
+ disp->magic = DISPATCH_MAGIC;
+
+ *dispp = disp;
+ return (ISC_R_SUCCESS);
+
+ /*
+ * error returns
+ */
+ kill_lock:
+ DESTROYLOCK(&disp->lock);
+ deallocate:
+ isc_mempool_put(mgr->dpool, disp);
+
+ return (result);
+}
+
+
+/*
+ * MUST be unlocked, and not used by anthing.
+ */
+static void
+dispatch_free(dns_dispatch_t **dispp)
+{
+ dns_dispatch_t *disp;
+ dns_dispatchmgr_t *mgr;
+ int i;
+
+ REQUIRE(VALID_DISPATCH(*dispp));
+ disp = *dispp;
+ *dispp = NULL;
+
+ mgr = disp->mgr;
+ REQUIRE(VALID_DISPATCHMGR(mgr));
+
+ if (disp->tcpmsg_valid) {
+ dns_tcpmsg_invalidate(&disp->tcpmsg);
+ disp->tcpmsg_valid = 0;
+ }
+
+ INSIST(disp->tcpbuffers == 0);
+ INSIST(disp->requests == 0);
+ INSIST(disp->recv_pending == 0);
+ INSIST(ISC_LIST_EMPTY(disp->activesockets));
+ INSIST(ISC_LIST_EMPTY(disp->inactivesockets));
+
+ isc_mempool_put(mgr->epool, disp->failsafe_ev);
+ disp->failsafe_ev = NULL;
+
+ if (disp->qid != NULL)
+ qid_destroy(mgr->mctx, &disp->qid);
+
+ if (disp->port_table != NULL) {
+ for (i = 0; i < DNS_DISPATCH_PORTTABLESIZE; i++)
+ INSIST(ISC_LIST_EMPTY(disp->port_table[i]));
+ isc_mem_put(mgr->mctx, disp->port_table,
+ sizeof(disp->port_table[0]) *
+ DNS_DISPATCH_PORTTABLESIZE);
+ }
+
+ if (disp->portpool != NULL)
+ isc_mempool_destroy(&disp->portpool);
+
+ disp->mgr = NULL;
+ DESTROYLOCK(&disp->lock);
+ disp->magic = 0;
+ isc_mempool_put(mgr->dpool, disp);
+}
+
+isc_result_t
+dns_dispatch_createtcp(dns_dispatchmgr_t *mgr, isc_socket_t *sock,
+ isc_taskmgr_t *taskmgr, unsigned int buffersize,
+ unsigned int maxbuffers, unsigned int maxrequests,
+ unsigned int buckets, unsigned int increment,
+ unsigned int attributes, dns_dispatch_t **dispp)
+{
+ isc_result_t result;
+ dns_dispatch_t *disp;
+
+ UNUSED(maxbuffers);
+ UNUSED(buffersize);
+
+ REQUIRE(VALID_DISPATCHMGR(mgr));
+ REQUIRE(isc_socket_gettype(sock) == isc_sockettype_tcp);
+ REQUIRE((attributes & DNS_DISPATCHATTR_TCP) != 0);
+ REQUIRE((attributes & DNS_DISPATCHATTR_UDP) == 0);
+
+ attributes |= DNS_DISPATCHATTR_PRIVATE; /* XXXMLG */
+
+ LOCK(&mgr->lock);
+
+ /*
+ * dispatch_allocate() checks mgr for us.
+ * qid_allocate() checks buckets and increment for us.
+ */
+ disp = NULL;
+ result = dispatch_allocate(mgr, maxrequests, &disp);
+ if (result != ISC_R_SUCCESS) {
+ UNLOCK(&mgr->lock);
+ return (result);
+ }
+
+ result = qid_allocate(mgr, buckets, increment, &disp->qid, ISC_FALSE);
+ if (result != ISC_R_SUCCESS)
+ goto deallocate_dispatch;
+
+ disp->socktype = isc_sockettype_tcp;
+ disp->socket = NULL;
+ isc_socket_attach(sock, &disp->socket);
+
+ disp->ntasks = 1;
+ disp->task[0] = NULL;
+ result = isc_task_create(taskmgr, 0, &disp->task[0]);
+ if (result != ISC_R_SUCCESS)
+ goto kill_socket;
+
+ disp->ctlevent = isc_event_allocate(mgr->mctx, disp,
+ DNS_EVENT_DISPATCHCONTROL,
+ destroy_disp, disp,
+ sizeof(isc_event_t));
+ if (disp->ctlevent == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto kill_task;
+ }
+
+ isc_task_setname(disp->task[0], "tcpdispatch", disp);
+
+ dns_tcpmsg_init(mgr->mctx, disp->socket, &disp->tcpmsg);
+ disp->tcpmsg_valid = 1;
+
+ disp->attributes = attributes;
+
+ /*
+ * Append it to the dispatcher list.
+ */
+ ISC_LIST_APPEND(mgr->list, disp, link);
+ UNLOCK(&mgr->lock);
+
+ mgr_log(mgr, LVL(90), "created TCP dispatcher %p", disp);
+ dispatch_log(disp, LVL(90), "created task %p", disp->task[0]);
+
+ *dispp = disp;
+
+ return (ISC_R_SUCCESS);
+
+ /*
+ * Error returns.
+ */
+ kill_task:
+ isc_task_detach(&disp->task[0]);
+ kill_socket:
+ isc_socket_detach(&disp->socket);
+ deallocate_dispatch:
+ dispatch_free(&disp);
+
+ UNLOCK(&mgr->lock);
+
+ return (result);
+}
+
+isc_result_t
+dns_dispatch_getudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr,
+ isc_taskmgr_t *taskmgr, isc_sockaddr_t *localaddr,
+ unsigned int buffersize,
+ unsigned int maxbuffers, unsigned int maxrequests,
+ unsigned int buckets, unsigned int increment,
+ unsigned int attributes, unsigned int mask,
+ dns_dispatch_t **dispp)
+{
+ isc_result_t result;
+ dns_dispatch_t *disp = NULL;
+
+ REQUIRE(VALID_DISPATCHMGR(mgr));
+ REQUIRE(sockmgr != NULL);
+ REQUIRE(localaddr != NULL);
+ REQUIRE(taskmgr != NULL);
+ REQUIRE(buffersize >= 512 && buffersize < (64 * 1024));
+ REQUIRE(maxbuffers > 0);
+ REQUIRE(buckets < 2097169); /* next prime > 65536 * 32 */
+ REQUIRE(increment > buckets);
+ REQUIRE(dispp != NULL && *dispp == NULL);
+ REQUIRE((attributes & DNS_DISPATCHATTR_TCP) == 0);
+
+ result = dns_dispatchmgr_setudp(mgr, buffersize, maxbuffers,
+ maxrequests, buckets, increment);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ LOCK(&mgr->lock);
+
+ if ((attributes & DNS_DISPATCHATTR_EXCLUSIVE) != 0) {
+ REQUIRE(isc_sockaddr_getport(localaddr) == 0);
+ goto createudp;
+ }
+
+ /*
+ * See if we have a dispatcher that matches.
+ */
+ result = dispatch_find(mgr, localaddr, attributes, mask, &disp);
+ if (result == ISC_R_SUCCESS) {
+ disp->refcount++;
+
+ if (disp->maxrequests < maxrequests)
+ disp->maxrequests = maxrequests;
+
+ if ((disp->attributes & DNS_DISPATCHATTR_NOLISTEN) == 0 &&
+ (attributes & DNS_DISPATCHATTR_NOLISTEN) != 0)
+ {
+ disp->attributes |= DNS_DISPATCHATTR_NOLISTEN;
+ if (disp->recv_pending != 0)
+ isc_socket_cancel(disp->socket, disp->task[0],
+ ISC_SOCKCANCEL_RECV);
+ }
+
+ UNLOCK(&disp->lock);
+ UNLOCK(&mgr->lock);
+
+ *dispp = disp;
+
+ return (ISC_R_SUCCESS);
+ }
+
+ createudp:
+ /*
+ * Nope, create one.
+ */
+ result = dispatch_createudp(mgr, sockmgr, taskmgr, localaddr,
+ maxrequests, attributes, &disp);
+ if (result != ISC_R_SUCCESS) {
+ UNLOCK(&mgr->lock);
+ return (result);
+ }
+
+ UNLOCK(&mgr->lock);
+ *dispp = disp;
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * mgr should be locked.
+ */
+
+#ifndef DNS_DISPATCH_HELD
+#define DNS_DISPATCH_HELD 20U
+#endif
+
+static isc_result_t
+get_udpsocket(dns_dispatchmgr_t *mgr, dns_dispatch_t *disp,
+ isc_socketmgr_t *sockmgr, isc_sockaddr_t *localaddr,
+ isc_socket_t **sockp)
+{
+ unsigned int i, j;
+ isc_socket_t *held[DNS_DISPATCH_HELD];
+ isc_sockaddr_t localaddr_bound;
+ isc_socket_t *sock = NULL;
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_boolean_t anyport;
+
+ INSIST(sockp != NULL && *sockp == NULL);
+
+ localaddr_bound = *localaddr;
+ anyport = ISC_TF(isc_sockaddr_getport(localaddr) == 0);
+
+ if (anyport) {
+ unsigned int nports;
+ in_port_t *ports;
+
+ /*
+ * If no port is specified, we first try to pick up a random
+ * port by ourselves.
+ */
+ if (isc_sockaddr_pf(&disp->local) == AF_INET) {
+ nports = disp->mgr->nv4ports;
+ ports = disp->mgr->v4ports;
+ } else {
+ nports = disp->mgr->nv6ports;
+ ports = disp->mgr->v6ports;
+ }
+ if (nports == 0)
+ return (ISC_R_ADDRNOTAVAIL);
+
+ for (i = 0; i < 1024; i++) {
+ in_port_t prt;
+
+ prt = ports[dispatch_arc4uniformrandom(
+ DISP_ARC4CTX(disp),
+ nports)];
+ isc_sockaddr_setport(&localaddr_bound, prt);
+ result = open_socket(sockmgr, &localaddr_bound,
+ 0, &sock);
+ if (result == ISC_R_SUCCESS ||
+ result != ISC_R_ADDRINUSE) {
+ disp->localport = prt;
+ *sockp = sock;
+ return (result);
+ }
+ }
+
+ /*
+ * If this fails 1024 times, we then ask the kernel for
+ * choosing one.
+ */
+ } else {
+ /* Allow to reuse address for non-random ports. */
+ result = open_socket(sockmgr, localaddr,
+ ISC_SOCKET_REUSEADDRESS, &sock);
+
+ if (result == ISC_R_SUCCESS)
+ *sockp = sock;
+
+ return (result);
+ }
+
+ memset(held, 0, sizeof(held));
+ i = 0;
+
+ for (j = 0; j < 0xffffU; j++) {
+ result = open_socket(sockmgr, localaddr, 0, &sock);
+ if (result != ISC_R_SUCCESS)
+ goto end;
+ else if (!anyport)
+ break;
+ else if (portavailable(mgr, sock, NULL))
+ break;
+ if (held[i] != NULL)
+ isc_socket_detach(&held[i]);
+ held[i++] = sock;
+ sock = NULL;
+ if (i == DNS_DISPATCH_HELD)
+ i = 0;
+ }
+ if (j == 0xffffU) {
+ mgr_log(mgr, ISC_LOG_ERROR,
+ "avoid-v%s-udp-ports: unable to allocate "
+ "an available port",
+ isc_sockaddr_pf(localaddr) == AF_INET ? "4" : "6");
+ result = ISC_R_FAILURE;
+ goto end;
+ }
+ *sockp = sock;
+
+end:
+ for (i = 0; i < DNS_DISPATCH_HELD; i++) {
+ if (held[i] != NULL)
+ isc_socket_detach(&held[i]);
+ }
+
+ return (result);
+}
+
+static isc_result_t
+dispatch_createudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr,
+ isc_taskmgr_t *taskmgr,
+ isc_sockaddr_t *localaddr,
+ unsigned int maxrequests,
+ unsigned int attributes,
+ dns_dispatch_t **dispp)
+{
+ isc_result_t result;
+ dns_dispatch_t *disp;
+ isc_socket_t *sock = NULL;
+ int i = 0;
+
+ /*
+ * dispatch_allocate() checks mgr for us.
+ */
+ disp = NULL;
+ result = dispatch_allocate(mgr, maxrequests, &disp);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if ((attributes & DNS_DISPATCHATTR_EXCLUSIVE) == 0) {
+ result = get_udpsocket(mgr, disp, sockmgr, localaddr, &sock);
+ if (result != ISC_R_SUCCESS)
+ goto deallocate_dispatch;
+ } else {
+ isc_sockaddr_t sa_any;
+
+ /*
+ * For dispatches using exclusive sockets with a specific
+ * source address, we only check if the specified address is
+ * available on the system. Query sockets will be created later
+ * on demand.
+ */
+ isc_sockaddr_anyofpf(&sa_any, isc_sockaddr_pf(localaddr));
+ if (!isc_sockaddr_eqaddr(&sa_any, localaddr)) {
+ result = open_socket(sockmgr, localaddr, 0, &sock);
+ if (sock != NULL)
+ isc_socket_detach(&sock);
+ if (result != ISC_R_SUCCESS)
+ goto deallocate_dispatch;
+ }
+
+ disp->port_table = isc_mem_get(mgr->mctx,
+ sizeof(disp->port_table[0]) *
+ DNS_DISPATCH_PORTTABLESIZE);
+ if (disp->port_table == NULL)
+ goto deallocate_dispatch;
+ for (i = 0; i < DNS_DISPATCH_PORTTABLESIZE; i++)
+ ISC_LIST_INIT(disp->port_table[i]);
+
+ result = isc_mempool_create(mgr->mctx, sizeof(dispportentry_t),
+ &disp->portpool);
+ if (result != ISC_R_SUCCESS)
+ goto deallocate_dispatch;
+ isc_mempool_setname(disp->portpool, "disp_portpool");
+ isc_mempool_setfreemax(disp->portpool, 128);
+ }
+ disp->socktype = isc_sockettype_udp;
+ disp->socket = sock;
+ disp->local = *localaddr;
+
+ if ((attributes & DNS_DISPATCHATTR_EXCLUSIVE) != 0)
+ disp->ntasks = MAX_INTERNAL_TASKS;
+ else
+ disp->ntasks = 1;
+ for (i = 0; i < disp->ntasks; i++) {
+ disp->task[i] = NULL;
+ result = isc_task_create(taskmgr, 0, &disp->task[i]);
+ if (result != ISC_R_SUCCESS) {
+ while (--i >= 0)
+ isc_task_destroy(&disp->task[i]);
+ goto kill_socket;
+ }
+ isc_task_setname(disp->task[i], "udpdispatch", disp);
+ }
+
+ disp->ctlevent = isc_event_allocate(mgr->mctx, disp,
+ DNS_EVENT_DISPATCHCONTROL,
+ destroy_disp, disp,
+ sizeof(isc_event_t));
+ if (disp->ctlevent == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto kill_task;
+ }
+
+ attributes &= ~DNS_DISPATCHATTR_TCP;
+ attributes |= DNS_DISPATCHATTR_UDP;
+ disp->attributes = attributes;
+
+ /*
+ * Append it to the dispatcher list.
+ */
+ ISC_LIST_APPEND(mgr->list, disp, link);
+
+ mgr_log(mgr, LVL(90), "created UDP dispatcher %p", disp);
+ dispatch_log(disp, LVL(90), "created task %p", disp->task[0]); /* XXX */
+ if (disp->socket != NULL)
+ dispatch_log(disp, LVL(90), "created socket %p", disp->socket);
+
+ *dispp = disp;
+ return (result);
+
+ /*
+ * Error returns.
+ */
+ kill_task:
+ for (i = 0; i < disp->ntasks; i++)
+ isc_task_detach(&disp->task[i]);
+ kill_socket:
+ if (disp->socket != NULL)
+ isc_socket_detach(&disp->socket);
+ deallocate_dispatch:
+ dispatch_free(&disp);
+
+ return (result);
+}
+
+void
+dns_dispatch_attach(dns_dispatch_t *disp, dns_dispatch_t **dispp) {
+ REQUIRE(VALID_DISPATCH(disp));
+ REQUIRE(dispp != NULL && *dispp == NULL);
+
+ LOCK(&disp->lock);
+ disp->refcount++;
+ UNLOCK(&disp->lock);
+
+ *dispp = disp;
+}
+
+/*
+ * It is important to lock the manager while we are deleting the dispatch,
+ * since dns_dispatch_getudp will call dispatch_find, which returns to
+ * the caller a dispatch but does not attach to it until later. _getudp
+ * locks the manager, however, so locking it here will keep us from attaching
+ * to a dispatcher that is in the process of going away.
+ */
+void
+dns_dispatch_detach(dns_dispatch_t **dispp) {
+ dns_dispatch_t *disp;
+ dispsocket_t *dispsock;
+ isc_boolean_t killit;
+
+ REQUIRE(dispp != NULL && VALID_DISPATCH(*dispp));
+
+ disp = *dispp;
+ *dispp = NULL;
+
+ LOCK(&disp->lock);
+
+ INSIST(disp->refcount > 0);
+ disp->refcount--;
+ killit = ISC_FALSE;
+ if (disp->refcount == 0) {
+ if (disp->recv_pending > 0)
+ isc_socket_cancel(disp->socket, disp->task[0],
+ ISC_SOCKCANCEL_RECV);
+ for (dispsock = ISC_LIST_HEAD(disp->activesockets);
+ dispsock != NULL;
+ dispsock = ISC_LIST_NEXT(dispsock, link)) {
+ isc_socket_cancel(dispsock->socket, dispsock->task,
+ ISC_SOCKCANCEL_RECV);
+ }
+ disp->shutting_down = 1;
+ }
+
+ dispatch_log(disp, LVL(90), "detach: refcount %d", disp->refcount);
+
+ killit = destroy_disp_ok(disp);
+ UNLOCK(&disp->lock);
+ if (killit)
+ isc_task_send(disp->task[0], &disp->ctlevent);
+}
+
+isc_result_t
+dns_dispatch_addresponse2(dns_dispatch_t *disp, isc_sockaddr_t *dest,
+ isc_task_t *task, isc_taskaction_t action, void *arg,
+ dns_messageid_t *idp, dns_dispentry_t **resp,
+ isc_socketmgr_t *sockmgr)
+{
+ dns_dispentry_t *res;
+ unsigned int bucket;
+ in_port_t localport = 0;
+ dns_messageid_t id;
+ int i;
+ isc_boolean_t ok;
+ dns_qid_t *qid;
+ dispsocket_t *dispsocket = NULL;
+ isc_result_t result;
+
+ REQUIRE(VALID_DISPATCH(disp));
+ REQUIRE(task != NULL);
+ REQUIRE(dest != NULL);
+ REQUIRE(resp != NULL && *resp == NULL);
+ REQUIRE(idp != NULL);
+ if ((disp->attributes & DNS_DISPATCHATTR_EXCLUSIVE) != 0)
+ REQUIRE(sockmgr != NULL);
+
+ LOCK(&disp->lock);
+
+ if (disp->shutting_down == 1) {
+ UNLOCK(&disp->lock);
+ return (ISC_R_SHUTTINGDOWN);
+ }
+
+ if (disp->requests >= disp->maxrequests) {
+ UNLOCK(&disp->lock);
+ return (ISC_R_QUOTA);
+ }
+
+ if ((disp->attributes & DNS_DISPATCHATTR_EXCLUSIVE) != 0 &&
+ disp->nsockets > DNS_DISPATCH_SOCKSQUOTA) {
+ dispsocket_t *oldestsocket;
+ dns_dispentry_t *oldestresp;
+ dns_dispatchevent_t *rev;
+
+ /*
+ * Kill oldest outstanding query if the number of sockets
+ * exceeds the quota to keep the room for new queries.
+ */
+ oldestsocket = ISC_LIST_HEAD(disp->activesockets);
+ oldestresp = oldestsocket->resp;
+ if (oldestresp != NULL && !oldestresp->item_out) {
+ rev = allocate_event(oldestresp->disp);
+ if (rev != NULL) {
+ rev->buffer.base = NULL;
+ rev->result = ISC_R_CANCELED;
+ rev->id = oldestresp->id;
+ ISC_EVENT_INIT(rev, sizeof(*rev), 0,
+ NULL, DNS_EVENT_DISPATCH,
+ oldestresp->action,
+ oldestresp->arg, oldestresp,
+ NULL, NULL);
+ oldestresp->item_out = ISC_TRUE;
+ isc_task_send(oldestresp->task,
+ ISC_EVENT_PTR(&rev));
+ }
+ }
+
+ /*
+ * Move this entry to the tail so that it won't (easily) be
+ * examined before actually being canceled.
+ */
+ ISC_LIST_UNLINK(disp->activesockets, oldestsocket, link);
+ ISC_LIST_APPEND(disp->activesockets, oldestsocket, link);
+ }
+
+ qid = DNS_QID(disp);
+ LOCK(&qid->lock);
+
+ if ((disp->attributes & DNS_DISPATCHATTR_EXCLUSIVE) != 0) {
+ /*
+ * Get a separate UDP socket with a random port number.
+ */
+ result = get_dispsocket(disp, dest, sockmgr, qid, &dispsocket,
+ &localport);
+ if (result != ISC_R_SUCCESS) {
+ UNLOCK(&qid->lock);
+ UNLOCK(&disp->lock);
+ return (result);
+ }
+ } else {
+ localport = disp->localport;
+ }
+
+ /*
+ * Try somewhat hard to find an unique ID.
+ */
+ id = (dns_messageid_t)dispatch_arc4random(DISP_ARC4CTX(disp));
+ bucket = dns_hash(qid, dest, id, localport);
+ ok = ISC_FALSE;
+ for (i = 0; i < 64; i++) {
+ if (entry_search(qid, dest, id, localport, bucket) == NULL) {
+ ok = ISC_TRUE;
+ break;
+ }
+ id += qid->qid_increment;
+ id &= 0x0000ffff;
+ bucket = dns_hash(qid, dest, id, localport);
+ }
+
+ if (!ok) {
+ UNLOCK(&qid->lock);
+ UNLOCK(&disp->lock);
+ return (ISC_R_NOMORE);
+ }
+
+ res = isc_mempool_get(disp->mgr->rpool);
+ if (res == NULL) {
+ UNLOCK(&qid->lock);
+ UNLOCK(&disp->lock);
+ if (dispsocket != NULL)
+ destroy_dispsocket(disp, &dispsocket);
+ return (ISC_R_NOMEMORY);
+ }
+
+ disp->refcount++;
+ disp->requests++;
+ res->task = NULL;
+ isc_task_attach(task, &res->task);
+ res->disp = disp;
+ res->id = id;
+ res->port = localport;
+ res->bucket = bucket;
+ res->host = *dest;
+ res->action = action;
+ res->arg = arg;
+ res->dispsocket = dispsocket;
+ if (dispsocket != NULL)
+ dispsocket->resp = res;
+ res->item_out = ISC_FALSE;
+ ISC_LIST_INIT(res->items);
+ ISC_LINK_INIT(res, link);
+ res->magic = RESPONSE_MAGIC;
+ ISC_LIST_APPEND(qid->qid_table[bucket], res, link);
+ UNLOCK(&qid->lock);
+
+ request_log(disp, res, LVL(90),
+ "attached to task %p", res->task);
+
+ if (((disp->attributes & DNS_DISPATCHATTR_UDP) != 0) ||
+ ((disp->attributes & DNS_DISPATCHATTR_CONNECTED) != 0)) {
+ result = startrecv(disp, dispsocket);
+ if (result != ISC_R_SUCCESS) {
+ LOCK(&qid->lock);
+ ISC_LIST_UNLINK(qid->qid_table[bucket], res, link);
+ UNLOCK(&qid->lock);
+
+ if (dispsocket != NULL)
+ destroy_dispsocket(disp, &dispsocket);
+
+ disp->refcount--;
+ disp->requests--;
+
+ UNLOCK(&disp->lock);
+ isc_task_detach(&res->task);
+ isc_mempool_put(disp->mgr->rpool, res);
+ return (result);
+ }
+ }
+
+ if (dispsocket != NULL)
+ ISC_LIST_APPEND(disp->activesockets, dispsocket, link);
+
+ UNLOCK(&disp->lock);
+
+ *idp = id;
+ *resp = res;
+
+ if ((disp->attributes & DNS_DISPATCHATTR_EXCLUSIVE) != 0)
+ INSIST(res->dispsocket != NULL);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_dispatch_addresponse(dns_dispatch_t *disp, isc_sockaddr_t *dest,
+ isc_task_t *task, isc_taskaction_t action, void *arg,
+ dns_messageid_t *idp, dns_dispentry_t **resp)
+{
+ REQUIRE(VALID_DISPATCH(disp));
+ REQUIRE((disp->attributes & DNS_DISPATCHATTR_EXCLUSIVE) == 0);
+
+ return (dns_dispatch_addresponse2(disp, dest, task, action, arg,
+ idp, resp, NULL));
+}
+
+void
+dns_dispatch_starttcp(dns_dispatch_t *disp) {
+
+ REQUIRE(VALID_DISPATCH(disp));
+
+ dispatch_log(disp, LVL(90), "starttcp %p", disp->task[0]);
+
+ LOCK(&disp->lock);
+ disp->attributes |= DNS_DISPATCHATTR_CONNECTED;
+ (void)startrecv(disp, NULL);
+ UNLOCK(&disp->lock);
+}
+
+void
+dns_dispatch_removeresponse(dns_dispentry_t **resp,
+ dns_dispatchevent_t **sockevent)
+{
+ dns_dispatchmgr_t *mgr;
+ dns_dispatch_t *disp;
+ dns_dispentry_t *res;
+ dispsocket_t *dispsock;
+ dns_dispatchevent_t *ev;
+ unsigned int bucket;
+ isc_boolean_t killit;
+ unsigned int n;
+ isc_eventlist_t events;
+ dns_qid_t *qid;
+
+ REQUIRE(resp != NULL);
+ REQUIRE(VALID_RESPONSE(*resp));
+
+ res = *resp;
+ *resp = NULL;
+
+ disp = res->disp;
+ REQUIRE(VALID_DISPATCH(disp));
+ mgr = disp->mgr;
+ REQUIRE(VALID_DISPATCHMGR(mgr));
+
+ qid = DNS_QID(disp);
+
+ if (sockevent != NULL) {
+ REQUIRE(*sockevent != NULL);
+ ev = *sockevent;
+ *sockevent = NULL;
+ } else {
+ ev = NULL;
+ }
+
+ LOCK(&disp->lock);
+
+ INSIST(disp->requests > 0);
+ disp->requests--;
+ INSIST(disp->refcount > 0);
+ disp->refcount--;
+ killit = ISC_FALSE;
+ if (disp->refcount == 0) {
+ if (disp->recv_pending > 0)
+ isc_socket_cancel(disp->socket, disp->task[0],
+ ISC_SOCKCANCEL_RECV);
+ for (dispsock = ISC_LIST_HEAD(disp->activesockets);
+ dispsock != NULL;
+ dispsock = ISC_LIST_NEXT(dispsock, link)) {
+ isc_socket_cancel(dispsock->socket, dispsock->task,
+ ISC_SOCKCANCEL_RECV);
+ }
+ disp->shutting_down = 1;
+ }
+
+ bucket = res->bucket;
+
+ LOCK(&qid->lock);
+ ISC_LIST_UNLINK(qid->qid_table[bucket], res, link);
+ UNLOCK(&qid->lock);
+
+ if (ev == NULL && res->item_out) {
+ /*
+ * We've posted our event, but the caller hasn't gotten it
+ * yet. Take it back.
+ */
+ ISC_LIST_INIT(events);
+ n = isc_task_unsend(res->task, res, DNS_EVENT_DISPATCH,
+ NULL, &events);
+ /*
+ * We had better have gotten it back.
+ */
+ INSIST(n == 1);
+ ev = (dns_dispatchevent_t *)ISC_LIST_HEAD(events);
+ }
+
+ if (ev != NULL) {
+ REQUIRE(res->item_out == ISC_TRUE);
+ res->item_out = ISC_FALSE;
+ if (ev->buffer.base != NULL)
+ free_buffer(disp, ev->buffer.base, ev->buffer.length);
+ free_event(disp, ev);
+ }
+
+ request_log(disp, res, LVL(90), "detaching from task %p", res->task);
+ isc_task_detach(&res->task);
+
+ if (res->dispsocket != NULL) {
+ isc_socket_cancel(res->dispsocket->socket,
+ res->dispsocket->task, ISC_SOCKCANCEL_RECV);
+ res->dispsocket->resp = NULL;
+ }
+
+ /*
+ * Free any buffered requests as well
+ */
+ ev = ISC_LIST_HEAD(res->items);
+ while (ev != NULL) {
+ ISC_LIST_UNLINK(res->items, ev, ev_link);
+ if (ev->buffer.base != NULL)
+ free_buffer(disp, ev->buffer.base, ev->buffer.length);
+ free_event(disp, ev);
+ ev = ISC_LIST_HEAD(res->items);
+ }
+ res->magic = 0;
+ isc_mempool_put(disp->mgr->rpool, res);
+ if (disp->shutting_down == 1)
+ do_cancel(disp);
+ else
+ (void)startrecv(disp, NULL);
+
+ killit = destroy_disp_ok(disp);
+ UNLOCK(&disp->lock);
+ if (killit)
+ isc_task_send(disp->task[0], &disp->ctlevent);
+}
+
+static void
+do_cancel(dns_dispatch_t *disp) {
+ dns_dispatchevent_t *ev;
+ dns_dispentry_t *resp;
+ dns_qid_t *qid;
+
+ if (disp->shutdown_out == 1)
+ return;
+
+ qid = DNS_QID(disp);
+
+ /*
+ * Search for the first response handler without packets outstanding
+ * unless a specific hander is given.
+ */
+ LOCK(&qid->lock);
+ for (resp = linear_first(qid);
+ resp != NULL && resp->item_out;
+ /* Empty. */)
+ resp = linear_next(qid, resp);
+
+ /*
+ * No one to send the cancel event to, so nothing to do.
+ */
+ if (resp == NULL)
+ goto unlock;
+
+ /*
+ * Send the shutdown failsafe event to this resp.
+ */
+ ev = disp->failsafe_ev;
+ ISC_EVENT_INIT(ev, sizeof(*ev), 0, NULL, DNS_EVENT_DISPATCH,
+ resp->action, resp->arg, resp, NULL, NULL);
+ ev->result = disp->shutdown_why;
+ ev->buffer.base = NULL;
+ ev->buffer.length = 0;
+ disp->shutdown_out = 1;
+ request_log(disp, resp, LVL(10),
+ "cancel: failsafe event %p -> task %p",
+ ev, resp->task);
+ resp->item_out = ISC_TRUE;
+ isc_task_send(resp->task, ISC_EVENT_PTR(&ev));
+ unlock:
+ UNLOCK(&qid->lock);
+}
+
+isc_socket_t *
+dns_dispatch_getsocket(dns_dispatch_t *disp) {
+ REQUIRE(VALID_DISPATCH(disp));
+
+ return (disp->socket);
+}
+
+isc_socket_t *
+dns_dispatch_getentrysocket(dns_dispentry_t *resp) {
+ REQUIRE(VALID_RESPONSE(resp));
+
+ if (resp->dispsocket != NULL)
+ return (resp->dispsocket->socket);
+ else
+ return (NULL);
+}
+
+isc_result_t
+dns_dispatch_getlocaladdress(dns_dispatch_t *disp, isc_sockaddr_t *addrp) {
+
+ REQUIRE(VALID_DISPATCH(disp));
+ REQUIRE(addrp != NULL);
+
+ if (disp->socktype == isc_sockettype_udp) {
+ *addrp = disp->local;
+ return (ISC_R_SUCCESS);
+ }
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+void
+dns_dispatch_cancel(dns_dispatch_t *disp) {
+ REQUIRE(VALID_DISPATCH(disp));
+
+ LOCK(&disp->lock);
+
+ if (disp->shutting_down == 1) {
+ UNLOCK(&disp->lock);
+ return;
+ }
+
+ disp->shutdown_why = ISC_R_CANCELED;
+ disp->shutting_down = 1;
+ do_cancel(disp);
+
+ UNLOCK(&disp->lock);
+
+ return;
+}
+
+unsigned int
+dns_dispatch_getattributes(dns_dispatch_t *disp) {
+ REQUIRE(VALID_DISPATCH(disp));
+
+ /*
+ * We don't bother locking disp here; it's the caller's responsibility
+ * to use only non volatile flags.
+ */
+ return (disp->attributes);
+}
+
+void
+dns_dispatch_changeattributes(dns_dispatch_t *disp,
+ unsigned int attributes, unsigned int mask)
+{
+ REQUIRE(VALID_DISPATCH(disp));
+ /* Exclusive attribute can only be set on creation */
+ REQUIRE((attributes & DNS_DISPATCHATTR_EXCLUSIVE) == 0);
+ /* Also, a dispatch with randomport specified cannot start listening */
+ REQUIRE((disp->attributes & DNS_DISPATCHATTR_EXCLUSIVE) == 0 ||
+ (attributes & DNS_DISPATCHATTR_NOLISTEN) == 0);
+
+ /* XXXMLG
+ * Should check for valid attributes here!
+ */
+
+ LOCK(&disp->lock);
+
+ if ((mask & DNS_DISPATCHATTR_NOLISTEN) != 0) {
+ if ((disp->attributes & DNS_DISPATCHATTR_NOLISTEN) != 0 &&
+ (attributes & DNS_DISPATCHATTR_NOLISTEN) == 0) {
+ disp->attributes &= ~DNS_DISPATCHATTR_NOLISTEN;
+ (void)startrecv(disp, NULL);
+ } else if ((disp->attributes & DNS_DISPATCHATTR_NOLISTEN)
+ == 0 &&
+ (attributes & DNS_DISPATCHATTR_NOLISTEN) != 0) {
+ disp->attributes |= DNS_DISPATCHATTR_NOLISTEN;
+ if (disp->recv_pending != 0)
+ isc_socket_cancel(disp->socket, disp->task[0],
+ ISC_SOCKCANCEL_RECV);
+ }
+ }
+
+ disp->attributes &= ~mask;
+ disp->attributes |= (attributes & mask);
+ UNLOCK(&disp->lock);
+}
+
+void
+dns_dispatch_importrecv(dns_dispatch_t *disp, isc_event_t *event) {
+ void *buf;
+ isc_socketevent_t *sevent, *newsevent;
+
+ REQUIRE(VALID_DISPATCH(disp));
+ REQUIRE((disp->attributes & DNS_DISPATCHATTR_NOLISTEN) != 0);
+ REQUIRE(event != NULL);
+
+ sevent = (isc_socketevent_t *)event;
+
+ INSIST(sevent->n <= disp->mgr->buffersize);
+ newsevent = (isc_socketevent_t *)
+ isc_event_allocate(disp->mgr->mctx, NULL,
+ DNS_EVENT_IMPORTRECVDONE, udp_shrecv,
+ disp, sizeof(isc_socketevent_t));
+ if (newsevent == NULL)
+ return;
+
+ buf = allocate_udp_buffer(disp);
+ if (buf == NULL) {
+ isc_event_free(ISC_EVENT_PTR(&newsevent));
+ return;
+ }
+ memcpy(buf, sevent->region.base, sevent->n);
+ newsevent->region.base = buf;
+ newsevent->region.length = disp->mgr->buffersize;
+ newsevent->n = sevent->n;
+ newsevent->result = sevent->result;
+ newsevent->address = sevent->address;
+ newsevent->timestamp = sevent->timestamp;
+ newsevent->pktinfo = sevent->pktinfo;
+ newsevent->attributes = sevent->attributes;
+
+ isc_task_send(disp->task[0], ISC_EVENT_PTR(&newsevent));
+}
+
+#if 0
+void
+dns_dispatchmgr_dump(dns_dispatchmgr_t *mgr) {
+ dns_dispatch_t *disp;
+ char foo[1024];
+
+ disp = ISC_LIST_HEAD(mgr->list);
+ while (disp != NULL) {
+ isc_sockaddr_format(&disp->local, foo, sizeof(foo));
+ printf("\tdispatch %p, addr %s\n", disp, foo);
+ disp = ISC_LIST_NEXT(disp, link);
+ }
+}
+#endif
diff --git a/lib/dns/dlz.c b/lib/dns/dlz.c
new file mode 100644
index 0000000..420eba3
--- /dev/null
+++ b/lib/dns/dlz.c
@@ -0,0 +1,510 @@
+/*
+ * Portions Copyright (C) 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ *
+ * 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 STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET 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.
+ *
+ * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was
+ * conceived and contributed by Rob Butler.
+ *
+ * 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 ROB BUTLER
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * ROB BUTLER 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.
+ */
+
+/* $Id: dlz.c,v 1.5 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file */
+
+/***
+ *** Imports
+ ***/
+
+#include <config.h>
+
+#include <dns/fixedname.h>
+#include <dns/log.h>
+#include <dns/master.h>
+#include <dns/dlz.h>
+
+
+#include <isc/buffer.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/once.h>
+#include <isc/rwlock.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+/***
+ *** Supported DLZ DB Implementations Registry
+ ***/
+
+static ISC_LIST(dns_dlzimplementation_t) dlz_implementations;
+static isc_rwlock_t dlz_implock;
+static isc_once_t once = ISC_ONCE_INIT;
+
+static void
+dlz_initialize(void) {
+ RUNTIME_CHECK(isc_rwlock_init(&dlz_implock, 0, 0) == ISC_R_SUCCESS);
+ ISC_LIST_INIT(dlz_implementations);
+}
+
+/*%
+ * Searches the dlz_implementations list for a driver matching name.
+ */
+static inline dns_dlzimplementation_t *
+dlz_impfind(const char *name) {
+ dns_dlzimplementation_t *imp;
+
+ for (imp = ISC_LIST_HEAD(dlz_implementations);
+ imp != NULL;
+ imp = ISC_LIST_NEXT(imp, link))
+ if (strcasecmp(name, imp->name) == 0)
+ return (imp);
+ return (NULL);
+}
+
+/***
+ *** Basic DLZ Methods
+ ***/
+
+isc_result_t
+dns_dlzallowzonexfr(dns_view_t *view, dns_name_t *name,
+ isc_sockaddr_t *clientaddr, dns_db_t **dbp)
+{
+ isc_result_t result;
+ dns_dlzallowzonexfr_t allowzonexfr;
+ dns_dlzdb_t *dlzdatabase;
+
+ /*
+ * Performs checks to make sure data is as we expect it to be.
+ */
+ REQUIRE(DNS_DLZ_VALID(view->dlzdatabase));
+ REQUIRE(name != NULL);
+ REQUIRE(dbp != NULL && *dbp == NULL);
+
+ /* ask driver if the zone is supported */
+ dlzdatabase = view->dlzdatabase;
+ allowzonexfr = dlzdatabase->implementation->methods->allowzonexfr;
+ result = (*allowzonexfr)(dlzdatabase->implementation->driverarg,
+ dlzdatabase->dbdata, dlzdatabase->mctx,
+ view->rdclass, name, clientaddr, dbp);
+
+ if (result == ISC_R_NOTIMPLEMENTED)
+ return (ISC_R_NOTFOUND);
+ return (result);
+}
+
+isc_result_t
+dns_dlzcreate(isc_mem_t *mctx, const char *dlzname, const char *drivername,
+ unsigned int argc, char *argv[], dns_dlzdb_t **dbp)
+{
+ dns_dlzimplementation_t *impinfo;
+ isc_result_t result;
+
+ /*
+ * initialize the dlz_implementations list, this is guaranteed
+ * to only really happen once.
+ */
+ RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS);
+
+ /*
+ * Performs checks to make sure data is as we expect it to be.
+ */
+ REQUIRE(dbp != NULL && *dbp == NULL);
+ REQUIRE(dlzname != NULL);
+ REQUIRE(drivername != NULL);
+ REQUIRE(mctx != NULL);
+
+ /* write log message */
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
+ "Loading '%s' using driver %s", dlzname, drivername);
+
+ /* lock the dlz_implementations list so we can search it. */
+ RWLOCK(&dlz_implock, isc_rwlocktype_read);
+
+ /* search for the driver implementation */
+ impinfo = dlz_impfind(drivername);
+ if (impinfo == NULL) {
+ RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+ "unsupported DLZ database driver '%s'."
+ " %s not loaded.",
+ drivername, dlzname);
+
+ return (ISC_R_NOTFOUND);
+ }
+
+ /* Allocate memory to hold the DLZ database driver */
+ (*dbp) = isc_mem_get(mctx, sizeof(dns_dlzdb_t));
+ if ((*dbp) == NULL) {
+ RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
+ return (ISC_R_NOMEMORY);
+ }
+
+ /* Make sure memory region is set to all 0's */
+ memset((*dbp), 0, sizeof(dns_dlzdb_t));
+
+ (*dbp)->implementation = impinfo;
+
+ /* Create a new database using implementation 'drivername'. */
+ result = ((impinfo->methods->create)(mctx, dlzname, argc, argv,
+ impinfo->driverarg,
+ &(*dbp)->dbdata));
+
+ /* mark the DLZ driver as valid */
+ if (result == ISC_R_SUCCESS) {
+ RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
+ (*dbp)->magic = DNS_DLZ_MAGIC;
+ isc_mem_attach(mctx, &(*dbp)->mctx);
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+ "DLZ driver loaded successfully.");
+ return (ISC_R_SUCCESS);
+ } else {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+ "DLZ driver failed to load.");
+ }
+
+ /* impinfo->methods->create failed. */
+ RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
+ isc_mem_put(mctx, (*dbp), sizeof(dns_dlzdb_t));
+ return (result);
+}
+
+void
+dns_dlzdestroy(dns_dlzdb_t **dbp) {
+ isc_mem_t *mctx;
+ dns_dlzdestroy_t destroy;
+
+ /* Write debugging message to log */
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+ "Unloading DLZ driver.");
+
+ /*
+ * Perform checks to make sure data is as we expect it to be.
+ */
+ REQUIRE(dbp != NULL && DNS_DLZ_VALID(*dbp));
+
+ /* call the drivers destroy method */
+ if ((*dbp) != NULL) {
+ mctx = (*dbp)->mctx;
+ destroy = (*dbp)->implementation->methods->destroy;
+ (*destroy)((*dbp)->implementation->driverarg,(*dbp)->dbdata);
+ /* return memory */
+ isc_mem_put(mctx, (*dbp), sizeof(dns_dlzdb_t));
+ isc_mem_detach(&mctx);
+ }
+
+ *dbp = NULL;
+}
+
+
+isc_result_t
+dns_dlzfindzone(dns_view_t *view, dns_name_t *name, unsigned int minlabels,
+ dns_db_t **dbp)
+{
+ dns_fixedname_t fname;
+ dns_name_t *zonename;
+ unsigned int namelabels;
+ unsigned int i;
+ isc_result_t result;
+ dns_dlzfindzone_t findzone;
+ dns_dlzdb_t *dlzdatabase;
+
+ /*
+ * Performs checks to make sure data is as we expect it to be.
+ */
+ REQUIRE(DNS_DLZ_VALID(view->dlzdatabase));
+ REQUIRE(name != NULL);
+ REQUIRE(dbp != NULL && *dbp == NULL);
+
+ /* setup a "fixed" dns name */
+ dns_fixedname_init(&fname);
+ zonename = dns_fixedname_name(&fname);
+
+ /* count the number of labels in the name */
+ namelabels = dns_name_countlabels(name);
+
+ /*
+ * loop through starting with the longest domain name and
+ * trying shorter names portions of the name until we find a
+ * match, have an error, or are below the 'minlabels'
+ * threshold. minlabels is 0, if the standard database didn't
+ * have a zone name match. Otherwise minlables is the number
+ * of labels in that name. We need to beat that for a
+ * "better" match for the DLZ database to be authoritative
+ * instead of the standard database.
+ */
+ for (i = namelabels; i > minlabels && i > 1; i--) {
+ if (i == namelabels) {
+ result = dns_name_copy(name, zonename, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ } else
+ dns_name_split(name, i, NULL, zonename);
+
+ /* ask SDLZ driver if the zone is supported */
+ dlzdatabase = view->dlzdatabase;
+ findzone = dlzdatabase->implementation->methods->findzone;
+ result = (*findzone)(dlzdatabase->implementation->driverarg,
+ dlzdatabase->dbdata, dlzdatabase->mctx,
+ view->rdclass, zonename, dbp);
+ if (result != ISC_R_NOTFOUND)
+ return (result);
+ }
+ return (ISC_R_NOTFOUND);
+}
+
+/*%
+ * Registers a DLZ driver. This basically just adds the dlz
+ * driver to the list of available drivers in the dlz_implementations list.
+ */
+isc_result_t
+dns_dlzregister(const char *drivername, const dns_dlzmethods_t *methods,
+ void *driverarg, isc_mem_t *mctx,
+ dns_dlzimplementation_t **dlzimp)
+{
+
+ dns_dlzimplementation_t *dlz_imp;
+
+ /* Write debugging message to log */
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+ "Registering DLZ driver '%s'", drivername);
+
+ /*
+ * Performs checks to make sure data is as we expect it to be.
+ */
+ REQUIRE(drivername != NULL);
+ REQUIRE(methods != NULL);
+ REQUIRE(methods->create != NULL);
+ REQUIRE(methods->destroy != NULL);
+ REQUIRE(methods->findzone != NULL);
+ REQUIRE(mctx != NULL);
+ REQUIRE(dlzimp != NULL && *dlzimp == NULL);
+
+ /*
+ * initialize the dlz_implementations list, this is guaranteed
+ * to only really happen once.
+ */
+ RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS);
+
+ /* lock the dlz_implementations list so we can modify it. */
+ RWLOCK(&dlz_implock, isc_rwlocktype_write);
+
+ /*
+ * check that another already registered driver isn't using
+ * the same name
+ */
+ dlz_imp = dlz_impfind(drivername);
+ if (dlz_imp != NULL) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+ "DLZ Driver '%s' already registered",
+ drivername);
+ RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
+ return (ISC_R_EXISTS);
+ }
+
+ /*
+ * Allocate memory for a dlz_implementation object. Error if
+ * we cannot.
+ */
+ dlz_imp = isc_mem_get(mctx, sizeof(dns_dlzimplementation_t));
+ if (dlz_imp == NULL) {
+ RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
+ return (ISC_R_NOMEMORY);
+ }
+
+ /* Make sure memory region is set to all 0's */
+ memset(dlz_imp, 0, sizeof(dns_dlzimplementation_t));
+
+ /* Store the data passed into this method */
+ dlz_imp->name = drivername;
+ dlz_imp->methods = methods;
+ dlz_imp->mctx = NULL;
+ dlz_imp->driverarg = driverarg;
+
+ /* attach the new dlz_implementation object to a memory context */
+ isc_mem_attach(mctx, &dlz_imp->mctx);
+
+ /*
+ * prepare the dlz_implementation object to be put in a list,
+ * and append it to the list
+ */
+ ISC_LINK_INIT(dlz_imp, link);
+ ISC_LIST_APPEND(dlz_implementations, dlz_imp, link);
+
+ /* Unlock the dlz_implementations list. */
+ RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
+
+ /* Pass back the dlz_implementation that we created. */
+ *dlzimp = dlz_imp;
+
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Helper function for dns_dlzstrtoargv().
+ * Pardon the gratuitous recursion.
+ */
+static isc_result_t
+dns_dlzstrtoargvsub(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 *));
+ if (*argvp == NULL)
+ return (ISC_R_NOMEMORY);
+ } 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 = dns_dlzstrtoargvsub(mctx, p, argcp, argvp, n + 1);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ (*argvp)[n] = s;
+ }
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Tokenize the string "s" into whitespace-separated words,
+ * return 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_put(). The string
+ * is modified in-place.
+ */
+isc_result_t
+dns_dlzstrtoargv(isc_mem_t *mctx, char *s,
+ unsigned int *argcp, char ***argvp)
+{
+ return(dns_dlzstrtoargvsub(mctx, s, argcp, argvp, 0));
+}
+
+/*%
+ * Unregisters a DLZ driver. This basically just removes the dlz
+ * driver from the list of available drivers in the dlz_implementations list.
+ */
+void
+dns_dlzunregister(dns_dlzimplementation_t **dlzimp) {
+ dns_dlzimplementation_t *dlz_imp;
+ isc_mem_t *mctx;
+
+ /* Write debugging message to log */
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+ "Unregistering DLZ driver.");
+
+ /*
+ * Performs checks to make sure data is as we expect it to be.
+ */
+ REQUIRE(dlzimp != NULL && *dlzimp != NULL);
+
+ /*
+ * initialize the dlz_implementations list, this is guaranteed
+ * to only really happen once.
+ */
+ RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS);
+
+ dlz_imp = *dlzimp;
+
+ /* lock the dlz_implementations list so we can modify it. */
+ RWLOCK(&dlz_implock, isc_rwlocktype_write);
+
+ /* remove the dlz_implementation object from the list */
+ ISC_LIST_UNLINK(dlz_implementations, dlz_imp, link);
+ mctx = dlz_imp->mctx;
+
+ /*
+ * return the memory back to the available memory pool and
+ * remove it from the memory context.
+ */
+ isc_mem_put(mctx, dlz_imp, sizeof(dns_dlzimplementation_t));
+ isc_mem_detach(&mctx);
+
+ /* Unlock the dlz_implementations list. */
+ RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
+}
diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c
new file mode 100644
index 0000000..f06d715
--- /dev/null
+++ b/lib/dns/dnssec.c
@@ -0,0 +1,892 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/*
+ * $Id: dnssec.c,v 1.93 2008/11/14 23:47:33 tbox Exp $
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdlib.h>
+
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/serial.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/dnssec.h>
+#include <dns/fixedname.h>
+#include <dns/keyvalues.h>
+#include <dns/message.h>
+#include <dns/rdata.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatastruct.h>
+#include <dns/result.h>
+#include <dns/tsig.h> /* for DNS_TSIG_FUDGE */
+
+#include <dst/result.h>
+
+#define is_response(msg) (msg->flags & DNS_MESSAGEFLAG_QR)
+
+#define RETERR(x) do { \
+ result = (x); \
+ if (result != ISC_R_SUCCESS) \
+ goto failure; \
+ } while (0)
+
+
+#define TYPE_SIGN 0
+#define TYPE_VERIFY 1
+
+static isc_result_t
+digest_callback(void *arg, isc_region_t *data);
+
+static int
+rdata_compare_wrapper(const void *rdata1, const void *rdata2);
+
+static isc_result_t
+rdataset_to_sortedarray(dns_rdataset_t *set, isc_mem_t *mctx,
+ dns_rdata_t **rdata, int *nrdata);
+
+static isc_result_t
+digest_callback(void *arg, isc_region_t *data) {
+ dst_context_t *ctx = arg;
+
+ return (dst_context_adddata(ctx, data));
+}
+
+/*
+ * Make qsort happy.
+ */
+static int
+rdata_compare_wrapper(const void *rdata1, const void *rdata2) {
+ return (dns_rdata_compare((const dns_rdata_t *)rdata1,
+ (const dns_rdata_t *)rdata2));
+}
+
+/*
+ * Sort the rdataset into an array.
+ */
+static isc_result_t
+rdataset_to_sortedarray(dns_rdataset_t *set, isc_mem_t *mctx,
+ dns_rdata_t **rdata, int *nrdata)
+{
+ isc_result_t ret;
+ int i = 0, n;
+ dns_rdata_t *data;
+
+ n = dns_rdataset_count(set);
+
+ data = isc_mem_get(mctx, n * sizeof(dns_rdata_t));
+ if (data == NULL)
+ return (ISC_R_NOMEMORY);
+
+ ret = dns_rdataset_first(set);
+ if (ret != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, data, n * sizeof(dns_rdata_t));
+ return (ret);
+ }
+
+ /*
+ * Put them in the array.
+ */
+ do {
+ dns_rdata_init(&data[i]);
+ dns_rdataset_current(set, &data[i++]);
+ } while (dns_rdataset_next(set) == ISC_R_SUCCESS);
+
+ /*
+ * Sort the array.
+ */
+ qsort(data, n, sizeof(dns_rdata_t), rdata_compare_wrapper);
+ *rdata = data;
+ *nrdata = n;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_dnssec_keyfromrdata(dns_name_t *name, dns_rdata_t *rdata, isc_mem_t *mctx,
+ dst_key_t **key)
+{
+ isc_buffer_t b;
+ isc_region_t r;
+
+ INSIST(name != NULL);
+ INSIST(rdata != NULL);
+ INSIST(mctx != NULL);
+ INSIST(key != NULL);
+ INSIST(*key == NULL);
+ REQUIRE(rdata->type == dns_rdatatype_key ||
+ rdata->type == dns_rdatatype_dnskey);
+
+ dns_rdata_toregion(rdata, &r);
+ isc_buffer_init(&b, r.base, r.length);
+ isc_buffer_add(&b, r.length);
+ return (dst_key_fromdns(name, rdata->rdclass, &b, mctx, key));
+}
+
+static isc_result_t
+digest_sig(dst_context_t *ctx, dns_rdata_t *sigrdata, dns_rdata_rrsig_t *sig) {
+ isc_region_t r;
+ isc_result_t ret;
+ dns_fixedname_t fname;
+
+ dns_rdata_toregion(sigrdata, &r);
+ INSIST(r.length >= 19);
+
+ r.length = 18;
+ ret = dst_context_adddata(ctx, &r);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+ dns_fixedname_init(&fname);
+ RUNTIME_CHECK(dns_name_downcase(&sig->signer,
+ dns_fixedname_name(&fname), NULL)
+ == ISC_R_SUCCESS);
+ dns_name_toregion(dns_fixedname_name(&fname), &r);
+ return (dst_context_adddata(ctx, &r));
+}
+
+isc_result_t
+dns_dnssec_sign(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
+ isc_stdtime_t *inception, isc_stdtime_t *expire,
+ isc_mem_t *mctx, isc_buffer_t *buffer, dns_rdata_t *sigrdata)
+{
+ dns_rdata_rrsig_t sig;
+ dns_rdata_t tmpsigrdata;
+ dns_rdata_t *rdatas;
+ int nrdatas, i;
+ isc_buffer_t sigbuf, envbuf;
+ isc_region_t r;
+ dst_context_t *ctx = NULL;
+ isc_result_t ret;
+ isc_buffer_t *databuf = NULL;
+ char data[256 + 8];
+ isc_uint32_t flags;
+ unsigned int sigsize;
+ dns_fixedname_t fnewname;
+
+ REQUIRE(name != NULL);
+ REQUIRE(dns_name_countlabels(name) <= 255);
+ REQUIRE(set != NULL);
+ REQUIRE(key != NULL);
+ REQUIRE(inception != NULL);
+ REQUIRE(expire != NULL);
+ REQUIRE(mctx != NULL);
+ REQUIRE(sigrdata != NULL);
+
+ if (*inception >= *expire)
+ return (DNS_R_INVALIDTIME);
+
+ /*
+ * Is the key allowed to sign data?
+ */
+ flags = dst_key_flags(key);
+ if (flags & DNS_KEYTYPE_NOAUTH)
+ return (DNS_R_KEYUNAUTHORIZED);
+ if ((flags & DNS_KEYFLAG_OWNERMASK) != DNS_KEYOWNER_ZONE)
+ return (DNS_R_KEYUNAUTHORIZED);
+
+ sig.mctx = mctx;
+ sig.common.rdclass = set->rdclass;
+ sig.common.rdtype = dns_rdatatype_rrsig;
+ ISC_LINK_INIT(&sig.common, link);
+
+ dns_name_init(&sig.signer, NULL);
+ dns_name_clone(dst_key_name(key), &sig.signer);
+
+ sig.covered = set->type;
+ sig.algorithm = dst_key_alg(key);
+ sig.labels = dns_name_countlabels(name) - 1;
+ if (dns_name_iswildcard(name))
+ sig.labels--;
+ sig.originalttl = set->ttl;
+ sig.timesigned = *inception;
+ sig.timeexpire = *expire;
+ sig.keyid = dst_key_id(key);
+ ret = dst_key_sigsize(key, &sigsize);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+ sig.siglen = sigsize;
+ /*
+ * The actual contents of sig.signature are not important yet, since
+ * they're not used in digest_sig().
+ */
+ sig.signature = isc_mem_get(mctx, sig.siglen);
+ if (sig.signature == NULL)
+ return (ISC_R_NOMEMORY);
+
+ ret = isc_buffer_allocate(mctx, &databuf, sigsize + 256 + 18);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_signature;
+
+ dns_rdata_init(&tmpsigrdata);
+ ret = dns_rdata_fromstruct(&tmpsigrdata, sig.common.rdclass,
+ sig.common.rdtype, &sig, databuf);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_databuf;
+
+ ret = dst_context_create(key, mctx, &ctx);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_databuf;
+
+ /*
+ * Digest the SIG rdata.
+ */
+ ret = digest_sig(ctx, &tmpsigrdata, &sig);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+
+ dns_fixedname_init(&fnewname);
+ RUNTIME_CHECK(dns_name_downcase(name, dns_fixedname_name(&fnewname),
+ NULL) == ISC_R_SUCCESS);
+ dns_name_toregion(dns_fixedname_name(&fnewname), &r);
+
+ /*
+ * Create an envelope for each rdata: <name|type|class|ttl>.
+ */
+ isc_buffer_init(&envbuf, data, sizeof(data));
+ memcpy(data, r.base, r.length);
+ isc_buffer_add(&envbuf, r.length);
+ isc_buffer_putuint16(&envbuf, set->type);
+ isc_buffer_putuint16(&envbuf, set->rdclass);
+ isc_buffer_putuint32(&envbuf, set->ttl);
+
+ ret = rdataset_to_sortedarray(set, mctx, &rdatas, &nrdatas);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+ isc_buffer_usedregion(&envbuf, &r);
+
+ for (i = 0; i < nrdatas; i++) {
+ isc_uint16_t len;
+ isc_buffer_t lenbuf;
+ isc_region_t lenr;
+
+ /*
+ * Skip duplicates.
+ */
+ if (i > 0 && dns_rdata_compare(&rdatas[i], &rdatas[i-1]) == 0)
+ continue;
+
+ /*
+ * Digest the envelope.
+ */
+ ret = dst_context_adddata(ctx, &r);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_array;
+
+ /*
+ * Digest the length of the rdata.
+ */
+ isc_buffer_init(&lenbuf, &len, sizeof(len));
+ INSIST(rdatas[i].length < 65536);
+ isc_buffer_putuint16(&lenbuf, (isc_uint16_t)rdatas[i].length);
+ isc_buffer_usedregion(&lenbuf, &lenr);
+ ret = dst_context_adddata(ctx, &lenr);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_array;
+
+ /*
+ * Digest the rdata.
+ */
+ ret = dns_rdata_digest(&rdatas[i], digest_callback, ctx);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_array;
+ }
+
+ isc_buffer_init(&sigbuf, sig.signature, sig.siglen);
+ ret = dst_context_sign(ctx, &sigbuf);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_array;
+ isc_buffer_usedregion(&sigbuf, &r);
+ if (r.length != sig.siglen) {
+ ret = ISC_R_NOSPACE;
+ goto cleanup_array;
+ }
+ memcpy(sig.signature, r.base, sig.siglen);
+
+ ret = dns_rdata_fromstruct(sigrdata, sig.common.rdclass,
+ sig.common.rdtype, &sig, buffer);
+
+cleanup_array:
+ isc_mem_put(mctx, rdatas, nrdatas * sizeof(dns_rdata_t));
+cleanup_context:
+ dst_context_destroy(&ctx);
+cleanup_databuf:
+ isc_buffer_free(&databuf);
+cleanup_signature:
+ isc_mem_put(mctx, sig.signature, sig.siglen);
+
+ return (ret);
+}
+
+isc_result_t
+dns_dnssec_verify2(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
+ isc_boolean_t ignoretime, isc_mem_t *mctx,
+ dns_rdata_t *sigrdata, dns_name_t *wild)
+{
+ dns_rdata_rrsig_t sig;
+ dns_fixedname_t fnewname;
+ isc_region_t r;
+ isc_buffer_t envbuf;
+ dns_rdata_t *rdatas;
+ int nrdatas, i;
+ isc_stdtime_t now;
+ isc_result_t ret;
+ unsigned char data[300];
+ dst_context_t *ctx = NULL;
+ int labels = 0;
+ isc_uint32_t flags;
+
+ REQUIRE(name != NULL);
+ REQUIRE(set != NULL);
+ REQUIRE(key != NULL);
+ REQUIRE(mctx != NULL);
+ REQUIRE(sigrdata != NULL && sigrdata->type == dns_rdatatype_rrsig);
+
+ ret = dns_rdata_tostruct(sigrdata, &sig, NULL);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ if (set->type != sig.covered)
+ return (DNS_R_SIGINVALID);
+
+ if (isc_serial_lt(sig.timeexpire, sig.timesigned))
+ return (DNS_R_SIGINVALID);
+
+ if (!ignoretime) {
+ isc_stdtime_get(&now);
+
+ /*
+ * Is SIG temporally valid?
+ */
+ if (isc_serial_lt((isc_uint32_t)now, sig.timesigned))
+ return (DNS_R_SIGFUTURE);
+ else if (isc_serial_lt(sig.timeexpire, (isc_uint32_t)now))
+ return (DNS_R_SIGEXPIRED);
+ }
+
+ /*
+ * NS, SOA and DNSSKEY records are signed by their owner.
+ * DS records are signed by the parent.
+ */
+ switch (set->type) {
+ case dns_rdatatype_ns:
+ case dns_rdatatype_soa:
+ case dns_rdatatype_dnskey:
+ if (!dns_name_equal(name, &sig.signer))
+ return (DNS_R_SIGINVALID);
+ break;
+ case dns_rdatatype_ds:
+ if (dns_name_equal(name, &sig.signer))
+ return (DNS_R_SIGINVALID);
+ /* FALLTHROUGH */
+ default:
+ if (!dns_name_issubdomain(name, &sig.signer))
+ return (DNS_R_SIGINVALID);
+ break;
+ }
+
+ /*
+ * Is the key allowed to sign data?
+ */
+ flags = dst_key_flags(key);
+ if (flags & DNS_KEYTYPE_NOAUTH)
+ return (DNS_R_KEYUNAUTHORIZED);
+ if ((flags & DNS_KEYFLAG_OWNERMASK) != DNS_KEYOWNER_ZONE)
+ return (DNS_R_KEYUNAUTHORIZED);
+
+ ret = dst_context_create(key, mctx, &ctx);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_struct;
+
+ /*
+ * Digest the SIG rdata (not including the signature).
+ */
+ ret = digest_sig(ctx, sigrdata, &sig);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+
+ /*
+ * If the name is an expanded wildcard, use the wildcard name.
+ */
+ dns_fixedname_init(&fnewname);
+ labels = dns_name_countlabels(name) - 1;
+ RUNTIME_CHECK(dns_name_downcase(name, dns_fixedname_name(&fnewname),
+ NULL) == ISC_R_SUCCESS);
+ if (labels - sig.labels > 0)
+ dns_name_split(dns_fixedname_name(&fnewname), sig.labels + 1,
+ NULL, dns_fixedname_name(&fnewname));
+
+ dns_name_toregion(dns_fixedname_name(&fnewname), &r);
+
+ /*
+ * Create an envelope for each rdata: <name|type|class|ttl>.
+ */
+ isc_buffer_init(&envbuf, data, sizeof(data));
+ if (labels - sig.labels > 0) {
+ isc_buffer_putuint8(&envbuf, 1);
+ isc_buffer_putuint8(&envbuf, '*');
+ memcpy(data + 2, r.base, r.length);
+ }
+ else
+ memcpy(data, r.base, r.length);
+ isc_buffer_add(&envbuf, r.length);
+ isc_buffer_putuint16(&envbuf, set->type);
+ isc_buffer_putuint16(&envbuf, set->rdclass);
+ isc_buffer_putuint32(&envbuf, sig.originalttl);
+
+ ret = rdataset_to_sortedarray(set, mctx, &rdatas, &nrdatas);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+
+ isc_buffer_usedregion(&envbuf, &r);
+
+ for (i = 0; i < nrdatas; i++) {
+ isc_uint16_t len;
+ isc_buffer_t lenbuf;
+ isc_region_t lenr;
+
+ /*
+ * Skip duplicates.
+ */
+ if (i > 0 && dns_rdata_compare(&rdatas[i], &rdatas[i-1]) == 0)
+ continue;
+
+ /*
+ * Digest the envelope.
+ */
+ ret = dst_context_adddata(ctx, &r);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_array;
+
+ /*
+ * Digest the rdata length.
+ */
+ isc_buffer_init(&lenbuf, &len, sizeof(len));
+ INSIST(rdatas[i].length < 65536);
+ isc_buffer_putuint16(&lenbuf, (isc_uint16_t)rdatas[i].length);
+ isc_buffer_usedregion(&lenbuf, &lenr);
+
+ /*
+ * Digest the rdata.
+ */
+ ret = dst_context_adddata(ctx, &lenr);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_array;
+ ret = dns_rdata_digest(&rdatas[i], digest_callback, ctx);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_array;
+ }
+
+ r.base = sig.signature;
+ r.length = sig.siglen;
+ ret = dst_context_verify(ctx, &r);
+ if (ret == DST_R_VERIFYFAILURE)
+ ret = DNS_R_SIGINVALID;
+
+cleanup_array:
+ isc_mem_put(mctx, rdatas, nrdatas * sizeof(dns_rdata_t));
+cleanup_context:
+ dst_context_destroy(&ctx);
+cleanup_struct:
+ dns_rdata_freestruct(&sig);
+
+ if (ret == ISC_R_SUCCESS && labels - sig.labels > 0) {
+ if (wild != NULL)
+ RUNTIME_CHECK(dns_name_concatenate(dns_wildcardname,
+ dns_fixedname_name(&fnewname),
+ wild, NULL) == ISC_R_SUCCESS);
+ ret = DNS_R_FROMWILDCARD;
+ }
+ return (ret);
+}
+
+isc_result_t
+dns_dnssec_verify(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
+ isc_boolean_t ignoretime, isc_mem_t *mctx,
+ dns_rdata_t *sigrdata)
+{
+ isc_result_t result;
+
+ result = dns_dnssec_verify2(name, set, key, ignoretime, mctx,
+ sigrdata, NULL);
+ if (result == DNS_R_FROMWILDCARD)
+ result = ISC_R_SUCCESS;
+ return (result);
+}
+
+#define is_zone_key(key) ((dst_key_flags(key) & DNS_KEYFLAG_OWNERMASK) \
+ == DNS_KEYOWNER_ZONE)
+
+isc_result_t
+dns_dnssec_findzonekeys2(dns_db_t *db, dns_dbversion_t *ver,
+ dns_dbnode_t *node, dns_name_t *name,
+ const char *directory, isc_mem_t *mctx,
+ unsigned int maxkeys, dst_key_t **keys,
+ unsigned int *nkeys)
+{
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_result_t result;
+ dst_key_t *pubkey = NULL;
+ unsigned int count = 0;
+
+ REQUIRE(nkeys != NULL);
+ REQUIRE(keys != NULL);
+
+ *nkeys = 0;
+ dns_rdataset_init(&rdataset);
+ RETERR(dns_db_findrdataset(db, node, ver, dns_rdatatype_dnskey, 0, 0,
+ &rdataset, NULL));
+ RETERR(dns_rdataset_first(&rdataset));
+ while (result == ISC_R_SUCCESS && count < maxkeys) {
+ pubkey = NULL;
+ dns_rdataset_current(&rdataset, &rdata);
+ RETERR(dns_dnssec_keyfromrdata(name, &rdata, mctx, &pubkey));
+ if (!is_zone_key(pubkey) ||
+ (dst_key_flags(pubkey) & DNS_KEYTYPE_NOAUTH) != 0)
+ goto next;
+ /* Corrupted .key file? */
+ if (!dns_name_equal(name, dst_key_name(pubkey)))
+ goto next;
+ keys[count] = NULL;
+ result = dst_key_fromfile(dst_key_name(pubkey),
+ dst_key_id(pubkey),
+ dst_key_alg(pubkey),
+ DST_TYPE_PUBLIC|DST_TYPE_PRIVATE,
+ directory,
+ mctx, &keys[count]);
+ if (result == ISC_R_FILENOTFOUND) {
+ keys[count] = pubkey;
+ pubkey = NULL;
+ count++;
+ goto next;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ if ((dst_key_flags(keys[count]) & DNS_KEYTYPE_NOAUTH) != 0) {
+ /* We should never get here. */
+ dst_key_free(&keys[count]);
+ goto next;
+ }
+ count++;
+ next:
+ if (pubkey != NULL)
+ dst_key_free(&pubkey);
+ dns_rdata_reset(&rdata);
+ result = dns_rdataset_next(&rdataset);
+ }
+ if (result != ISC_R_NOMORE)
+ goto failure;
+ if (count == 0)
+ result = ISC_R_NOTFOUND;
+ else
+ result = ISC_R_SUCCESS;
+
+ failure:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ if (pubkey != NULL)
+ dst_key_free(&pubkey);
+ if (result != ISC_R_SUCCESS)
+ while (count > 0)
+ dst_key_free(&keys[--count]);
+ *nkeys = count;
+ return (result);
+}
+
+isc_result_t
+dns_dnssec_findzonekeys(dns_db_t *db, dns_dbversion_t *ver,
+ dns_dbnode_t *node, dns_name_t *name, isc_mem_t *mctx,
+ unsigned int maxkeys, dst_key_t **keys,
+ unsigned int *nkeys)
+{
+ return (dns_dnssec_findzonekeys2(db, ver, node, name, NULL, mctx,
+ maxkeys, keys, nkeys));
+}
+
+isc_result_t
+dns_dnssec_signmessage(dns_message_t *msg, dst_key_t *key) {
+ dns_rdata_sig_t sig; /* SIG(0) */
+ unsigned char data[512];
+ unsigned char header[DNS_MESSAGE_HEADERLEN];
+ isc_buffer_t headerbuf, databuf, sigbuf;
+ unsigned int sigsize;
+ isc_buffer_t *dynbuf = NULL;
+ dns_rdata_t *rdata;
+ dns_rdatalist_t *datalist;
+ dns_rdataset_t *dataset;
+ isc_region_t r;
+ isc_stdtime_t now;
+ dst_context_t *ctx = NULL;
+ isc_mem_t *mctx;
+ isc_result_t result;
+ isc_boolean_t signeedsfree = ISC_TRUE;
+
+ REQUIRE(msg != NULL);
+ REQUIRE(key != NULL);
+
+ if (is_response(msg))
+ REQUIRE(msg->query.base != NULL);
+
+ mctx = msg->mctx;
+
+ memset(&sig, 0, sizeof(sig));
+
+ sig.mctx = mctx;
+ sig.common.rdclass = dns_rdataclass_any;
+ sig.common.rdtype = dns_rdatatype_sig; /* SIG(0) */
+ ISC_LINK_INIT(&sig.common, link);
+
+ sig.covered = 0;
+ sig.algorithm = dst_key_alg(key);
+ sig.labels = 0; /* the root name */
+ sig.originalttl = 0;
+
+ isc_stdtime_get(&now);
+ sig.timesigned = now - DNS_TSIG_FUDGE;
+ sig.timeexpire = now + DNS_TSIG_FUDGE;
+
+ sig.keyid = dst_key_id(key);
+
+ dns_name_init(&sig.signer, NULL);
+ dns_name_clone(dst_key_name(key), &sig.signer);
+
+ sig.siglen = 0;
+ sig.signature = NULL;
+
+ isc_buffer_init(&databuf, data, sizeof(data));
+
+ RETERR(dst_context_create(key, mctx, &ctx));
+
+ /*
+ * Digest the fields of the SIG - we can cheat and use
+ * dns_rdata_fromstruct. Since siglen is 0, the digested data
+ * is identical to dns format.
+ */
+ RETERR(dns_rdata_fromstruct(NULL, dns_rdataclass_any,
+ dns_rdatatype_sig /* SIG(0) */,
+ &sig, &databuf));
+ isc_buffer_usedregion(&databuf, &r);
+ RETERR(dst_context_adddata(ctx, &r));
+
+ /*
+ * If this is a response, digest the query.
+ */
+ if (is_response(msg))
+ RETERR(dst_context_adddata(ctx, &msg->query));
+
+ /*
+ * Digest the header.
+ */
+ isc_buffer_init(&headerbuf, header, sizeof(header));
+ dns_message_renderheader(msg, &headerbuf);
+ isc_buffer_usedregion(&headerbuf, &r);
+ RETERR(dst_context_adddata(ctx, &r));
+
+ /*
+ * Digest the remainder of the message.
+ */
+ isc_buffer_usedregion(msg->buffer, &r);
+ isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
+ RETERR(dst_context_adddata(ctx, &r));
+
+ RETERR(dst_key_sigsize(key, &sigsize));
+ sig.siglen = sigsize;
+ sig.signature = (unsigned char *) isc_mem_get(mctx, sig.siglen);
+ if (sig.signature == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto failure;
+ }
+
+ isc_buffer_init(&sigbuf, sig.signature, sig.siglen);
+ RETERR(dst_context_sign(ctx, &sigbuf));
+ dst_context_destroy(&ctx);
+
+ rdata = NULL;
+ RETERR(dns_message_gettemprdata(msg, &rdata));
+ RETERR(isc_buffer_allocate(msg->mctx, &dynbuf, 1024));
+ RETERR(dns_rdata_fromstruct(rdata, dns_rdataclass_any,
+ dns_rdatatype_sig /* SIG(0) */,
+ &sig, dynbuf));
+
+ isc_mem_put(mctx, sig.signature, sig.siglen);
+ signeedsfree = ISC_FALSE;
+
+ dns_message_takebuffer(msg, &dynbuf);
+
+ datalist = NULL;
+ RETERR(dns_message_gettemprdatalist(msg, &datalist));
+ datalist->rdclass = dns_rdataclass_any;
+ datalist->type = dns_rdatatype_sig; /* SIG(0) */
+ datalist->covers = 0;
+ datalist->ttl = 0;
+ ISC_LIST_INIT(datalist->rdata);
+ ISC_LIST_APPEND(datalist->rdata, rdata, link);
+ dataset = NULL;
+ RETERR(dns_message_gettemprdataset(msg, &dataset));
+ dns_rdataset_init(dataset);
+ RUNTIME_CHECK(dns_rdatalist_tordataset(datalist, dataset) == ISC_R_SUCCESS);
+ msg->sig0 = dataset;
+
+ return (ISC_R_SUCCESS);
+
+failure:
+ if (dynbuf != NULL)
+ isc_buffer_free(&dynbuf);
+ if (signeedsfree)
+ isc_mem_put(mctx, sig.signature, sig.siglen);
+ if (ctx != NULL)
+ dst_context_destroy(&ctx);
+
+ return (result);
+}
+
+isc_result_t
+dns_dnssec_verifymessage(isc_buffer_t *source, dns_message_t *msg,
+ dst_key_t *key)
+{
+ dns_rdata_sig_t sig; /* SIG(0) */
+ unsigned char header[DNS_MESSAGE_HEADERLEN];
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_region_t r, source_r, sig_r, header_r;
+ isc_stdtime_t now;
+ dst_context_t *ctx = NULL;
+ isc_mem_t *mctx;
+ isc_result_t result;
+ isc_uint16_t addcount;
+ isc_boolean_t signeedsfree = ISC_FALSE;
+
+ REQUIRE(source != NULL);
+ REQUIRE(msg != NULL);
+ REQUIRE(key != NULL);
+
+ mctx = msg->mctx;
+
+ msg->verify_attempted = 1;
+
+ if (is_response(msg)) {
+ if (msg->query.base == NULL)
+ return (DNS_R_UNEXPECTEDTSIG);
+ }
+
+ isc_buffer_usedregion(source, &source_r);
+
+ RETERR(dns_rdataset_first(msg->sig0));
+ dns_rdataset_current(msg->sig0, &rdata);
+
+ RETERR(dns_rdata_tostruct(&rdata, &sig, NULL));
+ signeedsfree = ISC_TRUE;
+
+ if (sig.labels != 0) {
+ result = DNS_R_SIGINVALID;
+ goto failure;
+ }
+
+ if (isc_serial_lt(sig.timeexpire, sig.timesigned)) {
+ result = DNS_R_SIGINVALID;
+ msg->sig0status = dns_tsigerror_badtime;
+ goto failure;
+ }
+
+ isc_stdtime_get(&now);
+ if (isc_serial_lt((isc_uint32_t)now, sig.timesigned)) {
+ result = DNS_R_SIGFUTURE;
+ msg->sig0status = dns_tsigerror_badtime;
+ goto failure;
+ }
+ else if (isc_serial_lt(sig.timeexpire, (isc_uint32_t)now)) {
+ result = DNS_R_SIGEXPIRED;
+ msg->sig0status = dns_tsigerror_badtime;
+ goto failure;
+ }
+
+ if (!dns_name_equal(dst_key_name(key), &sig.signer)) {
+ result = DNS_R_SIGINVALID;
+ msg->sig0status = dns_tsigerror_badkey;
+ goto failure;
+ }
+
+ RETERR(dst_context_create(key, mctx, &ctx));
+
+ /*
+ * Digest the SIG(0) record, except for the signature.
+ */
+ dns_rdata_toregion(&rdata, &r);
+ r.length -= sig.siglen;
+ RETERR(dst_context_adddata(ctx, &r));
+
+ /*
+ * If this is a response, digest the query.
+ */
+ if (is_response(msg))
+ RETERR(dst_context_adddata(ctx, &msg->query));
+
+ /*
+ * Extract the header.
+ */
+ memcpy(header, source_r.base, DNS_MESSAGE_HEADERLEN);
+
+ /*
+ * Decrement the additional field counter.
+ */
+ memcpy(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2);
+ addcount = htons((isc_uint16_t)(ntohs(addcount) - 1));
+ memcpy(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2);
+
+ /*
+ * Digest the modified header.
+ */
+ header_r.base = (unsigned char *) header;
+ header_r.length = DNS_MESSAGE_HEADERLEN;
+ RETERR(dst_context_adddata(ctx, &header_r));
+
+ /*
+ * Digest all non-SIG(0) records.
+ */
+ r.base = source_r.base + DNS_MESSAGE_HEADERLEN;
+ r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN;
+ RETERR(dst_context_adddata(ctx, &r));
+
+ sig_r.base = sig.signature;
+ sig_r.length = sig.siglen;
+ result = dst_context_verify(ctx, &sig_r);
+ if (result != ISC_R_SUCCESS) {
+ msg->sig0status = dns_tsigerror_badsig;
+ goto failure;
+ }
+
+ msg->verified_sig = 1;
+
+ dst_context_destroy(&ctx);
+ dns_rdata_freestruct(&sig);
+
+ return (ISC_R_SUCCESS);
+
+failure:
+ if (signeedsfree)
+ dns_rdata_freestruct(&sig);
+ if (ctx != NULL)
+ dst_context_destroy(&ctx);
+
+ return (result);
+}
diff --git a/lib/dns/ds.c b/lib/dns/ds.c
new file mode 100644
index 0000000..e994cc5
--- /dev/null
+++ b/lib/dns/ds.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2002, 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: ds.c,v 1.11 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <isc/buffer.h>
+#include <isc/region.h>
+#include <isc/sha1.h>
+#include <isc/sha2.h>
+#include <isc/util.h>
+
+#include <dns/ds.h>
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdatastruct.h>
+#include <dns/result.h>
+
+#include <dst/dst.h>
+
+isc_result_t
+dns_ds_buildrdata(dns_name_t *owner, dns_rdata_t *key,
+ unsigned int digest_type, unsigned char *buffer,
+ dns_rdata_t *rdata)
+{
+ dns_fixedname_t fname;
+ dns_name_t *name;
+ unsigned char digest[ISC_SHA256_DIGESTLENGTH];
+ isc_region_t r;
+ isc_buffer_t b;
+ dns_rdata_ds_t ds;
+
+ REQUIRE(key != NULL);
+ REQUIRE(key->type == dns_rdatatype_dnskey);
+
+ if (!dns_ds_digest_supported(digest_type))
+ return (ISC_R_NOTIMPLEMENTED);
+
+ dns_fixedname_init(&fname);
+ name = dns_fixedname_name(&fname);
+ (void)dns_name_downcase(owner, name, NULL);
+
+ memset(buffer, 0, DNS_DS_BUFFERSIZE);
+ isc_buffer_init(&b, buffer, DNS_DS_BUFFERSIZE);
+
+ if (digest_type == DNS_DSDIGEST_SHA1) {
+ isc_sha1_t sha1;
+ isc_sha1_init(&sha1);
+ dns_name_toregion(name, &r);
+ isc_sha1_update(&sha1, r.base, r.length);
+ dns_rdata_toregion(key, &r);
+ INSIST(r.length >= 4);
+ isc_sha1_update(&sha1, r.base, r.length);
+ isc_sha1_final(&sha1, digest);
+ } else {
+ isc_sha256_t sha256;
+ isc_sha256_init(&sha256);
+ dns_name_toregion(name, &r);
+ isc_sha256_update(&sha256, r.base, r.length);
+ dns_rdata_toregion(key, &r);
+ INSIST(r.length >= 4);
+ isc_sha256_update(&sha256, r.base, r.length);
+ isc_sha256_final(digest, &sha256);
+ }
+
+ ds.mctx = NULL;
+ ds.common.rdclass = key->rdclass;
+ ds.common.rdtype = dns_rdatatype_ds;
+ ds.algorithm = r.base[3];
+ ds.key_tag = dst_region_computeid(&r, ds.algorithm);
+ ds.digest_type = digest_type;
+ ds.length = (digest_type == DNS_DSDIGEST_SHA1) ?
+ ISC_SHA1_DIGESTLENGTH : ISC_SHA256_DIGESTLENGTH;
+ ds.digest = digest;
+
+ return (dns_rdata_fromstruct(rdata, key->rdclass, dns_rdatatype_ds,
+ &ds, &b));
+}
+
+isc_boolean_t
+dns_ds_digest_supported(unsigned int digest_type) {
+ return (ISC_TF(digest_type == DNS_DSDIGEST_SHA1 ||
+ digest_type == DNS_DSDIGEST_SHA256));
+}
diff --git a/lib/dns/dst_api.c b/lib/dns/dst_api.c
new file mode 100644
index 0000000..32f894d
--- /dev/null
+++ b/lib/dns/dst_api.c
@@ -0,0 +1,1310 @@
+/*
+ * Portions Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 NETWORK ASSOCIATES 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.
+ *
+ * Portions Copyright (C) 1995-2000 by Network Associates, 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 NETWORK ASSOCIATES 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.
+ */
+
+/*
+ * Principal Author: Brian Wellington
+ * $Id: dst_api.c,v 1.16 2008/11/14 22:53:46 marka Exp $
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdlib.h>
+
+#include <isc/buffer.h>
+#include <isc/dir.h>
+#include <isc/entropy.h>
+#include <isc/fsaccess.h>
+#include <isc/hmacsha.h>
+#include <isc/lex.h>
+#include <isc/mem.h>
+#include <isc/once.h>
+#include <isc/print.h>
+#include <isc/random.h>
+#include <isc/string.h>
+#include <isc/time.h>
+#include <isc/util.h>
+
+#include <dns/fixedname.h>
+#include <dns/keyvalues.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/ttl.h>
+#include <dns/types.h>
+
+#include <dst/result.h>
+
+#include "dst_internal.h"
+
+#define DST_AS_STR(t) ((t).value.as_textregion.base)
+
+static dst_func_t *dst_t_func[DST_MAX_ALGS];
+static isc_entropy_t *dst_entropy_pool = NULL;
+static unsigned int dst_entropy_flags = 0;
+static isc_boolean_t dst_initialized = ISC_FALSE;
+
+void gss_log(int level, const char *fmt, ...) ISC_FORMAT_PRINTF(2, 3);
+
+isc_mem_t *dst__memory_pool = NULL;
+
+/*
+ * Static functions.
+ */
+static dst_key_t * get_key_struct(dns_name_t *name,
+ unsigned int alg,
+ unsigned int flags,
+ unsigned int protocol,
+ unsigned int bits,
+ dns_rdataclass_t rdclass,
+ isc_mem_t *mctx);
+static isc_result_t write_public_key(const dst_key_t *key, int type,
+ const char *directory);
+static isc_result_t buildfilename(dns_name_t *name,
+ dns_keytag_t id,
+ unsigned int alg,
+ unsigned int type,
+ const char *directory,
+ isc_buffer_t *out);
+static isc_result_t computeid(dst_key_t *key);
+static isc_result_t frombuffer(dns_name_t *name,
+ unsigned int alg,
+ unsigned int flags,
+ unsigned int protocol,
+ dns_rdataclass_t rdclass,
+ isc_buffer_t *source,
+ isc_mem_t *mctx,
+ dst_key_t **keyp);
+
+static isc_result_t algorithm_status(unsigned int alg);
+
+static isc_result_t addsuffix(char *filename, unsigned int len,
+ const char *ofilename, const char *suffix);
+
+#define RETERR(x) \
+ do { \
+ result = (x); \
+ if (result != ISC_R_SUCCESS) \
+ goto out; \
+ } while (0)
+
+#define CHECKALG(alg) \
+ do { \
+ isc_result_t _r; \
+ _r = algorithm_status(alg); \
+ if (_r != ISC_R_SUCCESS) \
+ return (_r); \
+ } while (0); \
+
+static void *
+default_memalloc(void *arg, size_t size) {
+ UNUSED(arg);
+ if (size == 0U)
+ size = 1;
+ return (malloc(size));
+}
+
+static void
+default_memfree(void *arg, void *ptr) {
+ UNUSED(arg);
+ free(ptr);
+}
+
+isc_result_t
+dst_lib_init(isc_mem_t *mctx, isc_entropy_t *ectx, unsigned int eflags) {
+ isc_result_t result;
+
+ REQUIRE(mctx != NULL && ectx != NULL);
+ REQUIRE(dst_initialized == ISC_FALSE);
+
+ dst__memory_pool = NULL;
+
+#ifdef OPENSSL
+ UNUSED(mctx);
+ /*
+ * When using --with-openssl, there seems to be no good way of not
+ * leaking memory due to the openssl error handling mechanism.
+ * Avoid assertions by using a local memory context and not checking
+ * for leaks on exit. Note: as there are leaks we cannot use
+ * ISC_MEMFLAG_INTERNAL as it will free up memory still being used
+ * by libcrypto.
+ */
+ result = isc_mem_createx2(0, 0, default_memalloc, default_memfree,
+ NULL, &dst__memory_pool, 0);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ isc_mem_setname(dst__memory_pool, "dst", NULL);
+ isc_mem_setdestroycheck(dst__memory_pool, ISC_FALSE);
+#else
+ isc_mem_attach(mctx, &dst__memory_pool);
+#endif
+ isc_entropy_attach(ectx, &dst_entropy_pool);
+ dst_entropy_flags = eflags;
+
+ dst_result_register();
+
+ memset(dst_t_func, 0, sizeof(dst_t_func));
+ RETERR(dst__hmacmd5_init(&dst_t_func[DST_ALG_HMACMD5]));
+ RETERR(dst__hmacsha1_init(&dst_t_func[DST_ALG_HMACSHA1]));
+ RETERR(dst__hmacsha224_init(&dst_t_func[DST_ALG_HMACSHA224]));
+ RETERR(dst__hmacsha256_init(&dst_t_func[DST_ALG_HMACSHA256]));
+ RETERR(dst__hmacsha384_init(&dst_t_func[DST_ALG_HMACSHA384]));
+ RETERR(dst__hmacsha512_init(&dst_t_func[DST_ALG_HMACSHA512]));
+#ifdef OPENSSL
+ RETERR(dst__openssl_init());
+ RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSAMD5]));
+ RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSASHA1]));
+ RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_NSEC3RSASHA1]));
+#ifdef HAVE_OPENSSL_DSA
+ RETERR(dst__openssldsa_init(&dst_t_func[DST_ALG_DSA]));
+ RETERR(dst__openssldsa_init(&dst_t_func[DST_ALG_NSEC3DSA]));
+#endif
+ RETERR(dst__openssldh_init(&dst_t_func[DST_ALG_DH]));
+#endif /* OPENSSL */
+#ifdef GSSAPI
+ RETERR(dst__gssapi_init(&dst_t_func[DST_ALG_GSSAPI]));
+#endif
+ dst_initialized = ISC_TRUE;
+ return (ISC_R_SUCCESS);
+
+ out:
+ dst_lib_destroy();
+ return (result);
+}
+
+void
+dst_lib_destroy(void) {
+ int i;
+ RUNTIME_CHECK(dst_initialized == ISC_TRUE);
+ dst_initialized = ISC_FALSE;
+
+ for (i = 0; i < DST_MAX_ALGS; i++)
+ if (dst_t_func[i] != NULL && dst_t_func[i]->cleanup != NULL)
+ dst_t_func[i]->cleanup();
+#ifdef OPENSSL
+ dst__openssl_destroy();
+#endif
+ if (dst__memory_pool != NULL)
+ isc_mem_detach(&dst__memory_pool);
+ if (dst_entropy_pool != NULL)
+ isc_entropy_detach(&dst_entropy_pool);
+
+}
+
+isc_boolean_t
+dst_algorithm_supported(unsigned int alg) {
+ REQUIRE(dst_initialized == ISC_TRUE);
+
+ if (alg >= DST_MAX_ALGS || dst_t_func[alg] == NULL)
+ return (ISC_FALSE);
+ return (ISC_TRUE);
+}
+
+isc_result_t
+dst_context_create(dst_key_t *key, isc_mem_t *mctx, dst_context_t **dctxp) {
+ dst_context_t *dctx;
+ isc_result_t result;
+
+ REQUIRE(dst_initialized == ISC_TRUE);
+ REQUIRE(VALID_KEY(key));
+ REQUIRE(mctx != NULL);
+ REQUIRE(dctxp != NULL && *dctxp == NULL);
+
+ if (key->func->createctx == NULL)
+ return (DST_R_UNSUPPORTEDALG);
+ if (key->keydata.generic == NULL)
+ return (DST_R_NULLKEY);
+
+ dctx = isc_mem_get(mctx, sizeof(dst_context_t));
+ if (dctx == NULL)
+ return (ISC_R_NOMEMORY);
+ dctx->key = key;
+ dctx->mctx = mctx;
+ result = key->func->createctx(key, dctx);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, dctx, sizeof(dst_context_t));
+ return (result);
+ }
+ dctx->magic = CTX_MAGIC;
+ *dctxp = dctx;
+ return (ISC_R_SUCCESS);
+}
+
+void
+dst_context_destroy(dst_context_t **dctxp) {
+ dst_context_t *dctx;
+
+ REQUIRE(dctxp != NULL && VALID_CTX(*dctxp));
+
+ dctx = *dctxp;
+ INSIST(dctx->key->func->destroyctx != NULL);
+ dctx->key->func->destroyctx(dctx);
+ dctx->magic = 0;
+ isc_mem_put(dctx->mctx, dctx, sizeof(dst_context_t));
+ *dctxp = NULL;
+}
+
+isc_result_t
+dst_context_adddata(dst_context_t *dctx, const isc_region_t *data) {
+ REQUIRE(VALID_CTX(dctx));
+ REQUIRE(data != NULL);
+ INSIST(dctx->key->func->adddata != NULL);
+
+ return (dctx->key->func->adddata(dctx, data));
+}
+
+isc_result_t
+dst_context_sign(dst_context_t *dctx, isc_buffer_t *sig) {
+ dst_key_t *key;
+
+ REQUIRE(VALID_CTX(dctx));
+ REQUIRE(sig != NULL);
+
+ key = dctx->key;
+ CHECKALG(key->key_alg);
+ if (key->keydata.generic == NULL)
+ return (DST_R_NULLKEY);
+
+ if (key->func->sign == NULL)
+ return (DST_R_NOTPRIVATEKEY);
+ if (key->func->isprivate == NULL ||
+ key->func->isprivate(key) == ISC_FALSE)
+ return (DST_R_NOTPRIVATEKEY);
+
+ return (key->func->sign(dctx, sig));
+}
+
+isc_result_t
+dst_context_verify(dst_context_t *dctx, isc_region_t *sig) {
+ REQUIRE(VALID_CTX(dctx));
+ REQUIRE(sig != NULL);
+
+ CHECKALG(dctx->key->key_alg);
+ if (dctx->key->keydata.generic == NULL)
+ return (DST_R_NULLKEY);
+ if (dctx->key->func->verify == NULL)
+ return (DST_R_NOTPUBLICKEY);
+
+ return (dctx->key->func->verify(dctx, sig));
+}
+
+isc_result_t
+dst_key_computesecret(const dst_key_t *pub, const dst_key_t *priv,
+ isc_buffer_t *secret)
+{
+ REQUIRE(dst_initialized == ISC_TRUE);
+ REQUIRE(VALID_KEY(pub) && VALID_KEY(priv));
+ REQUIRE(secret != NULL);
+
+ CHECKALG(pub->key_alg);
+ CHECKALG(priv->key_alg);
+
+ if (pub->keydata.generic == NULL || priv->keydata.generic == NULL)
+ return (DST_R_NULLKEY);
+
+ if (pub->key_alg != priv->key_alg ||
+ pub->func->computesecret == NULL ||
+ priv->func->computesecret == NULL)
+ return (DST_R_KEYCANNOTCOMPUTESECRET);
+
+ if (dst_key_isprivate(priv) == ISC_FALSE)
+ return (DST_R_NOTPRIVATEKEY);
+
+ return (pub->func->computesecret(pub, priv, secret));
+}
+
+isc_result_t
+dst_key_tofile(const dst_key_t *key, int type, const char *directory) {
+ isc_result_t ret = ISC_R_SUCCESS;
+
+ REQUIRE(dst_initialized == ISC_TRUE);
+ REQUIRE(VALID_KEY(key));
+ REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0);
+
+ CHECKALG(key->key_alg);
+
+ if (key->func->tofile == NULL)
+ return (DST_R_UNSUPPORTEDALG);
+
+ if (type & DST_TYPE_PUBLIC) {
+ ret = write_public_key(key, type, directory);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+ }
+
+ if ((type & DST_TYPE_PRIVATE) &&
+ (key->key_flags & DNS_KEYFLAG_TYPEMASK) != DNS_KEYTYPE_NOKEY)
+ return (key->func->tofile(key, directory));
+ else
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dst_key_fromfile(dns_name_t *name, dns_keytag_t id,
+ unsigned int alg, int type, const char *directory,
+ isc_mem_t *mctx, dst_key_t **keyp)
+{
+ char filename[ISC_DIR_NAMEMAX];
+ isc_buffer_t b;
+ dst_key_t *key;
+ isc_result_t result;
+
+ REQUIRE(dst_initialized == ISC_TRUE);
+ REQUIRE(dns_name_isabsolute(name));
+ REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0);
+ REQUIRE(mctx != NULL);
+ REQUIRE(keyp != NULL && *keyp == NULL);
+
+ CHECKALG(alg);
+
+ isc_buffer_init(&b, filename, sizeof(filename));
+ result = buildfilename(name, id, alg, type, directory, &b);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ key = NULL;
+ result = dst_key_fromnamedfile(filename, type, mctx, &key);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = computeid(key);
+ if (result != ISC_R_SUCCESS) {
+ dst_key_free(&key);
+ return (result);
+ }
+
+ if (!dns_name_equal(name, key->key_name) || id != key->key_id ||
+ alg != key->key_alg) {
+ dst_key_free(&key);
+ return (DST_R_INVALIDPRIVATEKEY);
+ }
+ key->key_id = id;
+
+ *keyp = key;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dst_key_fromnamedfile(const char *filename, int type, isc_mem_t *mctx,
+ dst_key_t **keyp)
+{
+ isc_result_t result;
+ dst_key_t *pubkey = NULL, *key = NULL;
+ dns_keytag_t id;
+ char *newfilename = NULL;
+ int newfilenamelen = 0;
+ isc_lex_t *lex = NULL;
+
+ REQUIRE(dst_initialized == ISC_TRUE);
+ REQUIRE(filename != NULL);
+ REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0);
+ REQUIRE(mctx != NULL);
+ REQUIRE(keyp != NULL && *keyp == NULL);
+
+ newfilenamelen = strlen(filename) + 5;
+ newfilename = isc_mem_get(mctx, newfilenamelen);
+ if (newfilename == NULL)
+ return (ISC_R_NOMEMORY);
+ result = addsuffix(newfilename, newfilenamelen, filename, ".key");
+ INSIST(result == ISC_R_SUCCESS);
+
+ result = dst_key_read_public(newfilename, type, mctx, &pubkey);
+ isc_mem_put(mctx, newfilename, newfilenamelen);
+ newfilename = NULL;
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if ((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) == DST_TYPE_PUBLIC ||
+ (pubkey->key_flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY) {
+ result = computeid(pubkey);
+ if (result != ISC_R_SUCCESS) {
+ dst_key_free(&pubkey);
+ return (result);
+ }
+
+ *keyp = pubkey;
+ return (ISC_R_SUCCESS);
+ }
+
+ result = algorithm_status(pubkey->key_alg);
+ if (result != ISC_R_SUCCESS) {
+ dst_key_free(&pubkey);
+ return (result);
+ }
+
+ key = get_key_struct(pubkey->key_name, pubkey->key_alg,
+ pubkey->key_flags, pubkey->key_proto, 0,
+ pubkey->key_class, mctx);
+ id = pubkey->key_id;
+ dst_key_free(&pubkey);
+
+ if (key == NULL)
+ return (ISC_R_NOMEMORY);
+
+ if (key->func->parse == NULL)
+ RETERR(DST_R_UNSUPPORTEDALG);
+
+ newfilenamelen = strlen(filename) + 9;
+ newfilename = isc_mem_get(mctx, newfilenamelen);
+ if (newfilename == NULL)
+ RETERR(ISC_R_NOMEMORY);
+ result = addsuffix(newfilename, newfilenamelen, filename, ".private");
+ INSIST(result == ISC_R_SUCCESS);
+
+ RETERR(isc_lex_create(mctx, 1500, &lex));
+ RETERR(isc_lex_openfile(lex, newfilename));
+ isc_mem_put(mctx, newfilename, newfilenamelen);
+
+ RETERR(key->func->parse(key, lex));
+ isc_lex_destroy(&lex);
+
+ RETERR(computeid(key));
+
+ if (id != key->key_id)
+ RETERR(DST_R_INVALIDPRIVATEKEY);
+
+ *keyp = key;
+ return (ISC_R_SUCCESS);
+ out:
+ if (newfilename != NULL)
+ isc_mem_put(mctx, newfilename, newfilenamelen);
+ if (lex != NULL)
+ isc_lex_destroy(&lex);
+ dst_key_free(&key);
+ return (result);
+}
+
+isc_result_t
+dst_key_todns(const dst_key_t *key, isc_buffer_t *target) {
+ REQUIRE(dst_initialized == ISC_TRUE);
+ REQUIRE(VALID_KEY(key));
+ REQUIRE(target != NULL);
+
+ CHECKALG(key->key_alg);
+
+ if (key->func->todns == NULL)
+ return (DST_R_UNSUPPORTEDALG);
+
+ if (isc_buffer_availablelength(target) < 4)
+ return (ISC_R_NOSPACE);
+ isc_buffer_putuint16(target, (isc_uint16_t)(key->key_flags & 0xffff));
+ isc_buffer_putuint8(target, (isc_uint8_t)key->key_proto);
+ isc_buffer_putuint8(target, (isc_uint8_t)key->key_alg);
+
+ if (key->key_flags & DNS_KEYFLAG_EXTENDED) {
+ if (isc_buffer_availablelength(target) < 2)
+ return (ISC_R_NOSPACE);
+ isc_buffer_putuint16(target,
+ (isc_uint16_t)((key->key_flags >> 16)
+ & 0xffff));
+ }
+
+ if (key->keydata.generic == NULL) /*%< NULL KEY */
+ return (ISC_R_SUCCESS);
+
+ return (key->func->todns(key, target));
+}
+
+isc_result_t
+dst_key_fromdns(dns_name_t *name, dns_rdataclass_t rdclass,
+ isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp)
+{
+ isc_uint8_t alg, proto;
+ isc_uint32_t flags, extflags;
+ dst_key_t *key = NULL;
+ dns_keytag_t id;
+ isc_region_t r;
+ isc_result_t result;
+
+ REQUIRE(dst_initialized);
+
+ isc_buffer_remainingregion(source, &r);
+
+ if (isc_buffer_remaininglength(source) < 4)
+ return (DST_R_INVALIDPUBLICKEY);
+ flags = isc_buffer_getuint16(source);
+ proto = isc_buffer_getuint8(source);
+ alg = isc_buffer_getuint8(source);
+
+ id = dst_region_computeid(&r, alg);
+
+ if (flags & DNS_KEYFLAG_EXTENDED) {
+ if (isc_buffer_remaininglength(source) < 2)
+ return (DST_R_INVALIDPUBLICKEY);
+ extflags = isc_buffer_getuint16(source);
+ flags |= (extflags << 16);
+ }
+
+ result = frombuffer(name, alg, flags, proto, rdclass, source,
+ mctx, &key);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ key->key_id = id;
+
+ *keyp = key;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dst_key_frombuffer(dns_name_t *name, unsigned int alg,
+ unsigned int flags, unsigned int protocol,
+ dns_rdataclass_t rdclass,
+ isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp)
+{
+ dst_key_t *key = NULL;
+ isc_result_t result;
+
+ REQUIRE(dst_initialized);
+
+ result = frombuffer(name, alg, flags, protocol, rdclass, source,
+ mctx, &key);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = computeid(key);
+ if (result != ISC_R_SUCCESS) {
+ dst_key_free(&key);
+ return (result);
+ }
+
+ *keyp = key;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dst_key_tobuffer(const dst_key_t *key, isc_buffer_t *target) {
+ REQUIRE(dst_initialized == ISC_TRUE);
+ REQUIRE(VALID_KEY(key));
+ REQUIRE(target != NULL);
+
+ CHECKALG(key->key_alg);
+
+ if (key->func->todns == NULL)
+ return (DST_R_UNSUPPORTEDALG);
+
+ return (key->func->todns(key, target));
+}
+
+isc_result_t
+dst_key_privatefrombuffer(dst_key_t *key, isc_buffer_t *buffer) {
+ isc_lex_t *lex = NULL;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ REQUIRE(dst_initialized == ISC_TRUE);
+ REQUIRE(VALID_KEY(key));
+ REQUIRE(!dst_key_isprivate(key));
+ REQUIRE(buffer != NULL);
+
+ if (key->func->parse == NULL)
+ RETERR(DST_R_UNSUPPORTEDALG);
+
+ RETERR(isc_lex_create(key->mctx, 1500, &lex));
+ RETERR(isc_lex_openbuffer(lex, buffer));
+ RETERR(key->func->parse(key, lex));
+ out:
+ if (lex != NULL)
+ isc_lex_destroy(&lex);
+ return (result);
+}
+
+gss_ctx_id_t
+dst_key_getgssctx(const dst_key_t *key)
+{
+ REQUIRE(key != NULL);
+
+ return (key->keydata.gssctx);
+}
+
+isc_result_t
+dst_key_fromgssapi(dns_name_t *name, gss_ctx_id_t gssctx, isc_mem_t *mctx,
+ dst_key_t **keyp)
+{
+ dst_key_t *key;
+
+ REQUIRE(gssctx != NULL);
+ REQUIRE(keyp != NULL && *keyp == NULL);
+
+ key = get_key_struct(name, DST_ALG_GSSAPI, 0, DNS_KEYPROTO_DNSSEC,
+ 0, dns_rdataclass_in, mctx);
+ if (key == NULL)
+ return (ISC_R_NOMEMORY);
+
+ key->keydata.gssctx = gssctx;
+ *keyp = key;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dst_key_fromlabel(dns_name_t *name, int alg, unsigned int flags,
+ unsigned int protocol, dns_rdataclass_t rdclass,
+ const char *engine, const char *label, const char *pin,
+ isc_mem_t *mctx, dst_key_t **keyp)
+{
+ dst_key_t *key;
+ isc_result_t result;
+
+ REQUIRE(dst_initialized == ISC_TRUE);
+ REQUIRE(dns_name_isabsolute(name));
+ REQUIRE(mctx != NULL);
+ REQUIRE(keyp != NULL && *keyp == NULL);
+ REQUIRE(label != NULL);
+
+ CHECKALG(alg);
+
+ key = get_key_struct(name, alg, flags, protocol, 0, rdclass, mctx);
+ if (key == NULL)
+ return (ISC_R_NOMEMORY);
+
+ if (key->func->fromlabel == NULL) {
+ dst_key_free(&key);
+ return (DST_R_UNSUPPORTEDALG);
+ }
+
+ result = key->func->fromlabel(key, engine, label, pin);
+ if (result != ISC_R_SUCCESS) {
+ dst_key_free(&key);
+ return (result);
+ }
+
+ result = computeid(key);
+ if (result != ISC_R_SUCCESS) {
+ dst_key_free(&key);
+ return (result);
+ }
+
+ *keyp = key;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dst_key_generate(dns_name_t *name, unsigned int alg,
+ unsigned int bits, unsigned int param,
+ unsigned int flags, unsigned int protocol,
+ dns_rdataclass_t rdclass,
+ isc_mem_t *mctx, dst_key_t **keyp)
+{
+ dst_key_t *key;
+ isc_result_t ret;
+
+ REQUIRE(dst_initialized == ISC_TRUE);
+ REQUIRE(dns_name_isabsolute(name));
+ REQUIRE(mctx != NULL);
+ REQUIRE(keyp != NULL && *keyp == NULL);
+
+ CHECKALG(alg);
+
+ key = get_key_struct(name, alg, flags, protocol, bits, rdclass, mctx);
+ if (key == NULL)
+ return (ISC_R_NOMEMORY);
+
+ if (bits == 0) { /*%< NULL KEY */
+ key->key_flags |= DNS_KEYTYPE_NOKEY;
+ *keyp = key;
+ return (ISC_R_SUCCESS);
+ }
+
+ if (key->func->generate == NULL) {
+ dst_key_free(&key);
+ return (DST_R_UNSUPPORTEDALG);
+ }
+
+ ret = key->func->generate(key, param);
+ if (ret != ISC_R_SUCCESS) {
+ dst_key_free(&key);
+ return (ret);
+ }
+
+ ret = computeid(key);
+ if (ret != ISC_R_SUCCESS) {
+ dst_key_free(&key);
+ return (ret);
+ }
+
+ *keyp = key;
+ return (ISC_R_SUCCESS);
+}
+
+isc_boolean_t
+dst_key_compare(const dst_key_t *key1, const dst_key_t *key2) {
+ REQUIRE(dst_initialized == ISC_TRUE);
+ REQUIRE(VALID_KEY(key1));
+ REQUIRE(VALID_KEY(key2));
+
+ if (key1 == key2)
+ return (ISC_TRUE);
+ if (key1 == NULL || key2 == NULL)
+ return (ISC_FALSE);
+ if (key1->key_alg == key2->key_alg &&
+ key1->key_id == key2->key_id &&
+ key1->func->compare != NULL &&
+ key1->func->compare(key1, key2) == ISC_TRUE)
+ return (ISC_TRUE);
+ else
+ return (ISC_FALSE);
+}
+
+isc_boolean_t
+dst_key_paramcompare(const dst_key_t *key1, const dst_key_t *key2) {
+ REQUIRE(dst_initialized == ISC_TRUE);
+ REQUIRE(VALID_KEY(key1));
+ REQUIRE(VALID_KEY(key2));
+
+ if (key1 == key2)
+ return (ISC_TRUE);
+ if (key1 == NULL || key2 == NULL)
+ return (ISC_FALSE);
+ if (key1->key_alg == key2->key_alg &&
+ key1->func->paramcompare != NULL &&
+ key1->func->paramcompare(key1, key2) == ISC_TRUE)
+ return (ISC_TRUE);
+ else
+ return (ISC_FALSE);
+}
+
+void
+dst_key_free(dst_key_t **keyp) {
+ isc_mem_t *mctx;
+ dst_key_t *key;
+
+ REQUIRE(dst_initialized == ISC_TRUE);
+ REQUIRE(keyp != NULL && VALID_KEY(*keyp));
+
+ key = *keyp;
+ mctx = key->mctx;
+
+ if (key->keydata.generic != NULL) {
+ INSIST(key->func->destroy != NULL);
+ key->func->destroy(key);
+ }
+ if (key->engine != NULL)
+ isc_mem_free(mctx, key->engine);
+ if (key->label != NULL)
+ isc_mem_free(mctx, key->label);
+ dns_name_free(key->key_name, mctx);
+ isc_mem_put(mctx, key->key_name, sizeof(dns_name_t));
+ memset(key, 0, sizeof(dst_key_t));
+ isc_mem_put(mctx, key, sizeof(dst_key_t));
+ *keyp = NULL;
+}
+
+isc_boolean_t
+dst_key_isprivate(const dst_key_t *key) {
+ REQUIRE(VALID_KEY(key));
+ INSIST(key->func->isprivate != NULL);
+ return (key->func->isprivate(key));
+}
+
+isc_result_t
+dst_key_buildfilename(const dst_key_t *key, int type,
+ const char *directory, isc_buffer_t *out) {
+
+ REQUIRE(VALID_KEY(key));
+ REQUIRE(type == DST_TYPE_PRIVATE || type == DST_TYPE_PUBLIC ||
+ type == 0);
+
+ return (buildfilename(key->key_name, key->key_id, key->key_alg,
+ type, directory, out));
+}
+
+isc_result_t
+dst_key_sigsize(const dst_key_t *key, unsigned int *n) {
+ REQUIRE(dst_initialized == ISC_TRUE);
+ REQUIRE(VALID_KEY(key));
+ REQUIRE(n != NULL);
+
+ /* XXXVIX this switch statement is too sparse to gen a jump table. */
+ switch (key->key_alg) {
+ case DST_ALG_RSAMD5:
+ case DST_ALG_RSASHA1:
+ case DST_ALG_NSEC3RSASHA1:
+ *n = (key->key_size + 7) / 8;
+ break;
+ case DST_ALG_DSA:
+ case DST_ALG_NSEC3DSA:
+ *n = DNS_SIG_DSASIGSIZE;
+ break;
+ case DST_ALG_HMACMD5:
+ *n = 16;
+ break;
+ case DST_ALG_HMACSHA1:
+ *n = ISC_SHA1_DIGESTLENGTH;
+ break;
+ case DST_ALG_HMACSHA224:
+ *n = ISC_SHA224_DIGESTLENGTH;
+ break;
+ case DST_ALG_HMACSHA256:
+ *n = ISC_SHA256_DIGESTLENGTH;
+ break;
+ case DST_ALG_HMACSHA384:
+ *n = ISC_SHA384_DIGESTLENGTH;
+ break;
+ case DST_ALG_HMACSHA512:
+ *n = ISC_SHA512_DIGESTLENGTH;
+ break;
+ case DST_ALG_GSSAPI:
+ *n = 128; /*%< XXX */
+ break;
+ case DST_ALG_DH:
+ default:
+ return (DST_R_UNSUPPORTEDALG);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dst_key_secretsize(const dst_key_t *key, unsigned int *n) {
+ REQUIRE(dst_initialized == ISC_TRUE);
+ REQUIRE(VALID_KEY(key));
+ REQUIRE(n != NULL);
+
+ if (key->key_alg == DST_ALG_DH)
+ *n = (key->key_size + 7) / 8;
+ else
+ return (DST_R_UNSUPPORTEDALG);
+ return (ISC_R_SUCCESS);
+}
+
+/***
+ *** Static methods
+ ***/
+
+/*%
+ * Allocates a key structure and fills in some of the fields.
+ */
+static dst_key_t *
+get_key_struct(dns_name_t *name, unsigned int alg,
+ unsigned int flags, unsigned int protocol,
+ unsigned int bits, dns_rdataclass_t rdclass,
+ isc_mem_t *mctx)
+{
+ dst_key_t *key;
+ isc_result_t result;
+
+ key = (dst_key_t *) isc_mem_get(mctx, sizeof(dst_key_t));
+ if (key == NULL)
+ return (NULL);
+
+ memset(key, 0, sizeof(dst_key_t));
+ key->magic = KEY_MAGIC;
+
+ key->key_name = isc_mem_get(mctx, sizeof(dns_name_t));
+ if (key->key_name == NULL) {
+ isc_mem_put(mctx, key, sizeof(dst_key_t));
+ return (NULL);
+ }
+ dns_name_init(key->key_name, NULL);
+ result = dns_name_dup(name, mctx, key->key_name);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, key->key_name, sizeof(dns_name_t));
+ isc_mem_put(mctx, key, sizeof(dst_key_t));
+ return (NULL);
+ }
+ key->key_alg = alg;
+ key->key_flags = flags;
+ key->key_proto = protocol;
+ key->mctx = mctx;
+ key->keydata.generic = NULL;
+ key->key_size = bits;
+ key->key_class = rdclass;
+ key->func = dst_t_func[alg];
+ return (key);
+}
+
+/*%
+ * Reads a public key from disk
+ */
+isc_result_t
+dst_key_read_public(const char *filename, int type,
+ isc_mem_t *mctx, dst_key_t **keyp)
+{
+ u_char rdatabuf[DST_KEY_MAXSIZE];
+ isc_buffer_t b;
+ dns_fixedname_t name;
+ isc_lex_t *lex = NULL;
+ isc_token_t token;
+ isc_result_t ret;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ unsigned int opt = ISC_LEXOPT_DNSMULTILINE;
+ dns_rdataclass_t rdclass = dns_rdataclass_in;
+ isc_lexspecials_t specials;
+ isc_uint32_t ttl;
+ isc_result_t result;
+ dns_rdatatype_t keytype;
+
+ /*
+ * Open the file and read its formatted contents
+ * File format:
+ * domain.name [ttl] [class] [KEY|DNSKEY] <flags> <protocol> <algorithm> <key>
+ */
+
+ /* 1500 should be large enough for any key */
+ ret = isc_lex_create(mctx, 1500, &lex);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup;
+
+ memset(specials, 0, sizeof(specials));
+ specials['('] = 1;
+ specials[')'] = 1;
+ specials['"'] = 1;
+ isc_lex_setspecials(lex, specials);
+ isc_lex_setcomments(lex, ISC_LEXCOMMENT_DNSMASTERFILE);
+
+ ret = isc_lex_openfile(lex, filename);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup;
+
+#define NEXTTOKEN(lex, opt, token) { \
+ ret = isc_lex_gettoken(lex, opt, token); \
+ if (ret != ISC_R_SUCCESS) \
+ goto cleanup; \
+ }
+
+#define BADTOKEN() { \
+ ret = ISC_R_UNEXPECTEDTOKEN; \
+ goto cleanup; \
+ }
+
+ /* Read the domain name */
+ NEXTTOKEN(lex, opt, &token);
+ if (token.type != isc_tokentype_string)
+ BADTOKEN();
+
+ /*
+ * We don't support "@" in .key files.
+ */
+ if (!strcmp(DST_AS_STR(token), "@"))
+ BADTOKEN();
+
+ dns_fixedname_init(&name);
+ isc_buffer_init(&b, DST_AS_STR(token), strlen(DST_AS_STR(token)));
+ isc_buffer_add(&b, strlen(DST_AS_STR(token)));
+ ret = dns_name_fromtext(dns_fixedname_name(&name), &b, dns_rootname,
+ ISC_FALSE, NULL);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup;
+
+ /* Read the next word: either TTL, class, or 'KEY' */
+ NEXTTOKEN(lex, opt, &token);
+
+ /* If it's a TTL, read the next one */
+ result = dns_ttl_fromtext(&token.value.as_textregion, &ttl);
+ if (result == ISC_R_SUCCESS)
+ NEXTTOKEN(lex, opt, &token);
+
+ if (token.type != isc_tokentype_string)
+ BADTOKEN();
+
+ ret = dns_rdataclass_fromtext(&rdclass, &token.value.as_textregion);
+ if (ret == ISC_R_SUCCESS)
+ NEXTTOKEN(lex, opt, &token);
+
+ if (token.type != isc_tokentype_string)
+ BADTOKEN();
+
+ if (strcasecmp(DST_AS_STR(token), "DNSKEY") == 0)
+ keytype = dns_rdatatype_dnskey;
+ else if (strcasecmp(DST_AS_STR(token), "KEY") == 0)
+ keytype = dns_rdatatype_key; /*%< SIG(0), TKEY */
+ else
+ BADTOKEN();
+
+ if (((type & DST_TYPE_KEY) != 0 && keytype != dns_rdatatype_key) ||
+ ((type & DST_TYPE_KEY) == 0 && keytype != dns_rdatatype_dnskey)) {
+ ret = DST_R_BADKEYTYPE;
+ goto cleanup;
+ }
+
+ isc_buffer_init(&b, rdatabuf, sizeof(rdatabuf));
+ ret = dns_rdata_fromtext(&rdata, rdclass, keytype, lex, NULL,
+ ISC_FALSE, mctx, &b, NULL);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup;
+
+ ret = dst_key_fromdns(dns_fixedname_name(&name), rdclass, &b, mctx,
+ keyp);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup;
+
+ cleanup:
+ if (lex != NULL)
+ isc_lex_destroy(&lex);
+ return (ret);
+}
+
+static isc_boolean_t
+issymmetric(const dst_key_t *key) {
+ REQUIRE(dst_initialized == ISC_TRUE);
+ REQUIRE(VALID_KEY(key));
+
+ /* XXXVIX this switch statement is too sparse to gen a jump table. */
+ switch (key->key_alg) {
+ case DST_ALG_RSAMD5:
+ case DST_ALG_RSASHA1:
+ case DST_ALG_NSEC3RSASHA1:
+ case DST_ALG_DSA:
+ case DST_ALG_NSEC3DSA:
+ case DST_ALG_DH:
+ return (ISC_FALSE);
+ case DST_ALG_HMACMD5:
+ case DST_ALG_GSSAPI:
+ return (ISC_TRUE);
+ default:
+ return (ISC_FALSE);
+ }
+}
+
+/*%
+ * Writes a public key to disk in DNS format.
+ */
+static isc_result_t
+write_public_key(const dst_key_t *key, int type, const char *directory) {
+ FILE *fp;
+ isc_buffer_t keyb, textb, fileb, classb;
+ isc_region_t r;
+ char filename[ISC_DIR_NAMEMAX];
+ unsigned char key_array[DST_KEY_MAXSIZE];
+ char text_array[DST_KEY_MAXTEXTSIZE];
+ char class_array[10];
+ isc_result_t ret;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_fsaccess_t access;
+
+ REQUIRE(VALID_KEY(key));
+
+ isc_buffer_init(&keyb, key_array, sizeof(key_array));
+ isc_buffer_init(&textb, text_array, sizeof(text_array));
+ isc_buffer_init(&classb, class_array, sizeof(class_array));
+
+ ret = dst_key_todns(key, &keyb);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ isc_buffer_usedregion(&keyb, &r);
+ dns_rdata_fromregion(&rdata, key->key_class, dns_rdatatype_dnskey, &r);
+
+ ret = dns_rdata_totext(&rdata, (dns_name_t *) NULL, &textb);
+ if (ret != ISC_R_SUCCESS)
+ return (DST_R_INVALIDPUBLICKEY);
+
+ ret = dns_rdataclass_totext(key->key_class, &classb);
+ if (ret != ISC_R_SUCCESS)
+ return (DST_R_INVALIDPUBLICKEY);
+
+ /*
+ * Make the filename.
+ */
+ isc_buffer_init(&fileb, filename, sizeof(filename));
+ ret = dst_key_buildfilename(key, DST_TYPE_PUBLIC, directory, &fileb);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ /*
+ * Create public key file.
+ */
+ if ((fp = fopen(filename, "w")) == NULL)
+ return (DST_R_WRITEERROR);
+
+ if (issymmetric(key)) {
+ access = 0;
+ isc_fsaccess_add(ISC_FSACCESS_OWNER,
+ ISC_FSACCESS_READ | ISC_FSACCESS_WRITE,
+ &access);
+ (void)isc_fsaccess_set(filename, access);
+ }
+
+ ret = dns_name_print(key->key_name, fp);
+ if (ret != ISC_R_SUCCESS) {
+ fclose(fp);
+ return (ret);
+ }
+
+ fprintf(fp, " ");
+
+ isc_buffer_usedregion(&classb, &r);
+ fwrite(r.base, 1, r.length, fp);
+
+ if ((type & DST_TYPE_KEY) != 0)
+ fprintf(fp, " KEY ");
+ else
+ fprintf(fp, " DNSKEY ");
+
+ isc_buffer_usedregion(&textb, &r);
+ fwrite(r.base, 1, r.length, fp);
+
+ fputc('\n', fp);
+ fclose(fp);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+buildfilename(dns_name_t *name, dns_keytag_t id,
+ unsigned int alg, unsigned int type,
+ const char *directory, isc_buffer_t *out)
+{
+ const char *suffix = "";
+ unsigned int len;
+ isc_result_t result;
+
+ REQUIRE(out != NULL);
+ if ((type & DST_TYPE_PRIVATE) != 0)
+ suffix = ".private";
+ else if (type == DST_TYPE_PUBLIC)
+ suffix = ".key";
+ if (directory != NULL) {
+ if (isc_buffer_availablelength(out) < strlen(directory))
+ return (ISC_R_NOSPACE);
+ isc_buffer_putstr(out, directory);
+ if (strlen(directory) > 0U &&
+ directory[strlen(directory) - 1] != '/')
+ isc_buffer_putstr(out, "/");
+ }
+ if (isc_buffer_availablelength(out) < 1)
+ return (ISC_R_NOSPACE);
+ isc_buffer_putstr(out, "K");
+ result = dns_name_tofilenametext(name, ISC_FALSE, out);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ len = 1 + 3 + 1 + 5 + strlen(suffix) + 1;
+ if (isc_buffer_availablelength(out) < len)
+ return (ISC_R_NOSPACE);
+ sprintf((char *) isc_buffer_used(out), "+%03d+%05d%s", alg, id,
+ suffix);
+ isc_buffer_add(out, len);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+computeid(dst_key_t *key) {
+ isc_buffer_t dnsbuf;
+ unsigned char dns_array[DST_KEY_MAXSIZE];
+ isc_region_t r;
+ isc_result_t ret;
+
+ isc_buffer_init(&dnsbuf, dns_array, sizeof(dns_array));
+ ret = dst_key_todns(key, &dnsbuf);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ isc_buffer_usedregion(&dnsbuf, &r);
+ key->key_id = dst_region_computeid(&r, key->key_alg);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+frombuffer(dns_name_t *name, unsigned int alg, unsigned int flags,
+ unsigned int protocol, dns_rdataclass_t rdclass,
+ isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp)
+{
+ dst_key_t *key;
+ isc_result_t ret;
+
+ REQUIRE(dns_name_isabsolute(name));
+ REQUIRE(source != NULL);
+ REQUIRE(mctx != NULL);
+ REQUIRE(keyp != NULL && *keyp == NULL);
+
+ key = get_key_struct(name, alg, flags, protocol, 0, rdclass, mctx);
+ if (key == NULL)
+ return (ISC_R_NOMEMORY);
+
+ if (isc_buffer_remaininglength(source) > 0) {
+ ret = algorithm_status(alg);
+ if (ret != ISC_R_SUCCESS) {
+ dst_key_free(&key);
+ return (ret);
+ }
+ if (key->func->fromdns == NULL) {
+ dst_key_free(&key);
+ return (DST_R_UNSUPPORTEDALG);
+ }
+
+ ret = key->func->fromdns(key, source);
+ if (ret != ISC_R_SUCCESS) {
+ dst_key_free(&key);
+ return (ret);
+ }
+ }
+
+ *keyp = key;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+algorithm_status(unsigned int alg) {
+ REQUIRE(dst_initialized == ISC_TRUE);
+
+ if (dst_algorithm_supported(alg))
+ return (ISC_R_SUCCESS);
+#ifndef OPENSSL
+ if (alg == DST_ALG_RSAMD5 || alg == DST_ALG_RSASHA1 ||
+ alg == DST_ALG_DSA || alg == DST_ALG_DH ||
+ alg == DST_ALG_HMACMD5 || alg == DST_ALG_NSEC3DSA ||
+ alg == DST_ALG_NSEC3RSASHA1)
+ return (DST_R_NOCRYPTO);
+#endif
+ return (DST_R_UNSUPPORTEDALG);
+}
+
+static isc_result_t
+addsuffix(char *filename, unsigned int len, const char *ofilename,
+ const char *suffix)
+{
+ int olen = strlen(ofilename);
+ int n;
+
+ if (olen > 1 && ofilename[olen - 1] == '.')
+ olen -= 1;
+ else if (olen > 8 && strcmp(ofilename + olen - 8, ".private") == 0)
+ olen -= 8;
+ else if (olen > 4 && strcmp(ofilename + olen - 4, ".key") == 0)
+ olen -= 4;
+
+ n = snprintf(filename, len, "%.*s%s", olen, ofilename, suffix);
+ if (n < 0)
+ return (ISC_R_NOSPACE);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dst__entropy_getdata(void *buf, unsigned int len, isc_boolean_t pseudo) {
+ unsigned int flags = dst_entropy_flags;
+ if (pseudo)
+ flags &= ~ISC_ENTROPY_GOODONLY;
+ return (isc_entropy_getdata(dst_entropy_pool, buf, len, NULL, flags));
+}
+
+unsigned int
+dst__entropy_status(void) {
+ return (isc_entropy_status(dst_entropy_pool));
+}
diff --git a/lib/dns/dst_internal.h b/lib/dns/dst_internal.h
new file mode 100644
index 0000000..0c1a71c
--- /dev/null
+++ b/lib/dns/dst_internal.h
@@ -0,0 +1,217 @@
+/*
+ * Portions Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (C) 2000-2002 Internet Software Consortium.
+ *
+ * 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 NETWORK ASSOCIATES 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.
+ *
+ * Portions Copyright (C) 1995-2000 by Network Associates, 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 NETWORK ASSOCIATES 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.
+ */
+
+/* $Id: dst_internal.h,v 1.11 2008/04/01 23:47:10 tbox Exp $ */
+
+#ifndef DST_DST_INTERNAL_H
+#define DST_DST_INTERNAL_H 1
+
+#include <isc/lang.h>
+#include <isc/buffer.h>
+#include <isc/int.h>
+#include <isc/magic.h>
+#include <isc/region.h>
+#include <isc/types.h>
+#include <isc/md5.h>
+#include <isc/sha1.h>
+#include <isc/hmacmd5.h>
+#include <isc/hmacsha.h>
+
+#include <dst/dst.h>
+
+#ifdef OPENSSL
+#include <openssl/dh.h>
+#include <openssl/dsa.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/objects.h>
+#include <openssl/rsa.h>
+#endif
+
+ISC_LANG_BEGINDECLS
+
+#define KEY_MAGIC ISC_MAGIC('D','S','T','K')
+#define CTX_MAGIC ISC_MAGIC('D','S','T','C')
+
+#define VALID_KEY(x) ISC_MAGIC_VALID(x, KEY_MAGIC)
+#define VALID_CTX(x) ISC_MAGIC_VALID(x, CTX_MAGIC)
+
+extern isc_mem_t *dst__memory_pool;
+
+/***
+ *** Types
+ ***/
+
+typedef struct dst_func dst_func_t;
+
+typedef struct dst_hmacmd5_key dst_hmacmd5_key_t;
+typedef struct dst_hmacsha1_key dst_hmacsha1_key_t;
+typedef struct dst_hmacsha224_key dst_hmacsha224_key_t;
+typedef struct dst_hmacsha256_key dst_hmacsha256_key_t;
+typedef struct dst_hmacsha384_key dst_hmacsha384_key_t;
+typedef struct dst_hmacsha512_key dst_hmacsha512_key_t;
+
+/*% DST Key Structure */
+struct dst_key {
+ unsigned int magic;
+ dns_name_t * key_name; /*%< name of the key */
+ unsigned int key_size; /*%< size of the key in bits */
+ unsigned int key_proto; /*%< protocols this key is used for */
+ unsigned int key_alg; /*%< algorithm of the key */
+ isc_uint32_t key_flags; /*%< flags of the public key */
+ isc_uint16_t key_id; /*%< identifier of the key */
+ isc_uint16_t key_bits; /*%< hmac digest bits */
+ dns_rdataclass_t key_class; /*%< class of the key record */
+ isc_mem_t *mctx; /*%< memory context */
+ char *engine; /*%< engine name (HSM) */
+ char *label; /*%< engine label (HSM) */
+ union {
+ void *generic;
+ gss_ctx_id_t gssctx;
+#ifdef OPENSSL
+#if USE_EVP_RSA
+ RSA *rsa;
+#endif
+ DSA *dsa;
+ DH *dh;
+ EVP_PKEY *pkey;
+#endif
+ dst_hmacmd5_key_t *hmacmd5;
+ dst_hmacsha1_key_t *hmacsha1;
+ dst_hmacsha224_key_t *hmacsha224;
+ dst_hmacsha256_key_t *hmacsha256;
+ dst_hmacsha384_key_t *hmacsha384;
+ dst_hmacsha512_key_t *hmacsha512;
+
+ } keydata; /*%< pointer to key in crypto pkg fmt */
+ dst_func_t * func; /*%< crypto package specific functions */
+};
+
+struct dst_context {
+ unsigned int magic;
+ dst_key_t *key;
+ isc_mem_t *mctx;
+ union {
+ void *generic;
+ dst_gssapi_signverifyctx_t *gssctx;
+ isc_md5_t *md5ctx;
+ isc_sha1_t *sha1ctx;
+ isc_hmacmd5_t *hmacmd5ctx;
+ isc_hmacsha1_t *hmacsha1ctx;
+ isc_hmacsha224_t *hmacsha224ctx;
+ isc_hmacsha256_t *hmacsha256ctx;
+ isc_hmacsha384_t *hmacsha384ctx;
+ isc_hmacsha512_t *hmacsha512ctx;
+#ifdef OPENSSL
+ EVP_MD_CTX *evp_md_ctx;
+#endif
+ } ctxdata;
+};
+
+struct dst_func {
+ /*
+ * Context functions
+ */
+ isc_result_t (*createctx)(dst_key_t *key, dst_context_t *dctx);
+ void (*destroyctx)(dst_context_t *dctx);
+ isc_result_t (*adddata)(dst_context_t *dctx, const isc_region_t *data);
+
+ /*
+ * Key operations
+ */
+ isc_result_t (*sign)(dst_context_t *dctx, isc_buffer_t *sig);
+ isc_result_t (*verify)(dst_context_t *dctx, const isc_region_t *sig);
+ isc_result_t (*computesecret)(const dst_key_t *pub,
+ const dst_key_t *priv,
+ isc_buffer_t *secret);
+ isc_boolean_t (*compare)(const dst_key_t *key1, const dst_key_t *key2);
+ isc_boolean_t (*paramcompare)(const dst_key_t *key1,
+ const dst_key_t *key2);
+ isc_result_t (*generate)(dst_key_t *key, int parms);
+ isc_boolean_t (*isprivate)(const dst_key_t *key);
+ void (*destroy)(dst_key_t *key);
+
+ /* conversion functions */
+ isc_result_t (*todns)(const dst_key_t *key, isc_buffer_t *data);
+ isc_result_t (*fromdns)(dst_key_t *key, isc_buffer_t *data);
+ isc_result_t (*tofile)(const dst_key_t *key, const char *directory);
+ isc_result_t (*parse)(dst_key_t *key, isc_lex_t *lexer);
+
+ /* cleanup */
+ void (*cleanup)(void);
+
+ isc_result_t (*fromlabel)(dst_key_t *key, const char *engine,
+ const char *label, const char *pin);
+};
+
+/*%
+ * Initializers
+ */
+isc_result_t dst__openssl_init(void);
+
+isc_result_t dst__hmacmd5_init(struct dst_func **funcp);
+isc_result_t dst__hmacsha1_init(struct dst_func **funcp);
+isc_result_t dst__hmacsha224_init(struct dst_func **funcp);
+isc_result_t dst__hmacsha256_init(struct dst_func **funcp);
+isc_result_t dst__hmacsha384_init(struct dst_func **funcp);
+isc_result_t dst__hmacsha512_init(struct dst_func **funcp);
+isc_result_t dst__opensslrsa_init(struct dst_func **funcp);
+isc_result_t dst__openssldsa_init(struct dst_func **funcp);
+isc_result_t dst__openssldh_init(struct dst_func **funcp);
+isc_result_t dst__gssapi_init(struct dst_func **funcp);
+
+/*%
+ * Destructors
+ */
+void dst__openssl_destroy(void);
+
+/*%
+ * Memory allocators using the DST memory pool.
+ */
+void * dst__mem_alloc(size_t size);
+void dst__mem_free(void *ptr);
+void * dst__mem_realloc(void *ptr, size_t size);
+
+/*%
+ * Entropy retriever using the DST entropy pool.
+ */
+isc_result_t dst__entropy_getdata(void *buf, unsigned int len,
+ isc_boolean_t pseudo);
+
+/*
+ * Entropy status hook.
+ */
+unsigned int dst__entropy_status(void);
+
+ISC_LANG_ENDDECLS
+
+#endif /* DST_DST_INTERNAL_H */
+/*! \file */
diff --git a/lib/dns/dst_lib.c b/lib/dns/dst_lib.c
new file mode 100644
index 0000000..f1021d3
--- /dev/null
+++ b/lib/dns/dst_lib.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/*
+ * Principal Author: Brian Wellington
+ * $Id: dst_lib.c,v 1.5 2007/06/19 23:47:16 tbox Exp $
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stddef.h>
+
+#include <isc/once.h>
+#include <isc/msgcat.h>
+#include <isc/util.h>
+
+#include <dst/lib.h>
+
+/***
+ *** Globals
+ ***/
+
+LIBDNS_EXTERNAL_DATA isc_msgcat_t * dst_msgcat = NULL;
+
+
+/***
+ *** Private
+ ***/
+
+static isc_once_t msgcat_once = ISC_ONCE_INIT;
+
+
+/***
+ *** Functions
+ ***/
+
+static void
+open_msgcat(void) {
+ isc_msgcat_open("libdst.cat", &dst_msgcat);
+}
+
+void
+dst_lib_initmsgcat(void) {
+
+ /*
+ * Initialize the DST library's message catalog, dst_msgcat, if it
+ * has not already been initialized.
+ */
+
+ RUNTIME_CHECK(isc_once_do(&msgcat_once, open_msgcat) == ISC_R_SUCCESS);
+}
diff --git a/lib/dns/dst_openssl.h b/lib/dns/dst_openssl.h
new file mode 100644
index 0000000..80eef93
--- /dev/null
+++ b/lib/dns/dst_openssl.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: dst_openssl.h,v 1.7 2008/04/01 23:47:10 tbox Exp $ */
+
+#ifndef DST_OPENSSL_H
+#define DST_OPENSSL_H 1
+
+#include <isc/lang.h>
+#include <isc/result.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dst__openssl_toresult(isc_result_t fallback);
+
+ENGINE *
+dst__openssl_getengine(const char *name);
+
+isc_result_t
+dst__openssl_setdefault(const char *name);
+
+ISC_LANG_ENDDECLS
+
+#endif /* DST_OPENSSL_H */
+/*! \file */
diff --git a/lib/dns/dst_parse.c b/lib/dns/dst_parse.c
new file mode 100644
index 0000000..43ac9f3
--- /dev/null
+++ b/lib/dns/dst_parse.c
@@ -0,0 +1,532 @@
+/*
+ * Portions Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 NETWORK ASSOCIATES 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.
+ *
+ * Portions Copyright (C) 1995-2000 by Network Associates, 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 NETWORK ASSOCIATES 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.
+ */
+
+/*%
+ * Principal Author: Brian Wellington
+ * $Id: dst_parse.c,v 1.14 2008/03/31 23:47:11 tbox Exp $
+ */
+
+#include <config.h>
+
+#include <isc/base64.h>
+#include <isc/dir.h>
+#include <isc/fsaccess.h>
+#include <isc/lex.h>
+#include <isc/mem.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include "dst_internal.h"
+#include "dst_parse.h"
+#include "dst/result.h"
+
+#define DST_AS_STR(t) ((t).value.as_textregion.base)
+
+#define PRIVATE_KEY_STR "Private-key-format:"
+#define ALGORITHM_STR "Algorithm:"
+
+struct parse_map {
+ const int value;
+ const char *tag;
+};
+
+static struct parse_map map[] = {
+ {TAG_RSA_MODULUS, "Modulus:"},
+ {TAG_RSA_PUBLICEXPONENT, "PublicExponent:"},
+ {TAG_RSA_PRIVATEEXPONENT, "PrivateExponent:"},
+ {TAG_RSA_PRIME1, "Prime1:"},
+ {TAG_RSA_PRIME2, "Prime2:"},
+ {TAG_RSA_EXPONENT1, "Exponent1:"},
+ {TAG_RSA_EXPONENT2, "Exponent2:"},
+ {TAG_RSA_COEFFICIENT, "Coefficient:"},
+ {TAG_RSA_ENGINE, "Engine:" },
+ {TAG_RSA_LABEL, "Label:" },
+ {TAG_RSA_PIN, "PIN:" },
+
+ {TAG_DH_PRIME, "Prime(p):"},
+ {TAG_DH_GENERATOR, "Generator(g):"},
+ {TAG_DH_PRIVATE, "Private_value(x):"},
+ {TAG_DH_PUBLIC, "Public_value(y):"},
+
+ {TAG_DSA_PRIME, "Prime(p):"},
+ {TAG_DSA_SUBPRIME, "Subprime(q):"},
+ {TAG_DSA_BASE, "Base(g):"},
+ {TAG_DSA_PRIVATE, "Private_value(x):"},
+ {TAG_DSA_PUBLIC, "Public_value(y):"},
+
+ {TAG_HMACMD5_KEY, "Key:"},
+ {TAG_HMACMD5_BITS, "Bits:"},
+
+ {TAG_HMACSHA1_KEY, "Key:"},
+ {TAG_HMACSHA1_BITS, "Bits:"},
+
+ {TAG_HMACSHA224_KEY, "Key:"},
+ {TAG_HMACSHA224_BITS, "Bits:"},
+
+ {TAG_HMACSHA256_KEY, "Key:"},
+ {TAG_HMACSHA256_BITS, "Bits:"},
+
+ {TAG_HMACSHA384_KEY, "Key:"},
+ {TAG_HMACSHA384_BITS, "Bits:"},
+
+ {TAG_HMACSHA512_KEY, "Key:"},
+ {TAG_HMACSHA512_BITS, "Bits:"},
+
+ {0, NULL}
+};
+
+static int
+find_value(const char *s, const unsigned int alg) {
+ int i;
+
+ for (i = 0; ; i++) {
+ if (map[i].tag == NULL)
+ return (-1);
+ else if (strcasecmp(s, map[i].tag) == 0 &&
+ TAG_ALG(map[i].value) == alg)
+ return (map[i].value);
+ }
+}
+
+static const char *
+find_tag(const int value) {
+ int i;
+
+ for (i = 0; ; i++) {
+ if (map[i].tag == NULL)
+ return (NULL);
+ else if (value == map[i].value)
+ return (map[i].tag);
+ }
+}
+
+static int
+check_rsa(const dst_private_t *priv) {
+ int i, j;
+ isc_boolean_t have[RSA_NTAGS];
+ isc_boolean_t ok;
+ unsigned int mask;
+
+ for (i = 0; i < RSA_NTAGS; i++)
+ have[i] = ISC_FALSE;
+ for (j = 0; j < priv->nelements; j++) {
+ for (i = 0; i < RSA_NTAGS; i++)
+ if (priv->elements[j].tag == TAG(DST_ALG_RSAMD5, i))
+ break;
+ if (i == RSA_NTAGS)
+ return (-1);
+ have[i] = ISC_TRUE;
+ }
+
+ mask = ~0;
+ mask <<= sizeof(mask) * 8 - TAG_SHIFT;
+ mask >>= sizeof(mask) * 8 - TAG_SHIFT;
+
+ if (have[TAG_RSA_ENGINE & mask])
+ ok = have[TAG_RSA_MODULUS & mask] &&
+ have[TAG_RSA_PUBLICEXPONENT & mask] &&
+ have[TAG_RSA_LABEL & mask];
+ else
+ ok = have[TAG_RSA_MODULUS & mask] &&
+ have[TAG_RSA_PUBLICEXPONENT & mask] &&
+ have[TAG_RSA_PRIVATEEXPONENT & mask] &&
+ have[TAG_RSA_PRIME1 & mask] &&
+ have[TAG_RSA_PRIME2 & mask] &&
+ have[TAG_RSA_EXPONENT1 & mask] &&
+ have[TAG_RSA_EXPONENT2 & mask] &&
+ have[TAG_RSA_COEFFICIENT & mask];
+ return (ok ? 0 : -1 );
+}
+
+static int
+check_dh(const dst_private_t *priv) {
+ int i, j;
+ if (priv->nelements != DH_NTAGS)
+ return (-1);
+ for (i = 0; i < DH_NTAGS; i++) {
+ for (j = 0; j < priv->nelements; j++)
+ if (priv->elements[j].tag == TAG(DST_ALG_DH, i))
+ break;
+ if (j == priv->nelements)
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+check_dsa(const dst_private_t *priv) {
+ int i, j;
+ if (priv->nelements != DSA_NTAGS)
+ return (-1);
+ for (i = 0; i < DSA_NTAGS; i++) {
+ for (j = 0; j < priv->nelements; j++)
+ if (priv->elements[j].tag == TAG(DST_ALG_DSA, i))
+ break;
+ if (j == priv->nelements)
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+check_hmac_md5(const dst_private_t *priv, isc_boolean_t old) {
+ int i, j;
+
+ if (priv->nelements != HMACMD5_NTAGS) {
+ /*
+ * If this is a good old format and we are accepting
+ * the old format return success.
+ */
+ if (old && priv->nelements == OLD_HMACMD5_NTAGS &&
+ priv->elements[0].tag == TAG_HMACMD5_KEY)
+ return (0);
+ return (-1);
+ }
+ /*
+ * We must be new format at this point.
+ */
+ for (i = 0; i < HMACMD5_NTAGS; i++) {
+ for (j = 0; j < priv->nelements; j++)
+ if (priv->elements[j].tag == TAG(DST_ALG_HMACMD5, i))
+ break;
+ if (j == priv->nelements)
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+check_hmac_sha(const dst_private_t *priv, unsigned int ntags,
+ unsigned int alg)
+{
+ unsigned int i, j;
+ if (priv->nelements != ntags)
+ return (-1);
+ for (i = 0; i < ntags; i++) {
+ for (j = 0; j < priv->nelements; j++)
+ if (priv->elements[j].tag == TAG(alg, i))
+ break;
+ if (j == priv->nelements)
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+check_data(const dst_private_t *priv, const unsigned int alg,
+ isc_boolean_t old)
+{
+ /* XXXVIX this switch statement is too sparse to gen a jump table. */
+ switch (alg) {
+ case DST_ALG_RSAMD5:
+ case DST_ALG_RSASHA1:
+ return (check_rsa(priv));
+ case DST_ALG_DH:
+ return (check_dh(priv));
+ case DST_ALG_DSA:
+ return (check_dsa(priv));
+ case DST_ALG_HMACMD5:
+ return (check_hmac_md5(priv, old));
+ case DST_ALG_HMACSHA1:
+ return (check_hmac_sha(priv, HMACSHA1_NTAGS, alg));
+ case DST_ALG_HMACSHA224:
+ return (check_hmac_sha(priv, HMACSHA224_NTAGS, alg));
+ case DST_ALG_HMACSHA256:
+ return (check_hmac_sha(priv, HMACSHA256_NTAGS, alg));
+ case DST_ALG_HMACSHA384:
+ return (check_hmac_sha(priv, HMACSHA384_NTAGS, alg));
+ case DST_ALG_HMACSHA512:
+ return (check_hmac_sha(priv, HMACSHA512_NTAGS, alg));
+ default:
+ return (DST_R_UNSUPPORTEDALG);
+ }
+}
+
+void
+dst__privstruct_free(dst_private_t *priv, isc_mem_t *mctx) {
+ int i;
+
+ if (priv == NULL)
+ return;
+ for (i = 0; i < priv->nelements; i++) {
+ if (priv->elements[i].data == NULL)
+ continue;
+ memset(priv->elements[i].data, 0, MAXFIELDSIZE);
+ isc_mem_put(mctx, priv->elements[i].data, MAXFIELDSIZE);
+ }
+ priv->nelements = 0;
+}
+
+int
+dst__privstruct_parse(dst_key_t *key, unsigned int alg, isc_lex_t *lex,
+ isc_mem_t *mctx, dst_private_t *priv)
+{
+ int n = 0, major, minor;
+ isc_buffer_t b;
+ isc_token_t token;
+ unsigned char *data = NULL;
+ unsigned int opt = ISC_LEXOPT_EOL;
+ isc_result_t ret;
+
+ REQUIRE(priv != NULL);
+
+ priv->nelements = 0;
+ memset(priv->elements, 0, sizeof(priv->elements));
+
+#define NEXTTOKEN(lex, opt, token) \
+ do { \
+ ret = isc_lex_gettoken(lex, opt, token); \
+ if (ret != ISC_R_SUCCESS) \
+ goto fail; \
+ } while (0)
+
+#define READLINE(lex, opt, token) \
+ do { \
+ ret = isc_lex_gettoken(lex, opt, token); \
+ if (ret == ISC_R_EOF) \
+ break; \
+ else if (ret != ISC_R_SUCCESS) \
+ goto fail; \
+ } while ((*token).type != isc_tokentype_eol)
+
+ /*
+ * Read the description line.
+ */
+ NEXTTOKEN(lex, opt, &token);
+ if (token.type != isc_tokentype_string ||
+ strcmp(DST_AS_STR(token), PRIVATE_KEY_STR) != 0)
+ {
+ ret = DST_R_INVALIDPRIVATEKEY;
+ goto fail;
+ }
+
+ NEXTTOKEN(lex, opt, &token);
+ if (token.type != isc_tokentype_string ||
+ (DST_AS_STR(token))[0] != 'v')
+ {
+ ret = DST_R_INVALIDPRIVATEKEY;
+ goto fail;
+ }
+ if (sscanf(DST_AS_STR(token), "v%d.%d", &major, &minor) != 2)
+ {
+ ret = DST_R_INVALIDPRIVATEKEY;
+ goto fail;
+ }
+
+ if (major > MAJOR_VERSION ||
+ (major == MAJOR_VERSION && minor > MINOR_VERSION))
+ {
+ ret = DST_R_INVALIDPRIVATEKEY;
+ goto fail;
+ }
+
+ READLINE(lex, opt, &token);
+
+ /*
+ * Read the algorithm line.
+ */
+ NEXTTOKEN(lex, opt, &token);
+ if (token.type != isc_tokentype_string ||
+ strcmp(DST_AS_STR(token), ALGORITHM_STR) != 0)
+ {
+ ret = DST_R_INVALIDPRIVATEKEY;
+ goto fail;
+ }
+
+ NEXTTOKEN(lex, opt | ISC_LEXOPT_NUMBER, &token);
+ if (token.type != isc_tokentype_number ||
+ token.value.as_ulong != (unsigned long) dst_key_alg(key))
+ {
+ ret = DST_R_INVALIDPRIVATEKEY;
+ goto fail;
+ }
+
+ READLINE(lex, opt, &token);
+
+ /*
+ * Read the key data.
+ */
+ for (n = 0; n < MAXFIELDS; n++) {
+ int tag;
+ isc_region_t r;
+
+ do {
+ ret = isc_lex_gettoken(lex, opt, &token);
+ if (ret == ISC_R_EOF)
+ goto done;
+ if (ret != ISC_R_SUCCESS)
+ goto fail;
+ } while (token.type == isc_tokentype_eol);
+
+ if (token.type != isc_tokentype_string) {
+ ret = DST_R_INVALIDPRIVATEKEY;
+ goto fail;
+ }
+
+ tag = find_value(DST_AS_STR(token), alg);
+ if (tag < 0 || TAG_ALG(tag) != alg) {
+ ret = DST_R_INVALIDPRIVATEKEY;
+ goto fail;
+ }
+ priv->elements[n].tag = tag;
+
+ data = (unsigned char *) isc_mem_get(mctx, MAXFIELDSIZE);
+ if (data == NULL)
+ goto fail;
+
+ isc_buffer_init(&b, data, MAXFIELDSIZE);
+ ret = isc_base64_tobuffer(lex, &b, -1);
+ if (ret != ISC_R_SUCCESS)
+ goto fail;
+ isc_buffer_usedregion(&b, &r);
+ priv->elements[n].length = r.length;
+ priv->elements[n].data = r.base;
+
+ READLINE(lex, opt, &token);
+ data = NULL;
+ }
+ done:
+ priv->nelements = n;
+
+ if (check_data(priv, alg, ISC_TRUE) < 0)
+ goto fail;
+
+ return (ISC_R_SUCCESS);
+
+fail:
+ priv->nelements = n;
+ dst__privstruct_free(priv, mctx);
+ if (data != NULL)
+ isc_mem_put(mctx, data, MAXFIELDSIZE);
+
+ return (ret);
+}
+
+int
+dst__privstruct_writefile(const dst_key_t *key, const dst_private_t *priv,
+ const char *directory)
+{
+ FILE *fp;
+ int ret, i;
+ isc_result_t iret;
+ char filename[ISC_DIR_NAMEMAX];
+ char buffer[MAXFIELDSIZE * 2];
+ isc_buffer_t b;
+ isc_fsaccess_t access;
+
+ REQUIRE(priv != NULL);
+
+ if (check_data(priv, dst_key_alg(key), ISC_FALSE) < 0)
+ return (DST_R_INVALIDPRIVATEKEY);
+
+ isc_buffer_init(&b, filename, sizeof(filename));
+ ret = dst_key_buildfilename(key, DST_TYPE_PRIVATE, directory, &b);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ if ((fp = fopen(filename, "w")) == NULL)
+ return (DST_R_WRITEERROR);
+
+ access = 0;
+ isc_fsaccess_add(ISC_FSACCESS_OWNER,
+ ISC_FSACCESS_READ | ISC_FSACCESS_WRITE,
+ &access);
+ (void)isc_fsaccess_set(filename, access);
+
+ /* XXXDCL return value should be checked for full filesystem */
+ fprintf(fp, "%s v%d.%d\n", PRIVATE_KEY_STR, MAJOR_VERSION,
+ MINOR_VERSION);
+
+ fprintf(fp, "%s %d ", ALGORITHM_STR, dst_key_alg(key));
+ /* XXXVIX this switch statement is too sparse to gen a jump table. */
+ switch (dst_key_alg(key)) {
+ case DST_ALG_RSAMD5:
+ fprintf(fp, "(RSA)\n");
+ break;
+ case DST_ALG_DH:
+ fprintf(fp, "(DH)\n");
+ break;
+ case DST_ALG_DSA:
+ fprintf(fp, "(DSA)\n");
+ break;
+ case DST_ALG_RSASHA1:
+ fprintf(fp, "(RSASHA1)\n");
+ break;
+ case DST_ALG_HMACMD5:
+ fprintf(fp, "(HMAC_MD5)\n");
+ break;
+ case DST_ALG_HMACSHA1:
+ fprintf(fp, "(HMAC_SHA1)\n");
+ break;
+ case DST_ALG_HMACSHA224:
+ fprintf(fp, "(HMAC_SHA224)\n");
+ break;
+ case DST_ALG_HMACSHA256:
+ fprintf(fp, "(HMAC_SHA256)\n");
+ break;
+ case DST_ALG_HMACSHA384:
+ fprintf(fp, "(HMAC_SHA384)\n");
+ break;
+ case DST_ALG_HMACSHA512:
+ fprintf(fp, "(HMAC_SHA512)\n");
+ break;
+ default:
+ fprintf(fp, "(?)\n");
+ break;
+ }
+
+ for (i = 0; i < priv->nelements; i++) {
+ isc_buffer_t b;
+ isc_region_t r;
+ const char *s;
+
+ s = find_tag(priv->elements[i].tag);
+
+ r.base = priv->elements[i].data;
+ r.length = priv->elements[i].length;
+ isc_buffer_init(&b, buffer, sizeof(buffer));
+ iret = isc_base64_totext(&r, sizeof(buffer), "", &b);
+ if (iret != ISC_R_SUCCESS) {
+ fclose(fp);
+ return (DST_R_INVALIDPRIVATEKEY);
+ }
+ isc_buffer_usedregion(&b, &r);
+
+ fprintf(fp, "%s ", s);
+ fwrite(r.base, 1, r.length, fp);
+ fprintf(fp, "\n");
+ }
+
+ fclose(fp);
+ return (ISC_R_SUCCESS);
+}
+
+/*! \file */
diff --git a/lib/dns/dst_parse.h b/lib/dns/dst_parse.h
new file mode 100644
index 0000000..27c7580
--- /dev/null
+++ b/lib/dns/dst_parse.h
@@ -0,0 +1,134 @@
+/*
+ * Portions Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (C) 2000-2002 Internet Software Consortium.
+ *
+ * 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 NETWORK ASSOCIATES 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.
+ *
+ * Portions Copyright (C) 1995-2000 by Network Associates, 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 NETWORK ASSOCIATES 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.
+ */
+
+/* $Id: dst_parse.h,v 1.11 2008/05/15 00:50:26 each Exp $ */
+
+/*! \file */
+#ifndef DST_DST_PARSE_H
+#define DST_DST_PARSE_H 1
+
+#include <isc/lang.h>
+
+#include <dst/dst.h>
+
+#define MAJOR_VERSION 1
+#define MINOR_VERSION 2
+
+#define MAXFIELDSIZE 512
+#define MAXFIELDS 12
+
+#define TAG_SHIFT 4
+#define TAG_ALG(tag) ((unsigned int)(tag) >> TAG_SHIFT)
+#define TAG(alg, off) (((alg) << TAG_SHIFT) + (off))
+
+/* These are used by both RSA-MD5 and RSA-SHA1 */
+#define RSA_NTAGS 11
+#define TAG_RSA_MODULUS ((DST_ALG_RSAMD5 << TAG_SHIFT) + 0)
+#define TAG_RSA_PUBLICEXPONENT ((DST_ALG_RSAMD5 << TAG_SHIFT) + 1)
+#define TAG_RSA_PRIVATEEXPONENT ((DST_ALG_RSAMD5 << TAG_SHIFT) + 2)
+#define TAG_RSA_PRIME1 ((DST_ALG_RSAMD5 << TAG_SHIFT) + 3)
+#define TAG_RSA_PRIME2 ((DST_ALG_RSAMD5 << TAG_SHIFT) + 4)
+#define TAG_RSA_EXPONENT1 ((DST_ALG_RSAMD5 << TAG_SHIFT) + 5)
+#define TAG_RSA_EXPONENT2 ((DST_ALG_RSAMD5 << TAG_SHIFT) + 6)
+#define TAG_RSA_COEFFICIENT ((DST_ALG_RSAMD5 << TAG_SHIFT) + 7)
+#define TAG_RSA_ENGINE ((DST_ALG_RSAMD5 << TAG_SHIFT) + 8)
+#define TAG_RSA_LABEL ((DST_ALG_RSAMD5 << TAG_SHIFT) + 9)
+#define TAG_RSA_PIN ((DST_ALG_RSAMD5 << TAG_SHIFT) + 10)
+
+#define DH_NTAGS 4
+#define TAG_DH_PRIME ((DST_ALG_DH << TAG_SHIFT) + 0)
+#define TAG_DH_GENERATOR ((DST_ALG_DH << TAG_SHIFT) + 1)
+#define TAG_DH_PRIVATE ((DST_ALG_DH << TAG_SHIFT) + 2)
+#define TAG_DH_PUBLIC ((DST_ALG_DH << TAG_SHIFT) + 3)
+
+#define DSA_NTAGS 5
+#define TAG_DSA_PRIME ((DST_ALG_DSA << TAG_SHIFT) + 0)
+#define TAG_DSA_SUBPRIME ((DST_ALG_DSA << TAG_SHIFT) + 1)
+#define TAG_DSA_BASE ((DST_ALG_DSA << TAG_SHIFT) + 2)
+#define TAG_DSA_PRIVATE ((DST_ALG_DSA << TAG_SHIFT) + 3)
+#define TAG_DSA_PUBLIC ((DST_ALG_DSA << TAG_SHIFT) + 4)
+
+#define OLD_HMACMD5_NTAGS 1
+#define HMACMD5_NTAGS 2
+#define TAG_HMACMD5_KEY ((DST_ALG_HMACMD5 << TAG_SHIFT) + 0)
+#define TAG_HMACMD5_BITS ((DST_ALG_HMACMD5 << TAG_SHIFT) + 1)
+
+#define HMACSHA1_NTAGS 2
+#define TAG_HMACSHA1_KEY ((DST_ALG_HMACSHA1 << TAG_SHIFT) + 0)
+#define TAG_HMACSHA1_BITS ((DST_ALG_HMACSHA1 << TAG_SHIFT) + 1)
+
+#define HMACSHA224_NTAGS 2
+#define TAG_HMACSHA224_KEY ((DST_ALG_HMACSHA224 << TAG_SHIFT) + 0)
+#define TAG_HMACSHA224_BITS ((DST_ALG_HMACSHA224 << TAG_SHIFT) + 1)
+
+#define HMACSHA256_NTAGS 2
+#define TAG_HMACSHA256_KEY ((DST_ALG_HMACSHA256 << TAG_SHIFT) + 0)
+#define TAG_HMACSHA256_BITS ((DST_ALG_HMACSHA256 << TAG_SHIFT) + 1)
+
+#define HMACSHA384_NTAGS 2
+#define TAG_HMACSHA384_KEY ((DST_ALG_HMACSHA384 << TAG_SHIFT) + 0)
+#define TAG_HMACSHA384_BITS ((DST_ALG_HMACSHA384 << TAG_SHIFT) + 1)
+
+#define HMACSHA512_NTAGS 2
+#define TAG_HMACSHA512_KEY ((DST_ALG_HMACSHA512 << TAG_SHIFT) + 0)
+#define TAG_HMACSHA512_BITS ((DST_ALG_HMACSHA512 << TAG_SHIFT) + 1)
+
+struct dst_private_element {
+ unsigned short tag;
+ unsigned short length;
+ unsigned char *data;
+};
+
+typedef struct dst_private_element dst_private_element_t;
+
+struct dst_private {
+ unsigned short nelements;
+ dst_private_element_t elements[MAXFIELDS];
+};
+
+typedef struct dst_private dst_private_t;
+
+ISC_LANG_BEGINDECLS
+
+void
+dst__privstruct_free(dst_private_t *priv, isc_mem_t *mctx);
+
+int
+dst__privstruct_parse(dst_key_t *key, unsigned int alg, isc_lex_t *lex,
+ isc_mem_t *mctx, dst_private_t *priv);
+
+int
+dst__privstruct_writefile(const dst_key_t *key, const dst_private_t *priv,
+ const char *directory);
+
+ISC_LANG_ENDDECLS
+
+#endif /* DST_DST_PARSE_H */
diff --git a/lib/dns/dst_result.c b/lib/dns/dst_result.c
new file mode 100644
index 0000000..429dbb2
--- /dev/null
+++ b/lib/dns/dst_result.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/*%
+ * Principal Author: Brian Wellington
+ * $Id: dst_result.c,v 1.7 2008/04/01 23:47:10 tbox Exp $
+ */
+
+#include <config.h>
+
+#include <isc/once.h>
+#include <isc/util.h>
+
+#include <dst/result.h>
+#include <dst/lib.h>
+
+static const char *text[DST_R_NRESULTS] = {
+ "algorithm is unsupported", /*%< 0 */
+ "openssl failure", /*%< 1 */
+ "built with no crypto support", /*%< 2 */
+ "illegal operation for a null key", /*%< 3 */
+ "public key is invalid", /*%< 4 */
+ "private key is invalid", /*%< 5 */
+ "UNUSED6", /*%< 6 */
+ "error occurred writing key to disk", /*%< 7 */
+ "invalid algorithm specific parameter", /*%< 8 */
+ "UNUSED9", /*%< 9 */
+ "UNUSED10", /*%< 10 */
+ "sign failure", /*%< 11 */
+ "UNUSED12", /*%< 12 */
+ "UNUSED13", /*%< 13 */
+ "verify failure", /*%< 14 */
+ "not a public key", /*%< 15 */
+ "not a private key", /*%< 16 */
+ "not a key that can compute a secret", /*%< 17 */
+ "failure computing a shared secret", /*%< 18 */
+ "no randomness available", /*%< 19 */
+ "bad key type", /*%< 20 */
+ "no engine" /*%< 21 */
+};
+
+#define DST_RESULT_RESULTSET 2
+
+static isc_once_t once = ISC_ONCE_INIT;
+
+static void
+initialize_action(void) {
+ isc_result_t result;
+
+ result = isc_result_register(ISC_RESULTCLASS_DST, DST_R_NRESULTS,
+ text, dst_msgcat, DST_RESULT_RESULTSET);
+ if (result != ISC_R_SUCCESS)
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_result_register() failed: %u", result);
+}
+
+static void
+initialize(void) {
+ dst_lib_initmsgcat();
+ RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
+}
+
+const char *
+dst_result_totext(isc_result_t result) {
+ initialize();
+
+ return (isc_result_totext(result));
+}
+
+void
+dst_result_register(void) {
+ initialize();
+}
+
+/*! \file */
diff --git a/lib/dns/forward.c b/lib/dns/forward.c
new file mode 100644
index 0000000..39e2ef5
--- /dev/null
+++ b/lib/dns/forward.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: forward.c,v 1.12 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/rwlock.h>
+#include <isc/sockaddr.h>
+#include <isc/util.h>
+
+#include <dns/forward.h>
+#include <dns/rbt.h>
+#include <dns/result.h>
+#include <dns/types.h>
+
+struct dns_fwdtable {
+ /* Unlocked. */
+ unsigned int magic;
+ isc_mem_t *mctx;
+ isc_rwlock_t rwlock;
+ /* Locked by lock. */
+ dns_rbt_t *table;
+};
+
+#define FWDTABLEMAGIC ISC_MAGIC('F', 'w', 'd', 'T')
+#define VALID_FWDTABLE(ft) ISC_MAGIC_VALID(ft, FWDTABLEMAGIC)
+
+static void
+auto_detach(void *, void *);
+
+isc_result_t
+dns_fwdtable_create(isc_mem_t *mctx, dns_fwdtable_t **fwdtablep) {
+ dns_fwdtable_t *fwdtable;
+ isc_result_t result;
+
+ REQUIRE(fwdtablep != NULL && *fwdtablep == NULL);
+
+ fwdtable = isc_mem_get(mctx, sizeof(dns_fwdtable_t));
+ if (fwdtable == NULL)
+ return (ISC_R_NOMEMORY);
+
+ fwdtable->table = NULL;
+ result = dns_rbt_create(mctx, auto_detach, fwdtable, &fwdtable->table);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_fwdtable;
+
+ result = isc_rwlock_init(&fwdtable->rwlock, 0, 0);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_rbt;
+
+ fwdtable->mctx = NULL;
+ isc_mem_attach(mctx, &fwdtable->mctx);
+ fwdtable->magic = FWDTABLEMAGIC;
+ *fwdtablep = fwdtable;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup_rbt:
+ dns_rbt_destroy(&fwdtable->table);
+
+ cleanup_fwdtable:
+ isc_mem_put(mctx, fwdtable, sizeof(dns_fwdtable_t));
+
+ return (result);
+}
+
+isc_result_t
+dns_fwdtable_add(dns_fwdtable_t *fwdtable, dns_name_t *name,
+ isc_sockaddrlist_t *addrs, dns_fwdpolicy_t fwdpolicy)
+{
+ isc_result_t result;
+ dns_forwarders_t *forwarders;
+ isc_sockaddr_t *sa, *nsa;
+
+ REQUIRE(VALID_FWDTABLE(fwdtable));
+
+ forwarders = isc_mem_get(fwdtable->mctx, sizeof(dns_forwarders_t));
+ if (forwarders == NULL)
+ return (ISC_R_NOMEMORY);
+
+ ISC_LIST_INIT(forwarders->addrs);
+ for (sa = ISC_LIST_HEAD(*addrs);
+ sa != NULL;
+ sa = ISC_LIST_NEXT(sa, link))
+ {
+ nsa = isc_mem_get(fwdtable->mctx, sizeof(isc_sockaddr_t));
+ if (nsa == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ *nsa = *sa;
+ ISC_LINK_INIT(nsa, link);
+ ISC_LIST_APPEND(forwarders->addrs, nsa, link);
+ }
+ forwarders->fwdpolicy = fwdpolicy;
+
+ RWLOCK(&fwdtable->rwlock, isc_rwlocktype_write);
+ result = dns_rbt_addname(fwdtable->table, name, forwarders);
+ RWUNLOCK(&fwdtable->rwlock, isc_rwlocktype_write);
+
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ while (!ISC_LIST_EMPTY(forwarders->addrs)) {
+ sa = ISC_LIST_HEAD(forwarders->addrs);
+ ISC_LIST_UNLINK(forwarders->addrs, sa, link);
+ isc_mem_put(fwdtable->mctx, sa, sizeof(isc_sockaddr_t));
+ }
+ isc_mem_put(fwdtable->mctx, forwarders, sizeof(dns_forwarders_t));
+ return (result);
+}
+
+isc_result_t
+dns_fwdtable_find(dns_fwdtable_t *fwdtable, dns_name_t *name,
+ dns_forwarders_t **forwardersp)
+{
+ return (dns_fwdtable_find2(fwdtable, name, NULL, forwardersp));
+}
+
+isc_result_t
+dns_fwdtable_find2(dns_fwdtable_t *fwdtable, dns_name_t *name,
+ dns_name_t *foundname, dns_forwarders_t **forwardersp)
+{
+ isc_result_t result;
+
+ REQUIRE(VALID_FWDTABLE(fwdtable));
+
+ RWLOCK(&fwdtable->rwlock, isc_rwlocktype_read);
+
+ result = dns_rbt_findname(fwdtable->table, name, 0, foundname,
+ (void **)forwardersp);
+ if (result == DNS_R_PARTIALMATCH)
+ result = ISC_R_SUCCESS;
+
+ RWUNLOCK(&fwdtable->rwlock, isc_rwlocktype_read);
+
+ return (result);
+}
+
+void
+dns_fwdtable_destroy(dns_fwdtable_t **fwdtablep) {
+ dns_fwdtable_t *fwdtable;
+ isc_mem_t *mctx;
+
+ REQUIRE(fwdtablep != NULL && VALID_FWDTABLE(*fwdtablep));
+
+ fwdtable = *fwdtablep;
+
+ dns_rbt_destroy(&fwdtable->table);
+ isc_rwlock_destroy(&fwdtable->rwlock);
+ fwdtable->magic = 0;
+ mctx = fwdtable->mctx;
+ isc_mem_put(mctx, fwdtable, sizeof(dns_fwdtable_t));
+ isc_mem_detach(&mctx);
+
+ *fwdtablep = NULL;
+}
+
+/***
+ *** Private
+ ***/
+
+static void
+auto_detach(void *data, void *arg) {
+ dns_forwarders_t *forwarders = data;
+ dns_fwdtable_t *fwdtable = arg;
+ isc_sockaddr_t *sa;
+
+ UNUSED(arg);
+
+ while (!ISC_LIST_EMPTY(forwarders->addrs)) {
+ sa = ISC_LIST_HEAD(forwarders->addrs);
+ ISC_LIST_UNLINK(forwarders->addrs, sa, link);
+ isc_mem_put(fwdtable->mctx, sa, sizeof(isc_sockaddr_t));
+ }
+ isc_mem_put(fwdtable->mctx, forwarders, sizeof(dns_forwarders_t));
+}
diff --git a/lib/dns/gen-unix.h b/lib/dns/gen-unix.h
new file mode 100644
index 0000000..3db6206
--- /dev/null
+++ b/lib/dns/gen-unix.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: gen-unix.h,v 1.19 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file
+ * \brief
+ * This file is responsible for defining two operations that are not
+ * directly portable between Unix-like systems and Windows NT, option
+ * parsing and directory scanning. It is here because it was decided
+ * that the "gen" build utility was not to depend on libisc.a, so
+ * the functions delcared in isc/commandline.h and isc/dir.h could not
+ * be used.
+ *
+ * The commandline stuff is really just a wrapper around getopt().
+ * The dir stuff was shrunk to fit the needs of gen.c.
+ */
+
+#ifndef DNS_GEN_UNIX_H
+#define DNS_GEN_UNIX_H 1
+
+#include <sys/types.h> /* Required on some systems for dirent.h. */
+
+#include <dirent.h>
+#include <unistd.h> /* XXXDCL Required for ?. */
+
+#include <isc/boolean.h>
+#include <isc/lang.h>
+
+#ifdef NEED_OPTARG
+extern char *optarg;
+#endif
+
+#define isc_commandline_parse getopt
+#define isc_commandline_argument optarg
+
+typedef struct {
+ DIR *handle;
+ char *filename;
+} isc_dir_t;
+
+ISC_LANG_BEGINDECLS
+
+static isc_boolean_t
+start_directory(const char *path, isc_dir_t *dir) {
+ dir->handle = opendir(path);
+
+ if (dir->handle != NULL)
+ return (ISC_TRUE);
+ else
+ return (ISC_FALSE);
+
+}
+
+static isc_boolean_t
+next_file(isc_dir_t *dir) {
+ struct dirent *dirent;
+
+ dir->filename = NULL;
+
+ if (dir->handle != NULL) {
+ dirent = readdir(dir->handle);
+ if (dirent != NULL)
+ dir->filename = dirent->d_name;
+ }
+
+ if (dir->filename != NULL)
+ return (ISC_TRUE);
+ else
+ return (ISC_FALSE);
+}
+
+static void
+end_directory(isc_dir_t *dir) {
+ if (dir->handle != NULL)
+ (void)closedir(dir->handle);
+
+ dir->handle = NULL;
+}
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_GEN_UNIX_H */
diff --git a/lib/dns/gen-win32.h b/lib/dns/gen-win32.h
new file mode 100644
index 0000000..4b21a31
--- /dev/null
+++ b/lib/dns/gen-win32.h
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/*
+ * Copyright (c) 1987, 1993, 1994
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+/* $Id: gen-win32.h,v 1.23 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file
+ * \author Principal Authors: Computer Systems Research Group at UC Berkeley
+ * \author Principal ISC caretaker: DCL
+ */
+
+/*
+ * \note 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.
+ *
+ * This file is responsible for defining two operations that are not
+ * directly portable between Unix-like systems and Windows NT, option
+ * parsing and directory scanning. It is here because it was decided
+ * that the "gen" build utility was not to depend on libisc.a, so
+ * the functions delcared in isc/commandline.h and isc/dir.h could not
+ * be used.
+ *
+ * The commandline stuff is pretty much a straight copy from the initial
+ * isc/commandline.c. The dir stuff was shrunk to fit the needs of gen.c.
+ */
+
+#ifndef DNS_GEN_WIN32_H
+#define DNS_GEN_WIN32_H 1
+
+#include <stdio.h>
+#include <string.h>
+#include <windows.h>
+
+#include <isc/boolean.h>
+#include <isc/lang.h>
+
+int isc_commandline_index = 1; /* Index into parent argv vector. */
+int isc_commandline_option; /* Character checked for validity. */
+
+char *isc_commandline_argument; /* Argument associated with option. */
+char *isc_commandline_progname; /* For printing error messages. */
+
+isc_boolean_t isc_commandline_errprint = ISC_TRUE; /* Print error messages. */
+isc_boolean_t isc_commandline_reset = ISC_TRUE; /* Reset processing. */
+
+#define BADOPT '?'
+#define BADARG ':'
+#define ENDOPT ""
+
+ISC_LANG_BEGINDECLS
+
+/*
+ * getopt --
+ * Parse argc/argv argument vector.
+ */
+int
+isc_commandline_parse(int argc, char * const *argv, const char *options) {
+ static char *place = ENDOPT;
+ char *option; /* Index into *options of option. */
+
+ /*
+ * Update scanning pointer, either because a reset was requested or
+ * the previous argv was finished.
+ */
+ if (isc_commandline_reset || *place == '\0') {
+ isc_commandline_reset = ISC_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);
+}
+
+typedef struct {
+ HANDLE handle;
+ WIN32_FIND_DATA find_data;
+ isc_boolean_t first_file;
+ char *filename;
+} isc_dir_t;
+
+isc_boolean_t
+start_directory(const char *path, isc_dir_t *dir) {
+ char pattern[_MAX_PATH], *p;
+
+ /*
+ * Need space for slash-splat and final NUL.
+ */
+ if (strlen(path) + 3 > sizeof(pattern))
+ return (ISC_FALSE);
+
+ strcpy(pattern, path);
+
+ /*
+ * Append slash (if needed) and splat.
+ */
+ p = pattern + strlen(pattern);
+ if (p != pattern && p[-1] != '\\' && p[-1] != ':')
+ *p++ = '\\';
+ *p++ = '*';
+ *p++ = '\0';
+
+ dir->first_file = ISC_TRUE;
+
+ dir->handle = FindFirstFile(pattern, &dir->find_data);
+
+ if (dir->handle == INVALID_HANDLE_VALUE) {
+ dir->filename = NULL;
+ return (ISC_FALSE);
+ } else {
+ dir->filename = dir->find_data.cFileName;
+ return (ISC_TRUE);
+ }
+}
+
+isc_boolean_t
+next_file(isc_dir_t *dir) {
+ if (dir->first_file)
+ dir->first_file = ISC_FALSE;
+
+ else if (dir->handle != INVALID_HANDLE_VALUE) {
+ if (FindNextFile(dir->handle, &dir->find_data) == TRUE)
+ dir->filename = dir->find_data.cFileName;
+ else
+ dir->filename = NULL;
+
+ } else
+ dir->filename = NULL;
+
+ if (dir->filename != NULL)
+ return (ISC_TRUE);
+ else
+ return (ISC_FALSE);
+}
+
+void
+end_directory(isc_dir_t *dir) {
+ if (dir->handle != INVALID_HANDLE_VALUE)
+ FindClose(dir->handle);
+}
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_GEN_WIN32_H */
diff --git a/lib/dns/gen.c b/lib/dns/gen.c
new file mode 100644
index 0000000..ede8bc0
--- /dev/null
+++ b/lib/dns/gen.c
@@ -0,0 +1,888 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: gen.c,v 1.83 2008/09/25 04:02:38 tbox Exp $ */
+
+/*! \file */
+
+#ifdef WIN32
+/*
+ * Silence compiler warnings about using strcpy and friends.
+ */
+#define _CRT_SECURE_NO_DEPRECATE 1
+#endif
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifdef WIN32
+#include "gen-win32.h"
+#else
+#include "gen-unix.h"
+#endif
+
+#define TYPECLASSLEN 21
+
+#define FROMTEXTARGS "rdclass, type, lexer, origin, options, target, callbacks"
+#define FROMTEXTCLASS "rdclass"
+#define FROMTEXTTYPE "type"
+#define FROMTEXTDEF "result = DNS_R_UNKNOWN"
+
+#define TOTEXTARGS "rdata, tctx, target"
+#define TOTEXTCLASS "rdata->rdclass"
+#define TOTEXTTYPE "rdata->type"
+#define TOTEXTDEF "use_default = ISC_TRUE"
+
+#define FROMWIREARGS "rdclass, type, source, dctx, options, target"
+#define FROMWIRECLASS "rdclass"
+#define FROMWIRETYPE "type"
+#define FROMWIREDEF "use_default = ISC_TRUE"
+
+#define TOWIREARGS "rdata, cctx, target"
+#define TOWIRECLASS "rdata->rdclass"
+#define TOWIRETYPE "rdata->type"
+#define TOWIREDEF "use_default = ISC_TRUE"
+
+#define FROMSTRUCTARGS "rdclass, type, source, target"
+#define FROMSTRUCTCLASS "rdclass"
+#define FROMSTRUCTTYPE "type"
+#define FROMSTRUCTDEF "use_default = ISC_TRUE"
+
+#define TOSTRUCTARGS "rdata, target, mctx"
+#define TOSTRUCTCLASS "rdata->rdclass"
+#define TOSTRUCTTYPE "rdata->type"
+#define TOSTRUCTDEF "use_default = ISC_TRUE"
+
+#define FREESTRUCTARGS "source"
+#define FREESTRUCTCLASS "common->rdclass"
+#define FREESTRUCTTYPE "common->rdtype"
+#define FREESTRUCTDEF NULL
+
+#define COMPAREARGS "rdata1, rdata2"
+#define COMPARECLASS "rdata1->rdclass"
+#define COMPARETYPE "rdata1->type"
+#define COMPAREDEF "use_default = ISC_TRUE"
+
+#define ADDITIONALDATAARGS "rdata, add, arg"
+#define ADDITIONALDATACLASS "rdata->rdclass"
+#define ADDITIONALDATATYPE "rdata->type"
+#define ADDITIONALDATADEF "use_default = ISC_TRUE"
+
+#define DIGESTARGS "rdata, digest, arg"
+#define DIGESTCLASS "rdata->rdclass"
+#define DIGESTTYPE "rdata->type"
+#define DIGESTDEF "use_default = ISC_TRUE"
+
+#define CHECKOWNERARGS "name, rdclass, type, wildcard"
+#define CHECKOWNERCLASS "rdclass"
+#define CHECKOWNERTYPE "type"
+#define CHECKOWNERDEF "result = ISC_TRUE"
+
+#define CHECKNAMESARGS "rdata, owner, bad"
+#define CHECKNAMESCLASS "rdata->rdclass"
+#define CHECKNAMESTYPE "rdata->type"
+#define CHECKNAMESDEF "result = ISC_TRUE"
+
+const char copyright[] =
+"/*\n"
+" * Copyright (C) 2004%s Internet Systems Consortium, Inc. (\"ISC\")\n"
+" * Copyright (C) 1998-2003 Internet Software Consortium.\n"
+" *\n"
+" * Permission to use, copy, modify, and distribute this software for any\n"
+" * purpose with or without fee is hereby granted, provided that the above\n"
+" * copyright notice and this permission notice appear in all copies.\n"
+" *\n"
+" * THE SOFTWARE IS PROVIDED \"AS IS\" AND ISC DISCLAIMS ALL WARRANTIES WITH\n"
+" * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\n"
+" * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,\n"
+" * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\n"
+" * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE\n"
+" * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n"
+" * PERFORMANCE OF THIS SOFTWARE.\n"
+" */\n"
+"\n"
+"/***************\n"
+" ***************\n"
+" *************** THIS FILE IS AUTOMATICALLY GENERATED BY gen.c.\n"
+" *************** DO NOT EDIT!\n"
+" ***************\n"
+" ***************/\n"
+"\n"
+"/*! \\file */\n"
+"\n";
+
+#define TYPENAMES 256
+
+struct cc {
+ struct cc *next;
+ int rdclass;
+ char classname[TYPECLASSLEN];
+} *classes;
+
+struct tt {
+ struct tt *next;
+ int rdclass;
+ int type;
+ char classname[TYPECLASSLEN];
+ char typename[TYPECLASSLEN];
+ char dirname[256]; /* XXX Should be max path length */
+} *types;
+
+struct ttnam {
+ char typename[TYPECLASSLEN];
+ char macroname[TYPECLASSLEN];
+ char attr[256];
+ unsigned int sorted;
+ int type;
+} typenames[TYPENAMES];
+
+int maxtype = -1;
+
+char *
+upper(char *);
+char *
+funname(const char *, char *);
+void
+doswitch(const char *, const char *, const char *, const char *,
+ const char *, const char *);
+void
+dodecl(char *, char *, char *);
+void
+add(int, const char *, int, const char *, const char *);
+void
+sd(int, const char *, const char *, char);
+void
+insert_into_typenames(int, const char *, const char *);
+
+/*%
+ * If you use more than 10 of these in, say, a printf(), you'll have problems.
+ */
+char *
+upper(char *s) {
+ static int buf_to_use = 0;
+ static char buf[10][256];
+ char *b;
+ int c;
+
+ buf_to_use++;
+ if (buf_to_use > 9)
+ buf_to_use = 0;
+
+ b = buf[buf_to_use];
+ memset(b, 0, 256);
+
+ while ((c = (*s++) & 0xff))
+ *b++ = islower(c) ? toupper(c) : c;
+ *b = '\0';
+ return (buf[buf_to_use]);
+}
+
+char *
+funname(const char *s, char *buf) {
+ char *b = buf;
+ char c;
+
+ while ((c = *s++)) {
+ *b++ = (c == '-') ? '_' : c;
+ }
+ *b = '\0';
+ return (buf);
+}
+
+void
+doswitch(const char *name, const char *function, const char *args,
+ const char *tsw, const char *csw, const char *res)
+{
+ struct tt *tt;
+ int first = 1;
+ int lasttype = 0;
+ int subswitch = 0;
+ char buf1[TYPECLASSLEN], buf2[TYPECLASSLEN];
+ const char *result = " result =";
+
+ if (res == NULL)
+ result = "";
+
+ for (tt = types; tt != NULL; tt = tt->next) {
+ if (first) {
+ fprintf(stdout, "\n#define %s \\\n", name);
+ fprintf(stdout, "\tswitch (%s) { \\\n" /*}*/, tsw);
+ first = 0;
+ }
+ if (tt->type != lasttype && subswitch) {
+ if (res == NULL)
+ fprintf(stdout, "\t\tdefault: break; \\\n");
+ else
+ fprintf(stdout,
+ "\t\tdefault: %s; break; \\\n", res);
+ fputs(/*{*/ "\t\t} \\\n", stdout);
+ fputs("\t\tbreak; \\\n", stdout);
+ subswitch = 0;
+ }
+ if (tt->rdclass && tt->type != lasttype) {
+ fprintf(stdout, "\tcase %d: switch (%s) { \\\n" /*}*/,
+ tt->type, csw);
+ subswitch = 1;
+ }
+ if (tt->rdclass == 0)
+ fprintf(stdout,
+ "\tcase %d:%s %s_%s(%s); break;",
+ tt->type, result, function,
+ funname(tt->typename, buf1), args);
+ else
+ fprintf(stdout,
+ "\t\tcase %d:%s %s_%s_%s(%s); break;",
+ tt->rdclass, result, function,
+ funname(tt->classname, buf1),
+ funname(tt->typename, buf2), args);
+ fputs(" \\\n", stdout);
+ lasttype = tt->type;
+ }
+ if (subswitch) {
+ if (res == NULL)
+ fprintf(stdout, "\t\tdefault: break; \\\n");
+ else
+ fprintf(stdout, "\t\tdefault: %s; break; \\\n", res);
+ fputs(/*{*/ "\t\t} \\\n", stdout);
+ fputs("\t\tbreak; \\\n", stdout);
+ }
+ if (first) {
+ if (res == NULL)
+ fprintf(stdout, "\n#define %s\n", name);
+ else
+ fprintf(stdout, "\n#define %s %s;\n", name, res);
+ } else {
+ if (res == NULL)
+ fprintf(stdout, "\tdefault: break; \\\n");
+ else
+ fprintf(stdout, "\tdefault: %s; break; \\\n", res);
+ fputs(/*{*/ "\t}\n", stdout);
+ }
+}
+
+void
+dodecl(char *type, char *function, char *args) {
+ struct tt *tt;
+ char buf1[TYPECLASSLEN], buf2[TYPECLASSLEN];
+
+ fputs("\n", stdout);
+ for (tt = types; tt; tt = tt->next)
+ if (tt->rdclass)
+ fprintf(stdout,
+ "static inline %s %s_%s_%s(%s);\n",
+ type, function,
+ funname(tt->classname, buf1),
+ funname(tt->typename, buf2), args);
+ else
+ fprintf(stdout,
+ "static inline %s %s_%s(%s);\n",
+ type, function,
+ funname(tt->typename, buf1), args);
+}
+
+static struct ttnam *
+find_typename(int type) {
+ int i;
+
+ for (i = 0; i < TYPENAMES; i++) {
+ if (typenames[i].typename[0] != 0 &&
+ typenames[i].type == type)
+ return (&typenames[i]);
+ }
+ return (NULL);
+}
+
+void
+insert_into_typenames(int type, const char *typename, const char *attr) {
+ struct ttnam *ttn = NULL;
+ int c, i;
+ char tmp[256];
+
+ for (i = 0; i < TYPENAMES; i++) {
+ if (typenames[i].typename[0] != 0 &&
+ typenames[i].type == type &&
+ strcmp(typename, typenames[i].typename) != 0) {
+ fprintf(stderr,
+ "Error: type %d has two names: %s, %s\n",
+ type, typenames[i].typename, typename);
+ exit(1);
+ }
+ if (typenames[i].typename[0] == 0 && ttn == NULL)
+ ttn = &typenames[i];
+ }
+ if (ttn == NULL) {
+ fprintf(stderr, "Error: typenames array too small\n");
+ exit(1);
+ }
+
+ if (strlen(typename) > sizeof(ttn->typename) - 1) {
+ fprintf(stderr, "Error: type name %s is too long\n",
+ typename);
+ exit(1);
+ }
+ strcpy(ttn->typename, typename);
+ ttn->type = type;
+
+ strcpy(ttn->macroname, ttn->typename);
+ c = strlen(ttn->macroname);
+ while (c > 0) {
+ if (ttn->macroname[c - 1] == '-')
+ ttn->macroname[c - 1] = '_';
+ c--;
+ }
+
+ if (attr == NULL) {
+ sprintf(tmp, "RRTYPE_%s_ATTRIBUTES", upper(ttn->macroname));
+ attr = tmp;
+ }
+
+ if (ttn->attr[0] != 0 && strcmp(attr, ttn->attr) != 0) {
+ fprintf(stderr, "Error: type %d has different attributes: "
+ "%s, %s\n", type, ttn->attr, attr);
+ exit(1);
+ }
+
+ if (strlen(attr) > sizeof(ttn->attr) - 1) {
+ fprintf(stderr, "Error: attr (%s) [name %s] is too long\n",
+ attr, typename);
+ exit(1);
+ }
+ strcpy(ttn->attr, attr);
+ ttn->sorted = 0;
+ if (maxtype < type)
+ maxtype = type;
+}
+
+void
+add(int rdclass, const char *classname, int type, const char *typename,
+ const char *dirname)
+{
+ struct tt *newtt = (struct tt *)malloc(sizeof(*newtt));
+ struct tt *tt, *oldtt;
+ struct cc *newcc;
+ struct cc *cc, *oldcc;
+
+ insert_into_typenames(type, typename, NULL);
+
+ if (newtt == NULL) {
+ fprintf(stderr, "malloc() failed\n");
+ exit(1);
+ }
+
+ newtt->next = NULL;
+ newtt->rdclass = rdclass;
+ newtt->type = type;
+ strcpy(newtt->classname, classname);
+ strcpy(newtt->typename, typename);
+ if (strncmp(dirname, "./", 2) == 0)
+ dirname += 2;
+ strcpy(newtt->dirname, dirname);
+
+ tt = types;
+ oldtt = NULL;
+
+ while ((tt != NULL) && (tt->type < type)) {
+ oldtt = tt;
+ tt = tt->next;
+ }
+
+ while ((tt != NULL) && (tt->type == type) && (tt->rdclass < rdclass)) {
+ if (strcmp(tt->typename, typename) != 0)
+ exit(1);
+ oldtt = tt;
+ tt = tt->next;
+ }
+
+ if ((tt != NULL) && (tt->type == type) && (tt->rdclass == rdclass))
+ exit(1);
+
+ newtt->next = tt;
+ if (oldtt != NULL)
+ oldtt->next = newtt;
+ else
+ types = newtt;
+
+ /*
+ * Do a class switch for this type.
+ */
+ if (rdclass == 0)
+ return;
+
+ newcc = (struct cc *)malloc(sizeof(*newcc));
+ newcc->rdclass = rdclass;
+ strcpy(newcc->classname, classname);
+ cc = classes;
+ oldcc = NULL;
+
+ while ((cc != NULL) && (cc->rdclass < rdclass)) {
+ oldcc = cc;
+ cc = cc->next;
+ }
+
+ if ((cc != NULL) && cc->rdclass == rdclass) {
+ free((char *)newcc);
+ return;
+ }
+
+ newcc->next = cc;
+ if (oldcc != NULL)
+ oldcc->next = newcc;
+ else
+ classes = newcc;
+}
+
+void
+sd(int rdclass, const char *classname, const char *dirname, char filetype) {
+ char buf[sizeof("01234567890123456789_65535.h")];
+ char fmt[sizeof("%20[-0-9a-z]_%d.h")];
+ int type;
+ char typename[TYPECLASSLEN];
+ isc_dir_t dir;
+
+ if (!start_directory(dirname, &dir))
+ return;
+
+ sprintf(fmt,"%s%c", "%20[-0-9a-z]_%d.", filetype);
+ while (next_file(&dir)) {
+ if (sscanf(dir.filename, fmt, typename, &type) != 2)
+ continue;
+ if ((type > 65535) || (type < 0))
+ continue;
+
+ sprintf(buf, "%s_%d.%c", typename, type, filetype);
+ if (strcmp(buf, dir.filename) != 0)
+ continue;
+ add(rdclass, classname, type, typename, dirname);
+ }
+
+ end_directory(&dir);
+}
+
+static unsigned int
+HASH(char *string) {
+ unsigned int n;
+ unsigned char a, b;
+
+ n = strlen(string);
+ if (n == 0) {
+ fprintf(stderr, "n == 0?\n");
+ exit(1);
+ }
+ a = tolower((unsigned char)string[0]);
+ b = tolower((unsigned char)string[n - 1]);
+
+ return ((a + n) * b) % 256;
+}
+
+int
+main(int argc, char **argv) {
+ char buf[256]; /* XXX Should be max path length */
+ char srcdir[256]; /* XXX Should be max path length */
+ int rdclass;
+ char classname[TYPECLASSLEN];
+ struct tt *tt;
+ struct cc *cc;
+ struct ttnam *ttn, *ttn2;
+ unsigned int hash;
+ struct tm *tm;
+ time_t now;
+ char year[11];
+ int lasttype;
+ int code = 1;
+ int class_enum = 0;
+ int type_enum = 0;
+ int structs = 0;
+ int depend = 0;
+ int c, i, j;
+ char buf1[TYPECLASSLEN];
+ char filetype = 'c';
+ FILE *fd;
+ char *prefix = NULL;
+ char *suffix = NULL;
+ char *file = NULL;
+ isc_dir_t dir;
+
+ for (i = 0; i < TYPENAMES; i++)
+ memset(&typenames[i], 0, sizeof(typenames[i]));
+
+ strcpy(srcdir, "");
+ while ((c = isc_commandline_parse(argc, argv, "cdits:F:P:S:")) != -1)
+ switch (c) {
+ case 'c':
+ code = 0;
+ depend = 0;
+ type_enum = 0;
+ class_enum = 1;
+ filetype = 'c';
+ structs = 0;
+ break;
+ case 'd':
+ code = 0;
+ depend = 1;
+ class_enum = 0;
+ type_enum = 0;
+ structs = 0;
+ filetype = 'h';
+ break;
+ case 't':
+ code = 0;
+ depend = 0;
+ class_enum = 0;
+ type_enum = 1;
+ filetype = 'c';
+ structs = 0;
+ break;
+ case 'i':
+ code = 0;
+ depend = 0;
+ class_enum = 0;
+ type_enum = 0;
+ structs = 1;
+ filetype = 'h';
+ break;
+ case 's':
+ sprintf(srcdir, "%s/", isc_commandline_argument);
+ break;
+ case 'F':
+ file = isc_commandline_argument;
+ break;
+ case 'P':
+ prefix = isc_commandline_argument;
+ break;
+ case 'S':
+ suffix = isc_commandline_argument;
+ break;
+ case '?':
+ exit(1);
+ }
+
+ sprintf(buf, "%srdata", srcdir);
+
+ if (!start_directory(buf, &dir))
+ exit(1);
+
+ while (next_file(&dir)) {
+ if (sscanf(dir.filename, "%10[0-9a-z]_%d",
+ classname, &rdclass) != 2)
+ continue;
+ if ((rdclass > 65535) || (rdclass < 0))
+ continue;
+
+ sprintf(buf, "%srdata/%s_%d", srcdir, classname, rdclass);
+ if (strcmp(buf + 6 + strlen(srcdir), dir.filename) != 0)
+ continue;
+ sd(rdclass, classname, buf, filetype);
+ }
+ end_directory(&dir);
+ sprintf(buf, "%srdata/generic", srcdir);
+ sd(0, "", buf, filetype);
+
+ if (time(&now) != -1) {
+ if ((tm = localtime(&now)) != NULL && tm->tm_year > 104)
+ sprintf(year, "-%d", tm->tm_year + 1900);
+ else
+ year[0] = 0;
+ } else
+ year[0] = 0;
+
+ if (!depend) fprintf(stdout, copyright, year);
+
+ if (code) {
+ fputs("#ifndef DNS_CODE_H\n", stdout);
+ fputs("#define DNS_CODE_H 1\n\n", stdout);
+
+ fputs("#include <isc/boolean.h>\n", stdout);
+ fputs("#include <isc/result.h>\n\n", stdout);
+ fputs("#include <dns/name.h>\n\n", stdout);
+
+ for (tt = types; tt != NULL; tt = tt->next)
+ fprintf(stdout, "#include \"%s/%s_%d.c\"\n",
+ tt->dirname, tt->typename, tt->type);
+
+ fputs("\n\n", stdout);
+
+ doswitch("FROMTEXTSWITCH", "fromtext", FROMTEXTARGS,
+ FROMTEXTTYPE, FROMTEXTCLASS, FROMTEXTDEF);
+ doswitch("TOTEXTSWITCH", "totext", TOTEXTARGS,
+ TOTEXTTYPE, TOTEXTCLASS, TOTEXTDEF);
+ doswitch("FROMWIRESWITCH", "fromwire", FROMWIREARGS,
+ FROMWIRETYPE, FROMWIRECLASS, FROMWIREDEF);
+ doswitch("TOWIRESWITCH", "towire", TOWIREARGS,
+ TOWIRETYPE, TOWIRECLASS, TOWIREDEF);
+ doswitch("COMPARESWITCH", "compare", COMPAREARGS,
+ COMPARETYPE, COMPARECLASS, COMPAREDEF);
+ doswitch("FROMSTRUCTSWITCH", "fromstruct", FROMSTRUCTARGS,
+ FROMSTRUCTTYPE, FROMSTRUCTCLASS, FROMSTRUCTDEF);
+ doswitch("TOSTRUCTSWITCH", "tostruct", TOSTRUCTARGS,
+ TOSTRUCTTYPE, TOSTRUCTCLASS, TOSTRUCTDEF);
+ doswitch("FREESTRUCTSWITCH", "freestruct", FREESTRUCTARGS,
+ FREESTRUCTTYPE, FREESTRUCTCLASS, FREESTRUCTDEF);
+ doswitch("ADDITIONALDATASWITCH", "additionaldata",
+ ADDITIONALDATAARGS, ADDITIONALDATATYPE,
+ ADDITIONALDATACLASS, ADDITIONALDATADEF);
+ doswitch("DIGESTSWITCH", "digest",
+ DIGESTARGS, DIGESTTYPE,
+ DIGESTCLASS, DIGESTDEF);
+ doswitch("CHECKOWNERSWITCH", "checkowner",
+ CHECKOWNERARGS, CHECKOWNERTYPE,
+ CHECKOWNERCLASS, CHECKOWNERDEF);
+ doswitch("CHECKNAMESSWITCH", "checknames",
+ CHECKNAMESARGS, CHECKNAMESTYPE,
+ CHECKNAMESCLASS, CHECKNAMESDEF);
+
+ /*
+ * From here down, we are processing the rdata names and
+ * attributes.
+ */
+
+#define PRINT_COMMA(x) (x == maxtype ? "" : ",")
+
+#define METANOTQUESTION "DNS_RDATATYPEATTR_META | " \
+ "DNS_RDATATYPEATTR_NOTQUESTION"
+#define METAQUESTIONONLY "DNS_RDATATYPEATTR_META | " \
+ "DNS_RDATATYPEATTR_QUESTIONONLY"
+#define RESERVED "DNS_RDATATYPEATTR_RESERVED"
+
+ /*
+ * Add in reserved/special types. This will let us
+ * sort them without special cases.
+ */
+ insert_into_typenames(0, "reserved0", RESERVED);
+ insert_into_typenames(31, "eid", RESERVED);
+ insert_into_typenames(32, "nimloc", RESERVED);
+ insert_into_typenames(34, "atma", RESERVED);
+ insert_into_typenames(100, "uinfo", RESERVED);
+ insert_into_typenames(101, "uid", RESERVED);
+ insert_into_typenames(102, "gid", RESERVED);
+ insert_into_typenames(251, "ixfr", METAQUESTIONONLY);
+ insert_into_typenames(252, "axfr", METAQUESTIONONLY);
+ insert_into_typenames(253, "mailb", METAQUESTIONONLY);
+ insert_into_typenames(254, "maila", METAQUESTIONONLY);
+ insert_into_typenames(255, "any", METAQUESTIONONLY);
+
+ /*
+ * Spit out a quick and dirty hash function. Here,
+ * we walk through the list of type names, and calculate
+ * a hash. This isn't perfect, but it will generate "pretty
+ * good" estimates. Lowercase the characters before
+ * computing in all cases.
+ *
+ * Here, walk the list from top to bottom, calculating
+ * the hash (mod 256) for each name.
+ */
+ fprintf(stdout, "#define RDATATYPE_COMPARE(_s, _d, _tn, _n, _tp) \\\n");
+ fprintf(stdout, "\tdo { \\\n");
+ fprintf(stdout, "\t\tif (sizeof(_s) - 1 == _n && \\\n"
+ "\t\t strncasecmp(_s,(_tn),"
+ "(sizeof(_s) - 1)) == 0) { \\\n");
+ fprintf(stdout, "\t\t\tif ((dns_rdatatype_attributes(_d) & "
+ "DNS_RDATATYPEATTR_RESERVED) != 0) \\\n");
+ fprintf(stdout, "\t\t\t\treturn (ISC_R_NOTIMPLEMENTED); \\\n");
+ fprintf(stdout, "\t\t\t*(_tp) = _d; \\\n");
+ fprintf(stdout, "\t\t\treturn (ISC_R_SUCCESS); \\\n");
+ fprintf(stdout, "\t\t} \\\n");
+ fprintf(stdout, "\t} while (0)\n\n");
+
+ fprintf(stdout, "#define RDATATYPE_FROMTEXT_SW(_hash,"
+ "_typename,_length,_typep) \\\n");
+ fprintf(stdout, "\tswitch (_hash) { \\\n");
+ for (i = 0; i <= maxtype; i++) {
+ ttn = find_typename(i);
+ if (ttn == NULL)
+ continue;
+
+ /*
+ * Skip entries we already processed.
+ */
+ if (ttn->sorted != 0)
+ continue;
+
+ hash = HASH(ttn->typename);
+ fprintf(stdout, "\t\tcase %u: \\\n", hash);
+
+ /*
+ * Find all other entries that happen to match
+ * this hash.
+ */
+ for (j = 0; j <= maxtype; j++) {
+ ttn2 = find_typename(j);
+ if (ttn2 == NULL)
+ continue;
+ if (hash == HASH(ttn2->typename)) {
+ fprintf(stdout, "\t\t\tRDATATYPE_COMPARE"
+ "(\"%s\", %u, "
+ "_typename, _length, _typep); \\\n",
+ ttn2->typename, ttn2->type);
+ ttn2->sorted = 1;
+ }
+ }
+ fprintf(stdout, "\t\t\tbreak; \\\n");
+ }
+ fprintf(stdout, "\t}\n");
+
+ fprintf(stdout, "#define RDATATYPE_ATTRIBUTE_SW \\\n");
+ fprintf(stdout, "\tswitch (type) { \\\n");
+ for (i = 0; i <= maxtype; i++) {
+ ttn = find_typename(i);
+ if (ttn == NULL)
+ continue;
+ fprintf(stdout, "\tcase %u: return (%s); \\\n",
+ i, upper(ttn->attr));
+ }
+ fprintf(stdout, "\t}\n");
+
+ fprintf(stdout, "#define RDATATYPE_TOTEXT_SW \\\n");
+ fprintf(stdout, "\tswitch (type) { \\\n");
+ for (i = 0; i <= maxtype; i++) {
+ ttn = find_typename(i);
+ if (ttn == NULL)
+ continue;
+ fprintf(stdout, "\tcase %u: return "
+ "(str_totext(\"%s\", target)); \\\n",
+ i, upper(ttn->typename));
+ }
+ fprintf(stdout, "\t}\n");
+
+ fputs("#endif /* DNS_CODE_H */\n", stdout);
+ } else if (type_enum) {
+ char *s;
+
+ fprintf(stdout, "#ifndef DNS_ENUMTYPE_H\n");
+ fprintf(stdout, "#define DNS_ENUMTYPE_H 1\n\n");
+
+ fprintf(stdout, "enum {\n");
+ fprintf(stdout, "\tdns_rdatatype_none = 0,\n");
+
+ lasttype = 0;
+ for (tt = types; tt != NULL; tt = tt->next)
+ if (tt->type != lasttype)
+ fprintf(stdout,
+ "\tdns_rdatatype_%s = %d,\n",
+ funname(tt->typename, buf1),
+ lasttype = tt->type);
+
+ fprintf(stdout, "\tdns_rdatatype_ixfr = 251,\n");
+ fprintf(stdout, "\tdns_rdatatype_axfr = 252,\n");
+ fprintf(stdout, "\tdns_rdatatype_mailb = 253,\n");
+ fprintf(stdout, "\tdns_rdatatype_maila = 254,\n");
+ fprintf(stdout, "\tdns_rdatatype_any = 255\n");
+
+ fprintf(stdout, "};\n\n");
+
+ fprintf(stdout, "#define dns_rdatatype_none\t"
+ "((dns_rdatatype_t)dns_rdatatype_none)\n");
+
+ for (tt = types; tt != NULL; tt = tt->next)
+ if (tt->type != lasttype) {
+ s = funname(tt->typename, buf1);
+ fprintf(stdout,
+ "#define dns_rdatatype_%s\t%s"
+ "((dns_rdatatype_t)dns_rdatatype_%s)"
+ "\n",
+ s, strlen(s) < 2U ? "\t" : "", s);
+ lasttype = tt->type;
+ }
+
+ fprintf(stdout, "#define dns_rdatatype_ixfr\t"
+ "((dns_rdatatype_t)dns_rdatatype_ixfr)\n");
+ fprintf(stdout, "#define dns_rdatatype_axfr\t"
+ "((dns_rdatatype_t)dns_rdatatype_axfr)\n");
+ fprintf(stdout, "#define dns_rdatatype_mailb\t"
+ "((dns_rdatatype_t)dns_rdatatype_mailb)\n");
+ fprintf(stdout, "#define dns_rdatatype_maila\t"
+ "((dns_rdatatype_t)dns_rdatatype_maila)\n");
+ fprintf(stdout, "#define dns_rdatatype_any\t"
+ "((dns_rdatatype_t)dns_rdatatype_any)\n");
+
+ fprintf(stdout, "\n#endif /* DNS_ENUMTYPE_H */\n");
+
+ } else if (class_enum) {
+ char *s;
+ int classnum;
+
+ fprintf(stdout, "#ifndef DNS_ENUMCLASS_H\n");
+ fprintf(stdout, "#define DNS_ENUMCLASS_H 1\n\n");
+
+ fprintf(stdout, "enum {\n");
+
+ fprintf(stdout, "\tdns_rdataclass_reserved0 = 0,\n");
+ fprintf(stdout, "#define dns_rdataclass_reserved0 \\\n\t\t\t\t"
+ "((dns_rdataclass_t)dns_rdataclass_reserved0)\n");
+
+#define PRINTCLASS(name, num) \
+ do { \
+ s = funname(name, buf1); \
+ classnum = num; \
+ fprintf(stdout, "\tdns_rdataclass_%s = %d%s\n", s, classnum, \
+ classnum != 255 ? "," : ""); \
+ fprintf(stdout, "#define dns_rdataclass_%s\t" \
+ "((dns_rdataclass_t)dns_rdataclass_%s)\n", s, s); \
+ } while (0)
+
+ for (cc = classes; cc != NULL; cc = cc->next) {
+ if (cc->rdclass == 3)
+ PRINTCLASS("chaos", 3);
+ else if (cc->rdclass == 255)
+ PRINTCLASS("none", 254);
+ PRINTCLASS(cc->classname, cc->rdclass);
+ }
+
+#undef PRINTCLASS
+
+ fprintf(stdout, "};\n\n");
+ fprintf(stdout, "#endif /* DNS_ENUMCLASS_H */\n");
+ } else if (structs) {
+ if (prefix != NULL) {
+ if ((fd = fopen(prefix,"r")) != NULL) {
+ while (fgets(buf, sizeof(buf), fd) != NULL)
+ fputs(buf, stdout);
+ fclose(fd);
+ }
+ }
+ for (tt = types; tt != NULL; tt = tt->next) {
+ sprintf(buf, "%s/%s_%d.h",
+ tt->dirname, tt->typename, tt->type);
+ if ((fd = fopen(buf,"r")) != NULL) {
+ while (fgets(buf, sizeof(buf), fd) != NULL)
+ fputs(buf, stdout);
+ fclose(fd);
+ }
+ }
+ if (suffix != NULL) {
+ if ((fd = fopen(suffix,"r")) != NULL) {
+ while (fgets(buf, sizeof(buf), fd) != NULL)
+ fputs(buf, stdout);
+ fclose(fd);
+ }
+ }
+ } else if (depend) {
+ for (tt = types; tt != NULL; tt = tt->next)
+ fprintf(stdout, "%s:\t%s/%s_%d.h\n", file,
+ tt->dirname, tt->typename, tt->type);
+ }
+
+ if (ferror(stdout) != 0)
+ exit(1);
+
+ return (0);
+}
diff --git a/lib/dns/gssapi_link.c b/lib/dns/gssapi_link.c
new file mode 100644
index 0000000..0dd27bb
--- /dev/null
+++ b/lib/dns/gssapi_link.c
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/*
+ * $Id: gssapi_link.c,v 1.12 2008/11/11 03:55:01 marka Exp $
+ */
+
+#include <config.h>
+
+#ifdef GSSAPI
+
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dst/result.h>
+
+#include "dst_internal.h"
+#include "dst_parse.h"
+
+#include <dst/gssapi.h>
+
+#define INITIAL_BUFFER_SIZE 1024
+#define BUFFER_EXTRA 1024
+
+#define REGION_TO_GBUFFER(r, gb) \
+ do { \
+ (gb).length = (r).length; \
+ (gb).value = (r).base; \
+ } while (0)
+
+
+struct dst_gssapi_signverifyctx {
+ isc_buffer_t *buffer;
+};
+
+/*%
+ * Allocate a temporary "context" for use in gathering data for signing
+ * or verifying.
+ */
+static isc_result_t
+gssapi_create_signverify_ctx(dst_key_t *key, dst_context_t *dctx) {
+ dst_gssapi_signverifyctx_t *ctx;
+ isc_result_t result;
+
+ UNUSED(key);
+
+ ctx = isc_mem_get(dctx->mctx, sizeof(dst_gssapi_signverifyctx_t));
+ if (ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ ctx->buffer = NULL;
+ result = isc_buffer_allocate(dctx->mctx, &ctx->buffer,
+ INITIAL_BUFFER_SIZE);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(dctx->mctx, ctx, sizeof(dst_gssapi_signverifyctx_t));
+ return (result);
+ }
+
+ dctx->ctxdata.gssctx = ctx;
+
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Destroy the temporary sign/verify context.
+ */
+static void
+gssapi_destroy_signverify_ctx(dst_context_t *dctx) {
+ dst_gssapi_signverifyctx_t *ctx = dctx->ctxdata.gssctx;
+
+ if (ctx != NULL) {
+ if (ctx->buffer != NULL)
+ isc_buffer_free(&ctx->buffer);
+ isc_mem_put(dctx->mctx, ctx, sizeof(dst_gssapi_signverifyctx_t));
+ dctx->ctxdata.gssctx = NULL;
+ }
+}
+
+/*%
+ * Add data to our running buffer of data we will be signing or verifying.
+ * This code will see if the new data will fit in our existing buffer, and
+ * copy it in if it will. If not, it will attempt to allocate a larger
+ * buffer and copy old+new into it, and free the old buffer.
+ */
+static isc_result_t
+gssapi_adddata(dst_context_t *dctx, const isc_region_t *data) {
+ dst_gssapi_signverifyctx_t *ctx = dctx->ctxdata.gssctx;
+ isc_buffer_t *newbuffer = NULL;
+ isc_region_t r;
+ unsigned int length;
+ isc_result_t result;
+
+ result = isc_buffer_copyregion(ctx->buffer, data);
+ if (result == ISC_R_SUCCESS)
+ return (ISC_R_SUCCESS);
+
+ length = isc_buffer_length(ctx->buffer) + data->length + BUFFER_EXTRA;
+
+ result = isc_buffer_allocate(dctx->mctx, &newbuffer, length);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ isc_buffer_usedregion(ctx->buffer, &r);
+ (void)isc_buffer_copyregion(newbuffer, &r);
+ (void)isc_buffer_copyregion(newbuffer, data);
+
+ isc_buffer_free(&ctx->buffer);
+ ctx->buffer = newbuffer;
+
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Sign.
+ */
+static isc_result_t
+gssapi_sign(dst_context_t *dctx, isc_buffer_t *sig) {
+ dst_gssapi_signverifyctx_t *ctx = dctx->ctxdata.gssctx;
+ isc_region_t message;
+ gss_buffer_desc gmessage, gsig;
+ OM_uint32 minor, gret;
+ gss_ctx_id_t gssctx = dctx->key->keydata.gssctx;
+ char buf[1024];
+
+ /*
+ * Convert the data we wish to sign into a structure gssapi can
+ * understand.
+ */
+ isc_buffer_usedregion(ctx->buffer, &message);
+ REGION_TO_GBUFFER(message, gmessage);
+
+ /*
+ * Generate the signature.
+ */
+ gret = gss_get_mic(&minor, gssctx, GSS_C_QOP_DEFAULT, &gmessage,
+ &gsig);
+
+ /*
+ * If it did not complete, we log the result and return a generic
+ * failure code.
+ */
+ if (gret != GSS_S_COMPLETE) {
+ gss_log(3, "GSS sign error: %s",
+ gss_error_tostring(gret, minor, buf, sizeof(buf)));
+ return (ISC_R_FAILURE);
+ }
+
+ /*
+ * If it will not fit in our allocated buffer, return that we need
+ * more space.
+ */
+ if (gsig.length > isc_buffer_availablelength(sig)) {
+ gss_release_buffer(&minor, &gsig);
+ return (ISC_R_NOSPACE);
+ }
+
+ /*
+ * Copy the output into our buffer space, and release the gssapi
+ * allocated space.
+ */
+ isc_buffer_putmem(sig, gsig.value, gsig.length);
+ if (gsig.length != 0)
+ gss_release_buffer(&minor, &gsig);
+
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Verify.
+ */
+static isc_result_t
+gssapi_verify(dst_context_t *dctx, const isc_region_t *sig) {
+ dst_gssapi_signverifyctx_t *ctx = dctx->ctxdata.gssctx;
+ isc_region_t message, r;
+ gss_buffer_desc gmessage, gsig;
+ OM_uint32 minor, gret;
+ gss_ctx_id_t gssctx = dctx->key->keydata.gssctx;
+ unsigned char *buf;
+ char err[1024];
+
+ /*
+ * Convert the data we wish to sign into a structure gssapi can
+ * understand.
+ */
+ isc_buffer_usedregion(ctx->buffer, &message);
+ REGION_TO_GBUFFER(message, gmessage);
+
+ /*
+ * XXXMLG
+ * It seem that gss_verify_mic() modifies the signature buffer,
+ * at least on Heimdal's implementation. Copy it here to an allocated
+ * buffer.
+ */
+ buf = isc_mem_allocate(dst__memory_pool, sig->length);
+ if (buf == NULL)
+ return (ISC_R_FAILURE);
+ memcpy(buf, sig->base, sig->length);
+ r.base = buf;
+ r.length = sig->length;
+ REGION_TO_GBUFFER(r, gsig);
+
+ /*
+ * Verify the data.
+ */
+ gret = gss_verify_mic(&minor, gssctx, &gmessage, &gsig, NULL);
+
+ isc_mem_free(dst__memory_pool, buf);
+
+ /*
+ * Convert return codes into something useful to us.
+ */
+ if (gret != GSS_S_COMPLETE) {
+ gss_log(3, "GSS verify error: %s",
+ gss_error_tostring(gret, minor, err, sizeof(err)));
+ if (gret == GSS_S_DEFECTIVE_TOKEN ||
+ gret == GSS_S_BAD_SIG ||
+ gret == GSS_S_DUPLICATE_TOKEN ||
+ gret == GSS_S_OLD_TOKEN ||
+ gret == GSS_S_UNSEQ_TOKEN ||
+ gret == GSS_S_GAP_TOKEN ||
+ gret == GSS_S_CONTEXT_EXPIRED ||
+ gret == GSS_S_NO_CONTEXT ||
+ gret == GSS_S_FAILURE)
+ return(DST_R_VERIFYFAILURE);
+ else
+ return (ISC_R_FAILURE);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_boolean_t
+gssapi_compare(const dst_key_t *key1, const dst_key_t *key2) {
+ gss_ctx_id_t gsskey1 = key1->keydata.gssctx;
+ gss_ctx_id_t gsskey2 = key2->keydata.gssctx;
+
+ /* No idea */
+ return (ISC_TF(gsskey1 == gsskey2));
+}
+
+static isc_result_t
+gssapi_generate(dst_key_t *key, int unused) {
+ UNUSED(key);
+ UNUSED(unused);
+
+ /* No idea */
+ return (ISC_R_FAILURE);
+}
+
+static isc_boolean_t
+gssapi_isprivate(const dst_key_t *key) {
+ UNUSED(key);
+ return (ISC_TRUE);
+}
+
+static void
+gssapi_destroy(dst_key_t *key) {
+ REQUIRE(key != NULL);
+ dst_gssapi_deletectx(key->mctx, &key->keydata.gssctx);
+ key->keydata.gssctx = NULL;
+}
+
+static dst_func_t gssapi_functions = {
+ gssapi_create_signverify_ctx,
+ gssapi_destroy_signverify_ctx,
+ gssapi_adddata,
+ gssapi_sign,
+ gssapi_verify,
+ NULL, /*%< computesecret */
+ gssapi_compare,
+ NULL, /*%< paramcompare */
+ gssapi_generate,
+ gssapi_isprivate,
+ gssapi_destroy,
+ NULL, /*%< todns */
+ NULL, /*%< fromdns */
+ NULL, /*%< tofile */
+ NULL, /*%< parse */
+ NULL, /*%< cleanup */
+ NULL /*%< fromlabel */
+};
+
+isc_result_t
+dst__gssapi_init(dst_func_t **funcp) {
+ REQUIRE(funcp != NULL);
+ if (*funcp == NULL)
+ *funcp = &gssapi_functions;
+ return (ISC_R_SUCCESS);
+}
+
+#else
+int gssapi_link_unneeded = 1;
+#endif
+
+/*! \file */
diff --git a/lib/dns/gssapictx.c b/lib/dns/gssapictx.c
new file mode 100644
index 0000000..11eadb9
--- /dev/null
+++ b/lib/dns/gssapictx.c
@@ -0,0 +1,738 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: gssapictx.c,v 1.12 2008/04/03 06:09:04 tbox Exp $ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/buffer.h>
+#include <isc/dir.h>
+#include <isc/entropy.h>
+#include <isc/lex.h>
+#include <isc/mem.h>
+#include <isc/once.h>
+#include <isc/print.h>
+#include <isc/random.h>
+#include <isc/string.h>
+#include <isc/time.h>
+#include <isc/util.h>
+
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/result.h>
+#include <dns/types.h>
+#include <dns/keyvalues.h>
+#include <dns/log.h>
+
+#include <dst/gssapi.h>
+#include <dst/result.h>
+
+#include "dst_internal.h"
+
+/*
+ * If we're using our own SPNEGO implementation (see configure.in),
+ * pull it in now. Otherwise, we just use whatever GSSAPI supplies.
+ */
+#if defined(GSSAPI) && defined(USE_ISC_SPNEGO)
+#include "spnego.h"
+#define gss_accept_sec_context gss_accept_sec_context_spnego
+#define gss_init_sec_context gss_init_sec_context_spnego
+#endif
+
+/*
+ * Solaris8 apparently needs an explicit OID set, and Solaris10 needs
+ * one for anything but Kerberos. Supplying an explicit OID set
+ * doesn't appear to hurt anything in other implementations, so we
+ * always use one. If we're not using our own SPNEGO implementation,
+ * we include SPNEGO's OID.
+ */
+#if defined(GSSAPI)
+
+static unsigned char krb5_mech_oid_bytes[] = {
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02
+};
+
+#ifndef USE_ISC_SPNEGO
+static unsigned char spnego_mech_oid_bytes[] = {
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x02
+};
+#endif
+
+static gss_OID_desc mech_oid_set_array[] = {
+ { sizeof(krb5_mech_oid_bytes), krb5_mech_oid_bytes },
+#ifndef USE_ISC_SPNEGO
+ { sizeof(spnego_mech_oid_bytes), spnego_mech_oid_bytes },
+#endif
+};
+
+static gss_OID_set_desc mech_oid_set = {
+ sizeof(mech_oid_set_array) / sizeof(*mech_oid_set_array),
+ mech_oid_set_array
+};
+
+#endif
+
+#define REGION_TO_GBUFFER(r, gb) \
+ do { \
+ (gb).length = (r).length; \
+ (gb).value = (r).base; \
+ } while (0)
+
+#define GBUFFER_TO_REGION(gb, r) \
+ do { \
+ (r).length = (gb).length; \
+ (r).base = (gb).value; \
+ } while (0)
+
+
+#define RETERR(x) do { \
+ result = (x); \
+ if (result != ISC_R_SUCCESS) \
+ goto out; \
+ } while (0)
+
+#ifdef GSSAPI
+static inline void
+name_to_gbuffer(dns_name_t *name, isc_buffer_t *buffer,
+ gss_buffer_desc *gbuffer)
+{
+ dns_name_t tname, *namep;
+ isc_region_t r;
+ isc_result_t result;
+
+ if (!dns_name_isabsolute(name))
+ namep = name;
+ else
+ {
+ unsigned int labels;
+ dns_name_init(&tname, NULL);
+ labels = dns_name_countlabels(name);
+ dns_name_getlabelsequence(name, 0, labels - 1, &tname);
+ namep = &tname;
+ }
+
+ result = dns_name_totext(namep, ISC_FALSE, buffer);
+ isc_buffer_putuint8(buffer, 0);
+ isc_buffer_usedregion(buffer, &r);
+ REGION_TO_GBUFFER(r, *gbuffer);
+}
+
+static void
+log_cred(const gss_cred_id_t cred) {
+ OM_uint32 gret, minor, lifetime;
+ gss_name_t gname;
+ gss_buffer_desc gbuffer;
+ gss_cred_usage_t usage;
+ const char *usage_text;
+ char buf[1024];
+
+ gret = gss_inquire_cred(&minor, cred, &gname, &lifetime, &usage, NULL);
+ if (gret != GSS_S_COMPLETE) {
+ gss_log(3, "failed gss_inquire_cred: %s",
+ gss_error_tostring(gret, minor, buf, sizeof(buf)));
+ return;
+ }
+
+ gret = gss_display_name(&minor, gname, &gbuffer, NULL);
+ if (gret != GSS_S_COMPLETE)
+ gss_log(3, "failed gss_display_name: %s",
+ gss_error_tostring(gret, minor, buf, sizeof(buf)));
+ else {
+ switch (usage) {
+ case GSS_C_BOTH:
+ usage_text = "GSS_C_BOTH";
+ break;
+ case GSS_C_INITIATE:
+ usage_text = "GSS_C_INITIATE";
+ break;
+ case GSS_C_ACCEPT:
+ usage_text = "GSS_C_ACCEPT";
+ break;
+ default:
+ usage_text = "???";
+ }
+ gss_log(3, "gss cred: \"%s\", %s, %lu", (char *)gbuffer.value,
+ usage_text, (unsigned long)lifetime);
+ }
+
+ if (gret == GSS_S_COMPLETE) {
+ if (gbuffer.length != 0) {
+ gret = gss_release_buffer(&minor, &gbuffer);
+ if (gret != GSS_S_COMPLETE)
+ gss_log(3, "failed gss_release_buffer: %s",
+ gss_error_tostring(gret, minor, buf,
+ sizeof(buf)));
+ }
+ }
+
+ gret = gss_release_name(&minor, &gname);
+ if (gret != GSS_S_COMPLETE)
+ gss_log(3, "failed gss_release_name: %s",
+ gss_error_tostring(gret, minor, buf, sizeof(buf)));
+}
+#endif
+
+isc_result_t
+dst_gssapi_acquirecred(dns_name_t *name, isc_boolean_t initiate,
+ gss_cred_id_t *cred)
+{
+#ifdef GSSAPI
+ isc_buffer_t namebuf;
+ gss_name_t gname;
+ gss_buffer_desc gnamebuf;
+ unsigned char array[DNS_NAME_MAXTEXT + 1];
+ OM_uint32 gret, minor;
+ gss_OID_set mechs;
+ OM_uint32 lifetime;
+ gss_cred_usage_t usage;
+ char buf[1024];
+
+ REQUIRE(cred != NULL && *cred == NULL);
+
+ /*
+ * XXXSRA In theory we could use GSS_C_NT_HOSTBASED_SERVICE
+ * here when we're in the acceptor role, which would let us
+ * default the hostname and use a compiled in default service
+ * name of "DNS", giving one less thing to configure in
+ * named.conf. Unfortunately, this creates a circular
+ * dependency due to DNS-based realm lookup in at least one
+ * GSSAPI implementation (Heimdal). Oh well.
+ */
+ if (name != NULL) {
+ isc_buffer_init(&namebuf, array, sizeof(array));
+ name_to_gbuffer(name, &namebuf, &gnamebuf);
+ gret = gss_import_name(&minor, &gnamebuf,
+ GSS_C_NO_OID, &gname);
+ if (gret != GSS_S_COMPLETE) {
+ gss_log(3, "failed gss_import_name: %s",
+ gss_error_tostring(gret, minor, buf,
+ sizeof(buf)));
+ return (ISC_R_FAILURE);
+ }
+ } else
+ gname = NULL;
+
+ /* Get the credentials. */
+ if (gname != NULL)
+ gss_log(3, "acquiring credentials for %s",
+ (char *)gnamebuf.value);
+ else {
+ /* XXXDCL does this even make any sense? */
+ gss_log(3, "acquiring credentials for ?");
+ }
+
+ if (initiate)
+ usage = GSS_C_INITIATE;
+ else
+ usage = GSS_C_ACCEPT;
+
+ gret = gss_acquire_cred(&minor, gname, GSS_C_INDEFINITE,
+ &mech_oid_set,
+ usage, cred, &mechs, &lifetime);
+
+ if (gret != GSS_S_COMPLETE) {
+ gss_log(3, "failed to acquire %s credentials for %s: %s",
+ initiate ? "initiate" : "accept",
+ (char *)gnamebuf.value,
+ gss_error_tostring(gret, minor, buf, sizeof(buf)));
+ return (ISC_R_FAILURE);
+ }
+
+ gss_log(4, "acquired %s credentials for %s",
+ initiate ? "initiate" : "accept",
+ (char *)gnamebuf.value);
+
+ log_cred(*cred);
+
+ return (ISC_R_SUCCESS);
+#else
+ UNUSED(name);
+ UNUSED(initiate);
+ UNUSED(cred);
+
+ return (ISC_R_NOTIMPLEMENTED);
+#endif
+}
+
+isc_boolean_t
+dst_gssapi_identitymatchesrealmkrb5(dns_name_t *signer, dns_name_t *name,
+ dns_name_t *realm)
+{
+#ifdef GSSAPI
+ char sbuf[DNS_NAME_FORMATSIZE];
+ char nbuf[DNS_NAME_FORMATSIZE];
+ char rbuf[DNS_NAME_FORMATSIZE];
+ char *sname;
+ char *rname;
+
+ /*
+ * It is far, far easier to write the names we are looking at into
+ * a string, and do string operations on them.
+ */
+ dns_name_format(signer, sbuf, sizeof(sbuf));
+ if (name != NULL)
+ dns_name_format(name, nbuf, sizeof(nbuf));
+ dns_name_format(realm, rbuf, sizeof(rbuf));
+
+ /*
+ * Find the realm portion. This is the part after the @. If it
+ * does not exist, we don't have something we like, so we fail our
+ * compare.
+ */
+ rname = strstr(sbuf, "\\@");
+ if (rname == NULL)
+ return (isc_boolean_false);
+ *rname = '\0';
+ rname += 2;
+
+ /*
+ * Find the host portion of the signer's name. We do this by
+ * searching for the first / character. We then check to make
+ * certain the instance name is "host"
+ *
+ * This will work for
+ * host/example.com@EXAMPLE.COM
+ */
+ sname = strchr(sbuf, '/');
+ if (sname == NULL)
+ return (isc_boolean_false);
+ *sname = '\0';
+ sname++;
+ if (strcmp(sbuf, "host") != 0)
+ return (isc_boolean_false);
+
+ /*
+ * Now, we do a simple comparison between the name and the realm.
+ */
+ if (name != NULL) {
+ if ((strcasecmp(sname, nbuf) == 0)
+ && (strcmp(rname, rbuf) == 0))
+ return (isc_boolean_true);
+ } else {
+ if (strcmp(rname, rbuf) == 0)
+ return (isc_boolean_true);
+ }
+
+ return (isc_boolean_false);
+#else
+ UNUSED(signer);
+ UNUSED(name);
+ UNUSED(realm);
+ return (isc_boolean_false);
+#endif
+}
+
+isc_boolean_t
+dst_gssapi_identitymatchesrealmms(dns_name_t *signer, dns_name_t *name,
+ dns_name_t *realm)
+{
+#ifdef GSSAPI
+ char sbuf[DNS_NAME_FORMATSIZE];
+ char nbuf[DNS_NAME_FORMATSIZE];
+ char rbuf[DNS_NAME_FORMATSIZE];
+ char *sname;
+ char *nname;
+ char *rname;
+
+ /*
+ * It is far, far easier to write the names we are looking at into
+ * a string, and do string operations on them.
+ */
+ dns_name_format(signer, sbuf, sizeof(sbuf));
+ if (name != NULL)
+ dns_name_format(name, nbuf, sizeof(nbuf));
+ dns_name_format(realm, rbuf, sizeof(rbuf));
+
+ /*
+ * Find the realm portion. This is the part after the @. If it
+ * does not exist, we don't have something we like, so we fail our
+ * compare.
+ */
+ rname = strstr(sbuf, "\\@");
+ if (rname == NULL)
+ return (isc_boolean_false);
+ sname = strstr(sbuf, "\\$");
+ if (sname == NULL)
+ return (isc_boolean_false);
+
+ /*
+ * Verify that the $ and @ follow one another.
+ */
+ if (rname - sname != 2)
+ return (isc_boolean_false);
+
+ /*
+ * Find the host portion of the signer's name. Zero out the $ so
+ * it terminates the signer's name, and skip past the @ for
+ * the realm.
+ *
+ * All service principals in Microsoft format seem to be in
+ * machinename$@EXAMPLE.COM
+ * format.
+ */
+ *rname = '\0';
+ rname += 2;
+ *sname = '\0';
+ sname = sbuf;
+
+ /*
+ * Find the first . in the target name, and make it the end of
+ * the string. The rest of the name has to match the realm.
+ */
+ if (name != NULL) {
+ nname = strchr(nbuf, '.');
+ if (nname == NULL)
+ return (isc_boolean_false);
+ *nname++ = '\0';
+ }
+
+ /*
+ * Now, we do a simple comparison between the name and the realm.
+ */
+ if (name != NULL) {
+ if ((strcasecmp(sname, nbuf) == 0)
+ && (strcmp(rname, rbuf) == 0)
+ && (strcasecmp(nname, rbuf) == 0))
+ return (isc_boolean_true);
+ } else {
+ if (strcmp(rname, rbuf) == 0)
+ return (isc_boolean_true);
+ }
+
+
+ return (isc_boolean_false);
+#else
+ UNUSED(signer);
+ UNUSED(name);
+ UNUSED(realm);
+ return (isc_boolean_false);
+#endif
+}
+
+isc_result_t
+dst_gssapi_releasecred(gss_cred_id_t *cred) {
+#ifdef GSSAPI
+ OM_uint32 gret, minor;
+ char buf[1024];
+
+ REQUIRE(cred != NULL && *cred != NULL);
+
+ gret = gss_release_cred(&minor, cred);
+ if (gret != GSS_S_COMPLETE) {
+ /* Log the error, but still free the credential's memory */
+ gss_log(3, "failed releasing credential: %s",
+ gss_error_tostring(gret, minor, buf, sizeof(buf)));
+ }
+ *cred = NULL;
+
+ return(ISC_R_SUCCESS);
+#else
+ UNUSED(cred);
+
+ return (ISC_R_NOTIMPLEMENTED);
+#endif
+}
+
+isc_result_t
+dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken,
+ isc_buffer_t *outtoken, gss_ctx_id_t *gssctx)
+{
+#ifdef GSSAPI
+ isc_region_t r;
+ isc_buffer_t namebuf;
+ gss_name_t gname;
+ OM_uint32 gret, minor, ret_flags, flags;
+ gss_buffer_desc gintoken, *gintokenp, gouttoken = GSS_C_EMPTY_BUFFER;
+ isc_result_t result;
+ gss_buffer_desc gnamebuf;
+ unsigned char array[DNS_NAME_MAXTEXT + 1];
+ char buf[1024];
+
+ /* Client must pass us a valid gss_ctx_id_t here */
+ REQUIRE(gssctx != NULL);
+
+ isc_buffer_init(&namebuf, array, sizeof(array));
+ name_to_gbuffer(name, &namebuf, &gnamebuf);
+
+ /* Get the name as a GSS name */
+ gret = gss_import_name(&minor, &gnamebuf, GSS_C_NO_OID, &gname);
+ if (gret != GSS_S_COMPLETE) {
+ result = ISC_R_FAILURE;
+ goto out;
+ }
+
+ if (intoken != NULL) {
+ /* Don't call gss_release_buffer for gintoken! */
+ REGION_TO_GBUFFER(*intoken, gintoken);
+ gintokenp = &gintoken;
+ } else {
+ gintokenp = NULL;
+ }
+
+ flags = GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG |
+ GSS_C_SEQUENCE_FLAG | GSS_C_INTEG_FLAG;
+
+ gret = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, gssctx,
+ gname, GSS_SPNEGO_MECHANISM, flags,
+ 0, NULL, gintokenp,
+ NULL, &gouttoken, &ret_flags, NULL);
+
+ if (gret != GSS_S_COMPLETE && gret != GSS_S_CONTINUE_NEEDED) {
+ gss_log(3, "Failure initiating security context");
+ gss_log(3, "%s", gss_error_tostring(gret, minor,
+ buf, sizeof(buf)));
+ result = ISC_R_FAILURE;
+ goto out;
+ }
+
+ /*
+ * XXXSRA Not handled yet: RFC 3645 3.1.1: check ret_flags
+ * MUTUAL and INTEG flags, fail if either not set.
+ */
+
+ /*
+ * RFC 2744 states the a valid output token has a non-zero length.
+ */
+ if (gouttoken.length != 0) {
+ GBUFFER_TO_REGION(gouttoken, r);
+ RETERR(isc_buffer_copyregion(outtoken, &r));
+ (void)gss_release_buffer(&minor, &gouttoken);
+ }
+ (void)gss_release_name(&minor, &gname);
+
+ if (gret == GSS_S_COMPLETE)
+ result = ISC_R_SUCCESS;
+ else
+ result = DNS_R_CONTINUE;
+
+ out:
+ return (result);
+#else
+ UNUSED(name);
+ UNUSED(intoken);
+ UNUSED(outtoken);
+ UNUSED(gssctx);
+
+ return (ISC_R_NOTIMPLEMENTED);
+#endif
+}
+
+isc_result_t
+dst_gssapi_acceptctx(gss_cred_id_t cred,
+ isc_region_t *intoken, isc_buffer_t **outtoken,
+ gss_ctx_id_t *ctxout, dns_name_t *principal,
+ isc_mem_t *mctx)
+{
+#ifdef GSSAPI
+ isc_region_t r;
+ isc_buffer_t namebuf;
+ gss_buffer_desc gnamebuf = GSS_C_EMPTY_BUFFER, gintoken,
+ gouttoken = GSS_C_EMPTY_BUFFER;
+ OM_uint32 gret, minor;
+ gss_ctx_id_t context = GSS_C_NO_CONTEXT;
+ gss_name_t gname = NULL;
+ isc_result_t result;
+ char buf[1024];
+
+ REQUIRE(outtoken != NULL && *outtoken == NULL);
+
+ log_cred(cred);
+
+ REGION_TO_GBUFFER(*intoken, gintoken);
+
+ if (*ctxout == NULL)
+ context = GSS_C_NO_CONTEXT;
+ else
+ context = *ctxout;
+
+ gret = gss_accept_sec_context(&minor, &context, cred, &gintoken,
+ GSS_C_NO_CHANNEL_BINDINGS, &gname,
+ NULL, &gouttoken, NULL, NULL, NULL);
+
+ result = ISC_R_FAILURE;
+
+ switch (gret) {
+ case GSS_S_COMPLETE:
+ result = ISC_R_SUCCESS;
+ break;
+ case GSS_S_CONTINUE_NEEDED:
+ result = DNS_R_CONTINUE;
+ break;
+ case GSS_S_DEFECTIVE_TOKEN:
+ case GSS_S_DEFECTIVE_CREDENTIAL:
+ case GSS_S_BAD_SIG:
+ case GSS_S_DUPLICATE_TOKEN:
+ case GSS_S_OLD_TOKEN:
+ case GSS_S_NO_CRED:
+ case GSS_S_CREDENTIALS_EXPIRED:
+ case GSS_S_BAD_BINDINGS:
+ case GSS_S_NO_CONTEXT:
+ case GSS_S_BAD_MECH:
+ case GSS_S_FAILURE:
+ result = DNS_R_INVALIDTKEY;
+ /* fall through */
+ default:
+ gss_log(3, "failed gss_accept_sec_context: %s",
+ gss_error_tostring(gret, minor, buf, sizeof(buf)));
+ return (result);
+ }
+
+ if (gouttoken.length > 0) {
+ RETERR(isc_buffer_allocate(mctx, outtoken, gouttoken.length));
+ GBUFFER_TO_REGION(gouttoken, r);
+ RETERR(isc_buffer_copyregion(*outtoken, &r));
+ (void)gss_release_buffer(&minor, &gouttoken);
+ }
+
+ if (gret == GSS_S_COMPLETE) {
+ gret = gss_display_name(&minor, gname, &gnamebuf, NULL);
+ if (gret != GSS_S_COMPLETE) {
+ gss_log(3, "failed gss_display_name: %s",
+ gss_error_tostring(gret, minor,
+ buf, sizeof(buf)));
+ RETERR(ISC_R_FAILURE);
+ }
+
+ /*
+ * Compensate for a bug in Solaris8's implementation
+ * of gss_display_name(). Should be harmless in any
+ * case, since principal names really should not
+ * contain null characters.
+ */
+ if (gnamebuf.length > 0 &&
+ ((char *)gnamebuf.value)[gnamebuf.length - 1] == '\0')
+ gnamebuf.length--;
+
+ gss_log(3, "gss-api source name (accept) is %.*s",
+ (int)gnamebuf.length, (char *)gnamebuf.value);
+
+ GBUFFER_TO_REGION(gnamebuf, r);
+ isc_buffer_init(&namebuf, r.base, r.length);
+ isc_buffer_add(&namebuf, r.length);
+
+ RETERR(dns_name_fromtext(principal, &namebuf, dns_rootname,
+ ISC_FALSE, NULL));
+
+ if (gnamebuf.length != 0) {
+ gret = gss_release_buffer(&minor, &gnamebuf);
+ if (gret != GSS_S_COMPLETE)
+ gss_log(3, "failed gss_release_buffer: %s",
+ gss_error_tostring(gret, minor, buf,
+ sizeof(buf)));
+ }
+ }
+
+ *ctxout = context;
+
+ out:
+ if (gname != NULL) {
+ gret = gss_release_name(&minor, &gname);
+ if (gret != GSS_S_COMPLETE)
+ gss_log(3, "failed gss_release_name: %s",
+ gss_error_tostring(gret, minor, buf,
+ sizeof(buf)));
+ }
+
+ return (result);
+#else
+ UNUSED(cred);
+ UNUSED(intoken);
+ UNUSED(outtoken);
+ UNUSED(ctxout);
+ UNUSED(principal);
+ UNUSED(mctx);
+
+ return (ISC_R_NOTIMPLEMENTED);
+#endif
+}
+
+isc_result_t
+dst_gssapi_deletectx(isc_mem_t *mctx, gss_ctx_id_t *gssctx)
+{
+#ifdef GSSAPI
+ OM_uint32 gret, minor;
+ char buf[1024];
+
+ UNUSED(mctx);
+
+ REQUIRE(gssctx != NULL && *gssctx != NULL);
+
+ /* Delete the context from the GSS provider */
+ gret = gss_delete_sec_context(&minor, gssctx, GSS_C_NO_BUFFER);
+ if (gret != GSS_S_COMPLETE) {
+ /* Log the error, but still free the context's memory */
+ gss_log(3, "Failure deleting security context %s",
+ gss_error_tostring(gret, minor, buf, sizeof(buf)));
+ }
+ return(ISC_R_SUCCESS);
+#else
+ UNUSED(mctx);
+ UNUSED(gssctx);
+ return (ISC_R_NOTIMPLEMENTED);
+#endif
+}
+
+char *
+gss_error_tostring(isc_uint32_t major, isc_uint32_t minor,
+ char *buf, size_t buflen) {
+#ifdef GSSAPI
+ gss_buffer_desc msg_minor = GSS_C_EMPTY_BUFFER,
+ msg_major = GSS_C_EMPTY_BUFFER;
+ OM_uint32 msg_ctx, minor_stat;
+
+ /* Handle major status */
+ msg_ctx = 0;
+ (void)gss_display_status(&minor_stat, major, GSS_C_GSS_CODE,
+ GSS_C_NULL_OID, &msg_ctx, &msg_major);
+
+ /* Handle minor status */
+ msg_ctx = 0;
+ (void)gss_display_status(&minor_stat, minor, GSS_C_MECH_CODE,
+ GSS_C_NULL_OID, &msg_ctx, &msg_minor);
+
+ snprintf(buf, buflen, "GSSAPI error: Major = %s, Minor = %s.",
+ (char *)msg_major.value, (char *)msg_minor.value);
+
+ if (msg_major.length != 0)
+ (void)gss_release_buffer(&minor_stat, &msg_major);
+ if (msg_minor.length != 0)
+ (void)gss_release_buffer(&minor_stat, &msg_minor);
+ return(buf);
+#else
+ snprintf(buf, buflen, "GSSAPI error: Major = %u, Minor = %u.",
+ major, minor);
+
+ return (buf);
+#endif
+}
+
+void
+gss_log(int level, const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_TKEY, ISC_LOG_DEBUG(level), fmt, ap);
+ va_end(ap);
+}
+
+/*! \file */
diff --git a/lib/dns/hmac_link.c b/lib/dns/hmac_link.c
new file mode 100644
index 0000000..fce98d7
--- /dev/null
+++ b/lib/dns/hmac_link.c
@@ -0,0 +1,1687 @@
+/*
+ * Portions Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 NETWORK ASSOCIATES 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.
+ *
+ * Portions Copyright (C) 1995-2000 by Network Associates, 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 NETWORK ASSOCIATES 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.
+ */
+
+/*
+ * Principal Author: Brian Wellington
+ * $Id: hmac_link.c,v 1.11 2008/04/01 23:47:10 tbox Exp $
+ */
+
+#include <config.h>
+
+#include <isc/buffer.h>
+#include <isc/hmacmd5.h>
+#include <isc/hmacsha.h>
+#include <isc/md5.h>
+#include <isc/sha1.h>
+#include <isc/mem.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dst/result.h>
+
+#include "dst_internal.h"
+#include "dst_parse.h"
+
+#define HMAC_LEN 64
+#define HMAC_IPAD 0x36
+#define HMAC_OPAD 0x5c
+
+static isc_result_t hmacmd5_fromdns(dst_key_t *key, isc_buffer_t *data);
+
+struct dst_hmacmd5_key {
+ unsigned char key[HMAC_LEN];
+};
+
+static isc_result_t
+getkeybits(dst_key_t *key, struct dst_private_element *element) {
+
+ if (element->length != 2)
+ return (DST_R_INVALIDPRIVATEKEY);
+
+ key->key_bits = (element->data[0] << 8) + element->data[1];
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+hmacmd5_createctx(dst_key_t *key, dst_context_t *dctx) {
+ isc_hmacmd5_t *hmacmd5ctx;
+ dst_hmacmd5_key_t *hkey = key->keydata.hmacmd5;
+
+ hmacmd5ctx = isc_mem_get(dctx->mctx, sizeof(isc_hmacmd5_t));
+ if (hmacmd5ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ isc_hmacmd5_init(hmacmd5ctx, hkey->key, HMAC_LEN);
+ dctx->ctxdata.hmacmd5ctx = hmacmd5ctx;
+ return (ISC_R_SUCCESS);
+}
+
+static void
+hmacmd5_destroyctx(dst_context_t *dctx) {
+ isc_hmacmd5_t *hmacmd5ctx = dctx->ctxdata.hmacmd5ctx;
+
+ if (hmacmd5ctx != NULL) {
+ isc_hmacmd5_invalidate(hmacmd5ctx);
+ isc_mem_put(dctx->mctx, hmacmd5ctx, sizeof(isc_hmacmd5_t));
+ dctx->ctxdata.hmacmd5ctx = NULL;
+ }
+}
+
+static isc_result_t
+hmacmd5_adddata(dst_context_t *dctx, const isc_region_t *data) {
+ isc_hmacmd5_t *hmacmd5ctx = dctx->ctxdata.hmacmd5ctx;
+
+ isc_hmacmd5_update(hmacmd5ctx, data->base, data->length);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+hmacmd5_sign(dst_context_t *dctx, isc_buffer_t *sig) {
+ isc_hmacmd5_t *hmacmd5ctx = dctx->ctxdata.hmacmd5ctx;
+ unsigned char *digest;
+
+ if (isc_buffer_availablelength(sig) < ISC_MD5_DIGESTLENGTH)
+ return (ISC_R_NOSPACE);
+ digest = isc_buffer_used(sig);
+ isc_hmacmd5_sign(hmacmd5ctx, digest);
+ isc_buffer_add(sig, ISC_MD5_DIGESTLENGTH);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+hmacmd5_verify(dst_context_t *dctx, const isc_region_t *sig) {
+ isc_hmacmd5_t *hmacmd5ctx = dctx->ctxdata.hmacmd5ctx;
+
+ if (sig->length > ISC_MD5_DIGESTLENGTH)
+ return (DST_R_VERIFYFAILURE);
+
+ if (isc_hmacmd5_verify2(hmacmd5ctx, sig->base, sig->length))
+ return (ISC_R_SUCCESS);
+ else
+ return (DST_R_VERIFYFAILURE);
+}
+
+static isc_boolean_t
+hmacmd5_compare(const dst_key_t *key1, const dst_key_t *key2) {
+ dst_hmacmd5_key_t *hkey1, *hkey2;
+
+ hkey1 = key1->keydata.hmacmd5;
+ hkey2 = key2->keydata.hmacmd5;
+
+ if (hkey1 == NULL && hkey2 == NULL)
+ return (ISC_TRUE);
+ else if (hkey1 == NULL || hkey2 == NULL)
+ return (ISC_FALSE);
+
+ if (memcmp(hkey1->key, hkey2->key, HMAC_LEN) == 0)
+ return (ISC_TRUE);
+ else
+ return (ISC_FALSE);
+}
+
+static isc_result_t
+hmacmd5_generate(dst_key_t *key, int pseudorandom_ok) {
+ isc_buffer_t b;
+ isc_result_t ret;
+ int bytes;
+ unsigned char data[HMAC_LEN];
+
+ bytes = (key->key_size + 7) / 8;
+ if (bytes > HMAC_LEN) {
+ bytes = HMAC_LEN;
+ key->key_size = HMAC_LEN * 8;
+ }
+
+ memset(data, 0, HMAC_LEN);
+ ret = dst__entropy_getdata(data, bytes, ISC_TF(pseudorandom_ok != 0));
+
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ isc_buffer_init(&b, data, bytes);
+ isc_buffer_add(&b, bytes);
+ ret = hmacmd5_fromdns(key, &b);
+ memset(data, 0, HMAC_LEN);
+
+ return (ret);
+}
+
+static isc_boolean_t
+hmacmd5_isprivate(const dst_key_t *key) {
+ UNUSED(key);
+ return (ISC_TRUE);
+}
+
+static void
+hmacmd5_destroy(dst_key_t *key) {
+ dst_hmacmd5_key_t *hkey = key->keydata.hmacmd5;
+ memset(hkey, 0, sizeof(dst_hmacmd5_key_t));
+ isc_mem_put(key->mctx, hkey, sizeof(dst_hmacmd5_key_t));
+ key->keydata.hmacmd5 = NULL;
+}
+
+static isc_result_t
+hmacmd5_todns(const dst_key_t *key, isc_buffer_t *data) {
+ dst_hmacmd5_key_t *hkey;
+ unsigned int bytes;
+
+ REQUIRE(key->keydata.hmacmd5 != NULL);
+
+ hkey = key->keydata.hmacmd5;
+
+ bytes = (key->key_size + 7) / 8;
+ if (isc_buffer_availablelength(data) < bytes)
+ return (ISC_R_NOSPACE);
+ isc_buffer_putmem(data, hkey->key, bytes);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+hmacmd5_fromdns(dst_key_t *key, isc_buffer_t *data) {
+ dst_hmacmd5_key_t *hkey;
+ int keylen;
+ isc_region_t r;
+ isc_md5_t md5ctx;
+
+ isc_buffer_remainingregion(data, &r);
+ if (r.length == 0)
+ return (ISC_R_SUCCESS);
+
+ hkey = isc_mem_get(key->mctx, sizeof(dst_hmacmd5_key_t));
+ if (hkey == NULL)
+ return (ISC_R_NOMEMORY);
+
+ memset(hkey->key, 0, sizeof(hkey->key));
+
+ if (r.length > HMAC_LEN) {
+ isc_md5_init(&md5ctx);
+ isc_md5_update(&md5ctx, r.base, r.length);
+ isc_md5_final(&md5ctx, hkey->key);
+ keylen = ISC_MD5_DIGESTLENGTH;
+ }
+ else {
+ memcpy(hkey->key, r.base, r.length);
+ keylen = r.length;
+ }
+
+ key->key_size = keylen * 8;
+ key->keydata.hmacmd5 = hkey;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+hmacmd5_tofile(const dst_key_t *key, const char *directory) {
+ int cnt = 0;
+ dst_hmacmd5_key_t *hkey;
+ dst_private_t priv;
+ int bytes = (key->key_size + 7) / 8;
+ unsigned char buf[2];
+
+ if (key->keydata.hmacmd5 == NULL)
+ return (DST_R_NULLKEY);
+
+ hkey = key->keydata.hmacmd5;
+
+ priv.elements[cnt].tag = TAG_HMACMD5_KEY;
+ priv.elements[cnt].length = bytes;
+ priv.elements[cnt++].data = hkey->key;
+
+ buf[0] = (key->key_bits >> 8) & 0xffU;
+ buf[1] = key->key_bits & 0xffU;
+ priv.elements[cnt].tag = TAG_HMACMD5_BITS;
+ priv.elements[cnt].data = buf;
+ priv.elements[cnt++].length = 2;
+
+ priv.nelements = cnt;
+ return (dst__privstruct_writefile(key, &priv, directory));
+}
+
+static isc_result_t
+hmacmd5_parse(dst_key_t *key, isc_lex_t *lexer) {
+ dst_private_t priv;
+ isc_result_t result, tresult;
+ isc_buffer_t b;
+ isc_mem_t *mctx = key->mctx;
+ unsigned int i;
+
+ /* read private key file */
+ result = dst__privstruct_parse(key, DST_ALG_HMACMD5, lexer, mctx, &priv);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ key->key_bits = 0;
+ for (i = 0; i < priv.nelements && result == ISC_R_SUCCESS; i++) {
+ switch (priv.elements[i].tag) {
+ case TAG_HMACMD5_KEY:
+ isc_buffer_init(&b, priv.elements[i].data,
+ priv.elements[i].length);
+ isc_buffer_add(&b, priv.elements[i].length);
+ tresult = hmacmd5_fromdns(key, &b);
+ if (tresult != ISC_R_SUCCESS)
+ result = tresult;
+ break;
+ case TAG_HMACMD5_BITS:
+ tresult = getkeybits(key, &priv.elements[i]);
+ if (tresult != ISC_R_SUCCESS)
+ result = tresult;
+ break;
+ default:
+ result = DST_R_INVALIDPRIVATEKEY;
+ break;
+ }
+ }
+ dst__privstruct_free(&priv, mctx);
+ memset(&priv, 0, sizeof(priv));
+ return (result);
+}
+
+static dst_func_t hmacmd5_functions = {
+ hmacmd5_createctx,
+ hmacmd5_destroyctx,
+ hmacmd5_adddata,
+ hmacmd5_sign,
+ hmacmd5_verify,
+ NULL, /*%< computesecret */
+ hmacmd5_compare,
+ NULL, /*%< paramcompare */
+ hmacmd5_generate,
+ hmacmd5_isprivate,
+ hmacmd5_destroy,
+ hmacmd5_todns,
+ hmacmd5_fromdns,
+ hmacmd5_tofile,
+ hmacmd5_parse,
+ NULL, /*%< cleanup */
+ NULL, /*%< fromlabel */
+};
+
+isc_result_t
+dst__hmacmd5_init(dst_func_t **funcp) {
+ REQUIRE(funcp != NULL);
+ if (*funcp == NULL)
+ *funcp = &hmacmd5_functions;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t hmacsha1_fromdns(dst_key_t *key, isc_buffer_t *data);
+
+struct dst_hmacsha1_key {
+ unsigned char key[ISC_SHA1_DIGESTLENGTH];
+};
+
+static isc_result_t
+hmacsha1_createctx(dst_key_t *key, dst_context_t *dctx) {
+ isc_hmacsha1_t *hmacsha1ctx;
+ dst_hmacsha1_key_t *hkey = key->keydata.hmacsha1;
+
+ hmacsha1ctx = isc_mem_get(dctx->mctx, sizeof(isc_hmacsha1_t));
+ if (hmacsha1ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ isc_hmacsha1_init(hmacsha1ctx, hkey->key, ISC_SHA1_DIGESTLENGTH);
+ dctx->ctxdata.hmacsha1ctx = hmacsha1ctx;
+ return (ISC_R_SUCCESS);
+}
+
+static void
+hmacsha1_destroyctx(dst_context_t *dctx) {
+ isc_hmacsha1_t *hmacsha1ctx = dctx->ctxdata.hmacsha1ctx;
+
+ if (hmacsha1ctx != NULL) {
+ isc_hmacsha1_invalidate(hmacsha1ctx);
+ isc_mem_put(dctx->mctx, hmacsha1ctx, sizeof(isc_hmacsha1_t));
+ dctx->ctxdata.hmacsha1ctx = NULL;
+ }
+}
+
+static isc_result_t
+hmacsha1_adddata(dst_context_t *dctx, const isc_region_t *data) {
+ isc_hmacsha1_t *hmacsha1ctx = dctx->ctxdata.hmacsha1ctx;
+
+ isc_hmacsha1_update(hmacsha1ctx, data->base, data->length);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+hmacsha1_sign(dst_context_t *dctx, isc_buffer_t *sig) {
+ isc_hmacsha1_t *hmacsha1ctx = dctx->ctxdata.hmacsha1ctx;
+ unsigned char *digest;
+
+ if (isc_buffer_availablelength(sig) < ISC_SHA1_DIGESTLENGTH)
+ return (ISC_R_NOSPACE);
+ digest = isc_buffer_used(sig);
+ isc_hmacsha1_sign(hmacsha1ctx, digest, ISC_SHA1_DIGESTLENGTH);
+ isc_buffer_add(sig, ISC_SHA1_DIGESTLENGTH);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+hmacsha1_verify(dst_context_t *dctx, const isc_region_t *sig) {
+ isc_hmacsha1_t *hmacsha1ctx = dctx->ctxdata.hmacsha1ctx;
+
+ if (sig->length > ISC_SHA1_DIGESTLENGTH || sig->length == 0)
+ return (DST_R_VERIFYFAILURE);
+
+ if (isc_hmacsha1_verify(hmacsha1ctx, sig->base, sig->length))
+ return (ISC_R_SUCCESS);
+ else
+ return (DST_R_VERIFYFAILURE);
+}
+
+static isc_boolean_t
+hmacsha1_compare(const dst_key_t *key1, const dst_key_t *key2) {
+ dst_hmacsha1_key_t *hkey1, *hkey2;
+
+ hkey1 = key1->keydata.hmacsha1;
+ hkey2 = key2->keydata.hmacsha1;
+
+ if (hkey1 == NULL && hkey2 == NULL)
+ return (ISC_TRUE);
+ else if (hkey1 == NULL || hkey2 == NULL)
+ return (ISC_FALSE);
+
+ if (memcmp(hkey1->key, hkey2->key, ISC_SHA1_DIGESTLENGTH) == 0)
+ return (ISC_TRUE);
+ else
+ return (ISC_FALSE);
+}
+
+static isc_result_t
+hmacsha1_generate(dst_key_t *key, int pseudorandom_ok) {
+ isc_buffer_t b;
+ isc_result_t ret;
+ int bytes;
+ unsigned char data[HMAC_LEN];
+
+ bytes = (key->key_size + 7) / 8;
+ if (bytes > HMAC_LEN) {
+ bytes = HMAC_LEN;
+ key->key_size = HMAC_LEN * 8;
+ }
+
+ memset(data, 0, HMAC_LEN);
+ ret = dst__entropy_getdata(data, bytes, ISC_TF(pseudorandom_ok != 0));
+
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ isc_buffer_init(&b, data, bytes);
+ isc_buffer_add(&b, bytes);
+ ret = hmacsha1_fromdns(key, &b);
+ memset(data, 0, ISC_SHA1_DIGESTLENGTH);
+
+ return (ret);
+}
+
+static isc_boolean_t
+hmacsha1_isprivate(const dst_key_t *key) {
+ UNUSED(key);
+ return (ISC_TRUE);
+}
+
+static void
+hmacsha1_destroy(dst_key_t *key) {
+ dst_hmacsha1_key_t *hkey = key->keydata.hmacsha1;
+ memset(hkey, 0, sizeof(dst_hmacsha1_key_t));
+ isc_mem_put(key->mctx, hkey, sizeof(dst_hmacsha1_key_t));
+ key->keydata.hmacsha1 = NULL;
+}
+
+static isc_result_t
+hmacsha1_todns(const dst_key_t *key, isc_buffer_t *data) {
+ dst_hmacsha1_key_t *hkey;
+ unsigned int bytes;
+
+ REQUIRE(key->keydata.hmacsha1 != NULL);
+
+ hkey = key->keydata.hmacsha1;
+
+ bytes = (key->key_size + 7) / 8;
+ if (isc_buffer_availablelength(data) < bytes)
+ return (ISC_R_NOSPACE);
+ isc_buffer_putmem(data, hkey->key, bytes);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+hmacsha1_fromdns(dst_key_t *key, isc_buffer_t *data) {
+ dst_hmacsha1_key_t *hkey;
+ int keylen;
+ isc_region_t r;
+ isc_sha1_t sha1ctx;
+
+ isc_buffer_remainingregion(data, &r);
+ if (r.length == 0)
+ return (ISC_R_SUCCESS);
+
+ hkey = isc_mem_get(key->mctx, sizeof(dst_hmacsha1_key_t));
+ if (hkey == NULL)
+ return (ISC_R_NOMEMORY);
+
+ memset(hkey->key, 0, sizeof(hkey->key));
+
+ if (r.length > ISC_SHA1_DIGESTLENGTH) {
+ isc_sha1_init(&sha1ctx);
+ isc_sha1_update(&sha1ctx, r.base, r.length);
+ isc_sha1_final(&sha1ctx, hkey->key);
+ keylen = ISC_SHA1_DIGESTLENGTH;
+ }
+ else {
+ memcpy(hkey->key, r.base, r.length);
+ keylen = r.length;
+ }
+
+ key->key_size = keylen * 8;
+ key->keydata.hmacsha1 = hkey;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+hmacsha1_tofile(const dst_key_t *key, const char *directory) {
+ int cnt = 0;
+ dst_hmacsha1_key_t *hkey;
+ dst_private_t priv;
+ int bytes = (key->key_size + 7) / 8;
+ unsigned char buf[2];
+
+ if (key->keydata.hmacsha1 == NULL)
+ return (DST_R_NULLKEY);
+
+ hkey = key->keydata.hmacsha1;
+
+ priv.elements[cnt].tag = TAG_HMACSHA1_KEY;
+ priv.elements[cnt].length = bytes;
+ priv.elements[cnt++].data = hkey->key;
+
+ buf[0] = (key->key_bits >> 8) & 0xffU;
+ buf[1] = key->key_bits & 0xffU;
+ priv.elements[cnt].tag = TAG_HMACSHA1_BITS;
+ priv.elements[cnt].data = buf;
+ priv.elements[cnt++].length = 2;
+
+ priv.nelements = cnt;
+ return (dst__privstruct_writefile(key, &priv, directory));
+}
+
+static isc_result_t
+hmacsha1_parse(dst_key_t *key, isc_lex_t *lexer) {
+ dst_private_t priv;
+ isc_result_t result, tresult;
+ isc_buffer_t b;
+ isc_mem_t *mctx = key->mctx;
+ unsigned int i;
+
+ /* read private key file */
+ result = dst__privstruct_parse(key, DST_ALG_HMACSHA1, lexer, mctx,
+ &priv);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ key->key_bits = 0;
+ for (i = 0; i < priv.nelements; i++) {
+ switch (priv.elements[i].tag) {
+ case TAG_HMACSHA1_KEY:
+ isc_buffer_init(&b, priv.elements[i].data,
+ priv.elements[i].length);
+ isc_buffer_add(&b, priv.elements[i].length);
+ tresult = hmacsha1_fromdns(key, &b);
+ if (tresult != ISC_R_SUCCESS)
+ result = tresult;
+ break;
+ case TAG_HMACSHA1_BITS:
+ tresult = getkeybits(key, &priv.elements[i]);
+ if (tresult != ISC_R_SUCCESS)
+ result = tresult;
+ break;
+ default:
+ result = DST_R_INVALIDPRIVATEKEY;
+ break;
+ }
+ }
+ dst__privstruct_free(&priv, mctx);
+ memset(&priv, 0, sizeof(priv));
+ return (result);
+}
+
+static dst_func_t hmacsha1_functions = {
+ hmacsha1_createctx,
+ hmacsha1_destroyctx,
+ hmacsha1_adddata,
+ hmacsha1_sign,
+ hmacsha1_verify,
+ NULL, /* computesecret */
+ hmacsha1_compare,
+ NULL, /* paramcompare */
+ hmacsha1_generate,
+ hmacsha1_isprivate,
+ hmacsha1_destroy,
+ hmacsha1_todns,
+ hmacsha1_fromdns,
+ hmacsha1_tofile,
+ hmacsha1_parse,
+ NULL, /* cleanup */
+ NULL, /* fromlabel */
+};
+
+isc_result_t
+dst__hmacsha1_init(dst_func_t **funcp) {
+ REQUIRE(funcp != NULL);
+ if (*funcp == NULL)
+ *funcp = &hmacsha1_functions;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t hmacsha224_fromdns(dst_key_t *key, isc_buffer_t *data);
+
+struct dst_hmacsha224_key {
+ unsigned char key[ISC_SHA224_DIGESTLENGTH];
+};
+
+static isc_result_t
+hmacsha224_createctx(dst_key_t *key, dst_context_t *dctx) {
+ isc_hmacsha224_t *hmacsha224ctx;
+ dst_hmacsha224_key_t *hkey = key->keydata.hmacsha224;
+
+ hmacsha224ctx = isc_mem_get(dctx->mctx, sizeof(isc_hmacsha224_t));
+ if (hmacsha224ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ isc_hmacsha224_init(hmacsha224ctx, hkey->key, ISC_SHA224_DIGESTLENGTH);
+ dctx->ctxdata.hmacsha224ctx = hmacsha224ctx;
+ return (ISC_R_SUCCESS);
+}
+
+static void
+hmacsha224_destroyctx(dst_context_t *dctx) {
+ isc_hmacsha224_t *hmacsha224ctx = dctx->ctxdata.hmacsha224ctx;
+
+ if (hmacsha224ctx != NULL) {
+ isc_hmacsha224_invalidate(hmacsha224ctx);
+ isc_mem_put(dctx->mctx, hmacsha224ctx, sizeof(isc_hmacsha224_t));
+ dctx->ctxdata.hmacsha224ctx = NULL;
+ }
+}
+
+static isc_result_t
+hmacsha224_adddata(dst_context_t *dctx, const isc_region_t *data) {
+ isc_hmacsha224_t *hmacsha224ctx = dctx->ctxdata.hmacsha224ctx;
+
+ isc_hmacsha224_update(hmacsha224ctx, data->base, data->length);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+hmacsha224_sign(dst_context_t *dctx, isc_buffer_t *sig) {
+ isc_hmacsha224_t *hmacsha224ctx = dctx->ctxdata.hmacsha224ctx;
+ unsigned char *digest;
+
+ if (isc_buffer_availablelength(sig) < ISC_SHA224_DIGESTLENGTH)
+ return (ISC_R_NOSPACE);
+ digest = isc_buffer_used(sig);
+ isc_hmacsha224_sign(hmacsha224ctx, digest, ISC_SHA224_DIGESTLENGTH);
+ isc_buffer_add(sig, ISC_SHA224_DIGESTLENGTH);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+hmacsha224_verify(dst_context_t *dctx, const isc_region_t *sig) {
+ isc_hmacsha224_t *hmacsha224ctx = dctx->ctxdata.hmacsha224ctx;
+
+ if (sig->length > ISC_SHA224_DIGESTLENGTH || sig->length == 0)
+ return (DST_R_VERIFYFAILURE);
+
+ if (isc_hmacsha224_verify(hmacsha224ctx, sig->base, sig->length))
+ return (ISC_R_SUCCESS);
+ else
+ return (DST_R_VERIFYFAILURE);
+}
+
+static isc_boolean_t
+hmacsha224_compare(const dst_key_t *key1, const dst_key_t *key2) {
+ dst_hmacsha224_key_t *hkey1, *hkey2;
+
+ hkey1 = key1->keydata.hmacsha224;
+ hkey2 = key2->keydata.hmacsha224;
+
+ if (hkey1 == NULL && hkey2 == NULL)
+ return (ISC_TRUE);
+ else if (hkey1 == NULL || hkey2 == NULL)
+ return (ISC_FALSE);
+
+ if (memcmp(hkey1->key, hkey2->key, ISC_SHA224_DIGESTLENGTH) == 0)
+ return (ISC_TRUE);
+ else
+ return (ISC_FALSE);
+}
+
+static isc_result_t
+hmacsha224_generate(dst_key_t *key, int pseudorandom_ok) {
+ isc_buffer_t b;
+ isc_result_t ret;
+ int bytes;
+ unsigned char data[HMAC_LEN];
+
+ bytes = (key->key_size + 7) / 8;
+ if (bytes > HMAC_LEN) {
+ bytes = HMAC_LEN;
+ key->key_size = HMAC_LEN * 8;
+ }
+
+ memset(data, 0, HMAC_LEN);
+ ret = dst__entropy_getdata(data, bytes, ISC_TF(pseudorandom_ok != 0));
+
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ isc_buffer_init(&b, data, bytes);
+ isc_buffer_add(&b, bytes);
+ ret = hmacsha224_fromdns(key, &b);
+ memset(data, 0, ISC_SHA224_DIGESTLENGTH);
+
+ return (ret);
+}
+
+static isc_boolean_t
+hmacsha224_isprivate(const dst_key_t *key) {
+ UNUSED(key);
+ return (ISC_TRUE);
+}
+
+static void
+hmacsha224_destroy(dst_key_t *key) {
+ dst_hmacsha224_key_t *hkey = key->keydata.hmacsha224;
+ memset(hkey, 0, sizeof(dst_hmacsha224_key_t));
+ isc_mem_put(key->mctx, hkey, sizeof(dst_hmacsha224_key_t));
+ key->keydata.hmacsha224 = NULL;
+}
+
+static isc_result_t
+hmacsha224_todns(const dst_key_t *key, isc_buffer_t *data) {
+ dst_hmacsha224_key_t *hkey;
+ unsigned int bytes;
+
+ REQUIRE(key->keydata.hmacsha224 != NULL);
+
+ hkey = key->keydata.hmacsha224;
+
+ bytes = (key->key_size + 7) / 8;
+ if (isc_buffer_availablelength(data) < bytes)
+ return (ISC_R_NOSPACE);
+ isc_buffer_putmem(data, hkey->key, bytes);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+hmacsha224_fromdns(dst_key_t *key, isc_buffer_t *data) {
+ dst_hmacsha224_key_t *hkey;
+ int keylen;
+ isc_region_t r;
+ isc_sha224_t sha224ctx;
+
+ isc_buffer_remainingregion(data, &r);
+ if (r.length == 0)
+ return (ISC_R_SUCCESS);
+
+ hkey = isc_mem_get(key->mctx, sizeof(dst_hmacsha224_key_t));
+ if (hkey == NULL)
+ return (ISC_R_NOMEMORY);
+
+ memset(hkey->key, 0, sizeof(hkey->key));
+
+ if (r.length > ISC_SHA224_DIGESTLENGTH) {
+ isc_sha224_init(&sha224ctx);
+ isc_sha224_update(&sha224ctx, r.base, r.length);
+ isc_sha224_final(hkey->key, &sha224ctx);
+ keylen = ISC_SHA224_DIGESTLENGTH;
+ }
+ else {
+ memcpy(hkey->key, r.base, r.length);
+ keylen = r.length;
+ }
+
+ key->key_size = keylen * 8;
+ key->keydata.hmacsha224 = hkey;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+hmacsha224_tofile(const dst_key_t *key, const char *directory) {
+ int cnt = 0;
+ dst_hmacsha224_key_t *hkey;
+ dst_private_t priv;
+ int bytes = (key->key_size + 7) / 8;
+ unsigned char buf[2];
+
+ if (key->keydata.hmacsha224 == NULL)
+ return (DST_R_NULLKEY);
+
+ hkey = key->keydata.hmacsha224;
+
+ priv.elements[cnt].tag = TAG_HMACSHA224_KEY;
+ priv.elements[cnt].length = bytes;
+ priv.elements[cnt++].data = hkey->key;
+
+ buf[0] = (key->key_bits >> 8) & 0xffU;
+ buf[1] = key->key_bits & 0xffU;
+ priv.elements[cnt].tag = TAG_HMACSHA224_BITS;
+ priv.elements[cnt].data = buf;
+ priv.elements[cnt++].length = 2;
+
+ priv.nelements = cnt;
+ return (dst__privstruct_writefile(key, &priv, directory));
+}
+
+static isc_result_t
+hmacsha224_parse(dst_key_t *key, isc_lex_t *lexer) {
+ dst_private_t priv;
+ isc_result_t result, tresult;
+ isc_buffer_t b;
+ isc_mem_t *mctx = key->mctx;
+ unsigned int i;
+
+ /* read private key file */
+ result = dst__privstruct_parse(key, DST_ALG_HMACSHA224, lexer, mctx,
+ &priv);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ key->key_bits = 0;
+ for (i = 0; i < priv.nelements; i++) {
+ switch (priv.elements[i].tag) {
+ case TAG_HMACSHA224_KEY:
+ isc_buffer_init(&b, priv.elements[i].data,
+ priv.elements[i].length);
+ isc_buffer_add(&b, priv.elements[i].length);
+ tresult = hmacsha224_fromdns(key, &b);
+ if (tresult != ISC_R_SUCCESS)
+ result = tresult;
+ break;
+ case TAG_HMACSHA224_BITS:
+ tresult = getkeybits(key, &priv.elements[i]);
+ if (tresult != ISC_R_SUCCESS)
+ result = tresult;
+ break;
+ default:
+ result = DST_R_INVALIDPRIVATEKEY;
+ break;
+ }
+ }
+ dst__privstruct_free(&priv, mctx);
+ memset(&priv, 0, sizeof(priv));
+ return (result);
+}
+
+static dst_func_t hmacsha224_functions = {
+ hmacsha224_createctx,
+ hmacsha224_destroyctx,
+ hmacsha224_adddata,
+ hmacsha224_sign,
+ hmacsha224_verify,
+ NULL, /* computesecret */
+ hmacsha224_compare,
+ NULL, /* paramcompare */
+ hmacsha224_generate,
+ hmacsha224_isprivate,
+ hmacsha224_destroy,
+ hmacsha224_todns,
+ hmacsha224_fromdns,
+ hmacsha224_tofile,
+ hmacsha224_parse,
+ NULL, /* cleanup */
+ NULL, /* fromlabel */
+};
+
+isc_result_t
+dst__hmacsha224_init(dst_func_t **funcp) {
+ REQUIRE(funcp != NULL);
+ if (*funcp == NULL)
+ *funcp = &hmacsha224_functions;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t hmacsha256_fromdns(dst_key_t *key, isc_buffer_t *data);
+
+struct dst_hmacsha256_key {
+ unsigned char key[ISC_SHA256_DIGESTLENGTH];
+};
+
+static isc_result_t
+hmacsha256_createctx(dst_key_t *key, dst_context_t *dctx) {
+ isc_hmacsha256_t *hmacsha256ctx;
+ dst_hmacsha256_key_t *hkey = key->keydata.hmacsha256;
+
+ hmacsha256ctx = isc_mem_get(dctx->mctx, sizeof(isc_hmacsha256_t));
+ if (hmacsha256ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ isc_hmacsha256_init(hmacsha256ctx, hkey->key, ISC_SHA256_DIGESTLENGTH);
+ dctx->ctxdata.hmacsha256ctx = hmacsha256ctx;
+ return (ISC_R_SUCCESS);
+}
+
+static void
+hmacsha256_destroyctx(dst_context_t *dctx) {
+ isc_hmacsha256_t *hmacsha256ctx = dctx->ctxdata.hmacsha256ctx;
+
+ if (hmacsha256ctx != NULL) {
+ isc_hmacsha256_invalidate(hmacsha256ctx);
+ isc_mem_put(dctx->mctx, hmacsha256ctx, sizeof(isc_hmacsha256_t));
+ dctx->ctxdata.hmacsha256ctx = NULL;
+ }
+}
+
+static isc_result_t
+hmacsha256_adddata(dst_context_t *dctx, const isc_region_t *data) {
+ isc_hmacsha256_t *hmacsha256ctx = dctx->ctxdata.hmacsha256ctx;
+
+ isc_hmacsha256_update(hmacsha256ctx, data->base, data->length);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+hmacsha256_sign(dst_context_t *dctx, isc_buffer_t *sig) {
+ isc_hmacsha256_t *hmacsha256ctx = dctx->ctxdata.hmacsha256ctx;
+ unsigned char *digest;
+
+ if (isc_buffer_availablelength(sig) < ISC_SHA256_DIGESTLENGTH)
+ return (ISC_R_NOSPACE);
+ digest = isc_buffer_used(sig);
+ isc_hmacsha256_sign(hmacsha256ctx, digest, ISC_SHA256_DIGESTLENGTH);
+ isc_buffer_add(sig, ISC_SHA256_DIGESTLENGTH);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+hmacsha256_verify(dst_context_t *dctx, const isc_region_t *sig) {
+ isc_hmacsha256_t *hmacsha256ctx = dctx->ctxdata.hmacsha256ctx;
+
+ if (sig->length > ISC_SHA256_DIGESTLENGTH || sig->length == 0)
+ return (DST_R_VERIFYFAILURE);
+
+ if (isc_hmacsha256_verify(hmacsha256ctx, sig->base, sig->length))
+ return (ISC_R_SUCCESS);
+ else
+ return (DST_R_VERIFYFAILURE);
+}
+
+static isc_boolean_t
+hmacsha256_compare(const dst_key_t *key1, const dst_key_t *key2) {
+ dst_hmacsha256_key_t *hkey1, *hkey2;
+
+ hkey1 = key1->keydata.hmacsha256;
+ hkey2 = key2->keydata.hmacsha256;
+
+ if (hkey1 == NULL && hkey2 == NULL)
+ return (ISC_TRUE);
+ else if (hkey1 == NULL || hkey2 == NULL)
+ return (ISC_FALSE);
+
+ if (memcmp(hkey1->key, hkey2->key, ISC_SHA256_DIGESTLENGTH) == 0)
+ return (ISC_TRUE);
+ else
+ return (ISC_FALSE);
+}
+
+static isc_result_t
+hmacsha256_generate(dst_key_t *key, int pseudorandom_ok) {
+ isc_buffer_t b;
+ isc_result_t ret;
+ int bytes;
+ unsigned char data[HMAC_LEN];
+
+ bytes = (key->key_size + 7) / 8;
+ if (bytes > HMAC_LEN) {
+ bytes = HMAC_LEN;
+ key->key_size = HMAC_LEN * 8;
+ }
+
+ memset(data, 0, HMAC_LEN);
+ ret = dst__entropy_getdata(data, bytes, ISC_TF(pseudorandom_ok != 0));
+
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ isc_buffer_init(&b, data, bytes);
+ isc_buffer_add(&b, bytes);
+ ret = hmacsha256_fromdns(key, &b);
+ memset(data, 0, ISC_SHA256_DIGESTLENGTH);
+
+ return (ret);
+}
+
+static isc_boolean_t
+hmacsha256_isprivate(const dst_key_t *key) {
+ UNUSED(key);
+ return (ISC_TRUE);
+}
+
+static void
+hmacsha256_destroy(dst_key_t *key) {
+ dst_hmacsha256_key_t *hkey = key->keydata.hmacsha256;
+ memset(hkey, 0, sizeof(dst_hmacsha256_key_t));
+ isc_mem_put(key->mctx, hkey, sizeof(dst_hmacsha256_key_t));
+ key->keydata.hmacsha256 = NULL;
+}
+
+static isc_result_t
+hmacsha256_todns(const dst_key_t *key, isc_buffer_t *data) {
+ dst_hmacsha256_key_t *hkey;
+ unsigned int bytes;
+
+ REQUIRE(key->keydata.hmacsha256 != NULL);
+
+ hkey = key->keydata.hmacsha256;
+
+ bytes = (key->key_size + 7) / 8;
+ if (isc_buffer_availablelength(data) < bytes)
+ return (ISC_R_NOSPACE);
+ isc_buffer_putmem(data, hkey->key, bytes);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+hmacsha256_fromdns(dst_key_t *key, isc_buffer_t *data) {
+ dst_hmacsha256_key_t *hkey;
+ int keylen;
+ isc_region_t r;
+ isc_sha256_t sha256ctx;
+
+ isc_buffer_remainingregion(data, &r);
+ if (r.length == 0)
+ return (ISC_R_SUCCESS);
+
+ hkey = isc_mem_get(key->mctx, sizeof(dst_hmacsha256_key_t));
+ if (hkey == NULL)
+ return (ISC_R_NOMEMORY);
+
+ memset(hkey->key, 0, sizeof(hkey->key));
+
+ if (r.length > ISC_SHA256_DIGESTLENGTH) {
+ isc_sha256_init(&sha256ctx);
+ isc_sha256_update(&sha256ctx, r.base, r.length);
+ isc_sha256_final(hkey->key, &sha256ctx);
+ keylen = ISC_SHA256_DIGESTLENGTH;
+ }
+ else {
+ memcpy(hkey->key, r.base, r.length);
+ keylen = r.length;
+ }
+
+ key->key_size = keylen * 8;
+ key->keydata.hmacsha256 = hkey;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+hmacsha256_tofile(const dst_key_t *key, const char *directory) {
+ int cnt = 0;
+ dst_hmacsha256_key_t *hkey;
+ dst_private_t priv;
+ int bytes = (key->key_size + 7) / 8;
+ unsigned char buf[2];
+
+ if (key->keydata.hmacsha256 == NULL)
+ return (DST_R_NULLKEY);
+
+ hkey = key->keydata.hmacsha256;
+
+ priv.elements[cnt].tag = TAG_HMACSHA256_KEY;
+ priv.elements[cnt].length = bytes;
+ priv.elements[cnt++].data = hkey->key;
+
+ buf[0] = (key->key_bits >> 8) & 0xffU;
+ buf[1] = key->key_bits & 0xffU;
+ priv.elements[cnt].tag = TAG_HMACSHA256_BITS;
+ priv.elements[cnt].data = buf;
+ priv.elements[cnt++].length = 2;
+
+ priv.nelements = cnt;
+ return (dst__privstruct_writefile(key, &priv, directory));
+}
+
+static isc_result_t
+hmacsha256_parse(dst_key_t *key, isc_lex_t *lexer) {
+ dst_private_t priv;
+ isc_result_t result, tresult;
+ isc_buffer_t b;
+ isc_mem_t *mctx = key->mctx;
+ unsigned int i;
+
+ /* read private key file */
+ result = dst__privstruct_parse(key, DST_ALG_HMACSHA256, lexer, mctx,
+ &priv);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ key->key_bits = 0;
+ for (i = 0; i < priv.nelements; i++) {
+ switch (priv.elements[i].tag) {
+ case TAG_HMACSHA256_KEY:
+ isc_buffer_init(&b, priv.elements[i].data,
+ priv.elements[i].length);
+ isc_buffer_add(&b, priv.elements[i].length);
+ tresult = hmacsha256_fromdns(key, &b);
+ if (tresult != ISC_R_SUCCESS)
+ result = tresult;
+ break;
+ case TAG_HMACSHA256_BITS:
+ tresult = getkeybits(key, &priv.elements[i]);
+ if (tresult != ISC_R_SUCCESS)
+ result = tresult;
+ break;
+ default:
+ result = DST_R_INVALIDPRIVATEKEY;
+ break;
+ }
+ }
+ dst__privstruct_free(&priv, mctx);
+ memset(&priv, 0, sizeof(priv));
+ return (result);
+}
+
+static dst_func_t hmacsha256_functions = {
+ hmacsha256_createctx,
+ hmacsha256_destroyctx,
+ hmacsha256_adddata,
+ hmacsha256_sign,
+ hmacsha256_verify,
+ NULL, /* computesecret */
+ hmacsha256_compare,
+ NULL, /* paramcompare */
+ hmacsha256_generate,
+ hmacsha256_isprivate,
+ hmacsha256_destroy,
+ hmacsha256_todns,
+ hmacsha256_fromdns,
+ hmacsha256_tofile,
+ hmacsha256_parse,
+ NULL, /* cleanup */
+ NULL, /* fromlabel */
+};
+
+isc_result_t
+dst__hmacsha256_init(dst_func_t **funcp) {
+ REQUIRE(funcp != NULL);
+ if (*funcp == NULL)
+ *funcp = &hmacsha256_functions;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t hmacsha384_fromdns(dst_key_t *key, isc_buffer_t *data);
+
+struct dst_hmacsha384_key {
+ unsigned char key[ISC_SHA384_DIGESTLENGTH];
+};
+
+static isc_result_t
+hmacsha384_createctx(dst_key_t *key, dst_context_t *dctx) {
+ isc_hmacsha384_t *hmacsha384ctx;
+ dst_hmacsha384_key_t *hkey = key->keydata.hmacsha384;
+
+ hmacsha384ctx = isc_mem_get(dctx->mctx, sizeof(isc_hmacsha384_t));
+ if (hmacsha384ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ isc_hmacsha384_init(hmacsha384ctx, hkey->key, ISC_SHA384_DIGESTLENGTH);
+ dctx->ctxdata.hmacsha384ctx = hmacsha384ctx;
+ return (ISC_R_SUCCESS);
+}
+
+static void
+hmacsha384_destroyctx(dst_context_t *dctx) {
+ isc_hmacsha384_t *hmacsha384ctx = dctx->ctxdata.hmacsha384ctx;
+
+ if (hmacsha384ctx != NULL) {
+ isc_hmacsha384_invalidate(hmacsha384ctx);
+ isc_mem_put(dctx->mctx, hmacsha384ctx, sizeof(isc_hmacsha384_t));
+ dctx->ctxdata.hmacsha384ctx = NULL;
+ }
+}
+
+static isc_result_t
+hmacsha384_adddata(dst_context_t *dctx, const isc_region_t *data) {
+ isc_hmacsha384_t *hmacsha384ctx = dctx->ctxdata.hmacsha384ctx;
+
+ isc_hmacsha384_update(hmacsha384ctx, data->base, data->length);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+hmacsha384_sign(dst_context_t *dctx, isc_buffer_t *sig) {
+ isc_hmacsha384_t *hmacsha384ctx = dctx->ctxdata.hmacsha384ctx;
+ unsigned char *digest;
+
+ if (isc_buffer_availablelength(sig) < ISC_SHA384_DIGESTLENGTH)
+ return (ISC_R_NOSPACE);
+ digest = isc_buffer_used(sig);
+ isc_hmacsha384_sign(hmacsha384ctx, digest, ISC_SHA384_DIGESTLENGTH);
+ isc_buffer_add(sig, ISC_SHA384_DIGESTLENGTH);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+hmacsha384_verify(dst_context_t *dctx, const isc_region_t *sig) {
+ isc_hmacsha384_t *hmacsha384ctx = dctx->ctxdata.hmacsha384ctx;
+
+ if (sig->length > ISC_SHA384_DIGESTLENGTH || sig->length == 0)
+ return (DST_R_VERIFYFAILURE);
+
+ if (isc_hmacsha384_verify(hmacsha384ctx, sig->base, sig->length))
+ return (ISC_R_SUCCESS);
+ else
+ return (DST_R_VERIFYFAILURE);
+}
+
+static isc_boolean_t
+hmacsha384_compare(const dst_key_t *key1, const dst_key_t *key2) {
+ dst_hmacsha384_key_t *hkey1, *hkey2;
+
+ hkey1 = key1->keydata.hmacsha384;
+ hkey2 = key2->keydata.hmacsha384;
+
+ if (hkey1 == NULL && hkey2 == NULL)
+ return (ISC_TRUE);
+ else if (hkey1 == NULL || hkey2 == NULL)
+ return (ISC_FALSE);
+
+ if (memcmp(hkey1->key, hkey2->key, ISC_SHA384_DIGESTLENGTH) == 0)
+ return (ISC_TRUE);
+ else
+ return (ISC_FALSE);
+}
+
+static isc_result_t
+hmacsha384_generate(dst_key_t *key, int pseudorandom_ok) {
+ isc_buffer_t b;
+ isc_result_t ret;
+ int bytes;
+ unsigned char data[HMAC_LEN];
+
+ bytes = (key->key_size + 7) / 8;
+ if (bytes > HMAC_LEN) {
+ bytes = HMAC_LEN;
+ key->key_size = HMAC_LEN * 8;
+ }
+
+ memset(data, 0, HMAC_LEN);
+ ret = dst__entropy_getdata(data, bytes, ISC_TF(pseudorandom_ok != 0));
+
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ isc_buffer_init(&b, data, bytes);
+ isc_buffer_add(&b, bytes);
+ ret = hmacsha384_fromdns(key, &b);
+ memset(data, 0, ISC_SHA384_DIGESTLENGTH);
+
+ return (ret);
+}
+
+static isc_boolean_t
+hmacsha384_isprivate(const dst_key_t *key) {
+ UNUSED(key);
+ return (ISC_TRUE);
+}
+
+static void
+hmacsha384_destroy(dst_key_t *key) {
+ dst_hmacsha384_key_t *hkey = key->keydata.hmacsha384;
+ memset(hkey, 0, sizeof(dst_hmacsha384_key_t));
+ isc_mem_put(key->mctx, hkey, sizeof(dst_hmacsha384_key_t));
+ key->keydata.hmacsha384 = NULL;
+}
+
+static isc_result_t
+hmacsha384_todns(const dst_key_t *key, isc_buffer_t *data) {
+ dst_hmacsha384_key_t *hkey;
+ unsigned int bytes;
+
+ REQUIRE(key->keydata.hmacsha384 != NULL);
+
+ hkey = key->keydata.hmacsha384;
+
+ bytes = (key->key_size + 7) / 8;
+ if (isc_buffer_availablelength(data) < bytes)
+ return (ISC_R_NOSPACE);
+ isc_buffer_putmem(data, hkey->key, bytes);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+hmacsha384_fromdns(dst_key_t *key, isc_buffer_t *data) {
+ dst_hmacsha384_key_t *hkey;
+ int keylen;
+ isc_region_t r;
+ isc_sha384_t sha384ctx;
+
+ isc_buffer_remainingregion(data, &r);
+ if (r.length == 0)
+ return (ISC_R_SUCCESS);
+
+ hkey = isc_mem_get(key->mctx, sizeof(dst_hmacsha384_key_t));
+ if (hkey == NULL)
+ return (ISC_R_NOMEMORY);
+
+ memset(hkey->key, 0, sizeof(hkey->key));
+
+ if (r.length > ISC_SHA384_DIGESTLENGTH) {
+ isc_sha384_init(&sha384ctx);
+ isc_sha384_update(&sha384ctx, r.base, r.length);
+ isc_sha384_final(hkey->key, &sha384ctx);
+ keylen = ISC_SHA384_DIGESTLENGTH;
+ }
+ else {
+ memcpy(hkey->key, r.base, r.length);
+ keylen = r.length;
+ }
+
+ key->key_size = keylen * 8;
+ key->keydata.hmacsha384 = hkey;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+hmacsha384_tofile(const dst_key_t *key, const char *directory) {
+ int cnt = 0;
+ dst_hmacsha384_key_t *hkey;
+ dst_private_t priv;
+ int bytes = (key->key_size + 7) / 8;
+ unsigned char buf[2];
+
+ if (key->keydata.hmacsha384 == NULL)
+ return (DST_R_NULLKEY);
+
+ hkey = key->keydata.hmacsha384;
+
+ priv.elements[cnt].tag = TAG_HMACSHA384_KEY;
+ priv.elements[cnt].length = bytes;
+ priv.elements[cnt++].data = hkey->key;
+
+ buf[0] = (key->key_bits >> 8) & 0xffU;
+ buf[1] = key->key_bits & 0xffU;
+ priv.elements[cnt].tag = TAG_HMACSHA384_BITS;
+ priv.elements[cnt].data = buf;
+ priv.elements[cnt++].length = 2;
+
+ priv.nelements = cnt;
+ return (dst__privstruct_writefile(key, &priv, directory));
+}
+
+static isc_result_t
+hmacsha384_parse(dst_key_t *key, isc_lex_t *lexer) {
+ dst_private_t priv;
+ isc_result_t result, tresult;
+ isc_buffer_t b;
+ isc_mem_t *mctx = key->mctx;
+ unsigned int i;
+
+ /* read private key file */
+ result = dst__privstruct_parse(key, DST_ALG_HMACSHA384, lexer, mctx,
+ &priv);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ key->key_bits = 0;
+ for (i = 0; i < priv.nelements; i++) {
+ switch (priv.elements[i].tag) {
+ case TAG_HMACSHA384_KEY:
+ isc_buffer_init(&b, priv.elements[i].data,
+ priv.elements[i].length);
+ isc_buffer_add(&b, priv.elements[i].length);
+ tresult = hmacsha384_fromdns(key, &b);
+ if (tresult != ISC_R_SUCCESS)
+ result = tresult;
+ break;
+ case TAG_HMACSHA384_BITS:
+ tresult = getkeybits(key, &priv.elements[i]);
+ if (tresult != ISC_R_SUCCESS)
+ result = tresult;
+ break;
+ default:
+ result = DST_R_INVALIDPRIVATEKEY;
+ break;
+ }
+ }
+ dst__privstruct_free(&priv, mctx);
+ memset(&priv, 0, sizeof(priv));
+ return (result);
+}
+
+static dst_func_t hmacsha384_functions = {
+ hmacsha384_createctx,
+ hmacsha384_destroyctx,
+ hmacsha384_adddata,
+ hmacsha384_sign,
+ hmacsha384_verify,
+ NULL, /* computesecret */
+ hmacsha384_compare,
+ NULL, /* paramcompare */
+ hmacsha384_generate,
+ hmacsha384_isprivate,
+ hmacsha384_destroy,
+ hmacsha384_todns,
+ hmacsha384_fromdns,
+ hmacsha384_tofile,
+ hmacsha384_parse,
+ NULL, /* cleanup */
+ NULL, /* fromlabel */
+};
+
+isc_result_t
+dst__hmacsha384_init(dst_func_t **funcp) {
+ REQUIRE(funcp != NULL);
+ if (*funcp == NULL)
+ *funcp = &hmacsha384_functions;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t hmacsha512_fromdns(dst_key_t *key, isc_buffer_t *data);
+
+struct dst_hmacsha512_key {
+ unsigned char key[ISC_SHA512_DIGESTLENGTH];
+};
+
+static isc_result_t
+hmacsha512_createctx(dst_key_t *key, dst_context_t *dctx) {
+ isc_hmacsha512_t *hmacsha512ctx;
+ dst_hmacsha512_key_t *hkey = key->keydata.hmacsha512;
+
+ hmacsha512ctx = isc_mem_get(dctx->mctx, sizeof(isc_hmacsha512_t));
+ if (hmacsha512ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ isc_hmacsha512_init(hmacsha512ctx, hkey->key, ISC_SHA512_DIGESTLENGTH);
+ dctx->ctxdata.hmacsha512ctx = hmacsha512ctx;
+ return (ISC_R_SUCCESS);
+}
+
+static void
+hmacsha512_destroyctx(dst_context_t *dctx) {
+ isc_hmacsha512_t *hmacsha512ctx = dctx->ctxdata.hmacsha512ctx;
+
+ if (hmacsha512ctx != NULL) {
+ isc_hmacsha512_invalidate(hmacsha512ctx);
+ isc_mem_put(dctx->mctx, hmacsha512ctx, sizeof(isc_hmacsha512_t));
+ dctx->ctxdata.hmacsha512ctx = NULL;
+ }
+}
+
+static isc_result_t
+hmacsha512_adddata(dst_context_t *dctx, const isc_region_t *data) {
+ isc_hmacsha512_t *hmacsha512ctx = dctx->ctxdata.hmacsha512ctx;
+
+ isc_hmacsha512_update(hmacsha512ctx, data->base, data->length);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+hmacsha512_sign(dst_context_t *dctx, isc_buffer_t *sig) {
+ isc_hmacsha512_t *hmacsha512ctx = dctx->ctxdata.hmacsha512ctx;
+ unsigned char *digest;
+
+ if (isc_buffer_availablelength(sig) < ISC_SHA512_DIGESTLENGTH)
+ return (ISC_R_NOSPACE);
+ digest = isc_buffer_used(sig);
+ isc_hmacsha512_sign(hmacsha512ctx, digest, ISC_SHA512_DIGESTLENGTH);
+ isc_buffer_add(sig, ISC_SHA512_DIGESTLENGTH);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+hmacsha512_verify(dst_context_t *dctx, const isc_region_t *sig) {
+ isc_hmacsha512_t *hmacsha512ctx = dctx->ctxdata.hmacsha512ctx;
+
+ if (sig->length > ISC_SHA512_DIGESTLENGTH || sig->length == 0)
+ return (DST_R_VERIFYFAILURE);
+
+ if (isc_hmacsha512_verify(hmacsha512ctx, sig->base, sig->length))
+ return (ISC_R_SUCCESS);
+ else
+ return (DST_R_VERIFYFAILURE);
+}
+
+static isc_boolean_t
+hmacsha512_compare(const dst_key_t *key1, const dst_key_t *key2) {
+ dst_hmacsha512_key_t *hkey1, *hkey2;
+
+ hkey1 = key1->keydata.hmacsha512;
+ hkey2 = key2->keydata.hmacsha512;
+
+ if (hkey1 == NULL && hkey2 == NULL)
+ return (ISC_TRUE);
+ else if (hkey1 == NULL || hkey2 == NULL)
+ return (ISC_FALSE);
+
+ if (memcmp(hkey1->key, hkey2->key, ISC_SHA512_DIGESTLENGTH) == 0)
+ return (ISC_TRUE);
+ else
+ return (ISC_FALSE);
+}
+
+static isc_result_t
+hmacsha512_generate(dst_key_t *key, int pseudorandom_ok) {
+ isc_buffer_t b;
+ isc_result_t ret;
+ int bytes;
+ unsigned char data[HMAC_LEN];
+
+ bytes = (key->key_size + 7) / 8;
+ if (bytes > HMAC_LEN) {
+ bytes = HMAC_LEN;
+ key->key_size = HMAC_LEN * 8;
+ }
+
+ memset(data, 0, HMAC_LEN);
+ ret = dst__entropy_getdata(data, bytes, ISC_TF(pseudorandom_ok != 0));
+
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ isc_buffer_init(&b, data, bytes);
+ isc_buffer_add(&b, bytes);
+ ret = hmacsha512_fromdns(key, &b);
+ memset(data, 0, ISC_SHA512_DIGESTLENGTH);
+
+ return (ret);
+}
+
+static isc_boolean_t
+hmacsha512_isprivate(const dst_key_t *key) {
+ UNUSED(key);
+ return (ISC_TRUE);
+}
+
+static void
+hmacsha512_destroy(dst_key_t *key) {
+ dst_hmacsha512_key_t *hkey = key->keydata.hmacsha512;
+ memset(hkey, 0, sizeof(dst_hmacsha512_key_t));
+ isc_mem_put(key->mctx, hkey, sizeof(dst_hmacsha512_key_t));
+ key->keydata.hmacsha512 = NULL;
+}
+
+static isc_result_t
+hmacsha512_todns(const dst_key_t *key, isc_buffer_t *data) {
+ dst_hmacsha512_key_t *hkey;
+ unsigned int bytes;
+
+ REQUIRE(key->keydata.hmacsha512 != NULL);
+
+ hkey = key->keydata.hmacsha512;
+
+ bytes = (key->key_size + 7) / 8;
+ if (isc_buffer_availablelength(data) < bytes)
+ return (ISC_R_NOSPACE);
+ isc_buffer_putmem(data, hkey->key, bytes);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+hmacsha512_fromdns(dst_key_t *key, isc_buffer_t *data) {
+ dst_hmacsha512_key_t *hkey;
+ int keylen;
+ isc_region_t r;
+ isc_sha512_t sha512ctx;
+
+ isc_buffer_remainingregion(data, &r);
+ if (r.length == 0)
+ return (ISC_R_SUCCESS);
+
+ hkey = isc_mem_get(key->mctx, sizeof(dst_hmacsha512_key_t));
+ if (hkey == NULL)
+ return (ISC_R_NOMEMORY);
+
+ memset(hkey->key, 0, sizeof(hkey->key));
+
+ if (r.length > ISC_SHA512_DIGESTLENGTH) {
+ isc_sha512_init(&sha512ctx);
+ isc_sha512_update(&sha512ctx, r.base, r.length);
+ isc_sha512_final(hkey->key, &sha512ctx);
+ keylen = ISC_SHA512_DIGESTLENGTH;
+ }
+ else {
+ memcpy(hkey->key, r.base, r.length);
+ keylen = r.length;
+ }
+
+ key->key_size = keylen * 8;
+ key->keydata.hmacsha512 = hkey;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+hmacsha512_tofile(const dst_key_t *key, const char *directory) {
+ int cnt = 0;
+ dst_hmacsha512_key_t *hkey;
+ dst_private_t priv;
+ int bytes = (key->key_size + 7) / 8;
+ unsigned char buf[2];
+
+ if (key->keydata.hmacsha512 == NULL)
+ return (DST_R_NULLKEY);
+
+ hkey = key->keydata.hmacsha512;
+
+ priv.elements[cnt].tag = TAG_HMACSHA512_KEY;
+ priv.elements[cnt].length = bytes;
+ priv.elements[cnt++].data = hkey->key;
+
+ buf[0] = (key->key_bits >> 8) & 0xffU;
+ buf[1] = key->key_bits & 0xffU;
+ priv.elements[cnt].tag = TAG_HMACSHA512_BITS;
+ priv.elements[cnt].data = buf;
+ priv.elements[cnt++].length = 2;
+
+ priv.nelements = cnt;
+ return (dst__privstruct_writefile(key, &priv, directory));
+}
+
+static isc_result_t
+hmacsha512_parse(dst_key_t *key, isc_lex_t *lexer) {
+ dst_private_t priv;
+ isc_result_t result, tresult;
+ isc_buffer_t b;
+ isc_mem_t *mctx = key->mctx;
+ unsigned int i;
+
+ /* read private key file */
+ result = dst__privstruct_parse(key, DST_ALG_HMACSHA512, lexer, mctx,
+ &priv);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ key->key_bits = 0;
+ for (i = 0; i < priv.nelements; i++) {
+ switch (priv.elements[i].tag) {
+ case TAG_HMACSHA512_KEY:
+ isc_buffer_init(&b, priv.elements[i].data,
+ priv.elements[i].length);
+ isc_buffer_add(&b, priv.elements[i].length);
+ tresult = hmacsha512_fromdns(key, &b);
+ if (tresult != ISC_R_SUCCESS)
+ result = tresult;
+ break;
+ case TAG_HMACSHA512_BITS:
+ tresult = getkeybits(key, &priv.elements[i]);
+ if (tresult != ISC_R_SUCCESS)
+ result = tresult;
+ break;
+ default:
+ result = DST_R_INVALIDPRIVATEKEY;
+ break;
+ }
+ }
+ dst__privstruct_free(&priv, mctx);
+ memset(&priv, 0, sizeof(priv));
+ return (result);
+}
+
+static dst_func_t hmacsha512_functions = {
+ hmacsha512_createctx,
+ hmacsha512_destroyctx,
+ hmacsha512_adddata,
+ hmacsha512_sign,
+ hmacsha512_verify,
+ NULL, /* computesecret */
+ hmacsha512_compare,
+ NULL, /* paramcompare */
+ hmacsha512_generate,
+ hmacsha512_isprivate,
+ hmacsha512_destroy,
+ hmacsha512_todns,
+ hmacsha512_fromdns,
+ hmacsha512_tofile,
+ hmacsha512_parse,
+ NULL, /* cleanup */
+ NULL, /* fromlabel */
+};
+
+isc_result_t
+dst__hmacsha512_init(dst_func_t **funcp) {
+ REQUIRE(funcp != NULL);
+ if (*funcp == NULL)
+ *funcp = &hmacsha512_functions;
+ return (ISC_R_SUCCESS);
+}
+
+/*! \file */
diff --git a/lib/dns/include/Makefile.in b/lib/dns/include/Makefile.in
new file mode 100644
index 0000000..b52cb98
--- /dev/null
+++ b/lib/dns/include/Makefile.in
@@ -0,0 +1,25 @@
+# Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 1998-2001 Internet Software Consortium.
+#
+# 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 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.
+
+# $Id: Makefile.in,v 1.15 2007/06/19 23:47:16 tbox Exp $
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+top_srcdir = @top_srcdir@
+
+SUBDIRS = dns dst
+TARGETS =
+
+@BIND9_MAKE_RULES@
diff --git a/lib/dns/include/dns/Makefile.in b/lib/dns/include/dns/Makefile.in
new file mode 100644
index 0000000..e9e049e
--- /dev/null
+++ b/lib/dns/include/dns/Makefile.in
@@ -0,0 +1,54 @@
+# Copyright (C) 2004, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 1998-2003 Internet Software Consortium.
+#
+# 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 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.
+
+# $Id: Makefile.in,v 1.55 2008/11/14 23:47:33 tbox Exp $
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+top_srcdir = @top_srcdir@
+
+@BIND9_VERSION@
+
+HEADERS = acl.h adb.h byaddr.h cache.h callbacks.h \
+ cert.h compress.h \
+ db.h dbiterator.h dbtable.h diff.h dispatch.h dlz.h \
+ dnssec.h ds.h events.h fixedname.h iptable.h journal.h keyflags.h \
+ keytable.h keyvalues.h lib.h log.h master.h masterdump.h \
+ message.h name.h ncache.h \
+ nsec.h peer.h portlist.h rbt.h rcode.h \
+ rdata.h rdataclass.h rdatalist.h rdataset.h rdatasetiter.h \
+ rdataslab.h rdatatype.h request.h resolver.h result.h \
+ rootns.h sdb.h sdlz.h secalg.h secproto.h soa.h ssu.h \
+ tcpmsg.h time.h tkey.h \
+ tsig.h ttl.h types.h validator.h version.h view.h xfrin.h \
+ zone.h zonekey.h zt.h
+
+GENHEADERS = enumclass.h enumtype.h rdatastruct.h
+
+SUBDIRS =
+TARGETS =
+
+@BIND9_MAKE_RULES@
+
+installdirs:
+ $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${includedir}/dns
+
+install:: installdirs
+ for i in ${HEADERS}; do \
+ ${INSTALL_DATA} ${srcdir}/$$i ${DESTDIR}${includedir}/dns ; \
+ done
+ for i in ${GENHEADERS}; do \
+ ${INSTALL_DATA} $$i ${DESTDIR}${includedir}/dns ; \
+ done
diff --git a/lib/dns/include/dns/acache.h b/lib/dns/include/dns/acache.h
new file mode 100644
index 0000000..28990c2
--- /dev/null
+++ b/lib/dns/include/dns/acache.h
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2004, 2006, 2007 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 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.
+ */
+
+/* $Id: acache.h,v 1.8 2007/06/19 23:47:16 tbox Exp $ */
+
+#ifndef DNS_ACACHE_H
+#define DNS_ACACHE_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*
+ * Acache
+ *
+ * The Additional Cache Object
+ *
+ * This module manages internal caching entries that correspond to
+ * the additional section data of a DNS DB node (an RRset header, more
+ * accurately). An additional cache entry is expected to be (somehow)
+ * attached to a particular RR in a particular DB node, and contains a set
+ * of information of an additional data for the DB node.
+ *
+ * An additional cache object is intended to be created as a per-view
+ * object, and manages all cache entries within the view.
+ *
+ * The intended usage of the additional caching is to provide a short cut
+ * to additional glue RRs of an NS RR. For each NS RR, it is often
+ * necessary to look for glue RRs to make a proper response. Once the
+ * glue RRs are known, the additional caching allows the client to
+ * associate the information to the original NS RR so that further
+ * expensive lookups can be avoided for the NS RR.
+ *
+ * Each additional cache entry contains information to identify a
+ * particular DB node and (optionally) an associated RRset. The
+ * information consists of its zone, database, the version of the
+ * database, database node, and RRset.
+ *
+ * A "negative" information can also be cached. For example, if a glue
+ * RR does not exist as an authoritative data in the same zone as that
+ * of the NS RR, this fact can be cached by specifying a NULL pointer
+ * for the database, version, and node. (See the description for
+ * dns_acache_getentry() below for more details.)
+ *
+ * Since each member stored in an additional cache entry holds a reference
+ * to a corresponding object, a stale cache entry may cause unnecessary
+ * memory consumption. For instance, when a zone is reloaded, additional
+ * cache entries that have a reference to the zone (and its DB and/or
+ * DB nodes) can delay the cleanup of the referred objects. In order to
+ * minimize such a bad effect, this module provides several cleanup
+ * mechanisms.
+ *
+ * The first one is a shutdown procedure called when the associated view
+ * is shut down. In this case, dns_acache_shutdown() will be called and
+ * all cache entries will be purged. This mechanism will help the
+ * situation when the configuration is reloaded or the main server is
+ * stopped.
+ *
+ * Per-DB cleanup mechanism is also provided. Each additional cache entry
+ * is associated with related DB, which is expected to have been
+ * registered when the DB was created by dns_acache_setdb(). If a
+ * particular DB is going to be destroyed, the primary holder of the DB,
+ * a typical example of which is a zone, will call dns_acache_putdb().
+ * Then this module will clean-up all cache entries associated with the
+ * DB. This mechanism is effective when a secondary zone DB is going to
+ * be stale after a zone transfer.
+ *
+ * Finally, this module supports for periodic clean-up of stale entries.
+ * Each cache entry has a timestamp field, which is updated every time
+ * the entry is referred. A periodically invoked cleaner checks the
+ * timestamp of each entry, and purge entries that have not been referred
+ * for a certain period. The cleaner interval can be specified by
+ * dns_acache_setcleaninginterval(). If the periodic clean-up is not
+ * enough, it is also possible to specify the upper limit of entries
+ * in terms of the memory consumption. If the maximum value is
+ * specified, the cleaner is invoked when the memory consumption reaches
+ * the high watermark inferred from the maximum value. In this case,
+ * the cleaner will use more aggressive algorithm to decide the "victim"
+ * entries. The maximum value can be specified by
+ * dns_acache_setcachesize().
+ *
+ * When a cache entry is going to be purged within this module, the
+ * callback function specified at the creation time will be called.
+ * The callback function is expected to release all internal resources
+ * related to the entry, which will typically be specific to DB
+ * implementation, and to call dns_acache_detachentry(). The callback
+ * mechanism is very important, since the holder of an additional cache
+ * entry may not be able to initiate the clean-up of the entry, due to
+ * the reference ordering. For example, as long as an additional cache
+ * entry has a reference to a DB object, the DB cannot be freed, in which
+ * a DB node may have a reference to the cache entry.
+ *
+ * Credits:
+ * The basic idea of this kind of short-cut for frequently used
+ * information is similar to the "pre-compiled answer" approach adopted
+ * in nsd by NLnet LABS with RIPE NCC. Our work here is an independent
+ * effort, but the success of nsd encouraged us to pursue this path.
+ *
+ * The design and implementation of the periodic memory management and
+ * the upper limitation of memory consumption was derived from the cache
+ * DB implementation of BIND9.
+ *
+ * MP:
+ * There are two main locks in this module. One is for each entry, and
+ * the other is for the additional cache object.
+ *
+ * Reliability:
+ * The callback function for a cache entry is called with holding the
+ * entry lock. Thus, it implicitly assumes the callback function does not
+ * call a function that can require the lock. Typically, the only
+ * function that can be called from the callback function safely is
+ * dns_acache_detachentry(). The breakage of this implicit assumption
+ * may cause a deadlock.
+ *
+ * Resources:
+ * In a 32-bit architecture (such as i386), the following additional
+ * memory is required comparing to the case that disables this module.
+ * - 76 bytes for each additional cache entry
+ * - if the entry has a DNS name and associated RRset,
+ * * 44 bytes + size of the name (1-255 bytes)
+ * * 52 bytes x number_of_RRs
+ * - 28 bytes for each DB related to this module
+ *
+ * Using the additional cache also requires extra memory consumption in
+ * the DB implementation. In the current implementation for rbtdb, we
+ * need:
+ * - two additional pointers for each DB node (8 bytes for a 32-bit
+ * architecture
+ * - for each RR associated to an RR in a DB node, we also need
+ * a pointer and management objects to support the additional cache
+ * function. These are allocated on-demand. The total size is
+ * 32 bytes for a 32-bit architecture.
+ *
+ * Security:
+ * Since this module does not handle any low-level data directly,
+ * no security issue specific to this module is anticipated.
+ *
+ * Standards:
+ * None.
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <isc/mutex.h>
+#include <isc/lang.h>
+#include <isc/refcount.h>
+#include <isc/stdtime.h>
+
+#include <dns/types.h>
+
+/***
+ *** Functions
+ ***/
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_acache_create(dns_acache_t **acachep, isc_mem_t *mctx,
+ isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr);
+/*
+ * Create a new DNS additional cache object.
+ *
+ * Requires:
+ *
+ * 'mctx' is a valid memory context
+ *
+ * 'taskmgr' is a valid task manager
+ *
+ * 'timermgr' is a valid timer or NULL. If NULL, no periodic cleaning of
+ * the cache will take place.
+ *
+ * 'acachep' is a valid pointer, and *acachep == NULL
+ *
+ * Ensures:
+ *
+ * '*acachep' is attached to the newly created cache
+ *
+ * Returns:
+ *
+ * ISC_R_SUCCESS
+ * ISC_R_NOMEMORY
+ * ISC_R_UNEXPECTED
+ */
+
+void
+dns_acache_attach(dns_acache_t *source, dns_acache_t **targetp);
+/*
+ * Attach *targetp to cache.
+ *
+ * Requires:
+ *
+ * 'acache' is a valid additional cache.
+ *
+ * 'targetp' points to a NULL dns_acache_t *.
+ *
+ * Ensures:
+ *
+ * *targetp is attached to the 'source' additional cache.
+ */
+
+void
+dns_acache_detach(dns_acache_t **acachep);
+/*
+ * Detach *acachep from its cache.
+ *
+ * Requires:
+ *
+ * '*acachep' points to a valid additional cache.
+ *
+ * Ensures:
+ *
+ * *acachep is NULL.
+ *
+ * If '*acachep' is the last reference to the cache and the additional
+ * cache does not have an outstanding task, all resources used by the
+ * cache will be freed.
+ */
+
+void
+dns_acache_setcleaninginterval(dns_acache_t *acache, unsigned int t);
+/*
+ * Set the periodic cleaning interval of an additional cache to 'interval'
+ * seconds.
+ */
+
+void
+dns_acache_setcachesize(dns_acache_t *acache, isc_uint32_t size);
+/*
+ * Set the maximum additional cache size. 0 means unlimited.
+ */
+
+isc_result_t
+dns_acache_setdb(dns_acache_t *acache, dns_db_t *db);
+/*
+ * Set 'db' in 'acache' when the db can be referred from acache, in order
+ * to provide a hint for resolving the back reference.
+ *
+ * Requires:
+ * 'acache' is a valid acache pointer.
+ * 'db' is a valid DNS DB pointer.
+ *
+ * Ensures:
+ * 'acache' will have a reference to 'db'.
+ *
+ * Returns:
+ * ISC_R_SUCCESS
+ * ISC_R_EXISTS (which means the specified 'db' is already set)
+ * ISC_R_NOMEMORY
+ */
+
+isc_result_t
+dns_acache_putdb(dns_acache_t *acache, dns_db_t *db);
+/*
+ * Release 'db' from 'acache' if it has been set by dns_acache_setdb().
+ *
+ * Requires:
+ * 'acache' is a valid acache pointer.
+ * 'db' is a valid DNS DB pointer.
+ *
+ * Ensures:
+ * 'acache' will release the reference to 'db'. Additionally, the content
+ * of each cache entry that is related to the 'db' will be released via
+ * the callback function.
+ *
+ * Returns:
+ * ISC_R_SUCCESS
+ * ISC_R_NOTFOUND (which means the specified 'db' is not set in 'acache')
+ * ISC_R_NOMEMORY
+ */
+
+void
+dns_acache_shutdown(dns_acache_t *acache);
+/*
+ * Shutdown 'acache'.
+ *
+ * Requires:
+ *
+ * '*acache' is a valid additional cache.
+ */
+
+isc_result_t
+dns_acache_createentry(dns_acache_t *acache, dns_db_t *origdb,
+ void (*callback)(dns_acacheentry_t *, void **),
+ void *cbarg, dns_acacheentry_t **entryp);
+/*
+ * Create an additional cache entry. A new entry is created and attached to
+ * the given additional cache object. A callback function is also associated
+ * with the created entry, which will be called when the cache entry is purged
+ * for some reason.
+ *
+ * Requires:
+ *
+ * 'acache' is a valid additional cache.
+ * 'entryp' is a valid pointer, and *entryp == NULL
+ * 'origdb' is a valid DNS DB pointer.
+ * 'callback' and 'cbarg' can be NULL. In this case, however, the entry
+ * is meaningless (and will be cleaned-up in the next periodical
+ * cleaning).
+ *
+ * Ensures:
+ * '*entryp' will point to a new additional cache entry.
+ *
+ * Returns:
+ * ISC_R_SUCCESS
+ * ISC_R_NOMEMORY
+ */
+
+isc_result_t
+dns_acache_getentry(dns_acacheentry_t *entry, dns_zone_t **zonep,
+ dns_db_t **dbp, dns_dbversion_t **versionp,
+ dns_dbnode_t **nodep, dns_name_t *fname,
+ dns_message_t *msg, isc_stdtime_t now);
+/*
+ * Get content from a particular additional cache entry.
+ *
+ * Requires:
+ *
+ * 'entry' is a valid additional cache entry.
+ * 'zonep' is a NULL pointer or '*zonep' == NULL (this is the only
+ * optional parameter.)
+ * 'dbp' is a valid pointer, and '*dbp' == NULL
+ * 'versionp' is a valid pointer, and '*versionp' == NULL
+ * 'nodep' is a valid pointer, and '*nodep' == NULL
+ * 'fname' is a valid DNS name.
+ * 'msg' is a valid DNS message.
+ *
+ * Ensures:
+ * Several possible cases can happen according to the content.
+ * 1. For a positive cache entry,
+ * '*zonep' will point to the corresponding zone (if zonep is a valid
+ * pointer),
+ * '*dbp' will point to a DB for the zone,
+ * '*versionp' will point to its version, and
+ * '*nodep' will point to the corresponding DB node.
+ * 'fname' will have the DNS name of the DB node and contain a list of
+ * rdataset for the node (which can be an empty list).
+ *
+ * 2. For a negative cache entry that means no corresponding zone exists,
+ * '*zonep' == NULL (if zonep is a valid pointer)
+ * '*dbp', '*versionp', and '*nodep' will be NULL.
+ *
+ * 3. For a negative cache entry that means no corresponding DB node
+ * exists, '*zonep' will point to the corresponding zone (if zonep is a
+ * valid pointer),
+ * '*dbp' will point to a corresponding DB for zone,
+ * '*versionp' will point to its version.
+ * '*nodep' will be kept as NULL.
+ * 'fname' will not change.
+ *
+ * On failure, no new references will be created.
+ *
+ * Returns:
+ * ISC_R_SUCCESS
+ * ISC_R_NOMEMORY
+ */
+
+isc_result_t
+dns_acache_setentry(dns_acache_t *acache, dns_acacheentry_t *entry,
+ dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version,
+ dns_dbnode_t *node, dns_name_t *fname);
+/*
+ * Set content to a particular additional cache entry.
+ *
+ * Requires:
+ * 'acache' is a valid additional cache.
+ * 'entry' is a valid additional cache entry.
+ * All the others pointers are NULL or a valid pointer of the
+ * corresponding type.
+ *
+ * Returns:
+ * ISC_R_SUCCESS
+ * ISC_R_NOMEMORY
+ * ISC_R_NOTFOUND
+ */
+
+void
+dns_acache_cancelentry(dns_acacheentry_t *entry);
+/*
+ * Cancel the use of the cache entry 'entry'. This function is supposed to
+ * be called when the node that holds the entry finds the content is not
+ * correct any more. This function will try to release as much dependency as
+ * possible, and will be ready to be cleaned-up. The registered callback
+ * function will be canceled and will never called.
+ *
+ * Requires:
+ * 'entry' is a valid additional cache entry.
+ */
+
+void
+dns_acache_attachentry(dns_acacheentry_t *source, dns_acacheentry_t **targetp);
+/*
+ * Attach *targetp to the cache entry 'source'.
+ *
+ * Requires:
+ *
+ * 'source' is a valid additional cache entry.
+ *
+ * 'targetp' points to a NULL dns_acacheentry_t *.
+ *
+ * Ensures:
+ *
+ * *targetp is attached to 'source'.
+ */
+
+void
+dns_acache_detachentry(dns_acacheentry_t **entryp);
+/*
+ * Detach *entryp from its cache.
+ *
+ * Requires:
+ *
+ * '*entryp' points to a valid additional cache entry.
+ *
+ * Ensures:
+ *
+ * *entryp is NULL.
+ *
+ * If '*entryp' is the last reference to the entry,
+ * cache does not have an outstanding task, all resources used by the
+ * entry (including the entry object itself) will be freed.
+ */
+
+void
+dns_acache_countquerymiss(dns_acache_t *acache);
+/*
+ * Count up a missed acache query. XXXMLG need more docs.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_ACACHE_H */
diff --git a/lib/dns/include/dns/acl.h b/lib/dns/include/dns/acl.h
new file mode 100644
index 0000000..94537a5
--- /dev/null
+++ b/lib/dns/include/dns/acl.h
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: acl.h,v 1.31 2007/12/21 06:46:47 marka Exp $ */
+
+#ifndef DNS_ACL_H
+#define DNS_ACL_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/acl.h
+ * \brief
+ * Address match list handling.
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <isc/lang.h>
+#include <isc/magic.h>
+#include <isc/netaddr.h>
+#include <isc/refcount.h>
+
+#include <dns/name.h>
+#include <dns/types.h>
+#include <dns/iptable.h>
+
+/***
+ *** Types
+ ***/
+
+typedef enum {
+ dns_aclelementtype_ipprefix,
+ dns_aclelementtype_keyname,
+ dns_aclelementtype_nestedacl,
+ dns_aclelementtype_localhost,
+ dns_aclelementtype_localnets,
+ dns_aclelementtype_any
+} dns_aclelemettype_t;
+
+typedef struct dns_aclipprefix dns_aclipprefix_t;
+
+struct dns_aclipprefix {
+ isc_netaddr_t address; /* IP4/IP6 */
+ unsigned int prefixlen;
+};
+
+struct dns_aclelement {
+ dns_aclelemettype_t type;
+ isc_boolean_t negative;
+ dns_name_t keyname;
+ dns_acl_t *nestedacl;
+ int node_num;
+};
+
+struct dns_acl {
+ unsigned int magic;
+ isc_mem_t *mctx;
+ isc_refcount_t refcount;
+ dns_iptable_t *iptable;
+#define node_count iptable->radix->num_added_node
+ dns_aclelement_t *elements;
+ isc_boolean_t has_negatives;
+ unsigned int alloc; /*%< Elements allocated */
+ unsigned int length; /*%< Elements initialized */
+ char *name; /*%< Temporary use only */
+ ISC_LINK(dns_acl_t) nextincache; /*%< Ditto */
+};
+
+struct dns_aclenv {
+ dns_acl_t *localhost;
+ dns_acl_t *localnets;
+ isc_boolean_t match_mapped;
+};
+
+#define DNS_ACL_MAGIC ISC_MAGIC('D','a','c','l')
+#define DNS_ACL_VALID(a) ISC_MAGIC_VALID(a, DNS_ACL_MAGIC)
+
+/***
+ *** Functions
+ ***/
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target);
+/*%<
+ * Create a new ACL, including an IP table and an array with room
+ * for 'n' ACL elements. The elements are uninitialized and the
+ * length is 0.
+ */
+
+isc_result_t
+dns_acl_any(isc_mem_t *mctx, dns_acl_t **target);
+/*%<
+ * Create a new ACL that matches everything.
+ */
+
+isc_result_t
+dns_acl_none(isc_mem_t *mctx, dns_acl_t **target);
+/*%<
+ * Create a new ACL that matches nothing.
+ */
+
+isc_boolean_t
+dns_acl_isany(dns_acl_t *acl);
+/*%<
+ * Test whether ACL is set to "{ any; }"
+ */
+
+isc_boolean_t
+dns_acl_isnone(dns_acl_t *acl);
+/*%<
+ * Test whether ACL is set to "{ none; }"
+ */
+
+isc_result_t
+dns_acl_merge(dns_acl_t *dest, dns_acl_t *source, isc_boolean_t pos);
+/*%<
+ * Merge the contents of one ACL into another. Call dns_iptable_merge()
+ * for the IP tables, then concatenate the element arrays.
+ *
+ * If pos is set to false, then the nested ACL is to be negated. This
+ * means reverse the sense of each *positive* element or IP table node,
+ * but leave negatives alone, so as to prevent a double-negative causing
+ * an unexpected postive match in the parent ACL.
+ */
+
+void
+dns_acl_attach(dns_acl_t *source, dns_acl_t **target);
+
+void
+dns_acl_detach(dns_acl_t **aclp);
+
+isc_boolean_t
+dns_acl_isinsecure(const dns_acl_t *a);
+/*%<
+ * Return #ISC_TRUE iff the acl 'a' is considered insecure, that is,
+ * if it contains IP addresses other than those of the local host.
+ * This is intended for applications such as printing warning
+ * messages for suspect ACLs; it is not intended for making access
+ * control decisions. We make no guarantee that an ACL for which
+ * this function returns #ISC_FALSE is safe.
+ */
+
+isc_result_t
+dns_aclenv_init(isc_mem_t *mctx, dns_aclenv_t *env);
+/*%<
+ * Initialize ACL environment, setting up localhost and localnets ACLs
+ */
+
+void
+dns_aclenv_copy(dns_aclenv_t *t, dns_aclenv_t *s);
+
+void
+dns_aclenv_destroy(dns_aclenv_t *env);
+
+isc_result_t
+dns_acl_match(const isc_netaddr_t *reqaddr,
+ const dns_name_t *reqsigner,
+ const dns_acl_t *acl,
+ const dns_aclenv_t *env,
+ int *match,
+ const dns_aclelement_t **matchelt);
+/*%<
+ * General, low-level ACL matching. This is expected to
+ * be useful even for weird stuff like the topology and sortlist statements.
+ *
+ * Match the address 'reqaddr', and optionally the key name 'reqsigner',
+ * against 'acl'. 'reqsigner' may be NULL.
+ *
+ * If there is a match, '*match' will be set to an integer whose absolute
+ * value corresponds to the order in which the matching value was inserted
+ * into the ACL. For a positive match, this value will be positive; for a
+ * negative match, it will be negative.
+ *
+ * If there is no match, *match will be set to zero.
+ *
+ * If there is a match in the element list (either positive or negative)
+ * and 'matchelt' is non-NULL, *matchelt will be pointed to the matching
+ * element.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS Always succeeds.
+ */
+
+isc_boolean_t
+dns_aclelement_match(const isc_netaddr_t *reqaddr,
+ const dns_name_t *reqsigner,
+ const dns_aclelement_t *e,
+ const dns_aclenv_t *env,
+ const dns_aclelement_t **matchelt);
+/*%<
+ * Like dns_acl_match, but matches against the single ACL element 'e'
+ * rather than a complete ACL, and returns ISC_TRUE iff it matched.
+ *
+ * To determine whether the match was prositive or negative, the
+ * caller should examine e->negative. Since the element 'e' may be
+ * a reference to a named ACL or a nested ACL, a matching element
+ * returned through 'matchelt' is not necessarily 'e' itself.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_ACL_H */
diff --git a/lib/dns/include/dns/adb.h b/lib/dns/include/dns/adb.h
new file mode 100644
index 0000000..d4ac40c
--- /dev/null
+++ b/lib/dns/include/dns/adb.h
@@ -0,0 +1,634 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: adb.h,v 1.85 2008/04/03 06:09:04 tbox Exp $ */
+
+#ifndef DNS_ADB_H
+#define DNS_ADB_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/adb.h
+ *\brief
+ * DNS Address Database
+ *
+ * This module implements an address database (ADB) for mapping a name
+ * to an isc_sockaddr_t. It also provides statistical information on
+ * how good that address might be.
+ *
+ * A client will pass in a dns_name_t, and the ADB will walk through
+ * the rdataset looking up addresses associated with the name. If it
+ * is found on the internal lists, a structure is filled in with the
+ * address information and stats for found addresses.
+ *
+ * If the name cannot be found on the internal lists, a new entry will
+ * be created for a name if all the information needed can be found
+ * in the zone table or cache. This new address will then be returned.
+ *
+ * If a request must be made to remote servers to satisfy a name lookup,
+ * this module will start fetches to try to complete these addresses. When
+ * at least one more completes, an event is sent to the caller. If none of
+ * them resolve before the fetch times out, an event indicating this is
+ * sent instead.
+ *
+ * Records are stored internally until a timer expires. The timer is the
+ * smaller of the TTL or signature validity period.
+ *
+ * Lameness is stored per <qname,qtype> tuple, and this data hangs off each
+ * address field. When an address is marked lame for a given tuple the address
+ * will not be returned to a caller.
+ *
+ *
+ * MP:
+ *
+ *\li The ADB takes care of all necessary locking.
+ *
+ *\li Only the task which initiated the name lookup can cancel the lookup.
+ *
+ *
+ * Security:
+ *
+ *\li None, since all data stored is required to be pre-filtered.
+ * (Cache needs to be sane, fetches return bounds-checked and sanity-
+ * checked data, caller passes a good dns_name_t for the zone, etc)
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <isc/lang.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/sockaddr.h>
+
+#include <dns/types.h>
+#include <dns/view.h>
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Magic number checks
+ ***/
+
+#define DNS_ADBFIND_MAGIC ISC_MAGIC('a','d','b','H')
+#define DNS_ADBFIND_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBFIND_MAGIC)
+#define DNS_ADBADDRINFO_MAGIC ISC_MAGIC('a','d','A','I')
+#define DNS_ADBADDRINFO_VALID(x) ISC_MAGIC_VALID(x, DNS_ADBADDRINFO_MAGIC)
+
+
+/***
+ *** TYPES
+ ***/
+
+typedef struct dns_adbname dns_adbname_t;
+
+/*!
+ *\brief
+ * Represents a lookup for a single name.
+ *
+ * On return, the client can safely use "list", and can reorder the list.
+ * Items may not be _deleted_ from this list, however, or added to it
+ * other than by using the dns_adb_*() API.
+ */
+struct dns_adbfind {
+ /* Public */
+ unsigned int magic; /*%< RO: magic */
+ dns_adbaddrinfolist_t list; /*%< RO: list of addrs */
+ unsigned int query_pending; /*%< RO: partial list */
+ unsigned int partial_result; /*%< RO: addrs missing */
+ unsigned int options; /*%< RO: options */
+ isc_result_t result_v4; /*%< RO: v4 result */
+ isc_result_t result_v6; /*%< RO: v6 result */
+ ISC_LINK(dns_adbfind_t) publink; /*%< RW: client use */
+
+ /* Private */
+ isc_mutex_t lock; /* locks all below */
+ in_port_t port;
+ int name_bucket;
+ unsigned int flags;
+ dns_adbname_t *adbname;
+ dns_adb_t *adb;
+ isc_event_t event;
+ ISC_LINK(dns_adbfind_t) plink;
+};
+
+/*
+ * _INET:
+ * _INET6:
+ * return addresses of that type.
+ *
+ * _EMPTYEVENT:
+ * Only schedule an event if no addresses are known.
+ * Must set _WANTEVENT for this to be meaningful.
+ *
+ * _WANTEVENT:
+ * An event is desired. Check this bit in the returned find to see
+ * if one will actually be generated.
+ *
+ * _AVOIDFETCHES:
+ * If set, fetches will not be generated unless no addresses are
+ * available in any of the address families requested.
+ *
+ * _STARTATZONE:
+ * Fetches will start using the closest zone data or use the root servers.
+ * This is useful for reestablishing glue that has expired.
+ *
+ * _GLUEOK:
+ * _HINTOK:
+ * Glue or hints are ok. These are used when matching names already
+ * in the adb, and when dns databases are searched.
+ *
+ * _RETURNLAME:
+ * Return lame servers in a find, so that all addresses are returned.
+ *
+ * _LAMEPRUNED:
+ * At least one address was omitted from the list because it was lame.
+ * This bit will NEVER be set if _RETURNLAME is set in the createfind().
+ */
+/*% Return addresses of type INET. */
+#define DNS_ADBFIND_INET 0x00000001
+/*% Return addresses of type INET6. */
+#define DNS_ADBFIND_INET6 0x00000002
+#define DNS_ADBFIND_ADDRESSMASK 0x00000003
+/*%
+ * Only schedule an event if no addresses are known.
+ * Must set _WANTEVENT for this to be meaningful.
+ */
+#define DNS_ADBFIND_EMPTYEVENT 0x00000004
+/*%
+ * An event is desired. Check this bit in the returned find to see
+ * if one will actually be generated.
+ */
+#define DNS_ADBFIND_WANTEVENT 0x00000008
+/*%
+ * If set, fetches will not be generated unless no addresses are
+ * available in any of the address families requested.
+ */
+#define DNS_ADBFIND_AVOIDFETCHES 0x00000010
+/*%
+ * Fetches will start using the closest zone data or use the root servers.
+ * This is useful for reestablishing glue that has expired.
+ */
+#define DNS_ADBFIND_STARTATZONE 0x00000020
+/*%
+ * Glue or hints are ok. These are used when matching names already
+ * in the adb, and when dns databases are searched.
+ */
+#define DNS_ADBFIND_GLUEOK 0x00000040
+/*%
+ * Glue or hints are ok. These are used when matching names already
+ * in the adb, and when dns databases are searched.
+ */
+#define DNS_ADBFIND_HINTOK 0x00000080
+/*%
+ * Return lame servers in a find, so that all addresses are returned.
+ */
+#define DNS_ADBFIND_RETURNLAME 0x00000100
+/*%
+ * Only schedule an event if no addresses are known.
+ * Must set _WANTEVENT for this to be meaningful.
+ */
+#define DNS_ADBFIND_LAMEPRUNED 0x00000200
+
+/*%
+ * The answers to queries come back as a list of these.
+ */
+struct dns_adbaddrinfo {
+ unsigned int magic; /*%< private */
+
+ isc_sockaddr_t sockaddr; /*%< [rw] */
+ unsigned int srtt; /*%< [rw] microseconds */
+ unsigned int flags; /*%< [rw] */
+ dns_adbentry_t *entry; /*%< private */
+ ISC_LINK(dns_adbaddrinfo_t) publink;
+};
+
+/*!<
+ * The event sent to the caller task is just a plain old isc_event_t. It
+ * contains no data other than a simple status, passed in the "type" field
+ * to indicate that another address resolved, or all partially resolved
+ * addresses have failed to resolve.
+ *
+ * "sender" is the dns_adbfind_t used to issue this query.
+ *
+ * This is simply a standard event, with the "type" set to:
+ *
+ *\li #DNS_EVENT_ADBMOREADDRESSES -- another address resolved.
+ *\li #DNS_EVENT_ADBNOMOREADDRESSES -- all pending addresses failed,
+ * were canceled, or otherwise will
+ * not be usable.
+ *\li #DNS_EVENT_ADBCANCELED -- The request was canceled by a
+ * 3rd party.
+ *\li #DNS_EVENT_ADBNAMEDELETED -- The name was deleted, so this request
+ * was canceled.
+ *
+ * In each of these cases, the addresses returned by the initial call
+ * to dns_adb_createfind() can still be used until they are no longer needed.
+ */
+
+/****
+ **** FUNCTIONS
+ ****/
+
+
+isc_result_t
+dns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_timermgr_t *tmgr,
+ isc_taskmgr_t *taskmgr, dns_adb_t **newadb);
+/*%<
+ * Create a new ADB.
+ *
+ * Notes:
+ *
+ *\li Generally, applications should not create an ADB directly, but
+ * should instead call dns_view_createresolver().
+ *
+ * Requires:
+ *
+ *\li 'mem' must be a valid memory context.
+ *
+ *\li 'view' be a pointer to a valid view.
+ *
+ *\li 'tmgr' be a pointer to a valid timer manager.
+ *
+ *\li 'taskmgr' be a pointer to a valid task manager.
+ *
+ *\li 'newadb' != NULL && '*newadb' == NULL.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS after happiness.
+ *\li #ISC_R_NOMEMORY after resource allocation failure.
+ */
+
+void
+dns_adb_attach(dns_adb_t *adb, dns_adb_t **adbp);
+/*%
+ * Attach to an 'adb' to 'adbp'.
+ *
+ * Requires:
+ *\li 'adb' to be a valid dns_adb_t, created via dns_adb_create().
+ *\li 'adbp' to be a valid pointer to a *dns_adb_t which is initialized
+ * to NULL.
+ */
+
+void
+dns_adb_detach(dns_adb_t **adb);
+/*%
+ * Delete the ADB. Sets *ADB to NULL. Cancels any outstanding requests.
+ *
+ * Requires:
+ *
+ *\li 'adb' be non-NULL and '*adb' be a valid dns_adb_t, created via
+ * dns_adb_create().
+ */
+
+void
+dns_adb_whenshutdown(dns_adb_t *adb, isc_task_t *task, isc_event_t **eventp);
+/*%
+ * Send '*eventp' to 'task' when 'adb' has shutdown.
+ *
+ * Requires:
+ *
+ *\li '*adb' is a valid dns_adb_t.
+ *
+ *\li eventp != NULL && *eventp is a valid event.
+ *
+ * Ensures:
+ *
+ *\li *eventp == NULL
+ *
+ *\li The event's sender field is set to the value of adb when the event
+ * is sent.
+ */
+
+void
+dns_adb_shutdown(dns_adb_t *adb);
+/*%<
+ * Shutdown 'adb'.
+ *
+ * Requires:
+ *
+ * \li '*adb' is a valid dns_adb_t.
+ */
+
+isc_result_t
+dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action,
+ void *arg, dns_name_t *name, dns_name_t *qname,
+ dns_rdatatype_t qtype, unsigned int options,
+ isc_stdtime_t now, dns_name_t *target,
+ in_port_t port, dns_adbfind_t **find);
+/*%<
+ * Main interface for clients. The adb will look up the name given in
+ * "name" and will build up a list of found addresses, and perhaps start
+ * internal fetches to resolve names that are unknown currently.
+ *
+ * If other addresses resolve after this call completes, an event will
+ * be sent to the <task, taskaction, arg> with the sender of that event
+ * set to a pointer to the dns_adbfind_t returned by this function.
+ *
+ * If no events will be generated, the *find->result_v4 and/or result_v6
+ * members may be examined for address lookup status. The usual #ISC_R_SUCCESS,
+ * #ISC_R_FAILURE, #DNS_R_NXDOMAIN, and #DNS_R_NXRRSET are returned, along with
+ * #ISC_R_NOTFOUND meaning the ADB has not _yet_ found the values. In this
+ * latter case, retrying may produce more addresses.
+ *
+ * If events will be returned, the result_v[46] members are only valid
+ * when that event is actually returned.
+ *
+ * The list of addresses returned is unordered. The caller must impose
+ * any ordering required. The list will not contain "known bad" addresses,
+ * however. For instance, it will not return hosts that are known to be
+ * lame for the zone in question.
+ *
+ * The caller cannot (directly) modify the contents of the address list's
+ * fields other than the "link" field. All values can be read at any
+ * time, however.
+ *
+ * The "now" parameter is used only for determining which entries that
+ * have a specific time to live or expire time should be removed from
+ * the running database. If specified as zero, the current time will
+ * be retrieved and used.
+ *
+ * If 'target' is not NULL and 'name' is an alias (i.e. the name is
+ * CNAME'd or DNAME'd to another name), then 'target' will be updated with
+ * the domain name that 'name' is aliased to.
+ *
+ * All addresses returned will have the sockaddr's port set to 'port.'
+ * The caller may change them directly in the dns_adbaddrinfo_t since
+ * they are copies of the internal address only.
+ *
+ * XXXMLG Document options, especially the flags which control how
+ * events are sent.
+ *
+ * Requires:
+ *
+ *\li *adb be a valid isc_adb_t object.
+ *
+ *\li If events are to be sent, *task be a valid task,
+ * and isc_taskaction_t != NULL.
+ *
+ *\li *name is a valid dns_name_t.
+ *
+ *\li qname != NULL and *qname be a valid dns_name_t.
+ *
+ *\li target == NULL or target is a valid name with a buffer.
+ *
+ *\li find != NULL && *find == NULL.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS Addresses might have been returned, and events will be
+ * delivered for unresolved addresses.
+ *\li #ISC_R_NOMORE Addresses might have been returned, but no events
+ * will ever be posted for this context. This is only
+ * returned if task != NULL.
+ *\li #ISC_R_NOMEMORY insufficient resources
+ *\li #DNS_R_ALIAS 'name' is an alias for another name.
+ *
+ * Calls, and returns error codes from:
+ *
+ *\li isc_stdtime_get()
+ *
+ * Notes:
+ *
+ *\li No internal reference to "name" exists after this function
+ * returns.
+ */
+
+void
+dns_adb_cancelfind(dns_adbfind_t *find);
+/*%<
+ * Cancels the find, and sends the event off to the caller.
+ *
+ * It is an error to call dns_adb_cancelfind() on a find where
+ * no event is wanted, or will ever be sent.
+ *
+ * Note:
+ *
+ *\li It is possible that the real completion event was posted just
+ * before the dns_adb_cancelfind() call was made. In this case,
+ * dns_adb_cancelfind() will do nothing. The event callback needs
+ * to be prepared to find this situation (i.e. result is valid but
+ * the caller expects it to be canceled).
+ *
+ * Requires:
+ *
+ *\li 'find' be a valid dns_adbfind_t pointer.
+ *
+ *\li events would have been posted to the task. This can be checked
+ * with (find->options & DNS_ADBFIND_WANTEVENT).
+ *
+ * Ensures:
+ *
+ *\li The event was posted to the task.
+ */
+
+void
+dns_adb_destroyfind(dns_adbfind_t **find);
+/*%<
+ * Destroys the find reference.
+ *
+ * Note:
+ *
+ *\li This can only be called after the event was delivered for a
+ * find. Additionally, the event MUST have been freed via
+ * isc_event_free() BEFORE this function is called.
+ *
+ * Requires:
+ *
+ *\li 'find' != NULL and *find be valid dns_adbfind_t pointer.
+ *
+ * Ensures:
+ *
+ *\li No "address found" events will be posted to the originating task
+ * after this function returns.
+ */
+
+void
+dns_adb_dump(dns_adb_t *adb, FILE *f);
+/*%<
+ * This function is only used for debugging. It will dump as much of the
+ * state of the running system as possible.
+ *
+ * Requires:
+ *
+ *\li adb be valid.
+ *
+ *\li f != NULL, and is a file open for writing.
+ */
+
+void
+dns_adb_dumpfind(dns_adbfind_t *find, FILE *f);
+/*%<
+ * This function is only used for debugging. Dump the data associated
+ * with a find.
+ *
+ * Requires:
+ *
+ *\li find is valid.
+ *
+ * \li f != NULL, and is a file open for writing.
+ */
+
+isc_result_t
+dns_adb_marklame(dns_adb_t *adb, dns_adbaddrinfo_t *addr, dns_name_t *qname,
+ dns_rdatatype_t type, isc_stdtime_t expire_time);
+/*%<
+ * Mark the given address as lame for the <qname,qtype>. expire_time should
+ * be set to the time when the entry should expire. That is, if it is to
+ * expire 10 minutes in the future, it should set it to (now + 10 * 60).
+ *
+ * Requires:
+ *
+ *\li adb be valid.
+ *
+ *\li addr be valid.
+ *
+ *\li qname be the qname used in the dns_adb_createfind() call.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS -- all is well.
+ *\li #ISC_R_NOMEMORY -- could not mark address as lame.
+ */
+
+/*
+ * A reasonable default for RTT adjustments
+ */
+#define DNS_ADB_RTTADJDEFAULT 7 /*%< default scale */
+#define DNS_ADB_RTTADJREPLACE 0 /*%< replace with our rtt */
+#define DNS_ADB_RTTADJAGE 10 /*%< age this rtt */
+
+void
+dns_adb_adjustsrtt(dns_adb_t *adb, dns_adbaddrinfo_t *addr,
+ unsigned int rtt, unsigned int factor);
+/*%<
+ * Mix the round trip time into the existing smoothed rtt.
+
+ * The formula used
+ * (where srtt is the existing rtt value, and rtt and factor are arguments to
+ * this function):
+ *
+ *\code
+ * new_srtt = (old_srtt / 10 * factor) + (rtt / 10 * (10 - factor));
+ *\endcode
+ *
+ * XXXRTH Do we want to publish the formula? What if we want to change how
+ * this works later on? Recommend/require that the units are
+ * microseconds?
+ *
+ * Requires:
+ *
+ *\li adb be valid.
+ *
+ *\li addr be valid.
+ *
+ *\li 0 <= factor <= 10
+ *
+ * Note:
+ *
+ *\li The srtt in addr will be updated to reflect the new global
+ * srtt value. This may include changes made by others.
+ */
+
+void
+dns_adb_changeflags(dns_adb_t *adb, dns_adbaddrinfo_t *addr,
+ unsigned int bits, unsigned int mask);
+/*%
+ * Change Flags.
+ *
+ * Set the flags as given by:
+ *
+ *\li newflags = (oldflags & ~mask) | (bits & mask);
+ *
+ * Requires:
+ *
+ *\li adb be valid.
+ *
+ *\li addr be valid.
+ */
+
+isc_result_t
+dns_adb_findaddrinfo(dns_adb_t *adb, isc_sockaddr_t *sa,
+ dns_adbaddrinfo_t **addrp, isc_stdtime_t now);
+/*%<
+ * Return a dns_adbaddrinfo_t that is associated with address 'sa'.
+ *
+ * Requires:
+ *
+ *\li adb is valid.
+ *
+ *\li sa is valid.
+ *
+ *\li addrp != NULL && *addrp == NULL
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *\li #ISC_R_SHUTTINGDOWN
+ */
+
+void
+dns_adb_freeaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **addrp);
+/*%<
+ * Free a dns_adbaddrinfo_t allocated by dns_adb_findaddrinfo().
+ *
+ * Requires:
+ *
+ *\li adb is valid.
+ *
+ *\li *addrp is a valid dns_adbaddrinfo_t *.
+ */
+
+void
+dns_adb_flush(dns_adb_t *adb);
+/*%<
+ * Flushes all cached data from the adb.
+ *
+ * Requires:
+ *\li adb is valid.
+ */
+
+void
+dns_adb_setadbsize(dns_adb_t *adb, isc_uint32_t size);
+/*%<
+ * Set a target memory size. If memory usage exceeds the target
+ * size entries will be removed before they would have expired on
+ * a random basis.
+ *
+ * If 'size' is 0 then memory usage is unlimited.
+ *
+ * Requires:
+ *\li 'adb' is valid.
+ */
+
+void
+dns_adb_flushname(dns_adb_t *adb, dns_name_t *name);
+/*%<
+ * Flush 'name' from the adb cache.
+ *
+ * Requires:
+ *\li 'adb' is valid.
+ *\li 'name' is valid.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_ADB_H */
diff --git a/lib/dns/include/dns/bit.h b/lib/dns/include/dns/bit.h
new file mode 100644
index 0000000..28c733d
--- /dev/null
+++ b/lib/dns/include/dns/bit.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: bit.h,v 1.14 2007/06/19 23:47:16 tbox Exp $ */
+
+#ifndef DNS_BIT_H
+#define DNS_BIT_H 1
+
+/*! \file dns/bit.h */
+
+#include <isc/int.h>
+#include <isc/boolean.h>
+
+typedef isc_uint64_t dns_bitset_t;
+
+#define DNS_BIT_SET(bit, bitset) \
+ (*(bitset) |= ((dns_bitset_t)1 << (bit)))
+#define DNS_BIT_CLEAR(bit, bitset) \
+ (*(bitset) &= ~((dns_bitset_t)1 << (bit)))
+#define DNS_BIT_CHECK(bit, bitset) \
+ ISC_TF((*(bitset) & ((dns_bitset_t)1 << (bit))) \
+ == ((dns_bitset_t)1 << (bit)))
+
+#endif /* DNS_BIT_H */
+
diff --git a/lib/dns/include/dns/byaddr.h b/lib/dns/include/dns/byaddr.h
new file mode 100644
index 0000000..edf8430
--- /dev/null
+++ b/lib/dns/include/dns/byaddr.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: byaddr.h,v 1.22 2007/06/19 23:47:16 tbox Exp $ */
+
+#ifndef DNS_BYADDR_H
+#define DNS_BYADDR_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/byaddr.h
+ * \brief
+ * The byaddr module provides reverse lookup services for IPv4 and IPv6
+ * addresses.
+ *
+ * MP:
+ *\li The module ensures appropriate synchronization of data structures it
+ * creates and manipulates.
+ *
+ * Reliability:
+ *\li No anticipated impact.
+ *
+ * Resources:
+ *\li TBS
+ *
+ * Security:
+ *\li No anticipated impact.
+ *
+ * Standards:
+ *\li RFCs: 1034, 1035, 2181, TBS
+ *\li Drafts: TBS
+ */
+
+#include <isc/lang.h>
+#include <isc/event.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/*%
+ * A 'dns_byaddrevent_t' is returned when a byaddr completes.
+ * The sender field will be set to the byaddr that completed. If 'result'
+ * is ISC_R_SUCCESS, then 'names' will contain a list of names associated
+ * with the address. The recipient of the event must not change the list
+ * and must not refer to any of the name data after the event is freed.
+ */
+typedef struct dns_byaddrevent {
+ ISC_EVENT_COMMON(struct dns_byaddrevent);
+ isc_result_t result;
+ dns_namelist_t names;
+} dns_byaddrevent_t;
+
+/*
+ * This option is deprecated since we now only consider nibbles.
+#define DNS_BYADDROPT_IPV6NIBBLE 0x0001
+ */
+/*% Note DNS_BYADDROPT_IPV6NIBBLE is now deprecated. */
+#define DNS_BYADDROPT_IPV6INT 0x0002
+
+isc_result_t
+dns_byaddr_create(isc_mem_t *mctx, isc_netaddr_t *address, dns_view_t *view,
+ unsigned int options, isc_task_t *task,
+ isc_taskaction_t action, void *arg, dns_byaddr_t **byaddrp);
+/*%<
+ * Find the domain name of 'address'.
+ *
+ * Notes:
+ *
+ *\li There is a reverse lookup format for IPv6 addresses, 'nibble'
+ *
+ *\li The 'nibble' format for that address is
+ *
+ * \code
+ * 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.8.e.f.ip6.arpa.
+ * \endcode
+ *
+ *\li #DNS_BYADDROPT_IPV6INT can be used to get nibble lookups under ip6.int.
+ *
+ * Requires:
+ *
+ *\li 'mctx' is a valid mctx.
+ *
+ *\li 'address' is a valid IPv4 or IPv6 address.
+ *
+ *\li 'view' is a valid view which has a resolver.
+ *
+ *\li 'task' is a valid task.
+ *
+ *\li byaddrp != NULL && *byaddrp == NULL
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *
+ *\li Any resolver-related error (e.g. #ISC_R_SHUTTINGDOWN) may also be
+ * returned.
+ */
+
+void
+dns_byaddr_cancel(dns_byaddr_t *byaddr);
+/*%<
+ * Cancel 'byaddr'.
+ *
+ * Notes:
+ *
+ *\li If 'byaddr' has not completed, post its #DNS_EVENT_BYADDRDONE
+ * event with a result code of #ISC_R_CANCELED.
+ *
+ * Requires:
+ *
+ *\li 'byaddr' is a valid byaddr.
+ */
+
+void
+dns_byaddr_destroy(dns_byaddr_t **byaddrp);
+/*%<
+ * Destroy 'byaddr'.
+ *
+ * Requires:
+ *
+ *\li '*byaddrp' is a valid byaddr.
+ *
+ *\li The caller has received the #DNS_EVENT_BYADDRDONE event (either because
+ * the byaddr completed or because dns_byaddr_cancel() was called).
+ *
+ * Ensures:
+ *
+ *\li *byaddrp == NULL.
+ */
+
+isc_result_t
+dns_byaddr_createptrname(isc_netaddr_t *address, isc_boolean_t nibble,
+ dns_name_t *name);
+
+isc_result_t
+dns_byaddr_createptrname2(isc_netaddr_t *address, unsigned int options,
+ dns_name_t *name);
+/*%<
+ * Creates a name that would be used in a PTR query for this address. The
+ * nibble flag indicates that the 'nibble' format is to be used if an IPv6
+ * address is provided, instead of the 'bitstring' format. Since we dropped
+ * the support of the bitstring labels, it is expected that the flag is always
+ * set. 'options' are the same as for dns_byaddr_create().
+ *
+ * Requires:
+ *
+ * \li 'address' is a valid address.
+ * \li 'name' is a valid name with a dedicated buffer.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_BYADDR_H */
diff --git a/lib/dns/include/dns/cache.h b/lib/dns/include/dns/cache.h
new file mode 100644
index 0000000..7b37235
--- /dev/null
+++ b/lib/dns/include/dns/cache.h
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: cache.h,v 1.26 2007/06/19 23:47:16 tbox Exp $ */
+
+#ifndef DNS_CACHE_H
+#define DNS_CACHE_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/cache.h
+ * \brief
+ * Defines dns_cache_t, the cache object.
+ *
+ * Notes:
+ *\li A cache object contains DNS data of a single class.
+ * Multiple classes will be handled by creating multiple
+ * views, each with a different class and its own cache.
+ *
+ * MP:
+ *\li See notes at the individual functions.
+ *
+ * Reliability:
+ *
+ * Resources:
+ *
+ * Security:
+ *
+ * Standards:
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <isc/lang.h>
+#include <isc/stdtime.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Functions
+ ***/
+
+isc_result_t
+dns_cache_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
+ isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
+ const char *db_type, unsigned int db_argc, char **db_argv,
+ dns_cache_t **cachep);
+/*%<
+ * Create a new DNS cache.
+ *
+ * Requires:
+ *
+ *\li 'mctx' is a valid memory context
+ *
+ *\li 'taskmgr' is a valid task manager and 'timermgr' is a valid timer
+ * manager, or both are NULL. If NULL, no periodic cleaning of the
+ * cache will take place.
+ *
+ *\li 'cachep' is a valid pointer, and *cachep == NULL
+ *
+ * Ensures:
+ *
+ *\li '*cachep' is attached to the newly created cache
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ */
+
+void
+dns_cache_attach(dns_cache_t *cache, dns_cache_t **targetp);
+/*%<
+ * Attach *targetp to cache.
+ *
+ * Requires:
+ *
+ *\li 'cache' is a valid cache.
+ *
+ *\li 'targetp' points to a NULL dns_cache_t *.
+ *
+ * Ensures:
+ *
+ *\li *targetp is attached to cache.
+ */
+
+void
+dns_cache_detach(dns_cache_t **cachep);
+/*%<
+ * Detach *cachep from its cache.
+ *
+ * Requires:
+ *
+ *\li 'cachep' points to a valid cache.
+ *
+ * Ensures:
+ *
+ *\li *cachep is NULL.
+ *
+ *\li If '*cachep' is the last reference to the cache,
+ * all resources used by the cache will be freed
+ */
+
+void
+dns_cache_attachdb(dns_cache_t *cache, dns_db_t **dbp);
+/*%<
+ * Attach *dbp to the cache's database.
+ *
+ * Notes:
+ *
+ *\li This may be used to get a reference to the database for
+ * the purpose of cache lookups (XXX currently it is also
+ * the way to add data to the cache, but having a
+ * separate dns_cache_add() interface instead would allow
+ * more control over memory usage).
+ * The caller should call dns_db_detach() on the reference
+ * when it is no longer needed.
+ *
+ * Requires:
+ *
+ *\li 'cache' is a valid cache.
+ *
+ *\li 'dbp' points to a NULL dns_db *.
+ *
+ * Ensures:
+ *
+ *\li *dbp is attached to the database.
+ */
+
+
+isc_result_t
+dns_cache_setfilename(dns_cache_t *cache, const char *filename);
+/*%<
+ * If 'filename' is non-NULL, make the cache persistent.
+ * The cache's data will be stored in the given file.
+ * If 'filename' is NULL, make the cache non-persistent.
+ * Files that are no longer used are not unlinked automatically.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *\li Various file-related failures
+ */
+
+isc_result_t
+dns_cache_load(dns_cache_t *cache);
+/*%<
+ * If the cache has a file name, load the cache contents from the file.
+ * Previous cache contents are not discarded.
+ * If no file name has been set, do nothing and return success.
+ *
+ * MT:
+ *\li Multiple simultaneous attempts to load or dump the cache
+ * will be serialized with respect to one another, but
+ * the cache may be read and updated while the dump is
+ * in progress. Updates performed during loading
+ * may or may not be preserved, and reads may return
+ * either the old or the newly loaded data.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS
+ * \li Various failures depending on the database implementation type
+ */
+
+isc_result_t
+dns_cache_dump(dns_cache_t *cache);
+/*%<
+ * If the cache has a file name, write the cache contents to disk,
+ * overwriting any preexisting file. If no file name has been set,
+ * do nothing and return success.
+ *
+ * MT:
+ *\li Multiple simultaneous attempts to load or dump the cache
+ * will be serialized with respect to one another, but
+ * the cache may be read and updated while the dump is
+ * in progress. Updates performed during the dump may
+ * or may not be reflected in the dumped file.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS
+ * \li Various failures depending on the database implementation type
+ */
+
+isc_result_t
+dns_cache_clean(dns_cache_t *cache, isc_stdtime_t now);
+/*%<
+ * Force immediate cleaning of the cache, freeing all rdatasets
+ * whose TTL has expired as of 'now' and that have no pending
+ * references.
+ */
+
+void
+dns_cache_setcleaninginterval(dns_cache_t *cache, unsigned int interval);
+/*%<
+ * Set the periodic cache cleaning interval to 'interval' seconds.
+ */
+
+void
+dns_cache_setcachesize(dns_cache_t *cache, isc_uint32_t size);
+/*%<
+ * Set the maximum cache size. 0 means unlimited.
+ */
+
+isc_result_t
+dns_cache_flush(dns_cache_t *cache);
+/*%<
+ * Flushes all data from the cache.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ */
+
+isc_result_t
+dns_cache_flushname(dns_cache_t *cache, dns_name_t *name);
+/*
+ * Flushes a given name from the cache.
+ *
+ * Requires:
+ *\li 'cache' to be valid.
+ *\li 'name' to be valid.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *\li other error returns.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_CACHE_H */
diff --git a/lib/dns/include/dns/callbacks.h b/lib/dns/include/dns/callbacks.h
new file mode 100644
index 0000000..8a8385a
--- /dev/null
+++ b/lib/dns/include/dns/callbacks.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: callbacks.h,v 1.24 2007/06/19 23:47:16 tbox Exp $ */
+
+#ifndef DNS_CALLBACKS_H
+#define DNS_CALLBACKS_H 1
+
+/*! \file dns/callbacks.h */
+
+/***
+ *** Imports
+ ***/
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Types
+ ***/
+
+struct dns_rdatacallbacks {
+ /*%
+ * dns_load_master calls this when it has rdatasets to commit.
+ */
+ dns_addrdatasetfunc_t add;
+ /*%
+ * dns_load_master / dns_rdata_fromtext call this to issue a error.
+ */
+ void (*error)(struct dns_rdatacallbacks *, const char *, ...);
+ /*%
+ * dns_load_master / dns_rdata_fromtext call this to issue a warning.
+ */
+ void (*warn)(struct dns_rdatacallbacks *, const char *, ...);
+ /*%
+ * Private data handles for use by the above callback functions.
+ */
+ void *add_private;
+ void *error_private;
+ void *warn_private;
+};
+
+/***
+ *** Initialization
+ ***/
+
+void
+dns_rdatacallbacks_init(dns_rdatacallbacks_t *callbacks);
+/*%<
+ * Initialize 'callbacks'.
+ *
+ *
+ * \li 'error' and 'warn' are set to default callbacks that print the
+ * error message through the DNS library log context.
+ *
+ *\li All other elements are initialized to NULL.
+ *
+ * Requires:
+ * \li 'callbacks' is a valid dns_rdatacallbacks_t,
+ */
+
+void
+dns_rdatacallbacks_init_stdio(dns_rdatacallbacks_t *callbacks);
+/*%<
+ * Like dns_rdatacallbacks_init, but logs to stdio.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_CALLBACKS_H */
diff --git a/lib/dns/include/dns/cert.h b/lib/dns/include/dns/cert.h
new file mode 100644
index 0000000..1cda848
--- /dev/null
+++ b/lib/dns/include/dns/cert.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: cert.h,v 1.19 2007/06/19 23:47:16 tbox Exp $ */
+
+#ifndef DNS_CERT_H
+#define DNS_CERT_H 1
+
+/*! \file dns/cert.h */
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_cert_fromtext(dns_cert_t *certp, isc_textregion_t *source);
+/*%<
+ * Convert the text 'source' refers to into a certificate type.
+ * The text may contain either a mnemonic type name or a decimal type number.
+ *
+ * Requires:
+ *\li 'certp' is a valid pointer.
+ *
+ *\li 'source' is a valid text region.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS on success
+ *\li #ISC_R_RANGE numeric type is out of range
+ *\li #DNS_R_UNKNOWN mnemonic type is unknown
+ */
+
+isc_result_t
+dns_cert_totext(dns_cert_t cert, isc_buffer_t *target);
+/*%<
+ * Put a textual representation of certificate type 'cert' into 'target'.
+ *
+ * Requires:
+ *\li 'cert' is a valid cert.
+ *
+ *\li 'target' is a valid text buffer.
+ *
+ * Ensures:
+ *\li If the result is success:
+ * The used space in 'target' is updated.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS on success
+ *\li #ISC_R_NOSPACE target buffer is too small
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_CERT_H */
diff --git a/lib/dns/include/dns/compress.h b/lib/dns/include/dns/compress.h
new file mode 100644
index 0000000..0b2c9c3
--- /dev/null
+++ b/lib/dns/include/dns/compress.h
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: compress.h,v 1.40 2007/06/19 23:47:16 tbox Exp $ */
+
+#ifndef DNS_COMPRESS_H
+#define DNS_COMPRESS_H 1
+
+#include <isc/lang.h>
+#include <isc/region.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+#define DNS_COMPRESS_NONE 0x00 /*%< no compression */
+#define DNS_COMPRESS_GLOBAL14 0x01 /*%< "normal" compression. */
+#define DNS_COMPRESS_ALL 0x01 /*%< all compression. */
+#define DNS_COMPRESS_CASESENSITIVE 0x02 /*%< case sensitive compression. */
+
+/*! \file dns/compress.h
+ * Direct manipulation of the structures is strongly discouraged.
+ */
+
+#define DNS_COMPRESS_TABLESIZE 64
+#define DNS_COMPRESS_INITIALNODES 16
+
+typedef struct dns_compressnode dns_compressnode_t;
+
+struct dns_compressnode {
+ isc_region_t r;
+ isc_uint16_t offset;
+ isc_uint16_t count;
+ isc_uint8_t labels;
+ dns_compressnode_t *next;
+};
+
+struct dns_compress {
+ unsigned int magic; /*%< Magic number. */
+ unsigned int allowed; /*%< Allowed methods. */
+ int edns; /*%< Edns version or -1. */
+ /*% Global compression table. */
+ dns_compressnode_t *table[DNS_COMPRESS_TABLESIZE];
+ /*% Preallocated nodes for the table. */
+ dns_compressnode_t initialnodes[DNS_COMPRESS_INITIALNODES];
+ isc_uint16_t count; /*%< Number of nodes. */
+ isc_mem_t *mctx; /*%< Memory context. */
+};
+
+typedef enum {
+ DNS_DECOMPRESS_ANY, /*%< Any compression */
+ DNS_DECOMPRESS_STRICT, /*%< Allowed compression */
+ DNS_DECOMPRESS_NONE /*%< No compression */
+} dns_decompresstype_t;
+
+struct dns_decompress {
+ unsigned int magic; /*%< Magic number. */
+ unsigned int allowed; /*%< Allowed methods. */
+ int edns; /*%< Edns version or -1. */
+ dns_decompresstype_t type; /*%< Strict checking */
+};
+
+isc_result_t
+dns_compress_init(dns_compress_t *cctx, int edns, isc_mem_t *mctx);
+/*%<
+ * Inialise the compression context structure pointed to by 'cctx'.
+ *
+ * Requires:
+ * \li 'cctx' is a valid dns_compress_t structure.
+ * \li 'mctx' is an initialized memory context.
+ * Ensures:
+ * \li cctx->global is initialized.
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS
+ * \li failures from dns_rbt_create()
+ */
+
+void
+dns_compress_invalidate(dns_compress_t *cctx);
+
+/*%<
+ * Invalidate the compression structure pointed to by cctx.
+ *
+ * Requires:
+ *\li 'cctx' to be initialized.
+ */
+
+void
+dns_compress_setmethods(dns_compress_t *cctx, unsigned int allowed);
+
+/*%<
+ * Sets allowed compression methods.
+ *
+ * Requires:
+ *\li 'cctx' to be initialized.
+ */
+
+unsigned int
+dns_compress_getmethods(dns_compress_t *cctx);
+
+/*%<
+ * Gets allowed compression methods.
+ *
+ * Requires:
+ *\li 'cctx' to be initialized.
+ *
+ * Returns:
+ *\li allowed compression bitmap.
+ */
+
+void
+dns_compress_setsensitive(dns_compress_t *cctx, isc_boolean_t sensitive);
+
+/*
+ * Preserve the case of compressed domain names.
+ *
+ * Requires:
+ * 'cctx' to be initialized.
+ */
+
+isc_boolean_t
+dns_compress_getsensitive(dns_compress_t *cctx);
+/*
+ * Return whether case is to be preservered when compressing
+ * domain names.
+ *
+ * Requires:
+ * 'cctx' to be initialized.
+ */
+
+int
+dns_compress_getedns(dns_compress_t *cctx);
+
+/*%<
+ * Gets edns value.
+ *
+ * Requires:
+ *\li 'cctx' to be initialized.
+ *
+ * Returns:
+ *\li -1 .. 255
+ */
+
+isc_boolean_t
+dns_compress_findglobal(dns_compress_t *cctx, const dns_name_t *name,
+ dns_name_t *prefix, isc_uint16_t *offset);
+/*%<
+ * Finds longest possible match of 'name' in the global compression table.
+ *
+ * Requires:
+ *\li 'cctx' to be initialized.
+ *\li 'name' to be a absolute name.
+ *\li 'prefix' to be initialized.
+ *\li 'offset' to point to an isc_uint16_t.
+ *
+ * Ensures:
+ *\li 'prefix' and 'offset' are valid if ISC_TRUE is returned.
+ *
+ * Returns:
+ *\li #ISC_TRUE / #ISC_FALSE
+ */
+
+void
+dns_compress_add(dns_compress_t *cctx, const dns_name_t *name,
+ const dns_name_t *prefix, isc_uint16_t offset);
+/*%<
+ * Add compression pointers for 'name' to the compression table,
+ * not replacing existing pointers.
+ *
+ * Requires:
+ *\li 'cctx' initialized
+ *
+ *\li 'name' must be initialized and absolute, and must remain
+ * valid until the message compression is complete.
+ *
+ *\li 'prefix' must be a prefix returned by
+ * dns_compress_findglobal(), or the same as 'name'.
+ */
+
+void
+dns_compress_rollback(dns_compress_t *cctx, isc_uint16_t offset);
+
+/*%<
+ * Remove any compression pointers from global table >= offset.
+ *
+ * Requires:
+ *\li 'cctx' is initialized.
+ */
+
+void
+dns_decompress_init(dns_decompress_t *dctx, int edns,
+ dns_decompresstype_t type);
+
+/*%<
+ * Initializes 'dctx'.
+ * Records 'edns' and 'type' into the structure.
+ *
+ * Requires:
+ *\li 'dctx' to be a valid pointer.
+ */
+
+void
+dns_decompress_invalidate(dns_decompress_t *dctx);
+
+/*%<
+ * Invalidates 'dctx'.
+ *
+ * Requires:
+ *\li 'dctx' to be initialized
+ */
+
+void
+dns_decompress_setmethods(dns_decompress_t *dctx, unsigned int allowed);
+
+/*%<
+ * Sets 'dctx->allowed' to 'allowed'.
+ *
+ * Requires:
+ *\li 'dctx' to be initialized
+ */
+
+unsigned int
+dns_decompress_getmethods(dns_decompress_t *dctx);
+
+/*%<
+ * Returns 'dctx->allowed'
+ *
+ * Requires:
+ *\li 'dctx' to be initialized
+ */
+
+int
+dns_decompress_edns(dns_decompress_t *dctx);
+
+/*%<
+ * Returns 'dctx->edns'
+ *
+ * Requires:
+ *\li 'dctx' to be initialized
+ */
+
+dns_decompresstype_t
+dns_decompress_type(dns_decompress_t *dctx);
+
+/*%<
+ * Returns 'dctx->type'
+ *
+ * Requires:
+ *\li 'dctx' to be initialized
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_COMPRESS_H */
diff --git a/lib/dns/include/dns/db.h b/lib/dns/include/dns/db.h
new file mode 100644
index 0000000..8e22463
--- /dev/null
+++ b/lib/dns/include/dns/db.h
@@ -0,0 +1,1477 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: db.h,v 1.93 2008/09/24 02:46:22 marka Exp $ */
+
+#ifndef DNS_DB_H
+#define DNS_DB_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/db.h
+ * \brief
+ * The DNS DB interface allows named rdatasets to be stored and retrieved.
+ *
+ * The dns_db_t type is like a "virtual class". To actually use
+ * DBs, an implementation of the class is required.
+ *
+ * XXX more XXX
+ *
+ * MP:
+ * \li The module ensures appropriate synchronization of data structures it
+ * creates and manipulates.
+ *
+ * Reliability:
+ * \li No anticipated impact.
+ *
+ * Resources:
+ * \li TBS
+ *
+ * Security:
+ * \li No anticipated impact.
+ *
+ * Standards:
+ * \li None.
+ */
+
+/*****
+ ***** Imports
+ *****/
+
+#include <isc/lang.h>
+#include <isc/magic.h>
+#include <isc/ondestroy.h>
+#include <isc/stdtime.h>
+
+#include <dns/name.h>
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/*****
+ ***** Types
+ *****/
+
+typedef struct dns_dbmethods {
+ void (*attach)(dns_db_t *source, dns_db_t **targetp);
+ void (*detach)(dns_db_t **dbp);
+ isc_result_t (*beginload)(dns_db_t *db, dns_addrdatasetfunc_t *addp,
+ dns_dbload_t **dbloadp);
+ isc_result_t (*endload)(dns_db_t *db, dns_dbload_t **dbloadp);
+ isc_result_t (*dump)(dns_db_t *db, dns_dbversion_t *version,
+ const char *filename,
+ dns_masterformat_t masterformat);
+ void (*currentversion)(dns_db_t *db,
+ dns_dbversion_t **versionp);
+ isc_result_t (*newversion)(dns_db_t *db,
+ dns_dbversion_t **versionp);
+ void (*attachversion)(dns_db_t *db, dns_dbversion_t *source,
+ dns_dbversion_t **targetp);
+ void (*closeversion)(dns_db_t *db,
+ dns_dbversion_t **versionp,
+ isc_boolean_t commit);
+ isc_result_t (*findnode)(dns_db_t *db, dns_name_t *name,
+ isc_boolean_t create,
+ dns_dbnode_t **nodep);
+ isc_result_t (*find)(dns_db_t *db, dns_name_t *name,
+ dns_dbversion_t *version,
+ dns_rdatatype_t type, unsigned int options,
+ isc_stdtime_t now,
+ dns_dbnode_t **nodep, dns_name_t *foundname,
+ dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset);
+ isc_result_t (*findzonecut)(dns_db_t *db, dns_name_t *name,
+ unsigned int options, isc_stdtime_t now,
+ dns_dbnode_t **nodep,
+ dns_name_t *foundname,
+ dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset);
+ void (*attachnode)(dns_db_t *db,
+ dns_dbnode_t *source,
+ dns_dbnode_t **targetp);
+ void (*detachnode)(dns_db_t *db,
+ dns_dbnode_t **targetp);
+ isc_result_t (*expirenode)(dns_db_t *db, dns_dbnode_t *node,
+ isc_stdtime_t now);
+ void (*printnode)(dns_db_t *db, dns_dbnode_t *node,
+ FILE *out);
+ isc_result_t (*createiterator)(dns_db_t *db, unsigned int options,
+ dns_dbiterator_t **iteratorp);
+ isc_result_t (*findrdataset)(dns_db_t *db, dns_dbnode_t *node,
+ dns_dbversion_t *version,
+ dns_rdatatype_t type,
+ dns_rdatatype_t covers,
+ isc_stdtime_t now,
+ dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset);
+ isc_result_t (*allrdatasets)(dns_db_t *db, dns_dbnode_t *node,
+ dns_dbversion_t *version,
+ isc_stdtime_t now,
+ dns_rdatasetiter_t **iteratorp);
+ isc_result_t (*addrdataset)(dns_db_t *db, dns_dbnode_t *node,
+ dns_dbversion_t *version,
+ isc_stdtime_t now,
+ dns_rdataset_t *rdataset,
+ unsigned int options,
+ dns_rdataset_t *addedrdataset);
+ isc_result_t (*subtractrdataset)(dns_db_t *db, dns_dbnode_t *node,
+ dns_dbversion_t *version,
+ dns_rdataset_t *rdataset,
+ unsigned int options,
+ dns_rdataset_t *newrdataset);
+ isc_result_t (*deleterdataset)(dns_db_t *db, dns_dbnode_t *node,
+ dns_dbversion_t *version,
+ dns_rdatatype_t type,
+ dns_rdatatype_t covers);
+ isc_boolean_t (*issecure)(dns_db_t *db);
+ unsigned int (*nodecount)(dns_db_t *db);
+ isc_boolean_t (*ispersistent)(dns_db_t *db);
+ void (*overmem)(dns_db_t *db, isc_boolean_t overmem);
+ void (*settask)(dns_db_t *db, isc_task_t *);
+ isc_result_t (*getoriginnode)(dns_db_t *db, dns_dbnode_t **nodep);
+ void (*transfernode)(dns_db_t *db, dns_dbnode_t **sourcep,
+ dns_dbnode_t **targetp);
+ isc_result_t (*getnsec3parameters)(dns_db_t *db,
+ dns_dbversion_t *version,
+ dns_hash_t *hash,
+ isc_uint8_t *flags,
+ isc_uint16_t *iterations,
+ unsigned char *salt,
+ size_t *salt_len);
+ isc_result_t (*findnsec3node)(dns_db_t *db, dns_name_t *name,
+ isc_boolean_t create,
+ dns_dbnode_t **nodep);
+ isc_result_t (*setsigningtime)(dns_db_t *db,
+ dns_rdataset_t *rdataset,
+ isc_stdtime_t resign);
+ isc_result_t (*getsigningtime)(dns_db_t *db,
+ dns_rdataset_t *rdataset,
+ dns_name_t *name);
+ void (*resigned)(dns_db_t *db, dns_rdataset_t *rdataset,
+ dns_dbversion_t *version);
+ isc_boolean_t (*isdnssec)(dns_db_t *db);
+ dns_stats_t *(*getrrsetstats)(dns_db_t *db);
+} dns_dbmethods_t;
+
+typedef isc_result_t
+(*dns_dbcreatefunc_t)(isc_mem_t *mctx, dns_name_t *name,
+ dns_dbtype_t type, dns_rdataclass_t rdclass,
+ unsigned int argc, char *argv[], void *driverarg,
+ dns_db_t **dbp);
+
+#define DNS_DB_MAGIC ISC_MAGIC('D','N','S','D')
+#define DNS_DB_VALID(db) ISC_MAGIC_VALID(db, DNS_DB_MAGIC)
+
+/*%
+ * This structure is actually just the common prefix of a DNS db
+ * implementation's version of a dns_db_t.
+ * \brief
+ * Direct use of this structure by clients is forbidden. DB implementations
+ * may change the structure. 'magic' must be DNS_DB_MAGIC for any of the
+ * dns_db_ routines to work. DB implementations must maintain all DB
+ * invariants.
+ */
+struct dns_db {
+ unsigned int magic;
+ unsigned int impmagic;
+ dns_dbmethods_t * methods;
+ isc_uint16_t attributes;
+ dns_rdataclass_t rdclass;
+ dns_name_t origin;
+ isc_ondestroy_t ondest;
+ isc_mem_t * mctx;
+};
+
+#define DNS_DBATTR_CACHE 0x01
+#define DNS_DBATTR_STUB 0x02
+
+/*@{*/
+/*%
+ * Options that can be specified for dns_db_find().
+ */
+#define DNS_DBFIND_GLUEOK 0x01
+#define DNS_DBFIND_VALIDATEGLUE 0x02
+#define DNS_DBFIND_NOWILD 0x04
+#define DNS_DBFIND_PENDINGOK 0x08
+#define DNS_DBFIND_NOEXACT 0x10
+#define DNS_DBFIND_FORCENSEC 0x20
+#define DNS_DBFIND_COVERINGNSEC 0x40
+#define DNS_DBFIND_FORCENSEC3 0x80
+/*@}*/
+
+/*@{*/
+/*%
+ * Options that can be specified for dns_db_addrdataset().
+ */
+#define DNS_DBADD_MERGE 0x01
+#define DNS_DBADD_FORCE 0x02
+#define DNS_DBADD_EXACT 0x04
+#define DNS_DBADD_EXACTTTL 0x08
+/*@}*/
+
+/*%
+ * Options that can be specified for dns_db_subtractrdataset().
+ */
+#define DNS_DBSUB_EXACT 0x01
+
+/*@{*/
+/*%
+ * Iterator options
+ */
+#define DNS_DB_RELATIVENAMES 0x1
+#define DNS_DB_NSEC3ONLY 0x2
+#define DNS_DB_NONSEC3 0x4
+/*@}*/
+
+/*****
+ ***** Methods
+ *****/
+
+/***
+ *** Basic DB Methods
+ ***/
+
+isc_result_t
+dns_db_create(isc_mem_t *mctx, const char *db_type, dns_name_t *origin,
+ dns_dbtype_t type, dns_rdataclass_t rdclass,
+ unsigned int argc, char *argv[], dns_db_t **dbp);
+/*%<
+ * Create a new database using implementation 'db_type'.
+ *
+ * Notes:
+ * \li All names in the database must be subdomains of 'origin' and in class
+ * 'rdclass'. The database makes its own copy of the origin, so the
+ * caller may do whatever they like with 'origin' and its storage once the
+ * call returns.
+ *
+ * \li DB implementation-specific parameters are passed using argc and argv.
+ *
+ * Requires:
+ *
+ * \li dbp != NULL and *dbp == NULL
+ *
+ * \li 'origin' is a valid absolute domain name.
+ *
+ * \li mctx is a valid memory context
+ *
+ * Ensures:
+ *
+ * \li A copy of 'origin' has been made for the databases use, and the
+ * caller is free to do whatever they want with the name and storage
+ * associated with 'origin'.
+ *
+ * Returns:
+ *
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOMEMORY
+ * \li #ISC_R_NOTFOUND db_type not found
+ *
+ * \li Many other errors are possible, depending on what db_type was
+ * specified.
+ */
+
+void
+dns_db_attach(dns_db_t *source, dns_db_t **targetp);
+/*%<
+ * Attach *targetp to source.
+ *
+ * Requires:
+ *
+ * \li 'source' is a valid database.
+ *
+ * \li 'targetp' points to a NULL dns_db_t *.
+ *
+ * Ensures:
+ *
+ * \li *targetp is attached to source.
+ */
+
+void
+dns_db_detach(dns_db_t **dbp);
+/*%<
+ * Detach *dbp from its database.
+ *
+ * Requires:
+ *
+ * \li 'dbp' points to a valid database.
+ *
+ * Ensures:
+ *
+ * \li *dbp is NULL.
+ *
+ * \li If '*dbp' is the last reference to the database,
+ * all resources used by the database will be freed
+ */
+
+isc_result_t
+dns_db_ondestroy(dns_db_t *db, isc_task_t *task, isc_event_t **eventp);
+/*%<
+ * Causes 'eventp' to be sent to be sent to 'task' when the database is
+ * destroyed.
+ *
+ * Note; ownership of the eventp is taken from the caller (and *eventp is
+ * set to NULL). The sender field of the event is set to 'db' before it is
+ * sent to the task.
+ */
+
+isc_boolean_t
+dns_db_iscache(dns_db_t *db);
+/*%<
+ * Does 'db' have cache semantics?
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * Returns:
+ * \li #ISC_TRUE 'db' has cache semantics
+ * \li #ISC_FALSE otherwise
+ */
+
+isc_boolean_t
+dns_db_iszone(dns_db_t *db);
+/*%<
+ * Does 'db' have zone semantics?
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * Returns:
+ * \li #ISC_TRUE 'db' has zone semantics
+ * \li #ISC_FALSE otherwise
+ */
+
+isc_boolean_t
+dns_db_isstub(dns_db_t *db);
+/*%<
+ * Does 'db' have stub semantics?
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * Returns:
+ * \li #ISC_TRUE 'db' has zone semantics
+ * \li #ISC_FALSE otherwise
+ */
+
+isc_boolean_t
+dns_db_issecure(dns_db_t *db);
+/*%<
+ * Is 'db' secure?
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database with zone semantics.
+ *
+ * Returns:
+ * \li #ISC_TRUE 'db' is secure.
+ * \li #ISC_FALSE 'db' is not secure.
+ */
+
+isc_boolean_t
+dns_db_isdnssec(dns_db_t *db);
+/*%<
+ * Is 'db' secure or partially secure?
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database with zone semantics.
+ *
+ * Returns:
+ * \li #ISC_TRUE 'db' is secure or is partially.
+ * \li #ISC_FALSE 'db' is not secure.
+ */
+
+dns_name_t *
+dns_db_origin(dns_db_t *db);
+/*%<
+ * The origin of the database.
+ *
+ * Note: caller must not try to change this name.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * Returns:
+ *
+ * \li The origin of the database.
+ */
+
+dns_rdataclass_t
+dns_db_class(dns_db_t *db);
+/*%<
+ * The class of the database.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * Returns:
+ *
+ * \li The class of the database.
+ */
+
+isc_result_t
+dns_db_beginload(dns_db_t *db, dns_addrdatasetfunc_t *addp,
+ dns_dbload_t **dbloadp);
+/*%<
+ * Begin loading 'db'.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * \li This is the first attempt to load 'db'.
+ *
+ * \li addp != NULL && *addp == NULL
+ *
+ * \li dbloadp != NULL && *dbloadp == NULL
+ *
+ * Ensures:
+ *
+ * \li On success, *addp will be a valid dns_addrdatasetfunc_t suitable
+ * for loading 'db'. *dbloadp will be a valid DB load context which
+ * should be used as 'arg' when *addp is called.
+ *
+ * Returns:
+ *
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOMEMORY
+ *
+ * \li Other results are possible, depending upon the database
+ * implementation used, syntax errors in the master file, etc.
+ */
+
+isc_result_t
+dns_db_endload(dns_db_t *db, dns_dbload_t **dbloadp);
+/*%<
+ * Finish loading 'db'.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database that is being loaded.
+ *
+ * \li dbloadp != NULL and *dbloadp is a valid database load context.
+ *
+ * Ensures:
+ *
+ * \li *dbloadp == NULL
+ *
+ * Returns:
+ *
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOMEMORY
+ *
+ * \li Other results are possible, depending upon the database
+ * implementation used, syntax errors in the master file, etc.
+ */
+
+isc_result_t
+dns_db_load(dns_db_t *db, const char *filename);
+
+isc_result_t
+dns_db_load2(dns_db_t *db, const char *filename, dns_masterformat_t format);
+/*%<
+ * Load master file 'filename' into 'db'.
+ *
+ * Notes:
+ * \li This routine is equivalent to calling
+ *
+ *\code
+ * dns_db_beginload();
+ * dns_master_loadfile();
+ * dns_db_endload();
+ *\endcode
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * \li This is the first attempt to load 'db'.
+ *
+ * Returns:
+ *
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOMEMORY
+ *
+ * \li Other results are possible, depending upon the database
+ * implementation used, syntax errors in the master file, etc.
+ */
+
+isc_result_t
+dns_db_dump(dns_db_t *db, dns_dbversion_t *version, const char *filename);
+
+isc_result_t
+dns_db_dump2(dns_db_t *db, dns_dbversion_t *version, const char *filename,
+ dns_masterformat_t masterformat);
+/*%<
+ * Dump version 'version' of 'db' to master file 'filename'.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * \li 'version' is a valid version.
+ *
+ * Returns:
+ *
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOMEMORY
+ *
+ * \li Other results are possible, depending upon the database
+ * implementation used, OS file errors, etc.
+ */
+
+/***
+ *** Version Methods
+ ***/
+
+void
+dns_db_currentversion(dns_db_t *db, dns_dbversion_t **versionp);
+/*%<
+ * Open the current version for reading.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database with zone semantics.
+ *
+ * \li versionp != NULL && *verisonp == NULL
+ *
+ * Ensures:
+ *
+ * \li On success, '*versionp' is attached to the current version.
+ *
+ */
+
+isc_result_t
+dns_db_newversion(dns_db_t *db, dns_dbversion_t **versionp);
+/*%<
+ * Open a new version for reading and writing.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database with zone semantics.
+ *
+ * \li versionp != NULL && *verisonp == NULL
+ *
+ * Ensures:
+ *
+ * \li On success, '*versionp' is attached to the current version.
+ *
+ * Returns:
+ *
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOMEMORY
+ *
+ * \li Other results are possible, depending upon the database
+ * implementation used.
+ */
+
+void
+dns_db_attachversion(dns_db_t *db, dns_dbversion_t *source,
+ dns_dbversion_t **targetp);
+/*%<
+ * Attach '*targetp' to 'source'.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database with zone semantics.
+ *
+ * \li source is a valid open version
+ *
+ * \li targetp != NULL && *targetp == NULL
+ *
+ * Ensures:
+ *
+ * \li '*targetp' is attached to source.
+ */
+
+void
+dns_db_closeversion(dns_db_t *db, dns_dbversion_t **versionp,
+ isc_boolean_t commit);
+/*%<
+ * Close version '*versionp'.
+ *
+ * Note: if '*versionp' is a read-write version and 'commit' is ISC_TRUE,
+ * then all changes made in the version will take effect, otherwise they
+ * will be rolled back. The value if 'commit' is ignored for read-only
+ * versions.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database with zone semantics.
+ *
+ * \li '*versionp' refers to a valid version.
+ *
+ * \li If committing a writable version, then there must be no other
+ * outstanding references to the version (e.g. an active rdataset
+ * iterator).
+ *
+ * Ensures:
+ *
+ * \li *versionp == NULL
+ *
+ * \li If *versionp is a read-write version, and commit is ISC_TRUE, then
+ * the version will become the current version. If !commit, then all
+ * changes made in the version will be undone, and the version will
+ * not become the current version.
+ */
+
+/***
+ *** Node Methods
+ ***/
+
+isc_result_t
+dns_db_findnode(dns_db_t *db, dns_name_t *name, isc_boolean_t create,
+ dns_dbnode_t **nodep);
+/*%<
+ * Find the node with name 'name'.
+ *
+ * Notes:
+ * \li If 'create' is ISC_TRUE and no node with name 'name' exists, then
+ * such a node will be created.
+ *
+ * \li This routine is for finding or creating a node with the specified
+ * name. There are no partial matches. It is not suitable for use
+ * in building responses to ordinary DNS queries; clients which wish
+ * to do that should use dns_db_find() instead.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * \li 'name' is a valid, non-empty, absolute name.
+ *
+ * \li nodep != NULL && *nodep == NULL
+ *
+ * Ensures:
+ *
+ * \li On success, *nodep is attached to the node with name 'name'.
+ *
+ * Returns:
+ *
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOTFOUND If !create and name not found.
+ * \li #ISC_R_NOMEMORY Can only happen if create is ISC_TRUE.
+ *
+ * \li Other results are possible, depending upon the database
+ * implementation used.
+ */
+
+isc_result_t
+dns_db_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
+ dns_rdatatype_t type, unsigned int options, isc_stdtime_t now,
+ dns_dbnode_t **nodep, dns_name_t *foundname,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset);
+/*%<
+ * Find the best match for 'name' and 'type' in version 'version' of 'db'.
+ *
+ * Notes:
+ *
+ * \li If type == dns_rdataset_any, then rdataset will not be bound.
+ *
+ * \li If 'options' does not have #DNS_DBFIND_GLUEOK set, then no glue will
+ * be returned. For zone databases, glue is as defined in RFC2181.
+ * For cache databases, glue is any rdataset with a trust of
+ * dns_trust_glue.
+ *
+ * \li If 'options' does not have #DNS_DBFIND_PENDINGOK set, then no
+ * pending data will be returned. This option is only meaningful for
+ * cache databases.
+ *
+ * \li If the #DNS_DBFIND_NOWILD option is set, then wildcard matching will
+ * be disabled. This option is only meaningful for zone databases.
+ *
+ * \li If the #DNS_DBFIND_FORCENSEC option is set, the database is assumed to
+ * have NSEC records, and these will be returned when appropriate. This
+ * is only necessary when querying a database that was not secure
+ * when created.
+ *
+ * \li If the DNS_DBFIND_COVERINGNSEC option is set, then look for a
+ * NSEC record that potentially covers 'name' if a answer cannot
+ * be found. Note the returned NSEC needs to be checked to ensure
+ * that it is correct. This only affects answers returned from the
+ * cache.
+ *
+ * \li To respond to a query for SIG records, the caller should create a
+ * rdataset iterator and extract the signatures from each rdataset.
+ *
+ * \li Making queries of type ANY with #DNS_DBFIND_GLUEOK is not recommended,
+ * because the burden of determining whether a given rdataset is valid
+ * glue or not falls upon the caller.
+ *
+ * \li The 'now' field is ignored if 'db' is a zone database. If 'db' is a
+ * cache database, an rdataset will not be found unless it expires after
+ * 'now'. Any ANY query will not match unless at least one rdataset at
+ * the node expires after 'now'. If 'now' is zero, then the current time
+ * will be used.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * \li 'type' is not SIG, or a meta-RR type other than 'ANY' (e.g. 'OPT').
+ *
+ * \li 'nodep' is NULL, or nodep is a valid pointer and *nodep == NULL.
+ *
+ * \li 'foundname' is a valid name with a dedicated buffer.
+ *
+ * \li 'rdataset' is NULL, or is a valid unassociated rdataset.
+ *
+ * Ensures,
+ * on a non-error completion:
+ *
+ * \li If nodep != NULL, then it is bound to the found node.
+ *
+ * \li If foundname != NULL, then it contains the full name of the
+ * found node.
+ *
+ * \li If rdataset != NULL and type != dns_rdatatype_any, then
+ * rdataset is bound to the found rdataset.
+ *
+ * Non-error results are:
+ *
+ * \li #ISC_R_SUCCESS The desired node and type were
+ * found.
+ *
+ * \li #DNS_R_WILDCARD The desired node and type were
+ * found after performing
+ * wildcard matching. This is
+ * only returned if the
+ * #DNS_DBFIND_INDICATEWILD
+ * option is set; otherwise
+ * #ISC_R_SUCCESS is returned.
+ *
+ * \li #DNS_R_GLUE The desired node and type were
+ * found, but are glue. This
+ * result can only occur if
+ * the DNS_DBFIND_GLUEOK option
+ * is set. This result can only
+ * occur if 'db' is a zone
+ * database. If type ==
+ * dns_rdatatype_any, then the
+ * node returned may contain, or
+ * consist entirely of invalid
+ * glue (i.e. data occluded by a
+ * zone cut). The caller must
+ * take care not to return invalid
+ * glue to a client.
+ *
+ * \li #DNS_R_DELEGATION The data requested is beneath
+ * a zone cut. node, foundname,
+ * and rdataset reference the
+ * NS RRset of the zone cut.
+ * If 'db' is a cache database,
+ * then this is the deepest known
+ * delegation.
+ *
+ * \li #DNS_R_ZONECUT type == dns_rdatatype_any, and
+ * the desired node is a zonecut.
+ * The caller must take care not
+ * to return inappropriate glue
+ * to a client. This result can
+ * only occur if 'db' is a zone
+ * database and DNS_DBFIND_GLUEOK
+ * is set.
+ *
+ * \li #DNS_R_DNAME The data requested is beneath
+ * a DNAME. node, foundname,
+ * and rdataset reference the
+ * DNAME RRset.
+ *
+ * \li #DNS_R_CNAME The rdataset requested was not
+ * found, but there is a CNAME
+ * at the desired name. node,
+ * foundname, and rdataset
+ * reference the CNAME RRset.
+ *
+ * \li #DNS_R_NXDOMAIN The desired name does not
+ * exist.
+ *
+ * \li #DNS_R_NXRRSET The desired name exists, but
+ * the desired type does not.
+ *
+ * \li #ISC_R_NOTFOUND The desired name does not
+ * exist, and no delegation could
+ * be found. This result can only
+ * occur if 'db' is a cache
+ * database. The caller should
+ * use its nameserver(s) of last
+ * resort (e.g. root hints).
+ *
+ * \li #DNS_R_NCACHENXDOMAIN The desired name does not
+ * exist. 'node' is bound to the
+ * cache node with the desired
+ * name, and 'rdataset' contains
+ * the negative caching proof.
+ *
+ * \li #DNS_R_NCACHENXRRSET The desired type does not
+ * exist. 'node' is bound to the
+ * cache node with the desired
+ * name, and 'rdataset' contains
+ * the negative caching proof.
+ *
+ * \li #DNS_R_EMPTYNAME The name exists but there is
+ * no data at the name.
+ *
+ * \li #DNS_R_COVERINGNSEC The returned data is a NSEC
+ * that potentially covers 'name'.
+ *
+ * Error results:
+ *
+ * \li #ISC_R_NOMEMORY
+ *
+ * \li #DNS_R_BADDB Data that is required to be
+ * present in the DB, e.g. an NSEC
+ * record in a secure zone, is not
+ * present.
+ *
+ * \li Other results are possible, and should all be treated as
+ * errors.
+ */
+
+isc_result_t
+dns_db_findzonecut(dns_db_t *db, dns_name_t *name,
+ unsigned int options, isc_stdtime_t now,
+ dns_dbnode_t **nodep, dns_name_t *foundname,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset);
+/*%<
+ * Find the deepest known zonecut which encloses 'name' in 'db'.
+ *
+ * Notes:
+ *
+ * \li If the #DNS_DBFIND_NOEXACT option is set, then the zonecut returned
+ * (if any) will be the deepest known ancestor of 'name'.
+ *
+ * \li If 'now' is zero, then the current time will be used.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database with cache semantics.
+ *
+ * \li 'nodep' is NULL, or nodep is a valid pointer and *nodep == NULL.
+ *
+ * \li 'foundname' is a valid name with a dedicated buffer.
+ *
+ * \li 'rdataset' is NULL, or is a valid unassociated rdataset.
+ *
+ * Ensures, on a non-error completion:
+ *
+ * \li If nodep != NULL, then it is bound to the found node.
+ *
+ * \li If foundname != NULL, then it contains the full name of the
+ * found node.
+ *
+ * \li If rdataset != NULL and type != dns_rdatatype_any, then
+ * rdataset is bound to the found rdataset.
+ *
+ * Non-error results are:
+ *
+ * \li #ISC_R_SUCCESS
+ *
+ * \li #ISC_R_NOTFOUND
+ *
+ * \li Other results are possible, and should all be treated as
+ * errors.
+ */
+
+void
+dns_db_attachnode(dns_db_t *db, dns_dbnode_t *source, dns_dbnode_t **targetp);
+/*%<
+ * Attach *targetp to source.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * \li 'source' is a valid node.
+ *
+ * \li 'targetp' points to a NULL dns_dbnode_t *.
+ *
+ * Ensures:
+ *
+ * \li *targetp is attached to source.
+ */
+
+void
+dns_db_detachnode(dns_db_t *db, dns_dbnode_t **nodep);
+/*%<
+ * Detach *nodep from its node.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * \li 'nodep' points to a valid node.
+ *
+ * Ensures:
+ *
+ * \li *nodep is NULL.
+ */
+
+void
+dns_db_transfernode(dns_db_t *db, dns_dbnode_t **sourcep,
+ dns_dbnode_t **targetp);
+/*%<
+ * Transfer a node between pointer.
+ *
+ * This is equivalent to calling dns_db_attachnode() then dns_db_detachnode().
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * \li '*sourcep' is a valid node.
+ *
+ * \li 'targetp' points to a NULL dns_dbnode_t *.
+ *
+ * Ensures:
+ *
+ * \li '*sourcep' is NULL.
+ */
+
+isc_result_t
+dns_db_expirenode(dns_db_t *db, dns_dbnode_t *node, isc_stdtime_t now);
+/*%<
+ * Mark as stale all records at 'node' which expire at or before 'now'.
+ *
+ * Note: if 'now' is zero, then the current time will be used.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid cache database.
+ *
+ * \li 'node' is a valid node.
+ */
+
+void
+dns_db_printnode(dns_db_t *db, dns_dbnode_t *node, FILE *out);
+/*%<
+ * Print a textual representation of the contents of the node to
+ * 'out'.
+ *
+ * Note: this function is intended for debugging, not general use.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * \li 'node' is a valid node.
+ */
+
+/***
+ *** DB Iterator Creation
+ ***/
+
+isc_result_t
+dns_db_createiterator(dns_db_t *db, unsigned int options,
+ dns_dbiterator_t **iteratorp);
+/*%<
+ * Create an iterator for version 'version' of 'db'.
+ *
+ * Notes:
+ *
+ * \li One or more of the following options can be set.
+ * #DNS_DB_RELATIVENAMES
+ * #DNS_DB_NSEC3ONLY
+ * #DNS_DB_NONSEC3
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * \li iteratorp != NULL && *iteratorp == NULL
+ *
+ * Ensures:
+ *
+ * \li On success, *iteratorp will be a valid database iterator.
+ *
+ * Returns:
+ *
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOMEMORY
+ */
+
+/***
+ *** Rdataset Methods
+ ***/
+
+/*
+ * XXXRTH Should we check for glue and pending data in dns_db_findrdataset()?
+ */
+
+isc_result_t
+dns_db_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ dns_rdatatype_t type, dns_rdatatype_t covers,
+ isc_stdtime_t now, dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset);
+/*%<
+ * Search for an rdataset of type 'type' at 'node' that are in version
+ * 'version' of 'db'. If found, make 'rdataset' refer to it.
+ *
+ * Notes:
+ *
+ * \li If 'version' is NULL, then the current version will be used.
+ *
+ * \li Care must be used when using this routine to build a DNS response:
+ * 'node' should have been found with dns_db_find(), not
+ * dns_db_findnode(). No glue checking is done. No checking for
+ * pending data is done.
+ *
+ * \li The 'now' field is ignored if 'db' is a zone database. If 'db' is a
+ * cache database, an rdataset will not be found unless it expires after
+ * 'now'. If 'now' is zero, then the current time will be used.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * \li 'node' is a valid node.
+ *
+ * \li 'rdataset' is a valid, disassociated rdataset.
+ *
+ * \li 'sigrdataset' is a valid, disassociated rdataset, or it is NULL.
+ *
+ * \li If 'covers' != 0, 'type' must be SIG.
+ *
+ * \li 'type' is not a meta-RR type such as 'ANY' or 'OPT'.
+ *
+ * Ensures:
+ *
+ * \li On success, 'rdataset' is associated with the found rdataset.
+ *
+ * Returns:
+ *
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOTFOUND
+ *
+ * \li Other results are possible, depending upon the database
+ * implementation used.
+ */
+
+isc_result_t
+dns_db_allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ isc_stdtime_t now, dns_rdatasetiter_t **iteratorp);
+/*%<
+ * Make '*iteratorp' an rdataset iteratator for all rdatasets at 'node' in
+ * version 'version' of 'db'.
+ *
+ * Notes:
+ *
+ * \li If 'version' is NULL, then the current version will be used.
+ *
+ * \li The 'now' field is ignored if 'db' is a zone database. If 'db' is a
+ * cache database, an rdataset will not be found unless it expires after
+ * 'now'. Any ANY query will not match unless at least one rdataset at
+ * the node expires after 'now'. If 'now' is zero, then the current time
+ * will be used.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * \li 'node' is a valid node.
+ *
+ * \li iteratorp != NULL && *iteratorp == NULL
+ *
+ * Ensures:
+ *
+ * \li On success, '*iteratorp' is a valid rdataset iterator.
+ *
+ * Returns:
+ *
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOTFOUND
+ *
+ * \li Other results are possible, depending upon the database
+ * implementation used.
+ */
+
+isc_result_t
+dns_db_addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ isc_stdtime_t now, dns_rdataset_t *rdataset,
+ unsigned int options, dns_rdataset_t *addedrdataset);
+/*%<
+ * Add 'rdataset' to 'node' in version 'version' of 'db'.
+ *
+ * Notes:
+ *
+ * \li If the database has zone semantics, the #DNS_DBADD_MERGE option is set,
+ * and an rdataset of the same type as 'rdataset' already exists at
+ * 'node' then the contents of 'rdataset' will be merged with the existing
+ * rdataset. If the option is not set, then rdataset will replace any
+ * existing rdataset of the same type. If not merging and the
+ * #DNS_DBADD_FORCE option is set, then the data will update the database
+ * without regard to trust levels. If not forcing the data, then the
+ * rdataset will only be added if its trust level is >= the trust level of
+ * any existing rdataset. Forcing is only meaningful for cache databases.
+ * If #DNS_DBADD_EXACT is set then there must be no rdata in common between
+ * the old and new rdata sets. If #DNS_DBADD_EXACTTTL is set then both
+ * the old and new rdata sets must have the same ttl.
+ *
+ * \li The 'now' field is ignored if 'db' is a zone database. If 'db' is
+ * a cache database, then the added rdataset will expire no later than
+ * now + rdataset->ttl.
+ *
+ * \li If 'addedrdataset' is not NULL, then it will be attached to the
+ * resulting new rdataset in the database, or to the existing data if
+ * the existing data was better.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * \li 'node' is a valid node.
+ *
+ * \li 'rdataset' is a valid, associated rdataset with the same class
+ * as 'db'.
+ *
+ * \li 'addedrdataset' is NULL, or a valid, unassociated rdataset.
+ *
+ * \li The database has zone semantics and 'version' is a valid
+ * read-write version, or the database has cache semantics
+ * and version is NULL.
+ *
+ * \li If the database has cache semantics, the #DNS_DBADD_MERGE option must
+ * not be set.
+ *
+ * Returns:
+ *
+ * \li #ISC_R_SUCCESS
+ * \li #DNS_R_UNCHANGED The operation did not change anything.
+ * \li #ISC_R_NOMEMORY
+ * \li #DNS_R_NOTEXACT
+ *
+ * \li Other results are possible, depending upon the database
+ * implementation used.
+ */
+
+isc_result_t
+dns_db_subtractrdataset(dns_db_t *db, dns_dbnode_t *node,
+ dns_dbversion_t *version, dns_rdataset_t *rdataset,
+ unsigned int options, dns_rdataset_t *newrdataset);
+/*%<
+ * Remove any rdata in 'rdataset' from 'node' in version 'version' of
+ * 'db'.
+ *
+ * Notes:
+ *
+ * \li If 'newrdataset' is not NULL, then it will be attached to the
+ * resulting new rdataset in the database, unless the rdataset has
+ * become nonexistent. If DNS_DBSUB_EXACT is set then all elements
+ * of 'rdataset' must exist at 'node'.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * \li 'node' is a valid node.
+ *
+ * \li 'rdataset' is a valid, associated rdataset with the same class
+ * as 'db'.
+ *
+ * \li 'newrdataset' is NULL, or a valid, unassociated rdataset.
+ *
+ * \li The database has zone semantics and 'version' is a valid
+ * read-write version.
+ *
+ * Returns:
+ *
+ * \li #ISC_R_SUCCESS
+ * \li #DNS_R_UNCHANGED The operation did not change anything.
+ * \li #DNS_R_NXRRSET All rdata of the same type as those
+ * in 'rdataset' have been deleted.
+ * \li #DNS_R_NOTEXACT Some part of 'rdataset' did not
+ * exist and DNS_DBSUB_EXACT was set.
+ *
+ * \li Other results are possible, depending upon the database
+ * implementation used.
+ */
+
+isc_result_t
+dns_db_deleterdataset(dns_db_t *db, dns_dbnode_t *node,
+ dns_dbversion_t *version, dns_rdatatype_t type,
+ dns_rdatatype_t covers);
+/*%<
+ * Make it so that no rdataset of type 'type' exists at 'node' in version
+ * version 'version' of 'db'.
+ *
+ * Notes:
+ *
+ * \li If 'type' is dns_rdatatype_any, then no rdatasets will exist in
+ * 'version' (provided that the dns_db_deleterdataset() isn't followed
+ * by one or more dns_db_addrdataset() calls).
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * \li 'node' is a valid node.
+ *
+ * \li The database has zone semantics and 'version' is a valid
+ * read-write version, or the database has cache semantics
+ * and version is NULL.
+ *
+ * \li 'type' is not a meta-RR type, except for dns_rdatatype_any, which is
+ * allowed.
+ *
+ * \li If 'covers' != 0, 'type' must be SIG.
+ *
+ * Returns:
+ *
+ * \li #ISC_R_SUCCESS
+ * \li #DNS_R_UNCHANGED No rdatasets of 'type' existed before
+ * the operation was attempted.
+ *
+ * \li Other results are possible, depending upon the database
+ * implementation used.
+ */
+
+isc_result_t
+dns_db_getsoaserial(dns_db_t *db, dns_dbversion_t *ver, isc_uint32_t *serialp);
+/*%<
+ * Get the current SOA serial number from a zone database.
+ *
+ * Requires:
+ * \li 'db' is a valid database with zone semantics.
+ * \li 'ver' is a valid version.
+ */
+
+void
+dns_db_overmem(dns_db_t *db, isc_boolean_t overmem);
+/*%<
+ * Enable / disable agressive cache cleaning.
+ */
+
+unsigned int
+dns_db_nodecount(dns_db_t *db);
+/*%<
+ * Count the number of nodes in 'db'.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * Returns:
+ * \li The number of nodes in the database
+ */
+
+void
+dns_db_settask(dns_db_t *db, isc_task_t *task);
+/*%<
+ * If task is set then the final detach maybe performed asynchronously.
+ *
+ * Requires:
+ * \li 'db' is a valid database.
+ * \li 'task' to be valid or NULL.
+ */
+
+isc_boolean_t
+dns_db_ispersistent(dns_db_t *db);
+/*%<
+ * Is 'db' persistent? A persistent database does not need to be loaded
+ * from disk or written to disk.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * Returns:
+ * \li #ISC_TRUE 'db' is persistent.
+ * \li #ISC_FALSE 'db' is not persistent.
+ */
+
+isc_result_t
+dns_db_register(const char *name, dns_dbcreatefunc_t create, void *driverarg,
+ isc_mem_t *mctx, dns_dbimplementation_t **dbimp);
+
+/*%<
+ * Register a new database implementation and add it to the list of
+ * supported implementations.
+ *
+ * Requires:
+ *
+ * \li 'name' is not NULL
+ * \li 'order' is a valid function pointer
+ * \li 'mctx' is a valid memory context
+ * \li dbimp != NULL && *dbimp == NULL
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS The registration succeeded
+ * \li #ISC_R_NOMEMORY Out of memory
+ * \li #ISC_R_EXISTS A database implementation with the same name exists
+ *
+ * Ensures:
+ *
+ * \li *dbimp points to an opaque structure which must be passed to
+ * dns_db_unregister().
+ */
+
+void
+dns_db_unregister(dns_dbimplementation_t **dbimp);
+/*%<
+ * Remove a database implementation from the the list of supported
+ * implementations. No databases of this type can be active when this
+ * is called.
+ *
+ * Requires:
+ * \li dbimp != NULL && *dbimp == NULL
+ *
+ * Ensures:
+ *
+ * \li Any memory allocated in *dbimp will be freed.
+ */
+
+isc_result_t
+dns_db_getoriginnode(dns_db_t *db, dns_dbnode_t **nodep);
+/*%<
+ * Get the origin DB node corresponding to the DB's zone. This function
+ * should typically succeed unless the underlying DB implementation doesn't
+ * support the feature.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid zone database.
+ * \li 'nodep' != NULL && '*nodep' == NULL
+ *
+ * Ensures:
+ * \li On success, '*nodep' will point to the DB node of the zone's origin.
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOTFOUND - the DB implementation does not support this feature.
+ */
+
+isc_result_t
+dns_db_getnsec3parameters(dns_db_t *db, dns_dbversion_t *version,
+ dns_hash_t *hash, isc_uint8_t *flags,
+ isc_uint16_t *interations,
+ unsigned char *salt, size_t *salt_length);
+/*%<
+ * Get the NSEC3 parameters that are associated with this zone.
+ *
+ * Requires:
+ * \li 'db' is a valid zone database.
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOTFOUND - the DB implementation does not support this feature
+ * or this zone does not have NSEC3 records.
+ */
+
+isc_result_t
+dns_db_findnsec3node(dns_db_t *db, dns_name_t *name,
+ isc_boolean_t create, dns_dbnode_t **nodep);
+/*%<
+ * Find the NSEC3 node with name 'name'.
+ *
+ * Notes:
+ * \li If 'create' is ISC_TRUE and no node with name 'name' exists, then
+ * such a node will be created.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * \li 'name' is a valid, non-empty, absolute name.
+ *
+ * \li nodep != NULL && *nodep == NULL
+ *
+ * Ensures:
+ *
+ * \li On success, *nodep is attached to the node with name 'name'.
+ *
+ * Returns:
+ *
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOTFOUND If !create and name not found.
+ * \li #ISC_R_NOMEMORY Can only happen if create is ISC_TRUE.
+ *
+ * \li Other results are possible, depending upon the database
+ * implementation used.
+ */
+
+isc_result_t
+dns_db_setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset,
+ isc_stdtime_t resign);
+/*%<
+ * Sets the re-signing time associated with 'rdataset' to 'resign'.
+ *
+ * Requires:
+ * \li 'db' is a valid zone database.
+ * \li 'rdataset' to be associated with 'db'.
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOMEMORY
+ * \li #ISC_R_NOTIMPLEMENTED - Not supported by this DB implementation.
+ */
+
+isc_result_t
+dns_db_getsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, dns_name_t *name);
+/*%<
+ * Return the rdataset with the earliest signing time in the zone.
+ * Note: the rdataset is version agnostic.
+ *
+ * Requires:
+ * \li 'db' is a valid zone database.
+ * \li 'rdataset' to be initialized but not associated.
+ * \li 'name' to be NULL or have a buffer associated with it.
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOTFOUND - No dataset exists.
+ */
+
+void
+dns_db_resigned(dns_db_t *db, dns_rdataset_t *rdataset,
+ dns_dbversion_t *version);
+/*%<
+ * Mark 'rdataset' as not being available to be returned by
+ * dns_db_getsigningtime(). If the changes associated with 'version'
+ * are committed this will be permanent. If the version is not committed
+ * this change will be rolled back when the version is closed.
+ *
+ * Requires:
+ * \li 'db' is a valid zone database.
+ * \li 'rdataset' to be associated with 'db'.
+ * \li 'version' to be open for writing.
+ */
+
+dns_stats_t *
+dns_db_getrrsetstats(dns_db_t *db);
+/*%<
+ * Get statistics information counting RRsets stored in the DB, when available.
+ * The statistics may not be available depending on the DB implementation.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database (zone or cache).
+ *
+ * Returns:
+ * \li when available, a pointer to a statistics object created by
+ * dns_rdatasetstats_create(); otherwise NULL.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_DB_H */
diff --git a/lib/dns/include/dns/dbiterator.h b/lib/dns/include/dns/dbiterator.h
new file mode 100644
index 0000000..366d676
--- /dev/null
+++ b/lib/dns/include/dns/dbiterator.h
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: dbiterator.h,v 1.25 2007/06/19 23:47:16 tbox Exp $ */
+
+#ifndef DNS_DBITERATOR_H
+#define DNS_DBITERATOR_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/dbiterator.h
+ * \brief
+ * The DNS DB Iterator interface allows iteration of all of the nodes in a
+ * database.
+ *
+ * The dns_dbiterator_t type is like a "virtual class". To actually use
+ * it, an implementation of the class is required. This implementation is
+ * supplied by the database.
+ *
+ * It is the client's responsibility to call dns_db_detachnode() on all
+ * nodes returned.
+ *
+ * XXX &lt;more&gt; XXX
+ *
+ * MP:
+ *\li The iterator itself is not locked. The caller must ensure
+ * synchronization.
+ *
+ *\li The iterator methods ensure appropriate database locking.
+ *
+ * Reliability:
+ *\li No anticipated impact.
+ *
+ * Resources:
+ *\li TBS
+ *
+ * Security:
+ *\li No anticipated impact.
+ *
+ * Standards:
+ *\li None.
+ */
+
+/*****
+ ***** Imports
+ *****/
+
+#include <isc/lang.h>
+#include <isc/magic.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/*****
+ ***** Types
+ *****/
+
+typedef struct dns_dbiteratormethods {
+ void (*destroy)(dns_dbiterator_t **iteratorp);
+ isc_result_t (*first)(dns_dbiterator_t *iterator);
+ isc_result_t (*last)(dns_dbiterator_t *iterator);
+ isc_result_t (*seek)(dns_dbiterator_t *iterator, dns_name_t *name);
+ isc_result_t (*prev)(dns_dbiterator_t *iterator);
+ isc_result_t (*next)(dns_dbiterator_t *iterator);
+ isc_result_t (*current)(dns_dbiterator_t *iterator,
+ dns_dbnode_t **nodep, dns_name_t *name);
+ isc_result_t (*pause)(dns_dbiterator_t *iterator);
+ isc_result_t (*origin)(dns_dbiterator_t *iterator,
+ dns_name_t *name);
+} dns_dbiteratormethods_t;
+
+#define DNS_DBITERATOR_MAGIC ISC_MAGIC('D','N','S','I')
+#define DNS_DBITERATOR_VALID(dbi) ISC_MAGIC_VALID(dbi, DNS_DBITERATOR_MAGIC)
+/*%
+ * This structure is actually just the common prefix of a DNS db
+ * implementation's version of a dns_dbiterator_t.
+ *
+ * Clients may use the 'db' field of this structure. Except for that field,
+ * direct use of this structure by clients is forbidden. DB implementations
+ * may change the structure. 'magic' must be DNS_DBITERATOR_MAGIC for any of
+ * the dns_dbiterator routines to work. DB iterator implementations must
+ * maintain all DB iterator invariants.
+ */
+struct dns_dbiterator {
+ /* Unlocked. */
+ unsigned int magic;
+ dns_dbiteratormethods_t * methods;
+ dns_db_t * db;
+ isc_boolean_t relative_names;
+ isc_boolean_t cleaning;
+};
+
+void
+dns_dbiterator_destroy(dns_dbiterator_t **iteratorp);
+/*%<
+ * Destroy '*iteratorp'.
+ *
+ * Requires:
+ *
+ *\li '*iteratorp' is a valid iterator.
+ *
+ * Ensures:
+ *
+ *\li All resources used by the iterator are freed.
+ *
+ *\li *iteratorp == NULL.
+ */
+
+isc_result_t
+dns_dbiterator_first(dns_dbiterator_t *iterator);
+/*%<
+ * Move the node cursor to the first node in the database (if any).
+ *
+ * Requires:
+ *\li 'iterator' is a valid iterator.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMORE There are no nodes in the database.
+ *
+ *\li Other results are possible, depending on the DB implementation.
+ */
+
+isc_result_t
+dns_dbiterator_last(dns_dbiterator_t *iterator);
+/*%<
+ * Move the node cursor to the last node in the database (if any).
+ *
+ * Requires:
+ *\li 'iterator' is a valid iterator.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMORE There are no nodes in the database.
+ *
+ *\li Other results are possible, depending on the DB implementation.
+ */
+
+isc_result_t
+dns_dbiterator_seek(dns_dbiterator_t *iterator, dns_name_t *name);
+/*%<
+ * Move the node cursor to the node with name 'name'.
+ *
+ * Requires:
+ *\li 'iterator' is a valid iterator.
+ *
+ *\li 'name' is a valid name.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOTFOUND
+ *
+ *\li Other results are possible, depending on the DB implementation.
+ */
+
+isc_result_t
+dns_dbiterator_prev(dns_dbiterator_t *iterator);
+/*%<
+ * Move the node cursor to the previous node in the database (if any).
+ *
+ * Requires:
+ *\li 'iterator' is a valid iterator.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMORE There are no more nodes in the
+ * database.
+ *
+ *\li Other results are possible, depending on the DB implementation.
+ */
+
+isc_result_t
+dns_dbiterator_next(dns_dbiterator_t *iterator);
+/*%<
+ * Move the node cursor to the next node in the database (if any).
+ *
+ * Requires:
+ *\li 'iterator' is a valid iterator.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMORE There are no more nodes in the
+ * database.
+ *
+ *\li Other results are possible, depending on the DB implementation.
+ */
+
+isc_result_t
+dns_dbiterator_current(dns_dbiterator_t *iterator, dns_dbnode_t **nodep,
+ dns_name_t *name);
+/*%<
+ * Return the current node.
+ *
+ * Notes:
+ *\li If 'name' is not NULL, it will be set to the name of the node.
+ *
+ * Requires:
+ *\li 'iterator' is a valid iterator.
+ *
+ *\li nodep != NULL && *nodep == NULL
+ *
+ *\li The node cursor of 'iterator' is at a valid location (i.e. the
+ * result of last call to a cursor movement command was ISC_R_SUCCESS).
+ *
+ *\li 'name' is NULL, or is a valid name with a dedicated buffer.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS
+ *\li #DNS_R_NEWORIGIN If this iterator was created with
+ * 'relative_names' set to ISC_TRUE,
+ * then #DNS_R_NEWORIGIN will be returned
+ * when the origin the names are
+ * relative to changes. This result
+ * can occur only when 'name' is not
+ * NULL. This is also a successful
+ * result.
+ *
+ *\li Other results are possible, depending on the DB implementation.
+ */
+
+isc_result_t
+dns_dbiterator_pause(dns_dbiterator_t *iterator);
+/*%<
+ * Pause iteration.
+ *
+ * Calling a cursor movement method or dns_dbiterator_current() may cause
+ * database locks to be acquired. Rather than reacquire these locks every
+ * time one of these routines is called, the locks may simply be held.
+ * Calling dns_dbiterator_pause() releases any such locks. Iterator clients
+ * should call this routine any time they are not going to execute another
+ * iterator method in the immediate future.
+ *
+ * Requires:
+ *\li 'iterator' is a valid iterator.
+ *
+ * Ensures:
+ *\li Any database locks being held for efficiency of iterator access are
+ * released.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *
+ *\li Other results are possible, depending on the DB implementation.
+ */
+
+isc_result_t
+dns_dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name);
+/*%<
+ * Return the origin to which returned node names are relative.
+ *
+ * Requires:
+ *
+ *\li 'iterator' is a valid relative_names iterator.
+ *
+ *\li 'name' is a valid name with a dedicated buffer.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOSPACE
+ *
+ *\li Other results are possible, depending on the DB implementation.
+ */
+
+void
+dns_dbiterator_setcleanmode(dns_dbiterator_t *iterator, isc_boolean_t mode);
+/*%<
+ * Indicate that the given iterator is/is not cleaning the DB.
+ *
+ * Notes:
+ *\li When 'mode' is ISC_TRUE,
+ *
+ * Requires:
+ *\li 'iterator' is a valid iterator.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_DBITERATOR_H */
diff --git a/lib/dns/include/dns/dbtable.h b/lib/dns/include/dns/dbtable.h
new file mode 100644
index 0000000..503de95
--- /dev/null
+++ b/lib/dns/include/dns/dbtable.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: dbtable.h,v 1.23 2007/06/19 23:47:16 tbox Exp $ */
+
+#ifndef DNS_DBTABLE_H
+#define DNS_DBTABLE_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/dbtable.h
+ * \brief
+ * DNS DB Tables
+ *
+ * XXX TBS XXX
+ *
+ * MP:
+ *\li The module ensures appropriate synchronization of data structures it
+ * creates and manipulates.
+ *
+ * Reliability:
+ *\li No anticipated impact.
+ *
+ * Resources:
+ *\li None.
+ *
+ * Security:
+ *\li No anticipated impact.
+ *
+ * Standards:
+ *\li None.
+ */
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+#define DNS_DBTABLEFIND_NOEXACT 0x01
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_dbtable_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
+ dns_dbtable_t **dbtablep);
+/*%<
+ * Make a new dbtable of class 'rdclass'
+ *
+ * Requires:
+ *\li mctx != NULL
+ * \li dbtablep != NULL && *dptablep == NULL
+ *\li 'rdclass' is a valid class
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *\li #ISC_R_UNEXPECTED
+ */
+
+void
+dns_dbtable_attach(dns_dbtable_t *source, dns_dbtable_t **targetp);
+/*%<
+ * Attach '*targetp' to 'source'.
+ *
+ * Requires:
+ *
+ *\li 'source' is a valid dbtable.
+ *
+ *\li 'targetp' points to a NULL dns_dbtable_t *.
+ *
+ * Ensures:
+ *
+ *\li *targetp is attached to source.
+ */
+
+void
+dns_dbtable_detach(dns_dbtable_t **dbtablep);
+/*%<
+ * Detach *dbtablep from its dbtable.
+ *
+ * Requires:
+ *
+ *\li '*dbtablep' points to a valid dbtable.
+ *
+ * Ensures:
+ *
+ *\li *dbtablep is NULL.
+ *
+ *\li If '*dbtablep' is the last reference to the dbtable,
+ * all resources used by the dbtable will be freed
+ */
+
+isc_result_t
+dns_dbtable_add(dns_dbtable_t *dbtable, dns_db_t *db);
+/*%<
+ * Add 'db' to 'dbtable'.
+ *
+ * Requires:
+ *\li 'dbtable' is a valid dbtable.
+ *
+ *\li 'db' is a valid database with the same class as 'dbtable'
+ */
+
+void
+dns_dbtable_remove(dns_dbtable_t *dbtable, dns_db_t *db);
+/*%<
+ * Remove 'db' from 'dbtable'.
+ *
+ * Requires:
+ *\li 'db' was previously added to 'dbtable'.
+ */
+
+void
+dns_dbtable_adddefault(dns_dbtable_t *dbtable, dns_db_t *db);
+/*%<
+ * Use 'db' as the result of a dns_dbtable_find() if no better match is
+ * available.
+ */
+
+void
+dns_dbtable_getdefault(dns_dbtable_t *dbtable, dns_db_t **db);
+/*%<
+ * Get the 'db' used as the result of a dns_dbtable_find()
+ * if no better match is available.
+ */
+
+void
+dns_dbtable_removedefault(dns_dbtable_t *dbtable);
+/*%<
+ * Remove the default db from 'dbtable'.
+ */
+
+isc_result_t
+dns_dbtable_find(dns_dbtable_t *dbtable, dns_name_t *name,
+ unsigned int options, dns_db_t **dbp);
+/*%<
+ * Find the deepest match to 'name' in the dbtable, and return it
+ *
+ * Notes:
+ *\li If the DNS_DBTABLEFIND_NOEXACT option is set, the best partial
+ * match (if any) to 'name' will be returned.
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS on success
+ *\li something else: no default and match
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_DBTABLE_H */
diff --git a/lib/dns/include/dns/diff.h b/lib/dns/include/dns/diff.h
new file mode 100644
index 0000000..a60343e
--- /dev/null
+++ b/lib/dns/include/dns/diff.h
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: diff.h,v 1.15 2008/04/02 02:37:42 marka Exp $ */
+
+#ifndef DNS_DIFF_H
+#define DNS_DIFF_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/diff.h
+ * \brief
+ * A diff is a convenience type representing a list of changes to be
+ * made to a database.
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <isc/lang.h>
+#include <isc/magic.h>
+
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/types.h>
+
+/***
+ *** Types
+ ***/
+
+/*%
+ * A dns_difftuple_t represents a single RR being added or deleted.
+ * The RR type and class are in the 'rdata' member; the class is always
+ * the real one, not a DynDNS meta-class, so that the rdatas can be
+ * compared using dns_rdata_compare(). The TTL is significant
+ * even for deletions, because a deletion/addition pair cannot
+ * be canceled out if the TTL differs (it might be an explicit
+ * TTL update).
+ *
+ * Tuples are also used to represent complete RRs with owner
+ * names for a couple of other purposes, such as the
+ * individual RRs of a "RRset exists (value dependent)"
+ * prerequisite set. In this case, op==DNS_DIFFOP_EXISTS,
+ * and the TTL is ignored.
+ *
+ * DNS_DIFFOP_*RESIGN will cause the 'resign' attribute of the resulting
+ * RRset to be recomputed to be 'resign' seconds before the earliest RRSIG
+ * timeexpire.
+ */
+
+typedef enum {
+ DNS_DIFFOP_ADD = 0, /*%< Add an RR. */
+ DNS_DIFFOP_DEL = 1, /*%< Delete an RR. */
+ DNS_DIFFOP_EXISTS = 2, /*%< Assert RR existence. */
+ DNS_DIFFOP_ADDRESIGN = 4, /*%< ADD + RESIGN. */
+ DNS_DIFFOP_DELRESIGN = 5, /*%< DEL + RESIGN. */
+} dns_diffop_t;
+
+typedef struct dns_difftuple dns_difftuple_t;
+
+#define DNS_DIFFTUPLE_MAGIC ISC_MAGIC('D','I','F','T')
+#define DNS_DIFFTUPLE_VALID(t) ISC_MAGIC_VALID(t, DNS_DIFFTUPLE_MAGIC)
+
+struct dns_difftuple {
+ unsigned int magic;
+ isc_mem_t *mctx;
+ dns_diffop_t op;
+ dns_name_t name;
+ dns_ttl_t ttl;
+ dns_rdata_t rdata;
+ ISC_LINK(dns_difftuple_t) link;
+ /* Variable-size name data and rdata follows. */
+};
+
+/*%
+ * A dns_diff_t represents a set of changes being applied to
+ * a zone. Diffs are also used to represent "RRset exists
+ * (value dependent)" prerequisites.
+ */
+typedef struct dns_diff dns_diff_t;
+
+#define DNS_DIFF_MAGIC ISC_MAGIC('D','I','F','F')
+#define DNS_DIFF_VALID(t) ISC_MAGIC_VALID(t, DNS_DIFF_MAGIC)
+
+struct dns_diff {
+ unsigned int magic;
+ isc_mem_t * mctx;
+ /*
+ * Set the 'resign' attribute to this many second before the
+ * earliest RRSIG timeexpire.
+ */
+ isc_uint32_t resign;
+ ISC_LIST(dns_difftuple_t) tuples;
+};
+
+/* Type of comparision function for sorting diffs. */
+typedef int dns_diff_compare_func(const void *, const void *);
+
+/***
+ *** Functions
+ ***/
+
+ISC_LANG_BEGINDECLS
+
+/**************************************************************************/
+/*
+ * Maniuplation of diffs and tuples.
+ */
+
+isc_result_t
+dns_difftuple_create(isc_mem_t *mctx,
+ dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl,
+ dns_rdata_t *rdata, dns_difftuple_t **tp);
+/*%<
+ * Create a tuple. Deep copies are made of the name and rdata, so
+ * they need not remain valid after the call.
+ *
+ * Requires:
+ *\li *tp != NULL && *tp == NULL.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ * \li ISC_R_NOMEMORY
+ */
+
+void
+dns_difftuple_free(dns_difftuple_t **tp);
+/*%<
+ * Free a tuple.
+ *
+ * Requires:
+ * \li **tp is a valid tuple.
+ *
+ * Ensures:
+ * \li *tp == NULL
+ * \li All memory used by the tuple is freed.
+ */
+
+isc_result_t
+dns_difftuple_copy(dns_difftuple_t *orig, dns_difftuple_t **copyp);
+/*%<
+ * Copy a tuple.
+ *
+ * Requires:
+ * \li 'orig' points to a valid tuple
+ *\li copyp != NULL && *copyp == NULL
+ */
+
+void
+dns_diff_init(isc_mem_t *mctx, dns_diff_t *diff);
+/*%<
+ * Initialize a diff.
+ *
+ * Requires:
+ * \li 'diff' points to an uninitialized dns_diff_t
+ * \li allocated by the caller.
+ *
+ * Ensures:
+ * \li '*diff' is a valid, empty diff.
+ */
+
+void
+dns_diff_clear(dns_diff_t *diff);
+/*%<
+ * Clear a diff, destroying all its tuples.
+ *
+ * Requires:
+ * \li 'diff' points to a valid dns_diff_t.
+ *
+ * Ensures:
+ * \li Any tuples in the diff are destroyed.
+ * The diff now empty, but it is still valid
+ * and may be reused without calling dns_diff_init
+ * again. The only memory used is that of the
+ * dns_diff_t structure itself.
+ *
+ * Notes:
+ * \li Managing the memory of the dns_diff_t structure itself
+ * is the caller's responsibility.
+ */
+
+void
+dns_diff_append(dns_diff_t *diff, dns_difftuple_t **tuple);
+/*%<
+ * Append a single tuple to a diff.
+ *
+ *\li 'diff' is a valid diff.
+ * \li '*tuple' is a valid tuple.
+ *
+ * Ensures:
+ *\li *tuple is NULL.
+ *\li The tuple has been freed, or will be freed when the diff is cleared.
+ */
+
+void
+dns_diff_appendminimal(dns_diff_t *diff, dns_difftuple_t **tuple);
+/*%<
+ * Append 'tuple' to 'diff', removing any duplicate
+ * or conflicting updates as needed to create a minimal diff.
+ *
+ * Requires:
+ *\li 'diff' is a minimal diff.
+ *
+ * Ensures:
+ *\li 'diff' is still a minimal diff.
+ * \li *tuple is NULL.
+ * \li The tuple has been freed, or will be freed when the diff is cleared.
+ *
+ */
+
+isc_result_t
+dns_diff_sort(dns_diff_t *diff, dns_diff_compare_func *compare);
+/*%<
+ * Sort 'diff' in-place according to the comparison function 'compare'.
+ */
+
+isc_result_t
+dns_diff_apply(dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver);
+isc_result_t
+dns_diff_applysilently(dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver);
+/*%<
+ * Apply 'diff' to the database 'db'.
+ *
+ * dns_diff_apply() logs warnings about updates with no effect or
+ * with inconsistent TTLs; dns_diff_applysilently() does not.
+ *
+ * For efficiency, the diff should be sorted by owner name.
+ * If it is not sorted, operation will still be correct,
+ * but less efficient.
+ *
+ * Requires:
+ *\li *diff is a valid diff (possibly empty), containing
+ * tuples of type #DNS_DIFFOP_ADD and/or
+ * For #DNS_DIFFOP_DEL tuples, the TTL is ignored.
+ *
+ */
+
+isc_result_t
+dns_diff_load(dns_diff_t *diff, dns_addrdatasetfunc_t addfunc,
+ void *add_private);
+/*%<
+ * Like dns_diff_apply, but for use when loading a new database
+ * instead of modifying an existing one. This bypasses the
+ * database transaction mechanisms.
+ *
+ * Requires:
+ *\li 'addfunc' is a valid dns_addradatasetfunc_t obtained from
+ * dns_db_beginload()
+ *
+ *\li 'add_private' points to a corresponding dns_dbload_t *
+ * (XXX why is it a void pointer, then?)
+ */
+
+isc_result_t
+dns_diff_print(dns_diff_t *diff, FILE *file);
+
+/*%<
+ * Print the differences to 'file' or if 'file' is NULL via the
+ * logging system.
+ *
+ * Require:
+ *\li 'diff' to be valid.
+ *\li 'file' to refer to a open file or NULL.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *\li #ISC_R_UNEXPECTED
+ *\li any error from dns_rdataset_totext()
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_DIFF_H */
diff --git a/lib/dns/include/dns/dispatch.h b/lib/dns/include/dns/dispatch.h
new file mode 100644
index 0000000..8a0094e
--- /dev/null
+++ b/lib/dns/include/dns/dispatch.h
@@ -0,0 +1,501 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: dispatch.h,v 1.60 2008/06/23 23:47:11 tbox Exp $ */
+
+#ifndef DNS_DISPATCH_H
+#define DNS_DISPATCH_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/dispatch.h
+ * \brief
+ * DNS Dispatch Management
+ * Shared UDP and single-use TCP dispatches for queries and responses.
+ *
+ * MP:
+ *
+ *\li All locking is performed internally to each dispatch.
+ * Restrictions apply to dns_dispatch_removeresponse().
+ *
+ * Reliability:
+ *
+ * Resources:
+ *
+ * Security:
+ *
+ *\li Depends on the isc_socket_t and dns_message_t for prevention of
+ * buffer overruns.
+ *
+ * Standards:
+ *
+ *\li None.
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <isc/buffer.h>
+#include <isc/lang.h>
+#include <isc/socket.h>
+#include <dns/types.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/*%
+ * This event is sent to a task when a response comes in.
+ * No part of this structure should ever be modified by the caller,
+ * other than parts of the buffer. The holy parts of the buffer are
+ * the base and size of the buffer. All other parts of the buffer may
+ * be used. On event delivery the used region contains the packet.
+ *
+ * "id" is the received message id,
+ *
+ * "addr" is the host that sent it to us,
+ *
+ * "buffer" holds state on the received data.
+ *
+ * The "free" routine for this event will clean up itself as well as
+ * any buffer space allocated from common pools.
+ */
+
+struct dns_dispatchevent {
+ ISC_EVENT_COMMON(dns_dispatchevent_t); /*%< standard event common */
+ isc_result_t result; /*%< result code */
+ isc_int32_t id; /*%< message id */
+ isc_sockaddr_t addr; /*%< address recv'd from */
+ struct in6_pktinfo pktinfo; /*%< reply info for v6 */
+ isc_buffer_t buffer; /*%< data buffer */
+ isc_uint32_t attributes; /*%< mirrored from socket.h */
+};
+
+/*@{*/
+/*%
+ * Attributes for added dispatchers.
+ *
+ * Values with the mask 0xffff0000 are application defined.
+ * Values with the mask 0x0000ffff are library defined.
+ *
+ * Insane values (like setting both TCP and UDP) are not caught. Don't
+ * do that.
+ *
+ * _PRIVATE
+ * The dispatcher cannot be shared.
+ *
+ * _TCP, _UDP
+ * The dispatcher is a TCP or UDP socket.
+ *
+ * _IPV4, _IPV6
+ * The dispatcher uses an IPv4 or IPv6 socket.
+ *
+ * _NOLISTEN
+ * The dispatcher should not listen on the socket.
+ *
+ * _MAKEQUERY
+ * The dispatcher can be used to issue queries to other servers, and
+ * accept replies from them.
+ *
+ * _RANDOMPORT
+ * Previously used to indicate that the port of a dispatch UDP must be
+ * chosen randomly. This behavior now always applies and the attribute
+ * is obsoleted.
+ *
+ * _EXCLUSIVE
+ * A separate socket will be used on-demand for each transaction.
+ */
+#define DNS_DISPATCHATTR_PRIVATE 0x00000001U
+#define DNS_DISPATCHATTR_TCP 0x00000002U
+#define DNS_DISPATCHATTR_UDP 0x00000004U
+#define DNS_DISPATCHATTR_IPV4 0x00000008U
+#define DNS_DISPATCHATTR_IPV6 0x00000010U
+#define DNS_DISPATCHATTR_NOLISTEN 0x00000020U
+#define DNS_DISPATCHATTR_MAKEQUERY 0x00000040U
+#define DNS_DISPATCHATTR_CONNECTED 0x00000080U
+/*#define DNS_DISPATCHATTR_RANDOMPORT 0x00000100U*/
+#define DNS_DISPATCHATTR_EXCLUSIVE 0x00000200U
+/*@}*/
+
+isc_result_t
+dns_dispatchmgr_create(isc_mem_t *mctx, isc_entropy_t *entropy,
+ dns_dispatchmgr_t **mgrp);
+/*%<
+ * Creates a new dispatchmgr object.
+ *
+ * Requires:
+ *\li "mctx" be a valid memory context.
+ *
+ *\li mgrp != NULL && *mgrp == NULL
+ *
+ *\li "entropy" may be NULL, in which case an insecure random generator
+ * will be used. If it is non-NULL, it must be a valid entropy
+ * source.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS -- all ok
+ *
+ *\li anything else -- failure
+ */
+
+
+void
+dns_dispatchmgr_destroy(dns_dispatchmgr_t **mgrp);
+/*%<
+ * Destroys the dispatchmgr when it becomes empty. This could be
+ * immediately.
+ *
+ * Requires:
+ *\li mgrp != NULL && *mgrp is a valid dispatchmgr.
+ */
+
+
+void
+dns_dispatchmgr_setblackhole(dns_dispatchmgr_t *mgr, dns_acl_t *blackhole);
+/*%<
+ * Sets the dispatcher's "blackhole list," a list of addresses that will
+ * be ignored by all dispatchers created by the dispatchmgr.
+ *
+ * Requires:
+ * \li mgrp is a valid dispatchmgr
+ * \li blackhole is a valid acl
+ */
+
+
+dns_acl_t *
+dns_dispatchmgr_getblackhole(dns_dispatchmgr_t *mgr);
+/*%<
+ * Gets a pointer to the dispatcher's current blackhole list,
+ * without incrementing its reference count.
+ *
+ * Requires:
+ *\li mgr is a valid dispatchmgr
+ * Returns:
+ *\li A pointer to the current blackhole list, or NULL.
+ */
+
+void
+dns_dispatchmgr_setblackportlist(dns_dispatchmgr_t *mgr,
+ dns_portlist_t *portlist);
+/*%<
+ * This function is deprecated. Use dns_dispatchmgr_setavailports() instead.
+ *
+ * Requires:
+ *\li mgr is a valid dispatchmgr
+ */
+
+dns_portlist_t *
+dns_dispatchmgr_getblackportlist(dns_dispatchmgr_t *mgr);
+/*%<
+ * This function is deprecated and always returns NULL.
+ *
+ * Requires:
+ *\li mgr is a valid dispatchmgr
+ */
+
+isc_result_t
+dns_dispatchmgr_setavailports(dns_dispatchmgr_t *mgr, isc_portset_t *v4portset,
+ isc_portset_t *v6portset);
+/*%<
+ * Sets a list of UDP ports that can be used for outgoing UDP messages.
+ *
+ * Requires:
+ *\li mgr is a valid dispatchmgr
+ *\li v4portset is NULL or a valid port set
+ *\li v6portset is NULL or a valid port set
+ */
+
+void
+dns_dispatchmgr_setstats(dns_dispatchmgr_t *mgr, dns_stats_t *stats);
+/*%<
+ * Sets statistics counter for the dispatchmgr. This function is expected to
+ * be called only on zone creation (when necessary).
+ * Once installed, it cannot be removed or replaced. Also, there is no
+ * interface to get the installed stats from the zone; the caller must keep the
+ * stats to reference (e.g. dump) it later.
+ *
+ * Requires:
+ *\li mgr is a valid dispatchmgr with no managed dispatch.
+ *\li stats is a valid statistics supporting resolver statistics counters
+ * (see dns/stats.h).
+ */
+
+isc_result_t
+dns_dispatch_getudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr,
+ isc_taskmgr_t *taskmgr, isc_sockaddr_t *localaddr,
+ unsigned int buffersize,
+ unsigned int maxbuffers, unsigned int maxrequests,
+ unsigned int buckets, unsigned int increment,
+ unsigned int attributes, unsigned int mask,
+ dns_dispatch_t **dispp);
+/*%<
+ * Attach to existing dns_dispatch_t if one is found with dns_dispatchmgr_find,
+ * otherwise create a new UDP dispatch.
+ *
+ * Requires:
+ *\li All pointer parameters be valid for their respective types.
+ *
+ *\li dispp != NULL && *disp == NULL
+ *
+ *\li 512 <= buffersize <= 64k
+ *
+ *\li maxbuffers > 0
+ *
+ *\li buckets < 2097169
+ *
+ *\li increment > buckets
+ *
+ *\li (attributes & DNS_DISPATCHATTR_TCP) == 0
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS -- success.
+ *
+ *\li Anything else -- failure.
+ */
+
+isc_result_t
+dns_dispatch_createtcp(dns_dispatchmgr_t *mgr, isc_socket_t *sock,
+ isc_taskmgr_t *taskmgr, unsigned int buffersize,
+ unsigned int maxbuffers, unsigned int maxrequests,
+ unsigned int buckets, unsigned int increment,
+ unsigned int attributes, dns_dispatch_t **dispp);
+/*%<
+ * Create a new dns_dispatch and attach it to the provided isc_socket_t.
+ *
+ * For all dispatches, "buffersize" is the maximum packet size we will
+ * accept.
+ *
+ * "maxbuffers" and "maxrequests" control the number of buffers in the
+ * overall system and the number of buffers which can be allocated to
+ * requests.
+ *
+ * "buckets" is the number of buckets to use, and should be prime.
+ *
+ * "increment" is used in a collision avoidance function, and needs to be
+ * a prime > buckets, and not 2.
+ *
+ * Requires:
+ *
+ *\li mgr is a valid dispatch manager.
+ *
+ *\li sock is a valid.
+ *
+ *\li task is a valid task that can be used internally to this dispatcher.
+ *
+ * \li 512 <= buffersize <= 64k
+ *
+ *\li maxbuffers > 0.
+ *
+ *\li maxrequests <= maxbuffers.
+ *
+ *\li buckets < 2097169 (the next prime after 65536 * 32)
+ *
+ *\li increment > buckets (and prime).
+ *
+ *\li attributes includes #DNS_DISPATCHATTR_TCP and does not include
+ * #DNS_DISPATCHATTR_UDP.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS -- success.
+ *
+ *\li Anything else -- failure.
+ */
+
+void
+dns_dispatch_attach(dns_dispatch_t *disp, dns_dispatch_t **dispp);
+/*%<
+ * Attach to a dispatch handle.
+ *
+ * Requires:
+ *\li disp is valid.
+ *
+ *\li dispp != NULL && *dispp == NULL
+ */
+
+void
+dns_dispatch_detach(dns_dispatch_t **dispp);
+/*%<
+ * Detaches from the dispatch.
+ *
+ * Requires:
+ *\li dispp != NULL and *dispp be a valid dispatch.
+ */
+
+void
+dns_dispatch_starttcp(dns_dispatch_t *disp);
+/*%<
+ * Start processing of a TCP dispatch once the socket connects.
+ *
+ * Requires:
+ *\li 'disp' is valid.
+ */
+
+isc_result_t
+dns_dispatch_addresponse2(dns_dispatch_t *disp, isc_sockaddr_t *dest,
+ isc_task_t *task, isc_taskaction_t action, void *arg,
+ isc_uint16_t *idp, dns_dispentry_t **resp,
+ isc_socketmgr_t *sockmgr);
+
+isc_result_t
+dns_dispatch_addresponse(dns_dispatch_t *disp, isc_sockaddr_t *dest,
+ isc_task_t *task, isc_taskaction_t action, void *arg,
+ isc_uint16_t *idp, dns_dispentry_t **resp);
+/*%<
+ * Add a response entry for this dispatch.
+ *
+ * "*idp" is filled in with the assigned message ID, and *resp is filled in
+ * to contain the magic token used to request event flow stop.
+ *
+ * Arranges for the given task to get a callback for response packets. When
+ * the event is delivered, it must be returned using dns_dispatch_freeevent()
+ * or through dns_dispatch_removeresponse() for another to be delivered.
+ *
+ * Requires:
+ *\li "idp" be non-NULL.
+ *
+ *\li "task" "action" and "arg" be set as appropriate.
+ *
+ *\li "dest" be non-NULL and valid.
+ *
+ *\li "resp" be non-NULL and *resp be NULL
+ *
+ *\li "sockmgr" be NULL or a valid socket manager. If 'disp' has
+ * the DNS_DISPATCHATTR_EXCLUSIVE attribute, this must not be NULL,
+ * which also means dns_dispatch_addresponse() cannot be used.
+ *
+ * Ensures:
+ *
+ *\li &lt;id, dest> is a unique tuple. That means incoming messages
+ * are identifiable.
+ *
+ * Returns:
+ *
+ *\li ISC_R_SUCCESS -- all is well.
+ *\li ISC_R_NOMEMORY -- memory could not be allocated.
+ *\li ISC_R_NOMORE -- no more message ids can be allocated
+ * for this destination.
+ */
+
+
+void
+dns_dispatch_removeresponse(dns_dispentry_t **resp,
+ dns_dispatchevent_t **sockevent);
+/*%<
+ * Stops the flow of responses for the provided id and destination.
+ * If "sockevent" is non-NULL, the dispatch event and associated buffer is
+ * also returned to the system.
+ *
+ * Requires:
+ *\li "resp" != NULL and "*resp" contain a value previously allocated
+ * by dns_dispatch_addresponse();
+ *
+ *\li May only be called from within the task given as the 'task'
+ * argument to dns_dispatch_addresponse() when allocating '*resp'.
+ */
+
+isc_socket_t *
+dns_dispatch_getentrysocket(dns_dispentry_t *resp);
+
+isc_socket_t *
+dns_dispatch_getsocket(dns_dispatch_t *disp);
+/*%<
+ * Return the socket associated with this dispatcher.
+ *
+ * Requires:
+ *\li disp is valid.
+ *
+ * Returns:
+ *\li The socket the dispatcher is using.
+ */
+
+isc_result_t
+dns_dispatch_getlocaladdress(dns_dispatch_t *disp, isc_sockaddr_t *addrp);
+/*%<
+ * Return the local address for this dispatch.
+ * This currently only works for dispatches using UDP sockets.
+ *
+ * Requires:
+ *\li disp is valid.
+ *\li addrp to be non null.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ *\li ISC_R_NOTIMPLEMENTED
+ */
+
+void
+dns_dispatch_cancel(dns_dispatch_t *disp);
+/*%<
+ * cancel outstanding clients
+ *
+ * Requires:
+ *\li disp is valid.
+ */
+
+unsigned int
+dns_dispatch_getattributes(dns_dispatch_t *disp);
+/*%<
+ * Return the attributes (DNS_DISPATCHATTR_xxx) of this dispatch. Only the
+ * non-changeable attributes are expected to be referenced by the caller.
+ *
+ * Requires:
+ *\li disp is valid.
+ */
+
+void
+dns_dispatch_changeattributes(dns_dispatch_t *disp,
+ unsigned int attributes, unsigned int mask);
+/*%<
+ * Set the bits described by "mask" to the corresponding values in
+ * "attributes".
+ *
+ * That is:
+ *
+ * \code
+ * new = (old & ~mask) | (attributes & mask)
+ * \endcode
+ *
+ * This function has a side effect when #DNS_DISPATCHATTR_NOLISTEN changes.
+ * When the flag becomes off, the dispatch will start receiving on the
+ * corresponding socket. When the flag becomes on, receive events on the
+ * corresponding socket will be canceled.
+ *
+ * Requires:
+ *\li disp is valid.
+ *
+ *\li attributes are reasonable for the dispatch. That is, setting the UDP
+ * attribute on a TCP socket isn't reasonable.
+ */
+
+void
+dns_dispatch_importrecv(dns_dispatch_t *disp, isc_event_t *event);
+/*%<
+ * Inform the dispatcher of a socket receive. This is used for sockets
+ * shared between dispatchers and clients. If the dispatcher fails to copy
+ * or send the event, nothing happens.
+ *
+ * Requires:
+ *\li disp is valid, and the attribute DNS_DISPATCHATTR_NOLISTEN is set.
+ * event != NULL
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_DISPATCH_H */
diff --git a/lib/dns/include/dns/dlz.h b/lib/dns/include/dns/dlz.h
new file mode 100644
index 0000000..66c5899
--- /dev/null
+++ b/lib/dns/include/dns/dlz.h
@@ -0,0 +1,290 @@
+/*
+ * Portions Copyright (C) 2005-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ *
+ * 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 STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET 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.
+ *
+ * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was
+ * conceived and contributed by Rob Butler.
+ *
+ * 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 ROB BUTLER
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * ROB BUTLER 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.
+ */
+
+/* $Id: dlz.h,v 1.7 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file dns/dlz.h */
+
+#ifndef DLZ_H
+#define DLZ_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*
+ * DLZ Interface
+ *
+ * The DLZ interface allows zones to be looked up using a driver instead of
+ * Bind's default in memory zone table.
+ *
+ *
+ * Reliability:
+ * No anticipated impact.
+ *
+ * Resources:
+ *
+ * Security:
+ * No anticipated impact.
+ *
+ * Standards:
+ * None.
+ */
+
+/*****
+ ***** Imports
+ *****/
+
+#include <dns/name.h>
+#include <dns/types.h>
+#include <dns/view.h>
+
+#include <isc/lang.h>
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Types
+ ***/
+
+#define DNS_DLZ_MAGIC ISC_MAGIC('D','L','Z','D')
+#define DNS_DLZ_VALID(dlz) ISC_MAGIC_VALID(dlz, DNS_DLZ_MAGIC)
+
+typedef isc_result_t
+(*dns_dlzallowzonexfr_t)(void *driverarg, void *dbdata, isc_mem_t *mctx,
+ dns_rdataclass_t rdclass, dns_name_t *name,
+ isc_sockaddr_t *clientaddr,
+ dns_db_t **dbp);
+
+/*%<
+ * Method prototype. Drivers implementing the DLZ interface MUST
+ * supply an allow zone transfer method. This method is called when
+ * the DNS server is performing a zone transfer query. The driver's
+ * method should return ISC_R_SUCCESS and a database pointer to the
+ * name server if the zone is supported by the database, and zone
+ * transfer is allowed. Otherwise it will return ISC_R_NOTFOUND if
+ * the zone is not supported by the database, or ISC_R_NOPERM if zone
+ * transfers are not allowed. If an error occurs it should return a
+ * result code indicating the type of error.
+ */
+
+typedef isc_result_t
+(*dns_dlzcreate_t)(isc_mem_t *mctx, const char *dlzname, unsigned int argc,
+ char *argv[], void *driverarg, void **dbdata);
+
+/*%<
+ * Method prototype. Drivers implementing the DLZ interface MUST
+ * supply a create method. This method is called when the DNS server
+ * is starting up and creating drivers for use later.
+ */
+
+typedef void
+(*dns_dlzdestroy_t)(void *driverarg, void **dbdata);
+
+/*%<
+ * Method prototype. Drivers implementing the DLZ interface MUST
+ * supply a destroy method. This method is called when the DNS server
+ * is shuting down and no longer needs the driver.
+ */
+
+typedef isc_result_t
+(*dns_dlzfindzone_t)(void *driverarg, void *dbdata, isc_mem_t *mctx,
+ dns_rdataclass_t rdclass, dns_name_t *name,
+ dns_db_t **dbp);
+
+/*%<
+
+ * Method prototype. Drivers implementing the DLZ interface MUST
+ * supply a find zone method. This method is called when the DNS
+ * server is performing a query. The find zone method will be called
+ * with the longest possible name first, and continue to be called
+ * with successively shorter domain names, until any of the following
+ * occur:
+ *
+ * \li 1) a match is found, and the function returns (ISC_R_SUCCESS)
+ *
+ * \li 2) a problem occurs, and the functions returns anything other
+ * than (ISC_R_NOTFOUND)
+ * \li 3) we run out of domain name labels. I.E. we have tried the
+ * shortest domain name
+ * \li 4) the number of labels in the domain name is less than
+ * min_lables for dns_dlzfindzone
+ *
+ * The driver's find zone method should return ISC_R_SUCCESS and a
+ * database pointer to the name server if the zone is supported by the
+ * database. Otherwise it will return ISC_R_NOTFOUND, and a null
+ * pointer if the zone is not supported. If an error occurs it should
+ * return a result code indicating the type of error.
+ */
+
+/*% the methods supplied by a DLZ driver */
+typedef struct dns_dlzmethods {
+ dns_dlzcreate_t create;
+ dns_dlzdestroy_t destroy;
+ dns_dlzfindzone_t findzone;
+ dns_dlzallowzonexfr_t allowzonexfr;
+} dns_dlzmethods_t;
+
+/*% information about a DLZ driver */
+struct dns_dlzimplementation {
+ const char *name;
+ const dns_dlzmethods_t *methods;
+ isc_mem_t *mctx;
+ void *driverarg;
+ ISC_LINK(dns_dlzimplementation_t) link;
+};
+
+/*% an instance of a DLZ driver */
+struct dns_dlzdb {
+ unsigned int magic;
+ isc_mem_t *mctx;
+ dns_dlzimplementation_t *implementation;
+ void *dbdata;
+};
+
+
+/***
+ *** Method declarations
+ ***/
+
+isc_result_t
+dns_dlzallowzonexfr(dns_view_t *view, dns_name_t *name,
+ isc_sockaddr_t *clientaddr, dns_db_t **dbp);
+
+/*%<
+ * This method is called when the DNS server is performing a zone
+ * transfer query. It will call the DLZ driver's allow zone tranfer
+ * method.
+ */
+
+isc_result_t
+dns_dlzcreate(isc_mem_t *mctx, const char *dlzname,
+ const char *drivername, unsigned int argc,
+ char *argv[], dns_dlzdb_t **dbp);
+
+/*%<
+ * This method is called when the DNS server is starting up and
+ * creating drivers for use later. It will search the DLZ driver list
+ * for 'drivername' and return a DLZ driver via dbp if a match is
+ * found. If the DLZ driver supplies a create method, this function
+ * will call it.
+ */
+
+void
+dns_dlzdestroy(dns_dlzdb_t **dbp);
+
+/*%<
+ * This method is called when the DNS server is shuting down and no
+ * longer needs the driver. If the DLZ driver supplies a destroy
+ * methods, this function will call it.
+ */
+
+isc_result_t
+dns_dlzfindzone(dns_view_t *view, dns_name_t *name,
+ unsigned int minlabels, dns_db_t **dbp);
+
+/*%<
+ * This method is called when the DNS server is performing a query.
+ * It will call the DLZ driver's find zone method.
+ */
+
+isc_result_t
+dns_dlzregister(const char *drivername, const dns_dlzmethods_t *methods,
+ void *driverarg, isc_mem_t *mctx,
+ dns_dlzimplementation_t **dlzimp);
+
+/*%<
+ * Register a dynamically loadable zones (DLZ) driver for the database
+ * type 'drivername', implemented by the functions in '*methods'.
+ *
+ * dlzimp must point to a NULL dlz_implementation_t pointer. That is,
+ * dlzimp != NULL && *dlzimp == NULL. It will be assigned a value that
+ * will later be used to identify the driver when deregistering it.
+ */
+
+isc_result_t
+dns_dlzstrtoargv(isc_mem_t *mctx, char *s, unsigned int *argcp, char ***argvp);
+
+/*%<
+ * This method is called when the name server is starting up to parse
+ * the DLZ driver command line from named.conf. Basically it splits
+ * up a string into and argc / argv. The primary difference of this
+ * method is items between braces { } are considered only 1 word. for
+ * example the command line "this is { one grouped phrase } and this
+ * isn't" would be parsed into:
+ *
+ * \li argv[0]: "this"
+ * \li argv[1]: "is"
+ * \li argv{2]: " one grouped phrase "
+ * \li argv[3]: "and"
+ * \li argv[4]: "this"
+ * \li argv{5}: "isn't"
+ *
+ * braces should NOT be nested, more than one grouping in the command
+ * line is allowed. Notice, argv[2] has an extra space at the
+ * beginning and end. Extra spaces are not stripped between a
+ * grouping. You can do so in your driver if needed, or be sure not
+ * to put extra spaces before / after the braces.
+ */
+
+void
+dns_dlzunregister(dns_dlzimplementation_t **dlzimp);
+
+/*%<
+ * Removes the dlz driver from the list of registered dlz drivers.
+ * There must be no active dlz drivers of this type when this function
+ * is called.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DLZ_H */
diff --git a/lib/dns/include/dns/dnssec.h b/lib/dns/include/dns/dnssec.h
new file mode 100644
index 0000000..f8a59d0
--- /dev/null
+++ b/lib/dns/include/dns/dnssec.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: dnssec.h,v 1.32 2007/06/19 23:47:16 tbox Exp $ */
+
+#ifndef DNS_DNSSEC_H
+#define DNS_DNSSEC_H 1
+
+/*! \file dns/dnssec.h */
+
+#include <isc/lang.h>
+#include <isc/stdtime.h>
+
+#include <dns/types.h>
+
+#include <dst/dst.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_dnssec_keyfromrdata(dns_name_t *name, dns_rdata_t *rdata, isc_mem_t *mctx,
+ dst_key_t **key);
+/*%<
+ * Creates a DST key from a DNS record. Basically a wrapper around
+ * dst_key_fromdns().
+ *
+ * Requires:
+ *\li 'name' is not NULL
+ *\li 'rdata' is not NULL
+ *\li 'mctx' is not NULL
+ *\li 'key' is not NULL
+ *\li '*key' is NULL
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *\li DST_R_INVALIDPUBLICKEY
+ *\li various errors from dns_name_totext
+ */
+
+isc_result_t
+dns_dnssec_sign(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
+ isc_stdtime_t *inception, isc_stdtime_t *expire,
+ isc_mem_t *mctx, isc_buffer_t *buffer, dns_rdata_t *sigrdata);
+/*%<
+ * Generates a SIG record covering this rdataset. This has no effect
+ * on existing SIG records.
+ *
+ * Requires:
+ *\li 'name' (the owner name of the record) is a valid name
+ *\li 'set' is a valid rdataset
+ *\li 'key' is a valid key
+ *\li 'inception' is not NULL
+ *\li 'expire' is not NULL
+ *\li 'mctx' is not NULL
+ *\li 'buffer' is not NULL
+ *\li 'sigrdata' is not NULL
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *\li #ISC_R_NOSPACE
+ *\li #DNS_R_INVALIDTIME - the expiration is before the inception
+ *\li #DNS_R_KEYUNAUTHORIZED - the key cannot sign this data (either
+ * it is not a zone key or its flags prevent
+ * authentication)
+ *\li DST_R_*
+ */
+
+isc_result_t
+dns_dnssec_verify(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
+ isc_boolean_t ignoretime, isc_mem_t *mctx,
+ dns_rdata_t *sigrdata);
+
+isc_result_t
+dns_dnssec_verify2(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
+ isc_boolean_t ignoretime, isc_mem_t *mctx,
+ dns_rdata_t *sigrdata, dns_name_t *wild);
+/*%<
+ * Verifies the SIG record covering this rdataset signed by a specific
+ * key. This does not determine if the key's owner is authorized to
+ * sign this record, as this requires a resolver or database.
+ * If 'ignoretime' is ISC_TRUE, temporal validity will not be checked.
+ *
+ * Requires:
+ *\li 'name' (the owner name of the record) is a valid name
+ *\li 'set' is a valid rdataset
+ *\li 'key' is a valid key
+ *\li 'mctx' is not NULL
+ *\li 'sigrdata' is a valid rdata containing a SIG record
+ *\li 'wild' if non-NULL then is a valid and has a buffer.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *\li #DNS_R_FROMWILDCARD - the signature is valid and is from
+ * a wildcard expansion. dns_dnssec_verify2() only.
+ * 'wild' contains the name of the wildcard if non-NULL.
+ *\li #DNS_R_SIGINVALID - the signature fails to verify
+ *\li #DNS_R_SIGEXPIRED - the signature has expired
+ *\li #DNS_R_SIGFUTURE - the signature's validity period has not begun
+ *\li #DNS_R_KEYUNAUTHORIZED - the key cannot sign this data (either
+ * it is not a zone key or its flags prevent
+ * authentication)
+ *\li DST_R_*
+ */
+
+/*@{*/
+isc_result_t
+dns_dnssec_findzonekeys(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node,
+ dns_name_t *name, isc_mem_t *mctx,
+ unsigned int maxkeys, dst_key_t **keys,
+ unsigned int *nkeys);
+isc_result_t
+dns_dnssec_findzonekeys2(dns_db_t *db, dns_dbversion_t *ver,
+ dns_dbnode_t *node, dns_name_t *name,
+ const char *directory, isc_mem_t *mctx,
+ unsigned int maxkeys, dst_key_t **keys,
+ unsigned int *nkeys);
+/*%<
+ * Finds a set of zone keys.
+ * XXX temporary - this should be handled in dns_zone_t.
+ */
+/*@}*/
+
+isc_result_t
+dns_dnssec_signmessage(dns_message_t *msg, dst_key_t *key);
+/*%<
+ * Signs a message with a SIG(0) record. This is implicitly called by
+ * dns_message_renderend() if msg->sig0key is not NULL.
+ *
+ * Requires:
+ *\li 'msg' is a valid message
+ *\li 'key' is a valid key that can be used for signing
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *\li DST_R_*
+ */
+
+isc_result_t
+dns_dnssec_verifymessage(isc_buffer_t *source, dns_message_t *msg,
+ dst_key_t *key);
+/*%<
+ * Verifies a message signed by a SIG(0) record. This is not
+ * called implicitly by dns_message_parse(). If dns_message_signer()
+ * is called before dns_dnssec_verifymessage(), it will return
+ * #DNS_R_NOTVERIFIEDYET. dns_dnssec_verifymessage() will set
+ * the verified_sig0 flag in msg if the verify succeeds, and
+ * the sig0status field otherwise.
+ *
+ * Requires:
+ *\li 'source' is a valid buffer containing the unparsed message
+ *\li 'msg' is a valid message
+ *\li 'key' is a valid key
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *\li #ISC_R_NOTFOUND - no SIG(0) was found
+ *\li #DNS_R_SIGINVALID - the SIG record is not well-formed or
+ * was not generated by the key.
+ *\li DST_R_*
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_DNSSEC_H */
diff --git a/lib/dns/include/dns/ds.h b/lib/dns/include/dns/ds.h
new file mode 100644
index 0000000..b59fb83
--- /dev/null
+++ b/lib/dns/include/dns/ds.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: ds.h,v 1.10 2007/06/19 23:47:16 tbox Exp $ */
+
+#ifndef DNS_DS_H
+#define DNS_DS_H 1
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+#define DNS_DSDIGEST_SHA1 (1)
+#define DNS_DSDIGEST_SHA256 (2)
+
+/*
+ * Assuming SHA-256 digest type.
+ */
+#define DNS_DS_BUFFERSIZE (36)
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_ds_buildrdata(dns_name_t *owner, dns_rdata_t *key,
+ unsigned int digest_type, unsigned char *buffer,
+ dns_rdata_t *rdata);
+/*%<
+ * Build the rdata of a DS record.
+ *
+ * Requires:
+ *\li key Points to a valid DNS KEY record.
+ *\li buffer Points to a temporary buffer of at least
+ * #DNS_DS_BUFFERSIZE bytes.
+ *\li rdata Points to an initialized dns_rdata_t.
+ *
+ * Ensures:
+ * \li *rdata Contains a valid DS rdata. The 'data' member refers
+ * to 'buffer'.
+ */
+
+isc_boolean_t
+dns_ds_digest_supported(unsigned int digest_type);
+/*%<
+ * Is this digest algorithm supported by dns_ds_buildrdata()?
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_DS_H */
diff --git a/lib/dns/include/dns/events.h b/lib/dns/include/dns/events.h
new file mode 100644
index 0000000..7692c1b
--- /dev/null
+++ b/lib/dns/include/dns/events.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: events.h,v 1.49 2007/06/19 23:47:16 tbox Exp $ */
+
+#ifndef DNS_EVENTS_H
+#define DNS_EVENTS_H 1
+
+#include <isc/eventclass.h>
+
+/*! \file dns/events.h
+ * \brief
+ * Registry of DNS event numbers.
+ */
+
+#define DNS_EVENT_FETCHCONTROL (ISC_EVENTCLASS_DNS + 0)
+#define DNS_EVENT_FETCHDONE (ISC_EVENTCLASS_DNS + 1)
+#define DNS_EVENT_VIEWRESSHUTDOWN (ISC_EVENTCLASS_DNS + 2)
+#define DNS_EVENT_VIEWADBSHUTDOWN (ISC_EVENTCLASS_DNS + 3)
+#define DNS_EVENT_UPDATE (ISC_EVENTCLASS_DNS + 4)
+#define DNS_EVENT_UPDATEDONE (ISC_EVENTCLASS_DNS + 5)
+#define DNS_EVENT_DISPATCH (ISC_EVENTCLASS_DNS + 6)
+#define DNS_EVENT_TCPMSG (ISC_EVENTCLASS_DNS + 7)
+#define DNS_EVENT_ADBMOREADDRESSES (ISC_EVENTCLASS_DNS + 8)
+#define DNS_EVENT_ADBNOMOREADDRESSES (ISC_EVENTCLASS_DNS + 9)
+#define DNS_EVENT_ADBCANCELED (ISC_EVENTCLASS_DNS + 10)
+#define DNS_EVENT_ADBNAMEDELETED (ISC_EVENTCLASS_DNS + 11)
+#define DNS_EVENT_ADBSHUTDOWN (ISC_EVENTCLASS_DNS + 12)
+#define DNS_EVENT_ADBEXPIRED (ISC_EVENTCLASS_DNS + 13)
+#define DNS_EVENT_ADBCONTROL (ISC_EVENTCLASS_DNS + 14)
+#define DNS_EVENT_CACHECLEAN (ISC_EVENTCLASS_DNS + 15)
+#define DNS_EVENT_BYADDRDONE (ISC_EVENTCLASS_DNS + 16)
+#define DNS_EVENT_ZONECONTROL (ISC_EVENTCLASS_DNS + 17)
+#define DNS_EVENT_DBDESTROYED (ISC_EVENTCLASS_DNS + 18)
+#define DNS_EVENT_VALIDATORDONE (ISC_EVENTCLASS_DNS + 19)
+#define DNS_EVENT_REQUESTDONE (ISC_EVENTCLASS_DNS + 20)
+#define DNS_EVENT_VALIDATORSTART (ISC_EVENTCLASS_DNS + 21)
+#define DNS_EVENT_VIEWREQSHUTDOWN (ISC_EVENTCLASS_DNS + 22)
+#define DNS_EVENT_NOTIFYSENDTOADDR (ISC_EVENTCLASS_DNS + 23)
+#define DNS_EVENT_ZONE (ISC_EVENTCLASS_DNS + 24)
+#define DNS_EVENT_ZONESTARTXFRIN (ISC_EVENTCLASS_DNS + 25)
+#define DNS_EVENT_MASTERQUANTUM (ISC_EVENTCLASS_DNS + 26)
+#define DNS_EVENT_CACHEOVERMEM (ISC_EVENTCLASS_DNS + 27)
+#define DNS_EVENT_MASTERNEXTZONE (ISC_EVENTCLASS_DNS + 28)
+#define DNS_EVENT_IOREADY (ISC_EVENTCLASS_DNS + 29)
+#define DNS_EVENT_LOOKUPDONE (ISC_EVENTCLASS_DNS + 30)
+/* #define DNS_EVENT_unused (ISC_EVENTCLASS_DNS + 31) */
+#define DNS_EVENT_DISPATCHCONTROL (ISC_EVENTCLASS_DNS + 32)
+#define DNS_EVENT_REQUESTCONTROL (ISC_EVENTCLASS_DNS + 33)
+#define DNS_EVENT_DUMPQUANTUM (ISC_EVENTCLASS_DNS + 34)
+#define DNS_EVENT_IMPORTRECVDONE (ISC_EVENTCLASS_DNS + 35)
+#define DNS_EVENT_FREESTORAGE (ISC_EVENTCLASS_DNS + 36)
+#define DNS_EVENT_VIEWACACHESHUTDOWN (ISC_EVENTCLASS_DNS + 37)
+#define DNS_EVENT_ACACHECONTROL (ISC_EVENTCLASS_DNS + 38)
+#define DNS_EVENT_ACACHECLEAN (ISC_EVENTCLASS_DNS + 39)
+#define DNS_EVENT_ACACHEOVERMEM (ISC_EVENTCLASS_DNS + 40)
+
+#define DNS_EVENT_FIRSTEVENT (ISC_EVENTCLASS_DNS + 0)
+#define DNS_EVENT_LASTEVENT (ISC_EVENTCLASS_DNS + 65535)
+
+#endif /* DNS_EVENTS_H */
diff --git a/lib/dns/include/dns/fixedname.h b/lib/dns/include/dns/fixedname.h
new file mode 100644
index 0000000..5a2aaf3
--- /dev/null
+++ b/lib/dns/include/dns/fixedname.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: fixedname.h,v 1.19 2007/06/19 23:47:16 tbox Exp $ */
+
+#ifndef DNS_FIXEDNAME_H
+#define DNS_FIXEDNAME_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/fixedname.h
+ * \brief
+ * Fixed-size Names
+ *
+ * dns_fixedname_t is a convenience type containing a name, an offsets table,
+ * and a dedicated buffer big enough for the longest possible name.
+ *
+ * MP:
+ *\li The caller must ensure any required synchronization.
+ *
+ * Reliability:
+ *\li No anticipated impact.
+ *
+ * Resources:
+ *\li Per dns_fixedname_t:
+ *\code
+ * sizeof(dns_name_t) + sizeof(dns_offsets_t) +
+ * sizeof(isc_buffer_t) + 255 bytes + structure padding
+ *\endcode
+ *
+ * Security:
+ *\li No anticipated impact.
+ *
+ * Standards:
+ *\li None.
+ */
+
+/*****
+ ***** Imports
+ *****/
+
+#include <isc/buffer.h>
+
+#include <dns/name.h>
+
+/*****
+ ***** Types
+ *****/
+
+struct dns_fixedname {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_buffer_t buffer;
+ unsigned char data[DNS_NAME_MAXWIRE];
+};
+
+#define dns_fixedname_init(fn) \
+ do { \
+ dns_name_init(&((fn)->name), (fn)->offsets); \
+ isc_buffer_init(&((fn)->buffer), (fn)->data, \
+ DNS_NAME_MAXWIRE); \
+ dns_name_setbuffer(&((fn)->name), &((fn)->buffer)); \
+ } while (0)
+
+#define dns_fixedname_invalidate(fn) \
+ dns_name_invalidate(&((fn)->name))
+
+#define dns_fixedname_name(fn) (&((fn)->name))
+
+#endif /* DNS_FIXEDNAME_H */
diff --git a/lib/dns/include/dns/forward.h b/lib/dns/include/dns/forward.h
new file mode 100644
index 0000000..512c5e3
--- /dev/null
+++ b/lib/dns/include/dns/forward.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: forward.h,v 1.11 2007/06/19 23:47:16 tbox Exp $ */
+
+#ifndef DNS_FORWARD_H
+#define DNS_FORWARD_H 1
+
+/*! \file dns/forward.h */
+
+#include <isc/lang.h>
+#include <isc/result.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+struct dns_forwarders {
+ isc_sockaddrlist_t addrs;
+ dns_fwdpolicy_t fwdpolicy;
+};
+
+isc_result_t
+dns_fwdtable_create(isc_mem_t *mctx, dns_fwdtable_t **fwdtablep);
+/*%<
+ * Creates a new forwarding table.
+ *
+ * Requires:
+ * \li mctx is a valid memory context.
+ * \li fwdtablep != NULL && *fwdtablep == NULL
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOMEMORY
+ */
+
+isc_result_t
+dns_fwdtable_add(dns_fwdtable_t *fwdtable, dns_name_t *name,
+ isc_sockaddrlist_t *addrs, dns_fwdpolicy_t policy);
+/*%<
+ * Adds an entry to the forwarding table. The entry associates
+ * a domain with a list of forwarders and a forwarding policy. The
+ * addrs list is copied if not empty, so the caller should free its copy.
+ *
+ * Requires:
+ * \li fwdtable is a valid forwarding table.
+ * \li name is a valid name
+ * \li addrs is a valid list of sockaddrs, which may be empty.
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOMEMORY
+ */
+
+isc_result_t
+dns_fwdtable_find(dns_fwdtable_t *fwdtable, dns_name_t *name,
+ dns_forwarders_t **forwardersp);
+/*%<
+ * Finds a domain in the forwarding table. The closest matching parent
+ * domain is returned.
+ *
+ * Requires:
+ * \li fwdtable is a valid forwarding table.
+ * \li name is a valid name
+ * \li forwardersp != NULL && *forwardersp == NULL
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOTFOUND
+ */
+
+isc_result_t
+dns_fwdtable_find2(dns_fwdtable_t *fwdtable, dns_name_t *name,
+ dns_name_t *foundname, dns_forwarders_t **forwardersp);
+/*%<
+ * Finds a domain in the forwarding table. The closest matching parent
+ * domain is returned.
+ *
+ * Requires:
+ * \li fwdtable is a valid forwarding table.
+ * \li name is a valid name
+ * \li forwardersp != NULL && *forwardersp == NULL
+ * \li foundname to be NULL or a valid name with buffer.
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOTFOUND
+ */
+
+void
+dns_fwdtable_destroy(dns_fwdtable_t **fwdtablep);
+/*%<
+ * Destroys a forwarding table.
+ *
+ * Requires:
+ * \li fwtablep != NULL && *fwtablep != NULL
+ *
+ * Ensures:
+ * \li all memory associated with the forwarding table is freed.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_FORWARD_H */
diff --git a/lib/dns/include/dns/iptable.h b/lib/dns/include/dns/iptable.h
new file mode 100644
index 0000000..d7eb140
--- /dev/null
+++ b/lib/dns/include/dns/iptable.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2007 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 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.
+ */
+
+/* $Id: iptable.h,v 1.4 2007/09/14 01:46:05 marka Exp $ */
+
+#ifndef DNS_IPTABLE_H
+#define DNS_IPTABLE_H 1
+
+#include <isc/lang.h>
+#include <isc/magic.h>
+#include <isc/radix.h>
+
+struct dns_iptable {
+ unsigned int magic;
+ isc_mem_t *mctx;
+ isc_refcount_t refcount;
+ isc_radix_tree_t *radix;
+ ISC_LINK(dns_iptable_t) nextincache;
+};
+
+#define DNS_IPTABLE_MAGIC ISC_MAGIC('T','a','b','l')
+#define DNS_IPTABLE_VALID(a) ISC_MAGIC_VALID(a, DNS_IPTABLE_MAGIC)
+
+/***
+ *** Functions
+ ***/
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_iptable_create(isc_mem_t *mctx, dns_iptable_t **target);
+/*
+ * Create a new IP table and the underlying radix structure
+ */
+
+isc_result_t
+dns_iptable_addprefix(dns_iptable_t *tab, isc_netaddr_t *addr,
+ isc_uint16_t bitlen, isc_boolean_t pos);
+/*
+ * Add an IP prefix to an existing IP table
+ */
+
+isc_result_t
+dns_iptable_merge(dns_iptable_t *tab, dns_iptable_t *source, isc_boolean_t pos);
+/*
+ * Merge one IP table into another one.
+ */
+
+void
+dns_iptable_attach(dns_iptable_t *source, dns_iptable_t **target);
+
+void
+dns_iptable_detach(dns_iptable_t **tabp);
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_IPTABLE_H */
diff --git a/lib/dns/include/dns/journal.h b/lib/dns/include/dns/journal.h
new file mode 100644
index 0000000..72468f2
--- /dev/null
+++ b/lib/dns/include/dns/journal.h
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: journal.h,v 1.33 2008/04/01 23:47:10 tbox Exp $ */
+
+#ifndef DNS_JOURNAL_H
+#define DNS_JOURNAL_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/journal.h
+ * \brief
+ * Database journalling.
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <isc/lang.h>
+#include <isc/magic.h>
+
+#include <dns/name.h>
+#include <dns/diff.h>
+#include <dns/rdata.h>
+#include <dns/types.h>
+
+/***
+ *** Defines.
+ ***/
+#define DNS_JOURNALOPT_RESIGN 0x00000001
+
+/***
+ *** Types
+ ***/
+
+/*%
+ * A dns_journal_t represents an open journal file. This is an opaque type.
+ *
+ * A particular dns_journal_t object may be opened for writing, in which case
+ * it can be used for writing transactions to a journal file, or it can be
+ * opened for reading, in which case it can be used for reading transactions
+ * from (iterating over) a journal file. A single dns_journal_t object may
+ * not be used for both purposes.
+ */
+typedef struct dns_journal dns_journal_t;
+
+
+/***
+ *** Functions
+ ***/
+
+ISC_LANG_BEGINDECLS
+
+/**************************************************************************/
+
+isc_result_t
+dns_db_createsoatuple(dns_db_t *db, dns_dbversion_t *ver, isc_mem_t *mctx,
+ dns_diffop_t op, dns_difftuple_t **tp);
+/*!< brief
+ * Create a diff tuple for the current database SOA.
+ * XXX this probably belongs somewhere else.
+ */
+
+
+/*@{*/
+#define DNS_SERIAL_GT(a, b) ((int)(((a) - (b)) & 0xFFFFFFFF) > 0)
+#define DNS_SERIAL_GE(a, b) ((int)(((a) - (b)) & 0xFFFFFFFF) >= 0)
+/*!< brief
+ * Compare SOA serial numbers. DNS_SERIAL_GT(a, b) returns true iff
+ * a is "greater than" b where "greater than" is as defined in RFC1982.
+ * DNS_SERIAL_GE(a, b) returns true iff a is "greater than or equal to" b.
+ */
+/*@}*/
+
+/**************************************************************************/
+/*
+ * Journal object creation and destruction.
+ */
+
+isc_result_t
+dns_journal_open(isc_mem_t *mctx, const char *filename, isc_boolean_t write,
+ dns_journal_t **journalp);
+/*%<
+ * Open the journal file 'filename' and create a dns_journal_t object for it.
+ *
+ * If 'write' is ISC_TRUE, the journal is open for writing. If it does
+ * not exist, it is created.
+ *
+ * If 'write' is ISC_FALSE, the journal is open for reading. If it does
+ * not exist, ISC_R_NOTFOUND is returned.
+ */
+
+void
+dns_journal_destroy(dns_journal_t **journalp);
+/*%<
+ * Destroy a dns_journal_t, closing any open files and freeing its memory.
+ */
+
+/**************************************************************************/
+/*
+ * Writing transactions to journals.
+ */
+
+isc_result_t
+dns_journal_begin_transaction(dns_journal_t *j);
+/*%<
+ * Prepare to write a new transaction to the open journal file 'j'.
+ *
+ * Requires:
+ * \li 'j' is open for writing.
+ */
+
+isc_result_t
+dns_journal_writediff(dns_journal_t *j, dns_diff_t *diff);
+/*%<
+ * Write 'diff' to the current transaction of journal file 'j'.
+ *
+ * Requires:
+ * \li 'j' is open for writing and dns_journal_begin_transaction()
+ * has been called.
+ *
+ *\li 'diff' is a full or partial, correctly ordered IXFR
+ * difference sequence.
+ */
+
+isc_result_t
+dns_journal_commit(dns_journal_t *j);
+/*%<
+ * Commit the current transaction of journal file 'j'.
+ *
+ * Requires:
+ * \li 'j' is open for writing and dns_journal_begin_transaction()
+ * has been called.
+ *
+ * \li dns_journal_writediff() has been called one or more times
+ * to form a complete, correctly ordered IXFR difference
+ * sequence.
+ */
+
+isc_result_t
+dns_journal_write_transaction(dns_journal_t *j, dns_diff_t *diff);
+/*%
+ * Write a complete transaction at once to a journal file,
+ * sorting it if necessary, and commit it. Equivalent to calling
+ * dns_diff_sort(), dns_journal_begin_transaction(),
+ * dns_journal_writediff(), and dns_journal_commit().
+ *
+ * Requires:
+ *\li 'j' is open for writing.
+ *
+ * \li 'diff' contains exactly one SOA deletion, one SOA addition
+ * with a greater serial number, and possibly other changes,
+ * in arbitrary order.
+ */
+
+/**************************************************************************/
+/*
+ * Reading transactions from journals.
+ */
+
+isc_uint32_t
+dns_journal_first_serial(dns_journal_t *j);
+isc_uint32_t
+dns_journal_last_serial(dns_journal_t *j);
+/*%<
+ * Get the first and last addressable serial number in the journal.
+ */
+
+isc_result_t
+dns_journal_iter_init(dns_journal_t *j,
+ isc_uint32_t begin_serial, isc_uint32_t end_serial);
+/*%<
+ * Prepare to iterate over the transactions that will bring the database
+ * from SOA serial number 'begin_serial' to 'end_serial'.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ *\li ISC_R_RANGE begin_serial is outside the addressable range.
+ *\li ISC_R_NOTFOUND begin_serial is within the range of adressable
+ * serial numbers covered by the journal, but
+ * this particular serial number does not exist.
+ */
+
+/*@{*/
+isc_result_t
+dns_journal_first_rr(dns_journal_t *j);
+isc_result_t
+dns_journal_next_rr(dns_journal_t *j);
+/*%<
+ * Position the iterator at the first/next RR in a journal
+ * transaction sequence established using dns_journal_iter_init().
+ *
+ * Requires:
+ * \li dns_journal_iter_init() has been called.
+ *
+ */
+/*@}*/
+
+void
+dns_journal_current_rr(dns_journal_t *j, dns_name_t **name, isc_uint32_t *ttl,
+ dns_rdata_t **rdata);
+/*%<
+ * Get the name, ttl, and rdata of the current journal RR.
+ *
+ * Requires:
+ * \li The last call to dns_journal_first_rr() or dns_journal_next_rr()
+ * returned ISC_R_SUCCESS.
+ */
+
+/**************************************************************************/
+/*
+ * Database roll-forward.
+ */
+
+isc_result_t
+dns_journal_rollforward(isc_mem_t *mctx, dns_db_t *db, unsigned int options,
+ const char *filename);
+/*%<
+ * Roll forward (play back) the journal file "filename" into the
+ * database "db". This should be called when the server starts
+ * after a shutdown or crash.
+ *
+ * Requires:
+ *\li 'mctx' is a valid memory context.
+ *\li 'db' is a valid database which does not have a version
+ * open for writing.
+ *\li 'filename' is the name of the journal file belonging to 'db'.
+ *
+ * Returns:
+ *\li DNS_R_NOJOURNAL when journal does not exist.
+ *\li ISC_R_NOTFOUND when current serial in not in journal.
+ *\li ISC_R_RANGE when current serial in not in journals range.
+ *\li ISC_R_SUCCESS journal has been applied successfully to database.
+ * others
+ */
+
+isc_result_t
+dns_journal_print(isc_mem_t *mctx, const char *filename, FILE *file);
+/* For debugging not general use */
+
+isc_result_t
+dns_db_diff(isc_mem_t *mctx,
+ dns_db_t *dba, dns_dbversion_t *dbvera,
+ dns_db_t *dbb, dns_dbversion_t *dbverb,
+ const char *journal_filename);
+/*%<
+ * Compare the databases 'dba' and 'dbb' and generate a journal
+ * entry containing the changes to make 'dba' from 'dbb' (note
+ * the order). This journal entry will consist of a single,
+ * possibly very large transaction. Append the journal
+ * entry to the journal file specified by 'journal_filename'.
+ */
+
+isc_result_t
+dns_journal_compact(isc_mem_t *mctx, char *filename, isc_uint32_t serial,
+ isc_uint32_t target_size);
+/*%<
+ * Attempt to compact the journal if it is greater that 'target_size'.
+ * Changes from 'serial' onwards will be preserved. If the journal
+ * exists and is non-empty 'serial' must exist in the journal.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_JOURNAL_H */
diff --git a/lib/dns/include/dns/keyflags.h b/lib/dns/include/dns/keyflags.h
new file mode 100644
index 0000000..74a1740
--- /dev/null
+++ b/lib/dns/include/dns/keyflags.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: keyflags.h,v 1.16 2007/06/19 23:47:16 tbox Exp $ */
+
+#ifndef DNS_KEYFLAGS_H
+#define DNS_KEYFLAGS_H 1
+
+/*! \file dns/keyflags.h */
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_keyflags_fromtext(dns_keyflags_t *flagsp, isc_textregion_t *source);
+/*%<
+ * Convert the text 'source' refers to into a DNSSEC KEY flags value.
+ * The text may contain either a set of flag mnemonics separated by
+ * vertical bars or a decimal flags value. For compatibility with
+ * older versions of BIND and the DNSSEC signer, octal values
+ * prefixed with a zero and hexadecimal values prefixed with "0x"
+ * are also accepted.
+ *
+ * Requires:
+ *\li 'flagsp' is a valid pointer.
+ *
+ *\li 'source' is a valid text region.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS on success
+ *\li ISC_R_RANGE numeric flag value is out of range
+ *\li DNS_R_UNKNOWN mnemonic flag is unknown
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_KEYFLAGS_H */
diff --git a/lib/dns/include/dns/keytable.h b/lib/dns/include/dns/keytable.h
new file mode 100644
index 0000000..553aa99
--- /dev/null
+++ b/lib/dns/include/dns/keytable.h
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: keytable.h,v 1.16 2007/06/19 23:47:16 tbox Exp $ */
+
+#ifndef DNS_KEYTABLE_H
+#define DNS_KEYTABLE_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file
+ * \brief
+ * The keytable module provides services for storing and retrieving DNSSEC
+ * trusted keys, as well as the ability to find the deepest matching key
+ * for a given domain name.
+ *
+ * MP:
+ *\li The module ensures appropriate synchronization of data structures it
+ * creates and manipulates.
+ *
+ * Resources:
+ *\li TBS
+ *
+ * Security:
+ *\li No anticipated impact.
+ */
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+#include <dst/dst.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_keytable_create(isc_mem_t *mctx, dns_keytable_t **keytablep);
+/*%<
+ * Create a keytable.
+ *
+ * Requires:
+ *
+ *\li 'mctx' is a valid memory context.
+ *
+ *\li keytablep != NULL && *keytablep == NULL
+ *
+ * Ensures:
+ *
+ *\li On success, *keytablep is a valid, empty key table.
+ *
+ * Returns:
+ *
+ *\li ISC_R_SUCCESS
+ *
+ *\li Any other result indicates failure.
+ */
+
+
+void
+dns_keytable_attach(dns_keytable_t *source, dns_keytable_t **targetp);
+/*%<
+ * Attach *targetp to source.
+ *
+ * Requires:
+ *
+ *\li 'source' is a valid keytable.
+ *
+ *\li 'targetp' points to a NULL dns_keytable_t *.
+ *
+ * Ensures:
+ *
+ *\li *targetp is attached to source.
+ */
+
+void
+dns_keytable_detach(dns_keytable_t **keytablep);
+/*%<
+ * Detach *keytablep from its keytable.
+ *
+ * Requires:
+ *
+ *\li 'keytablep' points to a valid keytable.
+ *
+ * Ensures:
+ *
+ *\li *keytablep is NULL.
+ *
+ *\li If '*keytablep' is the last reference to the keytable,
+ * all resources used by the keytable will be freed
+ */
+
+isc_result_t
+dns_keytable_add(dns_keytable_t *keytable, dst_key_t **keyp);
+/*%<
+ * Add '*keyp' to 'keytable'.
+ *
+ * Notes:
+ *
+ *\li Ownership of *keyp is transferred to the keytable.
+ *
+ * Requires:
+ *
+ *\li keyp != NULL && *keyp is a valid dst_key_t *.
+ *
+ * Ensures:
+ *
+ *\li On success, *keyp == NULL
+ *
+ * Returns:
+ *
+ *\li ISC_R_SUCCESS
+ *
+ *\li Any other result indicates failure.
+ */
+
+isc_result_t
+dns_keytable_findkeynode(dns_keytable_t *keytable, dns_name_t *name,
+ dns_secalg_t algorithm, dns_keytag_t tag,
+ dns_keynode_t **keynodep);
+/*%<
+ * Search for a key named 'name', matching 'algorithm' and 'tag' in
+ * 'keytable'. This finds the first instance which matches. Use
+ * dns_keytable_findnextkeynode() to find other instances.
+ *
+ * Requires:
+ *
+ *\li 'keytable' is a valid keytable.
+ *
+ *\li 'name' is a valid absolute name.
+ *
+ *\li keynodep != NULL && *keynodep == NULL
+ *
+ * Returns:
+ *
+ *\li ISC_R_SUCCESS
+ *\li DNS_R_PARTIALMATCH the name existed in the keytable.
+ *\li ISC_R_NOTFOUND
+ *
+ *\li Any other result indicates an error.
+ */
+
+isc_result_t
+dns_keytable_findnextkeynode(dns_keytable_t *keytable, dns_keynode_t *keynode,
+ dns_keynode_t **nextnodep);
+/*%<
+ * Search for the next key with the same properties as 'keynode' in
+ * 'keytable' as found by dns_keytable_findkeynode().
+ *
+ * Requires:
+ *
+ *\li 'keytable' is a valid keytable.
+ *
+ *\li 'keynode' is a valid keynode.
+ *
+ *\li nextnodep != NULL && *nextnodep == NULL
+ *
+ * Returns:
+ *
+ *\li ISC_R_SUCCESS
+ *\li ISC_R_NOTFOUND
+ *
+ *\li Any other result indicates an error.
+ */
+
+isc_result_t
+dns_keytable_finddeepestmatch(dns_keytable_t *keytable, dns_name_t *name,
+ dns_name_t *foundname);
+/*%<
+ * Search for the deepest match of 'name' in 'keytable'.
+ *
+ * Requires:
+ *
+ *\li 'keytable' is a valid keytable.
+ *
+ *\li 'name' is a valid absolute name.
+ *
+ *\li 'foundname' is a name with a dedicated buffer.
+ *
+ * Returns:
+ *
+ *\li ISC_R_SUCCESS
+ *\li ISC_R_NOTFOUND
+ *
+ *\li Any other result indicates an error.
+ */
+
+void
+dns_keytable_detachkeynode(dns_keytable_t *keytable,
+ dns_keynode_t **keynodep);
+/*%<
+ * Give back a keynode found via dns_keytable_findkeynode().
+ *
+ * Requires:
+ *
+ *\li 'keytable' is a valid keytable.
+ *
+ *\li *keynodep is a valid keynode returned by a call to
+ * dns_keytable_findkeynode().
+ *
+ * Ensures:
+ *
+ *\li *keynodep == NULL
+ */
+
+isc_result_t
+dns_keytable_issecuredomain(dns_keytable_t *keytable, dns_name_t *name,
+ isc_boolean_t *wantdnssecp);
+/*%<
+ * Is 'name' at or beneath a trusted key?
+ *
+ * Requires:
+ *
+ *\li 'keytable' is a valid keytable.
+ *
+ *\li 'name' is a valid absolute name.
+ *
+ *\li '*wantsdnssecp' is a valid isc_boolean_t.
+ *
+ * Ensures:
+ *
+ *\li On success, *wantsdnssecp will be ISC_TRUE if and only if 'name'
+ * is at or beneath a trusted key.
+ *
+ * Returns:
+ *
+ *\li ISC_R_SUCCESS
+ *
+ *\li Any other result is an error.
+ */
+
+dst_key_t *
+dns_keynode_key(dns_keynode_t *keynode);
+/*%<
+ * Get the DST key associated with keynode.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_KEYTABLE_H */
diff --git a/lib/dns/include/dns/keyvalues.h b/lib/dns/include/dns/keyvalues.h
new file mode 100644
index 0000000..7040389
--- /dev/null
+++ b/lib/dns/include/dns/keyvalues.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001, 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: keyvalues.h,v 1.23 2008/09/25 04:02:39 tbox Exp $ */
+
+#ifndef DNS_KEYVALUES_H
+#define DNS_KEYVALUES_H 1
+
+/*! \file dns/keyvalues.h */
+
+/*
+ * Flags field of the KEY RR rdata
+ */
+#define DNS_KEYFLAG_TYPEMASK 0xC000 /*%< Mask for "type" bits */
+#define DNS_KEYTYPE_AUTHCONF 0x0000 /*%< Key usable for both */
+#define DNS_KEYTYPE_CONFONLY 0x8000 /*%< Key usable for confidentiality */
+#define DNS_KEYTYPE_AUTHONLY 0x4000 /*%< Key usable for authentication */
+#define DNS_KEYTYPE_NOKEY 0xC000 /*%< No key usable for either; no key */
+#define DNS_KEYTYPE_NOAUTH DNS_KEYTYPE_CONFONLY
+#define DNS_KEYTYPE_NOCONF DNS_KEYTYPE_AUTHONLY
+
+#define DNS_KEYFLAG_RESERVED2 0x2000 /*%< reserved - must be zero */
+#define DNS_KEYFLAG_EXTENDED 0x1000 /*%< key has extended flags */
+#define DNS_KEYFLAG_RESERVED4 0x0800 /*%< reserved - must be zero */
+#define DNS_KEYFLAG_RESERVED5 0x0400 /*%< reserved - must be zero */
+#define DNS_KEYFLAG_OWNERMASK 0x0300 /*%< these bits determine the type */
+#define DNS_KEYOWNER_USER 0x0000 /*%< key is assoc. with user */
+#define DNS_KEYOWNER_ENTITY 0x0200 /*%< key is assoc. with entity eg host */
+#define DNS_KEYOWNER_ZONE 0x0100 /*%< key is zone key */
+#define DNS_KEYOWNER_RESERVED 0x0300 /*%< reserved meaning */
+#define DNS_KEYFLAG_RESERVED8 0x0080 /*%< reserved - must be zero */
+#define DNS_KEYFLAG_RESERVED9 0x0040 /*%< reserved - must be zero */
+#define DNS_KEYFLAG_RESERVED10 0x0020 /*%< reserved - must be zero */
+#define DNS_KEYFLAG_RESERVED11 0x0010 /*%< reserved - must be zero */
+#define DNS_KEYFLAG_SIGNATORYMASK 0x000F /*%< key can sign RR's of same name */
+
+#define DNS_KEYFLAG_RESERVEDMASK (DNS_KEYFLAG_RESERVED2 | \
+ DNS_KEYFLAG_RESERVED4 | \
+ DNS_KEYFLAG_RESERVED5 | \
+ DNS_KEYFLAG_RESERVED8 | \
+ DNS_KEYFLAG_RESERVED9 | \
+ DNS_KEYFLAG_RESERVED10 | \
+ DNS_KEYFLAG_RESERVED11 )
+#define DNS_KEYFLAG_KSK 0x0001 /*%< key signing key */
+
+#define DNS_KEYFLAG_RESERVEDMASK2 0xFFFF /*%< no bits defined here */
+
+/* The Algorithm field of the KEY and SIG RR's is an integer, {1..254} */
+#define DNS_KEYALG_RSAMD5 1 /*%< RSA with MD5 */
+#define DNS_KEYALG_RSA DNS_KEYALG_RSAMD5
+#define DNS_KEYALG_DH 2 /*%< Diffie Hellman KEY */
+#define DNS_KEYALG_DSA 3 /*%< DSA KEY */
+#define DNS_KEYALG_NSEC3DSA 6
+#define DNS_KEYALG_DSS DNS_ALG_DSA
+#define DNS_KEYALG_ECC 4
+#define DNS_KEYALG_RSASHA1 5
+#define DNS_KEYALG_NSEC3RSASHA1 7
+#define DNS_KEYALG_INDIRECT 252
+#define DNS_KEYALG_PRIVATEDNS 253
+#define DNS_KEYALG_PRIVATEOID 254 /*%< Key begins with OID giving alg */
+
+/* Protocol values */
+#define DNS_KEYPROTO_RESERVED 0
+#define DNS_KEYPROTO_TLS 1
+#define DNS_KEYPROTO_EMAIL 2
+#define DNS_KEYPROTO_DNSSEC 3
+#define DNS_KEYPROTO_IPSEC 4
+#define DNS_KEYPROTO_ANY 255
+
+/* Signatures */
+#define DNS_SIG_RSAMINBITS 512 /*%< Size of a mod or exp in bits */
+#define DNS_SIG_RSAMAXBITS 2552
+ /* Total of binary mod and exp */
+#define DNS_SIG_RSAMAXBYTES ((DNS_SIG_RSAMAXBITS+7/8)*2+3)
+ /*%< Max length of text sig block */
+#define DNS_SIG_RSAMAXBASE64 (((DNS_SIG_RSAMAXBYTES+2)/3)*4)
+#define DNS_SIG_RSAMINSIZE ((DNS_SIG_RSAMINBITS+7)/8)
+#define DNS_SIG_RSAMAXSIZE ((DNS_SIG_RSAMAXBITS+7)/8)
+
+#define DNS_SIG_DSASIGSIZE 41
+#define DNS_SIG_DSAMINBITS 512
+#define DNS_SIG_DSAMAXBITS 1024
+#define DNS_SIG_DSAMINBYTES 213
+#define DNS_SIG_DSAMAXBYTES 405
+
+#endif /* DNS_KEYVALUES_H */
diff --git a/lib/dns/include/dns/lib.h b/lib/dns/include/dns/lib.h
new file mode 100644
index 0000000..fd3325b
--- /dev/null
+++ b/lib/dns/include/dns/lib.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: lib.h,v 1.16 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef DNS_LIB_H
+#define DNS_LIB_H 1
+
+/*! \file dns/lib.h */
+
+#include <isc/types.h>
+#include <isc/lang.h>
+
+ISC_LANG_BEGINDECLS
+
+/*%
+ * Tuning: external query load in packets per seconds.
+ */
+LIBDNS_EXTERNAL_DATA extern unsigned int dns_pps;
+LIBDNS_EXTERNAL_DATA extern isc_msgcat_t *dns_msgcat;
+
+void
+dns_lib_initmsgcat(void);
+/*%<
+ * Initialize the DNS library's message catalog, dns_msgcat, if it
+ * has not already been initialized.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_LIB_H */
diff --git a/lib/dns/include/dns/log.h b/lib/dns/include/dns/log.h
new file mode 100644
index 0000000..5adcedd
--- /dev/null
+++ b/lib/dns/include/dns/log.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001, 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: log.h,v 1.42 2007/06/18 23:47:42 tbox Exp $ */
+
+/*! \file dns/log.h
+ * \author Principal Authors: DCL */
+
+#ifndef DNS_LOG_H
+#define DNS_LOG_H 1
+
+#include <isc/lang.h>
+#include <isc/log.h>
+
+LIBDNS_EXTERNAL_DATA extern isc_log_t *dns_lctx;
+LIBDNS_EXTERNAL_DATA extern isc_logcategory_t dns_categories[];
+LIBDNS_EXTERNAL_DATA extern isc_logmodule_t dns_modules[];
+
+#define DNS_LOGCATEGORY_NOTIFY (&dns_categories[0])
+#define DNS_LOGCATEGORY_DATABASE (&dns_categories[1])
+#define DNS_LOGCATEGORY_SECURITY (&dns_categories[2])
+/* DNS_LOGCATEGORY_CONFIG superseded by CFG_LOGCATEGORY_CONFIG */
+#define DNS_LOGCATEGORY_DNSSEC (&dns_categories[4])
+#define DNS_LOGCATEGORY_RESOLVER (&dns_categories[5])
+#define DNS_LOGCATEGORY_XFER_IN (&dns_categories[6])
+#define DNS_LOGCATEGORY_XFER_OUT (&dns_categories[7])
+#define DNS_LOGCATEGORY_DISPATCH (&dns_categories[8])
+#define DNS_LOGCATEGORY_LAME_SERVERS (&dns_categories[9])
+#define DNS_LOGCATEGORY_DELEGATION_ONLY (&dns_categories[10])
+#define DNS_LOGCATEGORY_EDNS_DISABLED (&dns_categories[11])
+
+/* Backwards compatibility. */
+#define DNS_LOGCATEGORY_GENERAL ISC_LOGCATEGORY_GENERAL
+
+#define DNS_LOGMODULE_DB (&dns_modules[0])
+#define DNS_LOGMODULE_RBTDB (&dns_modules[1])
+#define DNS_LOGMODULE_RBTDB64 (&dns_modules[2])
+#define DNS_LOGMODULE_RBT (&dns_modules[3])
+#define DNS_LOGMODULE_RDATA (&dns_modules[4])
+#define DNS_LOGMODULE_MASTER (&dns_modules[5])
+#define DNS_LOGMODULE_MESSAGE (&dns_modules[6])
+#define DNS_LOGMODULE_CACHE (&dns_modules[7])
+#define DNS_LOGMODULE_CONFIG (&dns_modules[8])
+#define DNS_LOGMODULE_RESOLVER (&dns_modules[9])
+#define DNS_LOGMODULE_ZONE (&dns_modules[10])
+#define DNS_LOGMODULE_JOURNAL (&dns_modules[11])
+#define DNS_LOGMODULE_ADB (&dns_modules[12])
+#define DNS_LOGMODULE_XFER_IN (&dns_modules[13])
+#define DNS_LOGMODULE_XFER_OUT (&dns_modules[14])
+#define DNS_LOGMODULE_ACL (&dns_modules[15])
+#define DNS_LOGMODULE_VALIDATOR (&dns_modules[16])
+#define DNS_LOGMODULE_DISPATCH (&dns_modules[17])
+#define DNS_LOGMODULE_REQUEST (&dns_modules[18])
+#define DNS_LOGMODULE_MASTERDUMP (&dns_modules[19])
+#define DNS_LOGMODULE_TSIG (&dns_modules[20])
+#define DNS_LOGMODULE_TKEY (&dns_modules[21])
+#define DNS_LOGMODULE_SDB (&dns_modules[22])
+#define DNS_LOGMODULE_DIFF (&dns_modules[23])
+#define DNS_LOGMODULE_HINTS (&dns_modules[24])
+#define DNS_LOGMODULE_ACACHE (&dns_modules[25])
+#define DNS_LOGMODULE_DLZ (&dns_modules[26])
+
+ISC_LANG_BEGINDECLS
+
+void
+dns_log_init(isc_log_t *lctx);
+/*%
+ * Make the libdns categories and modules available for use with the
+ * ISC logging library.
+ *
+ * Requires:
+ *\li lctx is a valid logging context.
+ *
+ *\li dns_log_init() is called only once.
+ *
+ * Ensures:
+ * \li The catgories and modules defined above are available for
+ * use by isc_log_usechannnel() and isc_log_write().
+ */
+
+void
+dns_log_setcontext(isc_log_t *lctx);
+/*%
+ * Make the libdns library use the provided context for logging internal
+ * messages.
+ *
+ * Requires:
+ *\li lctx is a valid logging context.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_LOG_H */
diff --git a/lib/dns/include/dns/lookup.h b/lib/dns/include/dns/lookup.h
new file mode 100644
index 0000000..10f816f
--- /dev/null
+++ b/lib/dns/include/dns/lookup.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: lookup.h,v 1.12 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef DNS_LOOKUP_H
+#define DNS_LOOKUP_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/lookup.h
+ * \brief
+ * The lookup module performs simple DNS lookups. It implements
+ * the full resolver algorithm, both looking for local data and
+ * resoving external names as necessary.
+ *
+ * MP:
+ *\li The module ensures appropriate synchronization of data structures it
+ * creates and manipulates.
+ *
+ * Reliability:
+ *\li No anticipated impact.
+ *
+ * Resources:
+ *\li TBS
+ *
+ * Security:
+ *\li No anticipated impact.
+ *
+ * Standards:
+ *\li RFCs: 1034, 1035, 2181, TBS
+ *\li Drafts: TBS
+ */
+
+#include <isc/lang.h>
+#include <isc/event.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/*%
+ * A 'dns_lookupevent_t' is returned when a lookup completes.
+ * The sender field will be set to the lookup that completed. If 'result'
+ * is ISC_R_SUCCESS, then 'names' will contain a list of names associated
+ * with the address. The recipient of the event must not change the list
+ * and must not refer to any of the name data after the event is freed.
+ */
+typedef struct dns_lookupevent {
+ ISC_EVENT_COMMON(struct dns_lookupevent);
+ isc_result_t result;
+ dns_name_t *name;
+ dns_rdataset_t *rdataset;
+ dns_rdataset_t *sigrdataset;
+ dns_db_t *db;
+ dns_dbnode_t *node;
+} dns_lookupevent_t;
+
+isc_result_t
+dns_lookup_create(isc_mem_t *mctx, dns_name_t *name, dns_rdatatype_t type,
+ dns_view_t *view, unsigned int options, isc_task_t *task,
+ isc_taskaction_t action, void *arg, dns_lookup_t **lookupp);
+/*%<
+ * Finds the rrsets matching 'name' and 'type'.
+ *
+ * Requires:
+ *
+ *\li 'mctx' is a valid mctx.
+ *
+ *\li 'name' is a valid name.
+ *
+ *\li 'view' is a valid view which has a resolver.
+ *
+ *\li 'task' is a valid task.
+ *
+ *\li lookupp != NULL && *lookupp == NULL
+ *
+ * Returns:
+ *
+ *\li ISC_R_SUCCESS
+ *\li ISC_R_NOMEMORY
+ *
+ *\li Any resolver-related error (e.g. ISC_R_SHUTTINGDOWN) may also be
+ * returned.
+ */
+
+void
+dns_lookup_cancel(dns_lookup_t *lookup);
+/*%<
+ * Cancel 'lookup'.
+ *
+ * Notes:
+ *
+ *\li If 'lookup' has not completed, post its LOOKUPDONE event with a
+ * result code of ISC_R_CANCELED.
+ *
+ * Requires:
+ *
+ *\li 'lookup' is a valid lookup.
+ */
+
+void
+dns_lookup_destroy(dns_lookup_t **lookupp);
+/*%<
+ * Destroy 'lookup'.
+ *
+ * Requires:
+ *
+ *\li '*lookupp' is a valid lookup.
+ *
+ *\li The caller has received the LOOKUPDONE event (either because the
+ * lookup completed or because dns_lookup_cancel() was called).
+ *
+ * Ensures:
+ *
+ *\li *lookupp == NULL.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_LOOKUP_H */
diff --git a/lib/dns/include/dns/master.h b/lib/dns/include/dns/master.h
new file mode 100644
index 0000000..93a782d
--- /dev/null
+++ b/lib/dns/include/dns/master.h
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: master.h,v 1.51 2008/04/02 02:37:42 marka Exp $ */
+
+#ifndef DNS_MASTER_H
+#define DNS_MASTER_H 1
+
+/*! \file dns/master.h */
+
+/***
+ *** Imports
+ ***/
+
+#include <stdio.h>
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+/*
+ * Flags to be passed in the 'options' argument in the functions below.
+ */
+#define DNS_MASTER_AGETTL 0x00000001 /*%< Age the ttl based on $DATE. */
+#define DNS_MASTER_MANYERRORS 0x00000002 /*%< Continue processing on errors. */
+#define DNS_MASTER_NOINCLUDE 0x00000004 /*%< Disallow $INCLUDE directives. */
+#define DNS_MASTER_ZONE 0x00000008 /*%< Loading a zone master file. */
+#define DNS_MASTER_HINT 0x00000010 /*%< Loading a hint master file. */
+#define DNS_MASTER_SLAVE 0x00000020 /*%< Loading a slave master file. */
+#define DNS_MASTER_CHECKNS 0x00000040 /*%<
+ * Check NS records to see
+ * if they are an address
+ */
+#define DNS_MASTER_FATALNS 0x00000080 /*%<
+ * Treat DNS_MASTER_CHECKNS
+ * matches as fatal
+ */
+#define DNS_MASTER_CHECKNAMES 0x00000100
+#define DNS_MASTER_CHECKNAMESFAIL 0x00000200
+#define DNS_MASTER_CHECKWILDCARD 0x00000400 /* Check for internal wildcards. */
+#define DNS_MASTER_CHECKMX 0x00000800
+#define DNS_MASTER_CHECKMXFAIL 0x00001000
+
+#define DNS_MASTER_RESIGN 0x00002000
+
+ISC_LANG_BEGINDECLS
+
+/*
+ * Structures that implement the "raw" format for master dump.
+ * These are provided for a reference purpose only; in the actual
+ * encoding, we directly read/write each field so that the encoded data
+ * is always "packed", regardless of the hardware architecture.
+ */
+#define DNS_RAWFORMAT_VERSION 0
+
+/* Common header */
+typedef struct {
+ isc_uint32_t format; /* must be
+ * dns_masterformat_raw */
+ isc_uint32_t version; /* compatibility for future
+ * extensions */
+ isc_uint32_t dumptime; /* timestamp on creation
+ * (currently unused)
+ */
+} dns_masterrawheader_t;
+
+/* The structure for each RRset */
+typedef struct {
+ isc_uint32_t totallen; /* length of the data for this
+ * RRset, including the
+ * "header" part */
+ dns_rdataclass_t rdclass; /* 16-bit class */
+ dns_rdatatype_t type; /* 16-bit type */
+ dns_rdatatype_t covers; /* same as type */
+ dns_ttl_t ttl; /* 32-bit TTL */
+ isc_uint32_t nrdata; /* number of RRs in this set */
+ /* followed by encoded owner name, and then rdata */
+} dns_masterrawrdataset_t;
+
+/***
+ *** Function
+ ***/
+
+isc_result_t
+dns_master_loadfile(const char *master_file,
+ dns_name_t *top,
+ dns_name_t *origin,
+ dns_rdataclass_t zclass,
+ unsigned int options,
+ dns_rdatacallbacks_t *callbacks,
+ isc_mem_t *mctx);
+
+isc_result_t
+dns_master_loadfile2(const char *master_file,
+ dns_name_t *top,
+ dns_name_t *origin,
+ dns_rdataclass_t zclass,
+ unsigned int options,
+ dns_rdatacallbacks_t *callbacks,
+ isc_mem_t *mctx,
+ dns_masterformat_t format);
+
+isc_result_t
+dns_master_loadfile3(const char *master_file,
+ dns_name_t *top,
+ dns_name_t *origin,
+ dns_rdataclass_t zclass,
+ unsigned int options,
+ isc_uint32_t resign,
+ dns_rdatacallbacks_t *callbacks,
+ isc_mem_t *mctx,
+ dns_masterformat_t format);
+
+isc_result_t
+dns_master_loadstream(FILE *stream,
+ dns_name_t *top,
+ dns_name_t *origin,
+ dns_rdataclass_t zclass,
+ unsigned int options,
+ dns_rdatacallbacks_t *callbacks,
+ isc_mem_t *mctx);
+
+isc_result_t
+dns_master_loadbuffer(isc_buffer_t *buffer,
+ dns_name_t *top,
+ dns_name_t *origin,
+ dns_rdataclass_t zclass,
+ unsigned int options,
+ dns_rdatacallbacks_t *callbacks,
+ isc_mem_t *mctx);
+
+isc_result_t
+dns_master_loadlexer(isc_lex_t *lex,
+ dns_name_t *top,
+ dns_name_t *origin,
+ dns_rdataclass_t zclass,
+ unsigned int options,
+ dns_rdatacallbacks_t *callbacks,
+ isc_mem_t *mctx);
+
+isc_result_t
+dns_master_loadfileinc(const char *master_file,
+ dns_name_t *top,
+ dns_name_t *origin,
+ dns_rdataclass_t zclass,
+ unsigned int options,
+ dns_rdatacallbacks_t *callbacks,
+ isc_task_t *task,
+ dns_loaddonefunc_t done, void *done_arg,
+ dns_loadctx_t **ctxp, isc_mem_t *mctx);
+
+isc_result_t
+dns_master_loadfileinc2(const char *master_file,
+ dns_name_t *top,
+ dns_name_t *origin,
+ dns_rdataclass_t zclass,
+ unsigned int options,
+ dns_rdatacallbacks_t *callbacks,
+ isc_task_t *task,
+ dns_loaddonefunc_t done, void *done_arg,
+ dns_loadctx_t **ctxp, isc_mem_t *mctx,
+ dns_masterformat_t format);
+
+isc_result_t
+dns_master_loadfileinc3(const char *master_file,
+ dns_name_t *top,
+ dns_name_t *origin,
+ dns_rdataclass_t zclass,
+ unsigned int options,
+ isc_uint32_t resign,
+ dns_rdatacallbacks_t *callbacks,
+ isc_task_t *task,
+ dns_loaddonefunc_t done, void *done_arg,
+ dns_loadctx_t **ctxp, isc_mem_t *mctx,
+ dns_masterformat_t format);
+
+isc_result_t
+dns_master_loadstreaminc(FILE *stream,
+ dns_name_t *top,
+ dns_name_t *origin,
+ dns_rdataclass_t zclass,
+ unsigned int options,
+ dns_rdatacallbacks_t *callbacks,
+ isc_task_t *task,
+ dns_loaddonefunc_t done, void *done_arg,
+ dns_loadctx_t **ctxp, isc_mem_t *mctx);
+
+isc_result_t
+dns_master_loadbufferinc(isc_buffer_t *buffer,
+ dns_name_t *top,
+ dns_name_t *origin,
+ dns_rdataclass_t zclass,
+ unsigned int options,
+ dns_rdatacallbacks_t *callbacks,
+ isc_task_t *task,
+ dns_loaddonefunc_t done, void *done_arg,
+ dns_loadctx_t **ctxp, isc_mem_t *mctx);
+
+isc_result_t
+dns_master_loadlexerinc(isc_lex_t *lex,
+ dns_name_t *top,
+ dns_name_t *origin,
+ dns_rdataclass_t zclass,
+ unsigned int options,
+ dns_rdatacallbacks_t *callbacks,
+ isc_task_t *task,
+ dns_loaddonefunc_t done, void *done_arg,
+ dns_loadctx_t **ctxp, isc_mem_t *mctx);
+
+/*%<
+ * Loads a RFC1305 master file from a file, stream, buffer, or existing
+ * lexer into rdatasets and then calls 'callbacks->commit' to commit the
+ * rdatasets. Rdata memory belongs to dns_master_load and will be
+ * reused / released when the callback completes. dns_load_master will
+ * abort if callbacks->commit returns any value other than ISC_R_SUCCESS.
+ *
+ * If 'DNS_MASTER_AGETTL' is set and the master file contains one or more
+ * $DATE directives, the TTLs of the data will be aged accordingly.
+ *
+ * 'callbacks->commit' is assumed to call 'callbacks->error' or
+ * 'callbacks->warn' to generate any error messages required.
+ *
+ * 'done' is called with 'done_arg' and a result code when the loading
+ * is completed or has failed. If the initial setup fails 'done' is
+ * not called.
+ *
+ * 'resign' the number of seconds before a RRSIG expires that it should
+ * be re-signed. 0 is used if not provided.
+ *
+ * Requires:
+ *\li 'master_file' points to a valid string.
+ *\li 'lexer' points to a valid lexer.
+ *\li 'top' points to a valid name.
+ *\li 'origin' points to a valid name.
+ *\li 'callbacks->commit' points to a valid function.
+ *\li 'callbacks->error' points to a valid function.
+ *\li 'callbacks->warn' points to a valid function.
+ *\li 'mctx' points to a valid memory context.
+ *\li 'task' and 'done' to be valid.
+ *\li 'lmgr' to be valid.
+ *\li 'ctxp != NULL && ctxp == NULL'.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS upon successfully loading the master file.
+ *\li ISC_R_SEENINCLUDE upon successfully loading the master file with
+ * a $INCLUDE statement.
+ *\li ISC_R_NOMEMORY out of memory.
+ *\li ISC_R_UNEXPECTEDEND expected to be able to read a input token and
+ * there was not one.
+ *\li ISC_R_UNEXPECTED
+ *\li DNS_R_NOOWNER failed to specify a ownername.
+ *\li DNS_R_NOTTL failed to specify a ttl.
+ *\li DNS_R_BADCLASS record class did not match zone class.
+ *\li DNS_R_CONTINUE load still in progress (dns_master_load*inc() only).
+ *\li Any dns_rdata_fromtext() error code.
+ *\li Any error code from callbacks->commit().
+ */
+
+void
+dns_loadctx_detach(dns_loadctx_t **ctxp);
+/*%<
+ * Detach from the load context.
+ *
+ * Requires:
+ *\li '*ctxp' to be valid.
+ *
+ * Ensures:
+ *\li '*ctxp == NULL'
+ */
+
+void
+dns_loadctx_attach(dns_loadctx_t *source, dns_loadctx_t **target);
+/*%<
+ * Attach to the load context.
+ *
+ * Requires:
+ *\li 'source' to be valid.
+ *\li 'target != NULL && *target == NULL'.
+ */
+
+void
+dns_loadctx_cancel(dns_loadctx_t *ctx);
+/*%<
+ * Cancel loading the zone file associated with this load context.
+ *
+ * Requires:
+ *\li 'ctx' to be valid
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_MASTER_H */
diff --git a/lib/dns/include/dns/masterdump.h b/lib/dns/include/dns/masterdump.h
new file mode 100644
index 0000000..42521b3
--- /dev/null
+++ b/lib/dns/include/dns/masterdump.h
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: masterdump.h,v 1.42 2008/09/24 02:46:23 marka Exp $ */
+
+#ifndef DNS_MASTERDUMP_H
+#define DNS_MASTERDUMP_H 1
+
+/*! \file dns/masterdump.h */
+
+/***
+ *** Imports
+ ***/
+
+#include <stdio.h>
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+/***
+ *** Types
+ ***/
+
+typedef struct dns_master_style dns_master_style_t;
+
+/***
+ *** Definitions
+ ***/
+
+/*
+ * Flags affecting master file formatting. Flags 0x0000FFFF
+ * define the formatting of the rdata part and are defined in
+ * rdata.h.
+ */
+
+/*% Omit the owner name when possible. */
+#define DNS_STYLEFLAG_OMIT_OWNER 0x00010000U
+
+/*%
+ * Omit the TTL when possible. If DNS_STYLEFLAG_TTL is
+ * also set, this means no TTLs are ever printed
+ * because $TTL directives are generated before every
+ * change in the TTL. In this case, no columns need to
+ * be reserved for the TTL. Master files generated with
+ * these options will be rejected by BIND 4.x because it
+ * does not recognize the $TTL directive.
+ *
+ * If DNS_STYLEFLAG_TTL is not also set, the TTL will be
+ * omitted when it is equal to the previous TTL.
+ * This is correct according to RFC1035, but the
+ * TTLs may be silently misinterpreted by older
+ * versions of BIND which use the SOA MINTTL as a
+ * default TTL value.
+ */
+#define DNS_STYLEFLAG_OMIT_TTL 0x00020000U
+
+/*% Omit the class when possible. */
+#define DNS_STYLEFLAG_OMIT_CLASS 0x00040000U
+
+/*% Output $TTL directives. */
+#define DNS_STYLEFLAG_TTL 0x00080000U
+
+/*%
+ * Output $ORIGIN directives and print owner names relative to
+ * the origin when possible.
+ */
+#define DNS_STYLEFLAG_REL_OWNER 0x00100000U
+
+/*% Print domain names in RR data in relative form when possible.
+ For this to take effect, DNS_STYLEFLAG_REL_OWNER must also be set. */
+#define DNS_STYLEFLAG_REL_DATA 0x00200000U
+
+/*% Print the trust level of each rdataset. */
+#define DNS_STYLEFLAG_TRUST 0x00400000U
+
+/*% Print negative caching entries. */
+#define DNS_STYLEFLAG_NCACHE 0x00800000U
+
+/*% Never print the TTL. */
+#define DNS_STYLEFLAG_NO_TTL 0x01000000U
+
+/*% Never print the CLASS. */
+#define DNS_STYLEFLAG_NO_CLASS 0x02000000U
+
+/*% Report re-signing time. */
+#define DNS_STYLEFLAG_RESIGN 0x04000000U
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Constants
+ ***/
+
+/*%
+ * The default master file style.
+ *
+ * This uses $TTL directives to avoid the need to dedicate a
+ * tab stop for the TTL. The class is only printed for the first
+ * rrset in the file and shares a tab stop with the RR type.
+ */
+LIBDNS_EXTERNAL_DATA extern const dns_master_style_t dns_master_style_default;
+
+/*%
+ * A master file style that dumps zones to a very generic format easily
+ * imported/checked with external tools.
+ */
+LIBDNS_EXTERNAL_DATA extern const dns_master_style_t dns_master_style_full;
+
+/*%
+ * A master file style that prints explicit TTL values on each
+ * record line, never using $TTL statements. The TTL has a tab
+ * stop of its own, but the class and type share one.
+ */
+LIBDNS_EXTERNAL_DATA extern const dns_master_style_t
+ dns_master_style_explicitttl;
+
+/*%
+ * A master style format designed for cache files. It prints explicit TTL
+ * values on each record line and never uses $ORIGIN or relative names.
+ */
+LIBDNS_EXTERNAL_DATA extern const dns_master_style_t dns_master_style_cache;
+
+/*%
+ * A master style that prints name, ttl, class, type, and value on
+ * every line. Similar to explicitttl above, but more verbose.
+ * Intended for generating master files which can be easily parsed
+ * by perl scripts and similar applications.
+ */
+LIBDNS_EXTERNAL_DATA extern const dns_master_style_t dns_master_style_simple;
+
+/*%
+ * The style used for debugging, "dig" output, etc.
+ */
+LIBDNS_EXTERNAL_DATA extern const dns_master_style_t dns_master_style_debug;
+
+/***
+ *** Functions
+ ***/
+
+void
+dns_dumpctx_attach(dns_dumpctx_t *source, dns_dumpctx_t **target);
+/*%<
+ * Attach to a dump context.
+ *
+ * Require:
+ *\li 'source' to be valid.
+ *\li 'target' to be non NULL and '*target' to be NULL.
+ */
+
+void
+dns_dumpctx_detach(dns_dumpctx_t **dctxp);
+/*%<
+ * Detach from a dump context.
+ *
+ * Require:
+ *\li 'dctxp' to point to a valid dump context.
+ *
+ * Ensures:
+ *\li '*dctxp' is NULL.
+ */
+
+void
+dns_dumpctx_cancel(dns_dumpctx_t *dctx);
+/*%<
+ * Cancel a in progress dump.
+ *
+ * Require:
+ *\li 'dctx' to be valid.
+ */
+
+dns_dbversion_t *
+dns_dumpctx_version(dns_dumpctx_t *dctx);
+/*%<
+ * Return the version handle (if any) of the database being dumped.
+ *
+ * Require:
+ *\li 'dctx' to be valid.
+ */
+
+dns_db_t *
+dns_dumpctx_db(dns_dumpctx_t *dctx);
+/*%<
+ * Return the database being dumped.
+ *
+ * Require:
+ *\li 'dctx' to be valid.
+ */
+
+
+/*@{*/
+isc_result_t
+dns_master_dumptostreaminc(isc_mem_t *mctx, dns_db_t *db,
+ dns_dbversion_t *version,
+ const dns_master_style_t *style, FILE *f,
+ isc_task_t *task, dns_dumpdonefunc_t done,
+ void *done_arg, dns_dumpctx_t **dctxp);
+
+isc_result_t
+dns_master_dumptostream(isc_mem_t *mctx, dns_db_t *db,
+ dns_dbversion_t *version,
+ const dns_master_style_t *style, FILE *f);
+
+isc_result_t
+dns_master_dumptostream2(isc_mem_t *mctx, dns_db_t *db,
+ dns_dbversion_t *version,
+ const dns_master_style_t *style,
+ dns_masterformat_t format, FILE *f);
+/*%<
+ * Dump the database 'db' to the steam 'f' in the specified format by
+ * 'format'. If the format is dns_masterformat_text (the RFC1035 format),
+ * 'style' specifies the file style (e.g., &dns_master_style_default).
+ *
+ * dns_master_dumptostream() is an old form of dns_master_dumptostream2(),
+ * which always specifies the dns_masterformat_text format.
+ *
+ * Temporary dynamic memory may be allocated from 'mctx'.
+ *
+ * Require:
+ *\li 'task' to be valid.
+ *\li 'done' to be non NULL.
+ *\li 'dctxp' to be non NULL && '*dctxp' to be NULL.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ *\li ISC_R_CONTINUE dns_master_dumptostreaminc() only.
+ *\li ISC_R_NOMEMORY
+ *\li Any database or rrset iterator error.
+ *\li Any dns_rdata_totext() error code.
+ */
+/*@}*/
+
+/*@{*/
+isc_result_t
+dns_master_dumpinc(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
+ const dns_master_style_t *style, const char *filename,
+ isc_task_t *task, dns_dumpdonefunc_t done, void *done_arg,
+ dns_dumpctx_t **dctxp);
+
+isc_result_t
+dns_master_dumpinc2(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
+ const dns_master_style_t *style, const char *filename,
+ isc_task_t *task, dns_dumpdonefunc_t done, void *done_arg, dns_dumpctx_t **dctxp, dns_masterformat_t format);
+
+isc_result_t
+dns_master_dump(isc_mem_t *mctx, dns_db_t *db,
+ dns_dbversion_t *version,
+ const dns_master_style_t *style, const char *filename);
+
+isc_result_t
+dns_master_dump2(isc_mem_t *mctx, dns_db_t *db,
+ dns_dbversion_t *version,
+ const dns_master_style_t *style, const char *filename,
+ dns_masterformat_t format);
+
+/*%<
+ * Dump the database 'db' to the file 'filename' in the specified format by
+ * 'format'. If the format is dns_masterformat_text (the RFC1035 format),
+ * 'style' specifies the file style (e.g., &dns_master_style_default).
+ *
+ * dns_master_dumpinc() and dns_master_dump() are old forms of _dumpinc2()
+ * and _dump2(), respectively, which always specify the dns_masterformat_text
+ * format.
+ *
+ * Temporary dynamic memory may be allocated from 'mctx'.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ *\li ISC_R_CONTINUE dns_master_dumpinc() only.
+ *\li ISC_R_NOMEMORY
+ *\li Any database or rrset iterator error.
+ *\li Any dns_rdata_totext() error code.
+ */
+/*@}*/
+
+isc_result_t
+dns_master_rdatasettotext(dns_name_t *owner_name,
+ dns_rdataset_t *rdataset,
+ const dns_master_style_t *style,
+ isc_buffer_t *target);
+/*%<
+ * Convert 'rdataset' to text format, storing the result in 'target'.
+ *
+ * Notes:
+ *\li The rdata cursor position will be changed.
+ *
+ * Requires:
+ *\li 'rdataset' is a valid non-question rdataset.
+ *
+ *\li 'rdataset' is not empty.
+ */
+
+isc_result_t
+dns_master_questiontotext(dns_name_t *owner_name,
+ dns_rdataset_t *rdataset,
+ const dns_master_style_t *style,
+ isc_buffer_t *target);
+
+isc_result_t
+dns_master_dumpnodetostream(isc_mem_t *mctx, dns_db_t *db,
+ dns_dbversion_t *version,
+ dns_dbnode_t *node, dns_name_t *name,
+ const dns_master_style_t *style,
+ FILE *f);
+
+isc_result_t
+dns_master_dumpnode(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
+ dns_dbnode_t *node, dns_name_t *name,
+ const dns_master_style_t *style, const char *filename);
+
+isc_result_t
+dns_master_stylecreate(dns_master_style_t **style, unsigned int flags,
+ unsigned int ttl_column, unsigned int class_column,
+ unsigned int type_column, unsigned int rdata_column,
+ unsigned int line_length, unsigned int tab_width,
+ isc_mem_t *mctx);
+
+void
+dns_master_styledestroy(dns_master_style_t **style, isc_mem_t *mctx);
+
+const char *
+dns_trust_totext(dns_trust_t trust);
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_MASTERDUMP_H */
diff --git a/lib/dns/include/dns/message.h b/lib/dns/include/dns/message.h
new file mode 100644
index 0000000..0ed2ef7
--- /dev/null
+++ b/lib/dns/include/dns/message.h
@@ -0,0 +1,1343 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: message.h,v 1.125 2008/04/03 06:09:04 tbox Exp $ */
+
+#ifndef DNS_MESSAGE_H
+#define DNS_MESSAGE_H 1
+
+/***
+ *** Imports
+ ***/
+
+#include <isc/lang.h>
+#include <isc/magic.h>
+
+#include <dns/compress.h>
+#include <dns/masterdump.h>
+#include <dns/types.h>
+
+#include <dst/dst.h>
+
+/*! \file dns/message.h
+ * \brief Message Handling Module
+ *
+ * How this beast works:
+ *
+ * When a dns message is received in a buffer, dns_message_fromwire() is called
+ * on the memory region. Various items are checked including the format
+ * of the message (if counts are right, if counts consume the entire sections,
+ * and if sections consume the entire message) and known pseudo-RRs in the
+ * additional data section are analyzed and removed.
+ *
+ * TSIG checking is also done at this layer, and any DNSSEC transaction
+ * signatures should also be checked here.
+ *
+ * Notes on using the gettemp*() and puttemp*() functions:
+ *
+ * These functions return items (names, rdatasets, etc) allocated from some
+ * internal state of the dns_message_t.
+ *
+ * Names and rdatasets must be put back into the dns_message_t in
+ * one of two ways. Assume a name was allocated via
+ * dns_message_gettempname():
+ *
+ *\li (1) insert it into a section, using dns_message_addname().
+ *
+ *\li (2) return it to the message using dns_message_puttempname().
+ *
+ * The same applies to rdatasets.
+ *
+ * On the other hand, offsets, rdatalists and rdatas allocated using
+ * dns_message_gettemp*() will always be freed automatically
+ * when the message is reset or destroyed; calling dns_message_puttemp*()
+ * on rdatalists and rdatas is optional and serves only to enable the item
+ * to be reused multiple times during the lifetime of the message; offsets
+ * cannot be reused.
+ *
+ * Buffers allocated using isc_buffer_allocate() can be automatically freed
+ * as well by giving the buffer to the message using dns_message_takebuffer().
+ * Doing this will cause the buffer to be freed using isc_buffer_free()
+ * when the section lists are cleared, such as in a reset or in a destroy.
+ * Since the buffer itself exists until the message is destroyed, this sort
+ * of code can be written:
+ *
+ * \code
+ * buffer = isc_buffer_allocate(mctx, 512);
+ * name = NULL;
+ * name = dns_message_gettempname(message, &name);
+ * dns_name_init(name, NULL);
+ * result = dns_name_fromtext(name, &source, dns_rootname, ISC_FALSE,
+ * buffer);
+ * dns_message_takebuffer(message, &buffer);
+ * \endcode
+ *
+ *
+ * TODO:
+ *
+ * XXX Needed: ways to set and retrieve EDNS information, add rdata to a
+ * section, move rdata from one section to another, remove rdata, etc.
+ */
+
+#define DNS_MESSAGEFLAG_QR 0x8000U
+#define DNS_MESSAGEFLAG_AA 0x0400U
+#define DNS_MESSAGEFLAG_TC 0x0200U
+#define DNS_MESSAGEFLAG_RD 0x0100U
+#define DNS_MESSAGEFLAG_RA 0x0080U
+#define DNS_MESSAGEFLAG_AD 0x0020U
+#define DNS_MESSAGEFLAG_CD 0x0010U
+
+/*%< EDNS0 extended message flags */
+#define DNS_MESSAGEEXTFLAG_DO 0x8000U
+
+/*%< EDNS0 extended OPT codes */
+#define DNS_OPT_NSID 0x0003 /*%< NSID opt code */
+
+#define DNS_MESSAGE_REPLYPRESERVE (DNS_MESSAGEFLAG_RD|DNS_MESSAGEFLAG_CD)
+#define DNS_MESSAGEEXTFLAG_REPLYPRESERVE (DNS_MESSAGEEXTFLAG_DO)
+
+#define DNS_MESSAGE_HEADERLEN 12 /*%< 6 isc_uint16_t's */
+
+#define DNS_MESSAGE_MAGIC ISC_MAGIC('M','S','G','@')
+#define DNS_MESSAGE_VALID(msg) ISC_MAGIC_VALID(msg, DNS_MESSAGE_MAGIC)
+
+/*
+ * Ordering here matters. DNS_SECTION_ANY must be the lowest and negative,
+ * and DNS_SECTION_MAX must be one greater than the last used section.
+ */
+typedef int dns_section_t;
+#define DNS_SECTION_ANY (-1)
+#define DNS_SECTION_QUESTION 0
+#define DNS_SECTION_ANSWER 1
+#define DNS_SECTION_AUTHORITY 2
+#define DNS_SECTION_ADDITIONAL 3
+#define DNS_SECTION_MAX 4
+
+typedef int dns_pseudosection_t;
+#define DNS_PSEUDOSECTION_ANY (-1)
+#define DNS_PSEUDOSECTION_OPT 0
+#define DNS_PSEUDOSECTION_TSIG 1
+#define DNS_PSEUDOSECTION_SIG0 2
+#define DNS_PSEUDOSECTION_MAX 3
+
+typedef int dns_messagetextflag_t;
+#define DNS_MESSAGETEXTFLAG_NOCOMMENTS 0x0001
+#define DNS_MESSAGETEXTFLAG_NOHEADERS 0x0002
+
+/*
+ * Dynamic update names for these sections.
+ */
+#define DNS_SECTION_ZONE DNS_SECTION_QUESTION
+#define DNS_SECTION_PREREQUISITE DNS_SECTION_ANSWER
+#define DNS_SECTION_UPDATE DNS_SECTION_AUTHORITY
+
+/*
+ * These tell the message library how the created dns_message_t will be used.
+ */
+#define DNS_MESSAGE_INTENTUNKNOWN 0 /*%< internal use only */
+#define DNS_MESSAGE_INTENTPARSE 1 /*%< parsing messages */
+#define DNS_MESSAGE_INTENTRENDER 2 /*%< rendering */
+
+/*
+ * Control behavior of parsing
+ */
+#define DNS_MESSAGEPARSE_PRESERVEORDER 0x0001 /*%< preserve rdata order */
+#define DNS_MESSAGEPARSE_BESTEFFORT 0x0002 /*%< return a message if a
+ recoverable parse error
+ occurs */
+#define DNS_MESSAGEPARSE_CLONEBUFFER 0x0004 /*%< save a copy of the
+ source buffer */
+#define DNS_MESSAGEPARSE_IGNORETRUNCATION 0x0008 /*%< trucation errors are
+ * not fatal. */
+
+/*
+ * Control behavior of rendering
+ */
+#define DNS_MESSAGERENDER_ORDERED 0x0001 /*%< don't change order */
+#define DNS_MESSAGERENDER_PARTIAL 0x0002 /*%< allow a partial rdataset */
+#define DNS_MESSAGERENDER_OMITDNSSEC 0x0004 /*%< omit DNSSEC records */
+#define DNS_MESSAGERENDER_PREFER_A 0x0008 /*%< prefer A records in
+ additional section. */
+#define DNS_MESSAGERENDER_PREFER_AAAA 0x0010 /*%< prefer AAAA records in
+ additional section. */
+
+typedef struct dns_msgblock dns_msgblock_t;
+
+struct dns_message {
+ /* public from here down */
+ unsigned int magic;
+
+ dns_messageid_t id;
+ unsigned int flags;
+ dns_rcode_t rcode;
+ unsigned int opcode;
+ dns_rdataclass_t rdclass;
+
+ /* 4 real, 1 pseudo */
+ unsigned int counts[DNS_SECTION_MAX];
+
+ /* private from here down */
+ dns_namelist_t sections[DNS_SECTION_MAX];
+ dns_name_t *cursors[DNS_SECTION_MAX];
+ dns_rdataset_t *opt;
+ dns_rdataset_t *sig0;
+ dns_rdataset_t *tsig;
+
+ int state;
+ unsigned int from_to_wire : 2;
+ unsigned int header_ok : 1;
+ unsigned int question_ok : 1;
+ unsigned int tcp_continuation : 1;
+ unsigned int verified_sig : 1;
+ unsigned int verify_attempted : 1;
+ unsigned int free_query : 1;
+ unsigned int free_saved : 1;
+
+ unsigned int opt_reserved;
+ unsigned int sig_reserved;
+ unsigned int reserved; /* reserved space (render) */
+
+ isc_buffer_t *buffer;
+ dns_compress_t *cctx;
+
+ isc_mem_t *mctx;
+ isc_mempool_t *namepool;
+ isc_mempool_t *rdspool;
+
+ isc_bufferlist_t scratchpad;
+ isc_bufferlist_t cleanup;
+
+ ISC_LIST(dns_msgblock_t) rdatas;
+ ISC_LIST(dns_msgblock_t) rdatalists;
+ ISC_LIST(dns_msgblock_t) offsets;
+
+ ISC_LIST(dns_rdata_t) freerdata;
+ ISC_LIST(dns_rdatalist_t) freerdatalist;
+
+ dns_rcode_t tsigstatus;
+ dns_rcode_t querytsigstatus;
+ dns_name_t *tsigname; /* Owner name of TSIG, if any */
+ dns_rdataset_t *querytsig;
+ dns_tsigkey_t *tsigkey;
+ dst_context_t *tsigctx;
+ int sigstart;
+ int timeadjust;
+
+ dns_name_t *sig0name; /* Owner name of SIG0, if any */
+ dst_key_t *sig0key;
+ dns_rcode_t sig0status;
+ isc_region_t query;
+ isc_region_t saved;
+
+ dns_rdatasetorderfunc_t order;
+ const void * order_arg;
+};
+
+/***
+ *** Functions
+ ***/
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_message_create(isc_mem_t *mctx, unsigned int intent, dns_message_t **msgp);
+
+/*%<
+ * Create msg structure.
+ *
+ * This function will allocate some internal blocks of memory that are
+ * expected to be needed for parsing or rendering nearly any type of message.
+ *
+ * Requires:
+ *\li 'mctx' be a valid memory context.
+ *
+ *\li 'msgp' be non-null and '*msg' be NULL.
+ *
+ *\li 'intent' must be one of DNS_MESSAGE_INTENTPARSE or
+ * #DNS_MESSAGE_INTENTRENDER.
+ *
+ * Ensures:
+ *\li The data in "*msg" is set to indicate an unused and empty msg
+ * structure.
+ *
+ * Returns:
+ *\li #ISC_R_NOMEMORY -- out of memory
+ *\li #ISC_R_SUCCESS -- success
+ */
+
+void
+dns_message_reset(dns_message_t *msg, unsigned int intent);
+/*%<
+ * Reset a message structure to default state. All internal lists are freed
+ * or reset to a default state as well. This is simply a more efficient
+ * way to call dns_message_destroy() followed by dns_message_allocate(),
+ * since it avoid many memory allocations.
+ *
+ * If any data loanouts (buffers, names, rdatas, etc) were requested,
+ * the caller must no longer use them after this call.
+ *
+ * The intended next use of the message will be 'intent'.
+ *
+ * Requires:
+ *
+ *\li 'msg' be valid.
+ *
+ *\li 'intent' is DNS_MESSAGE_INTENTPARSE or DNS_MESSAGE_INTENTRENDER
+ */
+
+void
+dns_message_destroy(dns_message_t **msgp);
+/*%<
+ * Destroy all state in the message.
+ *
+ * Requires:
+ *
+ *\li 'msgp' be valid.
+ *
+ * Ensures:
+ *\li '*msgp' == NULL
+ */
+
+isc_result_t
+dns_message_sectiontotext(dns_message_t *msg, dns_section_t section,
+ const dns_master_style_t *style,
+ dns_messagetextflag_t flags,
+ isc_buffer_t *target);
+
+isc_result_t
+dns_message_pseudosectiontotext(dns_message_t *msg,
+ dns_pseudosection_t section,
+ const dns_master_style_t *style,
+ dns_messagetextflag_t flags,
+ isc_buffer_t *target);
+/*%<
+ * Convert section 'section' or 'pseudosection' of message 'msg' to
+ * a cleartext representation
+ *
+ * Notes:
+ * \li See dns_message_totext for meanings of flags.
+ *
+ * Requires:
+ *
+ *\li 'msg' is a valid message.
+ *
+ *\li 'style' is a valid master dump style.
+ *
+ *\li 'target' is a valid buffer.
+ *
+ *\li 'section' is a valid section label.
+ *
+ * Ensures:
+ *
+ *\li If the result is success:
+ * The used space in 'target' is updated.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOSPACE
+ *\li #ISC_R_NOMORE
+ *
+ *\li Note: On error return, *target may be partially filled with data.
+*/
+
+isc_result_t
+dns_message_totext(dns_message_t *msg, const dns_master_style_t *style,
+ dns_messagetextflag_t flags, isc_buffer_t *target);
+/*%<
+ * Convert all sections of message 'msg' to a cleartext representation
+ *
+ * Notes:
+ * \li In flags, If #DNS_MESSAGETEXTFLAG_OMITDOT is set, then the
+ * final '.' in absolute names will not be emitted. If
+ * #DNS_MESSAGETEXTFLAG_NOCOMMENTS is cleared, lines beginning
+ * with ";;" will be emitted indicating section name. If
+ * #DNS_MESSAGETEXTFLAG_NOHEADERS is cleared, header lines will
+ * be emitted.
+ *
+ * Requires:
+ *
+ *\li 'msg' is a valid message.
+ *
+ *\li 'style' is a valid master dump style.
+ *
+ *\li 'target' is a valid buffer.
+ *
+ * Ensures:
+ *
+ *\li If the result is success:
+ * The used space in 'target' is updated.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOSPACE
+ *\li #ISC_R_NOMORE
+ *
+ *\li Note: On error return, *target may be partially filled with data.
+ */
+
+isc_result_t
+dns_message_parse(dns_message_t *msg, isc_buffer_t *source,
+ unsigned int options);
+/*%<
+ * Parse raw wire data in 'source' as a DNS message.
+ *
+ * OPT records are detected and stored in the pseudo-section "opt".
+ * TSIGs are detected and stored in the pseudo-section "tsig".
+ *
+ * If #DNS_MESSAGEPARSE_PRESERVEORDER is set, or if the opcode of the message
+ * is UPDATE, a separate dns_name_t object will be created for each RR in the
+ * message. Each such dns_name_t will have a single rdataset containing the
+ * single RR, and the order of the RRs in the message is preserved.
+ * Otherwise, only one dns_name_t object will be created for each unique
+ * owner name in the section, and each such dns_name_t will have a list
+ * of rdatasets. To access the names and their data, use
+ * dns_message_firstname() and dns_message_nextname().
+ *
+ * If #DNS_MESSAGEPARSE_BESTEFFORT is set, errors in message content will
+ * not be considered FORMERRs. If the entire message can be parsed, it
+ * will be returned and DNS_R_RECOVERABLE will be returned.
+ *
+ * If #DNS_MESSAGEPARSE_IGNORETRUNCATION is set then return as many complete
+ * RR's as possible, DNS_R_RECOVERABLE will be returned.
+ *
+ * OPT and TSIG records are always handled specially, regardless of the
+ * 'preserve_order' setting.
+ *
+ * Requires:
+ *\li "msg" be valid.
+ *
+ *\li "buffer" be a wire format buffer.
+ *
+ * Ensures:
+ *\li The buffer's data format is correct.
+ *
+ *\li The buffer's contents verify as correct regarding header bits, buffer
+ * and rdata sizes, etc.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS -- all is well
+ *\li #ISC_R_NOMEMORY -- no memory
+ *\li #DNS_R_RECOVERABLE -- the message parsed properly, but contained
+ * errors.
+ *\li Many other errors possible XXXMLG
+ */
+
+isc_result_t
+dns_message_renderbegin(dns_message_t *msg, dns_compress_t *cctx,
+ isc_buffer_t *buffer);
+/*%<
+ * Begin rendering on a message. Only one call can be made to this function
+ * per message.
+ *
+ * The compression context is "owned" by the message library until
+ * dns_message_renderend() is called. It must be invalidated by the caller.
+ *
+ * The buffer is "owned" by the message library until dns_message_renderend()
+ * is called.
+ *
+ * Requires:
+ *
+ *\li 'msg' be valid.
+ *
+ *\li 'cctx' be valid.
+ *
+ *\li 'buffer' is a valid buffer.
+ *
+ * Side Effects:
+ *
+ *\li The buffer is cleared before it is used.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS -- all is well
+ *\li #ISC_R_NOSPACE -- output buffer is too small
+ */
+
+isc_result_t
+dns_message_renderchangebuffer(dns_message_t *msg, isc_buffer_t *buffer);
+/*%<
+ * Reset the buffer. This can be used after growing the old buffer
+ * on a ISC_R_NOSPACE return from most of the render functions.
+ *
+ * On successful completion, the old buffer is no longer used by the
+ * library. The new buffer is owned by the library until
+ * dns_message_renderend() is called.
+ *
+ * Requires:
+ *
+ *\li 'msg' be valid.
+ *
+ *\li dns_message_renderbegin() was called.
+ *
+ *\li buffer != NULL.
+ *
+ * Returns:
+ *\li #ISC_R_NOSPACE -- new buffer is too small
+ *\li #ISC_R_SUCCESS -- all is well.
+ */
+
+isc_result_t
+dns_message_renderreserve(dns_message_t *msg, unsigned int space);
+/*%<
+ * XXXMLG should use size_t rather than unsigned int once the buffer
+ * API is cleaned up
+ *
+ * Reserve "space" bytes in the given buffer.
+ *
+ * Requires:
+ *
+ *\li 'msg' be valid.
+ *
+ *\li dns_message_renderbegin() was called.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS -- all is well.
+ *\li #ISC_R_NOSPACE -- not enough free space in the buffer.
+ */
+
+void
+dns_message_renderrelease(dns_message_t *msg, unsigned int space);
+/*%<
+ * XXXMLG should use size_t rather than unsigned int once the buffer
+ * API is cleaned up
+ *
+ * Release "space" bytes in the given buffer that was previously reserved.
+ *
+ * Requires:
+ *
+ *\li 'msg' be valid.
+ *
+ *\li 'space' is less than or equal to the total amount of space reserved
+ * via prior calls to dns_message_renderreserve().
+ *
+ *\li dns_message_renderbegin() was called.
+ */
+
+isc_result_t
+dns_message_rendersection(dns_message_t *msg, dns_section_t section,
+ unsigned int options);
+/*%<
+ * Render all names, rdatalists, etc from the given section at the
+ * specified priority or higher.
+ *
+ * Requires:
+ *\li 'msg' be valid.
+ *
+ *\li 'section' be a valid section.
+ *
+ *\li dns_message_renderbegin() was called.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS -- all records were written, and there are
+ * no more records for this section.
+ *\li #ISC_R_NOSPACE -- Not enough room in the buffer to write
+ * all records requested.
+ *\li #DNS_R_MOREDATA -- All requested records written, and there
+ * are records remaining for this section.
+ */
+
+void
+dns_message_renderheader(dns_message_t *msg, isc_buffer_t *target);
+/*%<
+ * Render the message header. This is implicitly called by
+ * dns_message_renderend().
+ *
+ * Requires:
+ *
+ *\li 'msg' be a valid message.
+ *
+ *\li dns_message_renderbegin() was called.
+ *
+ *\li 'target' is a valid buffer with enough space to hold a message header
+ */
+
+isc_result_t
+dns_message_renderend(dns_message_t *msg);
+/*%<
+ * Finish rendering to the buffer. Note that more data can be in the
+ * 'msg' structure. Destroying the structure will free this, or in a multi-
+ * part EDNS1 message this data can be rendered to another buffer later.
+ *
+ * Requires:
+ *
+ *\li 'msg' be a valid message.
+ *
+ *\li dns_message_renderbegin() was called.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS -- all is well.
+ */
+
+void
+dns_message_renderreset(dns_message_t *msg);
+/*%<
+ * Reset the message so that it may be rendered again.
+ *
+ * Notes:
+ *
+ *\li If dns_message_renderbegin() has been called, dns_message_renderend()
+ * must be called before calling this function.
+ *
+ * Requires:
+ *
+ *\li 'msg' be a valid message with rendering intent.
+ */
+
+isc_result_t
+dns_message_firstname(dns_message_t *msg, dns_section_t section);
+/*%<
+ * Set internal per-section name pointer to the beginning of the section.
+ *
+ * The functions dns_message_firstname() and dns_message_nextname() may
+ * be used for iterating over the owner names in a section.
+ *
+ * Requires:
+ *
+ *\li 'msg' be valid.
+ *
+ *\li 'section' be a valid section.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS -- All is well.
+ *\li #ISC_R_NOMORE -- No names on given section.
+ */
+
+isc_result_t
+dns_message_nextname(dns_message_t *msg, dns_section_t section);
+/*%<
+ * Sets the internal per-section name pointer to point to the next name
+ * in that section.
+ *
+ * Requires:
+ *
+ * \li 'msg' be valid.
+ *
+ *\li 'section' be a valid section.
+ *
+ *\li dns_message_firstname() must have been called on this section,
+ * and the result was ISC_R_SUCCESS.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS -- All is well.
+ *\li #ISC_R_NOMORE -- No more names in given section.
+ */
+
+void
+dns_message_currentname(dns_message_t *msg, dns_section_t section,
+ dns_name_t **name);
+/*%<
+ * Sets 'name' to point to the name where the per-section internal name
+ * pointer is currently set.
+ *
+ * This function returns the name in the database, so any data associated
+ * with it (via the name's "list" member) contains the actual rdatasets.
+ *
+ * Requires:
+ *
+ *\li 'msg' be valid.
+ *
+ *\li 'name' be non-NULL, and *name be NULL.
+ *
+ *\li 'section' be a valid section.
+ *
+ *\li dns_message_firstname() must have been called on this section,
+ * and the result of it and any dns_message_nextname() calls was
+ * #ISC_R_SUCCESS.
+ */
+
+isc_result_t
+dns_message_findname(dns_message_t *msg, dns_section_t section,
+ dns_name_t *target, dns_rdatatype_t type,
+ dns_rdatatype_t covers, dns_name_t **foundname,
+ dns_rdataset_t **rdataset);
+/*%<
+ * Search for a name in the specified section. If it is found, *name is
+ * set to point to the name, and *rdataset is set to point to the found
+ * rdataset (if type is specified as other than dns_rdatatype_any).
+ *
+ * Requires:
+ *\li 'msg' be valid.
+ *
+ *\li 'section' be a valid section.
+ *
+ *\li If a pointer to the name is desired, 'foundname' should be non-NULL.
+ * If it is non-NULL, '*foundname' MUST be NULL.
+ *
+ *\li If a type other than dns_datatype_any is searched for, 'rdataset'
+ * may be non-NULL, '*rdataset' be NULL, and will point at the found
+ * rdataset. If the type is dns_datatype_any, 'rdataset' must be NULL.
+ *
+ *\li 'target' be a valid name.
+ *
+ *\li 'type' be a valid type.
+ *
+ *\li If 'type' is dns_rdatatype_rrsig, 'covers' must be a valid type.
+ * Otherwise it should be 0.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS -- all is well.
+ *\li #DNS_R_NXDOMAIN -- name does not exist in that section.
+ *\li #DNS_R_NXRRSET -- The name does exist, but the desired
+ * type does not.
+ */
+
+isc_result_t
+dns_message_findtype(dns_name_t *name, dns_rdatatype_t type,
+ dns_rdatatype_t covers, dns_rdataset_t **rdataset);
+/*%<
+ * Search the name for the specified type. If it is found, *rdataset is
+ * filled in with a pointer to that rdataset.
+ *
+ * Requires:
+ *\li if '**rdataset' is non-NULL, *rdataset needs to be NULL.
+ *
+ *\li 'type' be a valid type, and NOT dns_rdatatype_any.
+ *
+ *\li If 'type' is dns_rdatatype_rrsig, 'covers' must be a valid type.
+ * Otherwise it should be 0.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS -- all is well.
+ *\li #ISC_R_NOTFOUND -- the desired type does not exist.
+ */
+
+isc_result_t
+dns_message_find(dns_name_t *name, dns_rdataclass_t rdclass,
+ dns_rdatatype_t type, dns_rdatatype_t covers,
+ dns_rdataset_t **rdataset);
+/*%<
+ * Search the name for the specified rdclass and type. If it is found,
+ * *rdataset is filled in with a pointer to that rdataset.
+ *
+ * Requires:
+ *\li if '**rdataset' is non-NULL, *rdataset needs to be NULL.
+ *
+ *\li 'type' be a valid type, and NOT dns_rdatatype_any.
+ *
+ *\li If 'type' is dns_rdatatype_rrsig, 'covers' must be a valid type.
+ * Otherwise it should be 0.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS -- all is well.
+ *\li #ISC_R_NOTFOUND -- the desired type does not exist.
+ */
+
+void
+dns_message_movename(dns_message_t *msg, dns_name_t *name,
+ dns_section_t fromsection,
+ dns_section_t tosection);
+/*%<
+ * Move a name from one section to another.
+ *
+ * Requires:
+ *
+ *\li 'msg' be valid.
+ *
+ *\li 'name' must be a name already in 'fromsection'.
+ *
+ *\li 'fromsection' must be a valid section.
+ *
+ *\li 'tosection' must be a valid section.
+ */
+
+void
+dns_message_addname(dns_message_t *msg, dns_name_t *name,
+ dns_section_t section);
+/*%<
+ * Adds the name to the given section.
+ *
+ * It is the caller's responsibility to enforce any unique name requirements
+ * in a section.
+ *
+ * Requires:
+ *
+ *\li 'msg' be valid, and be a renderable message.
+ *
+ *\li 'name' be a valid absolute name.
+ *
+ *\li 'section' be a named section.
+ */
+
+void
+dns_message_removename(dns_message_t *msg, dns_name_t *name,
+ dns_section_t section);
+/*%<
+ * Remove a existing name from a given section.
+ *
+ * It is the caller's responsibility to ensure the name is part of the
+ * given section.
+ *
+ * Requires:
+ *
+ *\li 'msg' be valid, and be a renderable message.
+ *
+ *\li 'name' be a valid absolute name.
+ *
+ *\li 'section' be a named section.
+ */
+
+
+/*
+ * LOANOUT FUNCTIONS
+ *
+ * Each of these functions loan a particular type of data to the caller.
+ * The storage for these will vanish when the message is destroyed or
+ * reset, and must NOT be used after these operations.
+ */
+
+isc_result_t
+dns_message_gettempname(dns_message_t *msg, dns_name_t **item);
+/*%<
+ * Return a name that can be used for any temporary purpose, including
+ * inserting into the message's linked lists. The name must be returned
+ * to the message code using dns_message_puttempname() or inserted into
+ * one of the message's sections before the message is destroyed.
+ *
+ * It is the caller's responsibility to initialize this name.
+ *
+ * Requires:
+ *\li msg be a valid message
+ *
+ *\li item != NULL && *item == NULL
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS -- All is well.
+ *\li #ISC_R_NOMEMORY -- No item can be allocated.
+ */
+
+isc_result_t
+dns_message_gettempoffsets(dns_message_t *msg, dns_offsets_t **item);
+/*%<
+ * Return an offsets array that can be used for any temporary purpose,
+ * such as attaching to a temporary name. The offsets will be freed
+ * when the message is destroyed or reset.
+ *
+ * Requires:
+ *\li msg be a valid message
+ *
+ *\li item != NULL && *item == NULL
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS -- All is well.
+ *\li #ISC_R_NOMEMORY -- No item can be allocated.
+ */
+
+isc_result_t
+dns_message_gettemprdata(dns_message_t *msg, dns_rdata_t **item);
+/*%<
+ * Return a rdata that can be used for any temporary purpose, including
+ * inserting into the message's linked lists. The rdata will be freed
+ * when the message is destroyed or reset.
+ *
+ * Requires:
+ *\li msg be a valid message
+ *
+ *\li item != NULL && *item == NULL
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS -- All is well.
+ *\li #ISC_R_NOMEMORY -- No item can be allocated.
+ */
+
+isc_result_t
+dns_message_gettemprdataset(dns_message_t *msg, dns_rdataset_t **item);
+/*%<
+ * Return a rdataset that can be used for any temporary purpose, including
+ * inserting into the message's linked lists. The name must be returned
+ * to the message code using dns_message_puttempname() or inserted into
+ * one of the message's sections before the message is destroyed.
+ *
+ * Requires:
+ *\li msg be a valid message
+ *
+ *\li item != NULL && *item == NULL
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS -- All is well.
+ *\li #ISC_R_NOMEMORY -- No item can be allocated.
+ */
+
+isc_result_t
+dns_message_gettemprdatalist(dns_message_t *msg, dns_rdatalist_t **item);
+/*%<
+ * Return a rdatalist that can be used for any temporary purpose, including
+ * inserting into the message's linked lists. The rdatalist will be
+ * destroyed when the message is destroyed or reset.
+ *
+ * Requires:
+ *\li msg be a valid message
+ *
+ *\li item != NULL && *item == NULL
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS -- All is well.
+ *\li #ISC_R_NOMEMORY -- No item can be allocated.
+ */
+
+void
+dns_message_puttempname(dns_message_t *msg, dns_name_t **item);
+/*%<
+ * Return a borrowed name to the message's name free list.
+ *
+ * Requires:
+ *\li msg be a valid message
+ *
+ *\li item != NULL && *item point to a name returned by
+ * dns_message_gettempname()
+ *
+ * Ensures:
+ *\li *item == NULL
+ */
+
+void
+dns_message_puttemprdata(dns_message_t *msg, dns_rdata_t **item);
+/*%<
+ * Return a borrowed rdata to the message's rdata free list.
+ *
+ * Requires:
+ *\li msg be a valid message
+ *
+ *\li item != NULL && *item point to a rdata returned by
+ * dns_message_gettemprdata()
+ *
+ * Ensures:
+ *\li *item == NULL
+ */
+
+void
+dns_message_puttemprdataset(dns_message_t *msg, dns_rdataset_t **item);
+/*%<
+ * Return a borrowed rdataset to the message's rdataset free list.
+ *
+ * Requires:
+ *\li msg be a valid message
+ *
+ *\li item != NULL && *item point to a rdataset returned by
+ * dns_message_gettemprdataset()
+ *
+ * Ensures:
+ *\li *item == NULL
+ */
+
+void
+dns_message_puttemprdatalist(dns_message_t *msg, dns_rdatalist_t **item);
+/*%<
+ * Return a borrowed rdatalist to the message's rdatalist free list.
+ *
+ * Requires:
+ *\li msg be a valid message
+ *
+ *\li item != NULL && *item point to a rdatalist returned by
+ * dns_message_gettemprdatalist()
+ *
+ * Ensures:
+ *\li *item == NULL
+ */
+
+isc_result_t
+dns_message_peekheader(isc_buffer_t *source, dns_messageid_t *idp,
+ unsigned int *flagsp);
+/*%<
+ * Assume the remaining region of "source" is a DNS message. Peek into
+ * it and fill in "*idp" with the message id, and "*flagsp" with the flags.
+ *
+ * Requires:
+ *
+ *\li source != NULL
+ *
+ * Ensures:
+ *
+ *\li if (idp != NULL) *idp == message id.
+ *
+ *\li if (flagsp != NULL) *flagsp == message flags.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS -- all is well.
+ *
+ *\li #ISC_R_UNEXPECTEDEND -- buffer doesn't contain enough for a header.
+ */
+
+isc_result_t
+dns_message_reply(dns_message_t *msg, isc_boolean_t want_question_section);
+/*%<
+ * Start formatting a reply to the query in 'msg'.
+ *
+ * Requires:
+ *
+ *\li 'msg' is a valid message with parsing intent, and contains a query.
+ *
+ * Ensures:
+ *
+ *\li The message will have a rendering intent. If 'want_question_section'
+ * is true, the message opcode is query or notify, and the question
+ * section is present and properly formatted, then the question section
+ * will be included in the reply. All other sections will be cleared.
+ * The QR flag will be set, the RD flag will be preserved, and all other
+ * flags will be cleared.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS -- all is well.
+ *
+ *\li #DNS_R_FORMERR -- the header or question section of the
+ * message is invalid, replying is impossible.
+ * If DNS_R_FORMERR is returned when
+ * want_question_section is ISC_FALSE, then
+ * it's the header section that's bad;
+ * otherwise either of the header or question
+ * sections may be bad.
+ */
+
+dns_rdataset_t *
+dns_message_getopt(dns_message_t *msg);
+/*%<
+ * Get the OPT record for 'msg'.
+ *
+ * Requires:
+ *
+ *\li 'msg' is a valid message.
+ *
+ * Returns:
+ *
+ *\li The OPT rdataset of 'msg', or NULL if there isn't one.
+ */
+
+isc_result_t
+dns_message_setopt(dns_message_t *msg, dns_rdataset_t *opt);
+/*%<
+ * Set the OPT record for 'msg'.
+ *
+ * Requires:
+ *
+ *\li 'msg' is a valid message with rendering intent
+ * and no sections have been rendered.
+ *
+ *\li 'opt' is a valid OPT record.
+ *
+ * Ensures:
+ *
+ *\li The OPT record has either been freed or ownership of it has
+ * been transferred to the message.
+ *
+ *\li If ISC_R_SUCCESS was returned, the OPT record will be rendered
+ * when dns_message_renderend() is called.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS -- all is well.
+ *
+ *\li #ISC_R_NOSPACE -- there is no space for the OPT record.
+ */
+
+dns_rdataset_t *
+dns_message_gettsig(dns_message_t *msg, dns_name_t **owner);
+/*%<
+ * Get the TSIG record and owner for 'msg'.
+ *
+ * Requires:
+ *
+ *\li 'msg' is a valid message.
+ *\li 'owner' is NULL or *owner is NULL.
+ *
+ * Returns:
+ *
+ *\li The TSIG rdataset of 'msg', or NULL if there isn't one.
+ *
+ * Ensures:
+ *
+ * \li If 'owner' is not NULL, it will point to the owner name.
+ */
+
+isc_result_t
+dns_message_settsigkey(dns_message_t *msg, dns_tsigkey_t *key);
+/*%<
+ * Set the tsig key for 'msg'. This is only necessary for when rendering a
+ * query or parsing a response. The key (if non-NULL) is attached to, and
+ * will be detached when the message is destroyed.
+ *
+ * Requires:
+ *
+ *\li 'msg' is a valid message with rendering intent,
+ * dns_message_renderbegin() has been called, and no sections have been
+ * rendered.
+ *\li 'key' is a valid tsig key or NULL.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS -- all is well.
+ *
+ *\li #ISC_R_NOSPACE -- there is no space for the TSIG record.
+ */
+
+dns_tsigkey_t *
+dns_message_gettsigkey(dns_message_t *msg);
+/*%<
+ * Gets the tsig key for 'msg'.
+ *
+ * Requires:
+ *
+ *\li 'msg' is a valid message
+ */
+
+isc_result_t
+dns_message_setquerytsig(dns_message_t *msg, isc_buffer_t *querytsig);
+/*%<
+ * Indicates that 'querytsig' is the TSIG from the signed query for which
+ * 'msg' is the response. This is also used for chained TSIGs in TCP
+ * responses.
+ *
+ * Requires:
+ *
+ *\li 'querytsig' is a valid buffer as returned by dns_message_getquerytsig()
+ * or NULL
+ *
+ *\li 'msg' is a valid message
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ */
+
+isc_result_t
+dns_message_getquerytsig(dns_message_t *msg, isc_mem_t *mctx,
+ isc_buffer_t **querytsig);
+/*%<
+ * Gets the tsig from the TSIG from the signed query 'msg'. This is also used
+ * for chained TSIGs in TCP responses. Unlike dns_message_gettsig, this makes
+ * a copy of the data, so can be used if the message is destroyed.
+ *
+ * Requires:
+ *
+ *\li 'msg' is a valid signed message
+ *\li 'mctx' is a valid memory context
+ *\li querytsig != NULL && *querytsig == NULL
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *
+ * Ensures:
+ *\li 'tsig' points to NULL or an allocated buffer which must be freed
+ * by the caller.
+ */
+
+dns_rdataset_t *
+dns_message_getsig0(dns_message_t *msg, dns_name_t **owner);
+/*%<
+ * Get the SIG(0) record and owner for 'msg'.
+ *
+ * Requires:
+ *
+ *\li 'msg' is a valid message.
+ *\li 'owner' is NULL or *owner is NULL.
+ *
+ * Returns:
+ *
+ *\li The SIG(0) rdataset of 'msg', or NULL if there isn't one.
+ *
+ * Ensures:
+ *
+ * \li If 'owner' is not NULL, it will point to the owner name.
+ */
+
+isc_result_t
+dns_message_setsig0key(dns_message_t *msg, dst_key_t *key);
+/*%<
+ * Set the SIG(0) key for 'msg'.
+ *
+ * Requires:
+ *
+ *\li 'msg' is a valid message with rendering intent,
+ * dns_message_renderbegin() has been called, and no sections have been
+ * rendered.
+ *\li 'key' is a valid sig key or NULL.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS -- all is well.
+ *
+ *\li #ISC_R_NOSPACE -- there is no space for the SIG(0) record.
+ */
+
+dst_key_t *
+dns_message_getsig0key(dns_message_t *msg);
+/*%<
+ * Gets the SIG(0) key for 'msg'.
+ *
+ * Requires:
+ *
+ *\li 'msg' is a valid message
+ */
+
+void
+dns_message_takebuffer(dns_message_t *msg, isc_buffer_t **buffer);
+/*%<
+ * Give the *buffer to the message code to clean up when it is no
+ * longer needed. This is usually when the message is reset or
+ * destroyed.
+ *
+ * Requires:
+ *
+ *\li msg be a valid message.
+ *
+ *\li buffer != NULL && *buffer is a valid isc_buffer_t, which was
+ * dynamincally allocated via isc_buffer_allocate().
+ */
+
+isc_result_t
+dns_message_signer(dns_message_t *msg, dns_name_t *signer);
+/*%<
+ * If this message was signed, return the identity of the signer.
+ * Unless ISC_R_NOTFOUND is returned, signer will reflect the name of the
+ * key that signed the message.
+ *
+ * Requires:
+ *
+ *\li msg is a valid parsed message.
+ *\li signer is a valid name
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS - the message was signed, and *signer
+ * contains the signing identity
+ *
+ *\li #ISC_R_NOTFOUND - no TSIG or SIG(0) record is present in the
+ * message
+ *
+ *\li #DNS_R_TSIGVERIFYFAILURE - the message was signed by a TSIG, but the
+ * signature failed to verify
+ *
+ *\li #DNS_R_TSIGERRORSET - the message was signed by a TSIG and
+ * verified, but the query was rejected by
+ * the server
+ *
+ *\li #DNS_R_NOIDENTITY - the message was signed by a TSIG and
+ * verified, but the key has no identity since
+ * it was generated by an unsigned TKEY process
+ *
+ *\li #DNS_R_SIGINVALID - the message was signed by a SIG(0), but
+ * the signature failed to verify
+ *
+ *\li #DNS_R_NOTVERIFIEDYET - the message was signed by a TSIG or SIG(0),
+ * but the signature has not been verified yet
+ */
+
+isc_result_t
+dns_message_checksig(dns_message_t *msg, dns_view_t *view);
+/*%<
+ * If this message was signed, verify the signature.
+ *
+ * Requires:
+ *
+ *\li msg is a valid parsed message.
+ *\li view is a valid view or NULL
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS - the message was unsigned, or the message
+ * was signed correctly.
+ *
+ *\li #DNS_R_EXPECTEDTSIG - A TSIG was expected, but not seen
+ *\li #DNS_R_UNEXPECTEDTSIG - A TSIG was seen but not expected
+ *\li #DNS_R_TSIGVERIFYFAILURE - The TSIG failed to verify
+ */
+
+isc_result_t
+dns_message_rechecksig(dns_message_t *msg, dns_view_t *view);
+/*%<
+ * Reset the signature state and then if the message was signed,
+ * verify the message.
+ *
+ * Requires:
+ *
+ *\li msg is a valid parsed message.
+ *\li view is a valid view or NULL
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS - the message was unsigned, or the message
+ * was signed correctly.
+ *
+ *\li #DNS_R_EXPECTEDTSIG - A TSIG was expected, but not seen
+ *\li #DNS_R_UNEXPECTEDTSIG - A TSIG was seen but not expected
+ *\li #DNS_R_TSIGVERIFYFAILURE - The TSIG failed to verify
+ */
+
+void
+dns_message_resetsig(dns_message_t *msg);
+/*%<
+ * Reset the signature state.
+ *
+ * Requires:
+ *\li 'msg' is a valid parsed message.
+ */
+
+isc_region_t *
+dns_message_getrawmessage(dns_message_t *msg);
+/*%<
+ * Retrieve the raw message in compressed wire format. The message must
+ * have been successfully parsed for it to have been saved.
+ *
+ * Requires:
+ *\li msg is a valid parsed message.
+ *
+ * Returns:
+ *\li NULL if there is no saved message.
+ * a pointer to a region which refers the dns message.
+ */
+
+void
+dns_message_setsortorder(dns_message_t *msg, dns_rdatasetorderfunc_t order,
+ const void *order_arg);
+/*%<
+ * Define the order in which RR sets get rendered by
+ * dns_message_rendersection() to be the ascending order
+ * defined by the integer value returned by 'order' when
+ * given each RR and 'arg' as arguments. If 'order' and
+ * 'order_arg' are NULL, a default order is used.
+ *
+ * Requires:
+ *\li msg be a valid message.
+ *\li order_arg is NULL if and only if order is NULL.
+ */
+
+void
+dns_message_settimeadjust(dns_message_t *msg, int timeadjust);
+/*%<
+ * Adjust the time used to sign/verify a message by timeadjust.
+ * Currently only TSIG.
+ *
+ * Requires:
+ *\li msg be a valid message.
+ */
+
+int
+dns_message_gettimeadjust(dns_message_t *msg);
+/*%<
+ * Return the current time adjustment.
+ *
+ * Requires:
+ *\li msg be a valid message.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_MESSAGE_H */
diff --git a/lib/dns/include/dns/name.h b/lib/dns/include/dns/name.h
new file mode 100644
index 0000000..97c9efe
--- /dev/null
+++ b/lib/dns/include/dns/name.h
@@ -0,0 +1,1297 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: name.h,v 1.126 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef DNS_NAME_H
+#define DNS_NAME_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/name.h
+ * \brief
+ * Provides facilities for manipulating DNS names and labels, including
+ * conversions to and from wire format and text format.
+ *
+ * Given the large number of names possible in a nameserver, and because
+ * names occur in rdata, it was important to come up with a very efficient
+ * way of storing name data, but at the same time allow names to be
+ * manipulated. The decision was to store names in uncompressed wire format,
+ * and not to make them fully abstracted objects; i.e. certain parts of the
+ * server know names are stored that way. This saves a lot of memory, and
+ * makes adding names to messages easy. Having much of the server know
+ * the representation would be perilous, and we certainly don't want each
+ * user of names to be manipulating such a low-level structure. This is
+ * where the Names and Labels module comes in. The module allows name or
+ * label handles to be created and attached to uncompressed wire format
+ * regions. All name operations and conversions are done through these
+ * handles.
+ *
+ * MP:
+ *\li Clients of this module must impose any required synchronization.
+ *
+ * Reliability:
+ *\li This module deals with low-level byte streams. Errors in any of
+ * the functions are likely to crash the server or corrupt memory.
+ *
+ * Resources:
+ *\li None.
+ *
+ * Security:
+ *
+ *\li *** WARNING ***
+ *
+ *\li dns_name_fromwire() deals with raw network data. An error in
+ * this routine could result in the failure or hijacking of the server.
+ *
+ * Standards:
+ *\li RFC1035
+ *\li Draft EDNS0 (0)
+ *\li Draft Binary Labels (2)
+ *
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <stdio.h>
+
+#include <isc/boolean.h>
+#include <isc/lang.h>
+#include <isc/magic.h>
+#include <isc/region.h> /* Required for storage size of dns_label_t. */
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/*****
+ ***** Labels
+ *****
+ ***** A 'label' is basically a region. It contains one DNS wire format
+ ***** label of type 00 (ordinary).
+ *****/
+
+/*****
+ ***** Names
+ *****
+ ***** A 'name' is a handle to a binary region. It contains a sequence of one
+ ***** or more DNS wire format labels of type 00 (ordinary).
+ ***** Note that all names are not required to end with the root label,
+ ***** as they are in the actual DNS wire protocol.
+ *****/
+
+/***
+ *** Compression pointer chaining limit
+ ***/
+
+#define DNS_POINTER_MAXHOPS 16
+
+/***
+ *** Types
+ ***/
+
+/*%
+ * Clients are strongly discouraged from using this type directly, with
+ * the exception of the 'link' and 'list' fields which may be used directly
+ * for whatever purpose the client desires.
+ */
+struct dns_name {
+ unsigned int magic;
+ unsigned char * ndata;
+ unsigned int length;
+ unsigned int labels;
+ unsigned int attributes;
+ unsigned char * offsets;
+ isc_buffer_t * buffer;
+ ISC_LINK(dns_name_t) link;
+ ISC_LIST(dns_rdataset_t) list;
+};
+
+#define DNS_NAME_MAGIC ISC_MAGIC('D','N','S','n')
+
+#define DNS_NAMEATTR_ABSOLUTE 0x0001
+#define DNS_NAMEATTR_READONLY 0x0002
+#define DNS_NAMEATTR_DYNAMIC 0x0004
+#define DNS_NAMEATTR_DYNOFFSETS 0x0008
+#define DNS_NAMEATTR_NOCOMPRESS 0x0010
+/*
+ * Attributes below 0x0100 reserved for name.c usage.
+ */
+#define DNS_NAMEATTR_CACHE 0x0100 /*%< Used by resolver. */
+#define DNS_NAMEATTR_ANSWER 0x0200 /*%< Used by resolver. */
+#define DNS_NAMEATTR_NCACHE 0x0400 /*%< Used by resolver. */
+#define DNS_NAMEATTR_CHAINING 0x0800 /*%< Used by resolver. */
+#define DNS_NAMEATTR_CHASE 0x1000 /*%< Used by resolver. */
+#define DNS_NAMEATTR_WILDCARD 0x2000 /*%< Used by server. */
+
+#define DNS_NAME_DOWNCASE 0x0001
+#define DNS_NAME_CHECKNAMES 0x0002 /*%< Used by rdata. */
+#define DNS_NAME_CHECKNAMESFAIL 0x0004 /*%< Used by rdata. */
+#define DNS_NAME_CHECKREVERSE 0x0008 /*%< Used by rdata. */
+#define DNS_NAME_CHECKMX 0x0010 /*%< Used by rdata. */
+#define DNS_NAME_CHECKMXFAIL 0x0020 /*%< Used by rdata. */
+
+LIBDNS_EXTERNAL_DATA extern dns_name_t *dns_rootname;
+LIBDNS_EXTERNAL_DATA extern dns_name_t *dns_wildcardname;
+
+/*%
+ * Standard size of a wire format name
+ */
+#define DNS_NAME_MAXWIRE 255
+
+/*
+ * Text output filter procedure.
+ * 'target' is the buffer to be converted. The region to be converted
+ * is from 'buffer'->base + 'used_org' to the end of the used region.
+ */
+typedef isc_result_t (*dns_name_totextfilter_t)(isc_buffer_t *target,
+ unsigned int used_org,
+ isc_boolean_t absolute);
+
+/***
+ *** Initialization
+ ***/
+
+void
+dns_name_init(dns_name_t *name, unsigned char *offsets);
+/*%<
+ * Initialize 'name'.
+ *
+ * Notes:
+ * \li 'offsets' is never required to be non-NULL, but specifying a
+ * dns_offsets_t for 'offsets' will improve the performance of most
+ * name operations if the name is used more than once.
+ *
+ * Requires:
+ * \li 'name' is not NULL and points to a struct dns_name.
+ *
+ * \li offsets == NULL or offsets is a dns_offsets_t.
+ *
+ * Ensures:
+ * \li 'name' is a valid name.
+ * \li dns_name_countlabels(name) == 0
+ * \li dns_name_isabsolute(name) == ISC_FALSE
+ */
+
+void
+dns_name_reset(dns_name_t *name);
+/*%<
+ * Reinitialize 'name'.
+ *
+ * Notes:
+ * \li This function distinguishes itself from dns_name_init() in two
+ * key ways:
+ *
+ * \li + If any buffer is associated with 'name' (via dns_name_setbuffer()
+ * or by being part of a dns_fixedname_t) the link to the buffer
+ * is retained but the buffer itself is cleared.
+ *
+ * \li + Of the attributes associated with 'name', all are retained except
+ * DNS_NAMEATTR_ABSOLUTE.
+ *
+ * Requires:
+ * \li 'name' is a valid name.
+ *
+ * Ensures:
+ * \li 'name' is a valid name.
+ * \li dns_name_countlabels(name) == 0
+ * \li dns_name_isabsolute(name) == ISC_FALSE
+ */
+
+void
+dns_name_invalidate(dns_name_t *name);
+/*%<
+ * Make 'name' invalid.
+ *
+ * Requires:
+ * \li 'name' is a valid name.
+ *
+ * Ensures:
+ * \li If assertion checking is enabled, future attempts to use 'name'
+ * without initializing it will cause an assertion failure.
+ *
+ * \li If the name had a dedicated buffer, that association is ended.
+ */
+
+
+/***
+ *** Dedicated Buffers
+ ***/
+
+void
+dns_name_setbuffer(dns_name_t *name, isc_buffer_t *buffer);
+/*%<
+ * Dedicate a buffer for use with 'name'.
+ *
+ * Notes:
+ * \li Specification of a target buffer in dns_name_fromwire(),
+ * dns_name_fromtext(), and dns_name_concatentate() is optional if
+ * 'name' has a dedicated buffer.
+ *
+ * \li The caller must not write to buffer until the name has been
+ * invalidated or is otherwise known not to be in use.
+ *
+ * \li If buffer is NULL and the name previously had a dedicated buffer,
+ * than that buffer is no longer dedicated to use with this name.
+ * The caller is responsible for ensuring that the storage used by
+ * the name remains valid.
+ *
+ * Requires:
+ * \li 'name' is a valid name.
+ *
+ * \li 'buffer' is a valid binary buffer and 'name' doesn't have a
+ * dedicated buffer already, or 'buffer' is NULL.
+ */
+
+isc_boolean_t
+dns_name_hasbuffer(const dns_name_t *name);
+/*%<
+ * Does 'name' have a dedicated buffer?
+ *
+ * Requires:
+ * \li 'name' is a valid name.
+ *
+ * Returns:
+ * \li ISC_TRUE 'name' has a dedicated buffer.
+ * \li ISC_FALSE 'name' does not have a dedicated buffer.
+ */
+
+/***
+ *** Properties
+ ***/
+
+isc_boolean_t
+dns_name_isabsolute(const dns_name_t *name);
+/*%<
+ * Does 'name' end in the root label?
+ *
+ * Requires:
+ * \li 'name' is a valid name
+ *
+ * Returns:
+ * \li TRUE The last label in 'name' is the root label.
+ * \li FALSE The last label in 'name' is not the root label.
+ */
+
+isc_boolean_t
+dns_name_iswildcard(const dns_name_t *name);
+/*%<
+ * Is 'name' a wildcard name?
+ *
+ * Requires:
+ * \li 'name' is a valid name
+ *
+ * \li dns_name_countlabels(name) > 0
+ *
+ * Returns:
+ * \li TRUE The least significant label of 'name' is '*'.
+ * \li FALSE The least significant label of 'name' is not '*'.
+ */
+
+unsigned int
+dns_name_hash(dns_name_t *name, isc_boolean_t case_sensitive);
+/*%<
+ * Provide a hash value for 'name'.
+ *
+ * Note: if 'case_sensitive' is ISC_FALSE, then names which differ only in
+ * case will have the same hash value.
+ *
+ * Requires:
+ * \li 'name' is a valid name
+ *
+ * Returns:
+ * \li A hash value
+ */
+
+unsigned int
+dns_name_fullhash(dns_name_t *name, isc_boolean_t case_sensitive);
+/*%<
+ * Provide a hash value for 'name'. Unlike dns_name_hash(), this function
+ * always takes into account of the entire name to calculate the hash value.
+ *
+ * Note: if 'case_sensitive' is ISC_FALSE, then names which differ only in
+ * case will have the same hash value.
+ *
+ * Requires:
+ *\li 'name' is a valid name
+ *
+ * Returns:
+ *\li A hash value
+ */
+
+unsigned int
+dns_name_hashbylabel(dns_name_t *name, isc_boolean_t case_sensitive);
+/*%<
+ * Provide a hash value for 'name', where the hash value is the sum
+ * of the hash values of each label.
+ *
+ * Note: if 'case_sensitive' is ISC_FALSE, then names which differ only in
+ * case will have the same hash value.
+ *
+ * Requires:
+ *\li 'name' is a valid name
+ *
+ * Returns:
+ *\li A hash value
+ */
+
+/*
+ *** Comparisons
+ ***/
+
+dns_namereln_t
+dns_name_fullcompare(const dns_name_t *name1, const dns_name_t *name2,
+ int *orderp, unsigned int *nlabelsp);
+/*%<
+ * Determine the relative ordering under the DNSSEC order relation of
+ * 'name1' and 'name2', and also determine the hierarchical
+ * relationship of the names.
+ *
+ * Note: It makes no sense for one of the names to be relative and the
+ * other absolute. If both names are relative, then to be meaningfully
+ * compared the caller must ensure that they are both relative to the
+ * same domain.
+ *
+ * Requires:
+ *\li 'name1' is a valid name
+ *
+ *\li dns_name_countlabels(name1) > 0
+ *
+ *\li 'name2' is a valid name
+ *
+ *\li dns_name_countlabels(name2) > 0
+ *
+ *\li orderp and nlabelsp are valid pointers.
+ *
+ *\li Either name1 is absolute and name2 is absolute, or neither is.
+ *
+ * Ensures:
+ *
+ *\li *orderp is < 0 if name1 < name2, 0 if name1 = name2, > 0 if
+ * name1 > name2.
+ *
+ *\li *nlabelsp is the number of common significant labels.
+ *
+ * Returns:
+ *\li dns_namereln_none There's no hierarchical relationship
+ * between name1 and name2.
+ *\li dns_namereln_contains name1 properly contains name2; i.e.
+ * name2 is a proper subdomain of name1.
+ *\li dns_namereln_subdomain name1 is a proper subdomain of name2.
+ *\li dns_namereln_equal name1 and name2 are equal.
+ *\li dns_namereln_commonancestor name1 and name2 share a common
+ * ancestor.
+ */
+
+int
+dns_name_compare(const dns_name_t *name1, const dns_name_t *name2);
+/*%<
+ * Determine the relative ordering under the DNSSEC order relation of
+ * 'name1' and 'name2'.
+ *
+ * Note: It makes no sense for one of the names to be relative and the
+ * other absolute. If both names are relative, then to be meaningfully
+ * compared the caller must ensure that they are both relative to the
+ * same domain.
+ *
+ * Requires:
+ * \li 'name1' is a valid name
+ *
+ * \li 'name2' is a valid name
+ *
+ * \li Either name1 is absolute and name2 is absolute, or neither is.
+ *
+ * Returns:
+ * \li < 0 'name1' is less than 'name2'
+ * \li 0 'name1' is equal to 'name2'
+ * \li > 0 'name1' is greater than 'name2'
+ */
+
+isc_boolean_t
+dns_name_equal(const dns_name_t *name1, const dns_name_t *name2);
+/*%<
+ * Are 'name1' and 'name2' equal?
+ *
+ * Notes:
+ * \li Because it only needs to test for equality, dns_name_equal() can be
+ * significantly faster than dns_name_fullcompare() or dns_name_compare().
+ *
+ * \li Offsets tables are not used in the comparision.
+ *
+ * \li It makes no sense for one of the names to be relative and the
+ * other absolute. If both names are relative, then to be meaningfully
+ * compared the caller must ensure that they are both relative to the
+ * same domain.
+ *
+ * Requires:
+ * \li 'name1' is a valid name
+ *
+ * \li 'name2' is a valid name
+ *
+ * \li Either name1 is absolute and name2 is absolute, or neither is.
+ *
+ * Returns:
+ * \li ISC_TRUE 'name1' and 'name2' are equal
+ * \li ISC_FALSE 'name1' and 'name2' are not equal
+ */
+
+isc_boolean_t
+dns_name_caseequal(const dns_name_t *name1, const dns_name_t *name2);
+/*%<
+ * Case sensitive version of dns_name_equal().
+ */
+
+int
+dns_name_rdatacompare(const dns_name_t *name1, const dns_name_t *name2);
+/*%<
+ * Compare two names as if they are part of rdata in DNSSEC canonical
+ * form.
+ *
+ * Requires:
+ * \li 'name1' is a valid absolute name
+ *
+ * \li dns_name_countlabels(name1) > 0
+ *
+ * \li 'name2' is a valid absolute name
+ *
+ * \li dns_name_countlabels(name2) > 0
+ *
+ * Returns:
+ * \li < 0 'name1' is less than 'name2'
+ * \li 0 'name1' is equal to 'name2'
+ * \li > 0 'name1' is greater than 'name2'
+ */
+
+isc_boolean_t
+dns_name_issubdomain(const dns_name_t *name1, const dns_name_t *name2);
+/*%<
+ * Is 'name1' a subdomain of 'name2'?
+ *
+ * Notes:
+ * \li name1 is a subdomain of name2 if name1 is contained in name2, or
+ * name1 equals name2.
+ *
+ * \li It makes no sense for one of the names to be relative and the
+ * other absolute. If both names are relative, then to be meaningfully
+ * compared the caller must ensure that they are both relative to the
+ * same domain.
+ *
+ * Requires:
+ * \li 'name1' is a valid name
+ *
+ * \li 'name2' is a valid name
+ *
+ * \li Either name1 is absolute and name2 is absolute, or neither is.
+ *
+ * Returns:
+ * \li TRUE 'name1' is a subdomain of 'name2'
+ * \li FALSE 'name1' is not a subdomain of 'name2'
+ */
+
+isc_boolean_t
+dns_name_matcheswildcard(const dns_name_t *name, const dns_name_t *wname);
+/*%<
+ * Does 'name' match the wildcard specified in 'wname'?
+ *
+ * Notes:
+ * \li name matches the wildcard specified in wname if all labels
+ * following the wildcard in wname are identical to the same number
+ * of labels at the end of name.
+ *
+ * \li It makes no sense for one of the names to be relative and the
+ * other absolute. If both names are relative, then to be meaningfully
+ * compared the caller must ensure that they are both relative to the
+ * same domain.
+ *
+ * Requires:
+ * \li 'name' is a valid name
+ *
+ * \li dns_name_countlabels(name) > 0
+ *
+ * \li 'wname' is a valid name
+ *
+ * \li dns_name_countlabels(wname) > 0
+ *
+ * \li dns_name_iswildcard(wname) is true
+ *
+ * \li Either name is absolute and wname is absolute, or neither is.
+ *
+ * Returns:
+ * \li TRUE 'name' matches the wildcard specified in 'wname'
+ * \li FALSE 'name' does not match the wildcard specified in 'wname'
+ */
+
+/***
+ *** Labels
+ ***/
+
+unsigned int
+dns_name_countlabels(const dns_name_t *name);
+/*%<
+ * How many labels does 'name' have?
+ *
+ * Notes:
+ * \li In this case, as in other places, a 'label' is an ordinary label.
+ *
+ * Requires:
+ * \li 'name' is a valid name
+ *
+ * Ensures:
+ * \li The result is <= 128.
+ *
+ * Returns:
+ * \li The number of labels in 'name'.
+ */
+
+void
+dns_name_getlabel(const dns_name_t *name, unsigned int n, dns_label_t *label);
+/*%<
+ * Make 'label' refer to the 'n'th least significant label of 'name'.
+ *
+ * Notes:
+ * \li Numbering starts at 0.
+ *
+ * \li Given "rc.vix.com.", the label 0 is "rc", and label 3 is the
+ * root label.
+ *
+ * \li 'label' refers to the same memory as 'name', so 'name' must not
+ * be changed while 'label' is still in use.
+ *
+ * Requires:
+ * \li n < dns_name_countlabels(name)
+ */
+
+void
+dns_name_getlabelsequence(const dns_name_t *source, unsigned int first,
+ unsigned int n, dns_name_t *target);
+/*%<
+ * Make 'target' refer to the 'n' labels including and following 'first'
+ * in 'source'.
+ *
+ * Notes:
+ * \li Numbering starts at 0.
+ *
+ * \li Given "rc.vix.com.", the label 0 is "rc", and label 3 is the
+ * root label.
+ *
+ * \li 'target' refers to the same memory as 'source', so 'source'
+ * must not be changed while 'target' is still in use.
+ *
+ * Requires:
+ * \li 'source' and 'target' are valid names.
+ *
+ * \li first < dns_name_countlabels(name)
+ *
+ * \li first + n <= dns_name_countlabels(name)
+ */
+
+
+void
+dns_name_clone(const dns_name_t *source, dns_name_t *target);
+/*%<
+ * Make 'target' refer to the same name as 'source'.
+ *
+ * Notes:
+ *
+ * \li 'target' refers to the same memory as 'source', so 'source'
+ * must not be changed while 'target' is still in use.
+ *
+ * \li This call is functionally equivalent to:
+ *
+ * \code
+ * dns_name_getlabelsequence(source, 0,
+ * dns_name_countlabels(source),
+ * target);
+ * \endcode
+ *
+ * but is more efficient. Also, dns_name_clone() works even if 'source'
+ * is empty.
+ *
+ * Requires:
+ *
+ * \li 'source' is a valid name.
+ *
+ * \li 'target' is a valid name that is not read-only.
+ */
+
+/***
+ *** Conversions
+ ***/
+
+void
+dns_name_fromregion(dns_name_t *name, const isc_region_t *r);
+/*%<
+ * Make 'name' refer to region 'r'.
+ *
+ * Note:
+ * \li If the conversion encounters a root label before the end of the
+ * region the conversion stops and the length is set to the length
+ * so far converted. A maximum of 255 bytes is converted.
+ *
+ * Requires:
+ * \li The data in 'r' is a sequence of one or more type 00 or type 01000001
+ * labels.
+ */
+
+void
+dns_name_toregion(dns_name_t *name, isc_region_t *r);
+/*%<
+ * Make 'r' refer to 'name'.
+ *
+ * Requires:
+ *
+ * \li 'name' is a valid name.
+ *
+ * \li 'r' is a valid region.
+ */
+
+isc_result_t
+dns_name_fromwire(dns_name_t *name, isc_buffer_t *source,
+ dns_decompress_t *dctx, unsigned int options,
+ isc_buffer_t *target);
+/*%<
+ * Copy the possibly-compressed name at source (active region) into target,
+ * decompressing it.
+ *
+ * Notes:
+ * \li Decompression policy is controlled by 'dctx'.
+ *
+ * \li If DNS_NAME_DOWNCASE is set, any uppercase letters in 'source' will be
+ * downcased when they are copied into 'target'.
+ *
+ * Security:
+ *
+ * \li *** WARNING ***
+ *
+ * \li This routine will often be used when 'source' contains raw network
+ * data. A programming error in this routine could result in a denial
+ * of service, or in the hijacking of the server.
+ *
+ * Requires:
+ *
+ * \li 'name' is a valid name.
+ *
+ * \li 'source' is a valid buffer and the first byte of the active
+ * region should be the first byte of a DNS wire format domain name.
+ *
+ * \li 'target' is a valid buffer or 'target' is NULL and 'name' has
+ * a dedicated buffer.
+ *
+ * \li 'dctx' is a valid decompression context.
+ *
+ * Ensures:
+ *
+ * If result is success:
+ * \li If 'target' is not NULL, 'name' is attached to it.
+ *
+ * \li Uppercase letters are downcased in the copy iff
+ * DNS_NAME_DOWNCASE is set in options.
+ *
+ * \li The current location in source is advanced, and the used space
+ * in target is updated.
+ *
+ * Result:
+ * \li Success
+ * \li Bad Form: Label Length
+ * \li Bad Form: Unknown Label Type
+ * \li Bad Form: Name Length
+ * \li Bad Form: Compression type not allowed
+ * \li Bad Form: Bad compression pointer
+ * \li Bad Form: Input too short
+ * \li Resource Limit: Too many compression pointers
+ * \li Resource Limit: Not enough space in buffer
+ */
+
+isc_result_t
+dns_name_towire(const dns_name_t *name, dns_compress_t *cctx,
+ isc_buffer_t *target);
+/*%<
+ * Convert 'name' into wire format, compressing it as specified by the
+ * compression context 'cctx', and storing the result in 'target'.
+ *
+ * Notes:
+ * \li If the compression context allows global compression, then the
+ * global compression table may be updated.
+ *
+ * Requires:
+ * \li 'name' is a valid name
+ *
+ * \li dns_name_countlabels(name) > 0
+ *
+ * \li dns_name_isabsolute(name) == TRUE
+ *
+ * \li target is a valid buffer.
+ *
+ * \li Any offsets specified in a global compression table are valid
+ * for buffer.
+ *
+ * Ensures:
+ *
+ * If the result is success:
+ *
+ * \li The used space in target is updated.
+ *
+ * Returns:
+ * \li Success
+ * \li Resource Limit: Not enough space in buffer
+ */
+
+isc_result_t
+dns_name_fromtext(dns_name_t *name, isc_buffer_t *source,
+ dns_name_t *origin, unsigned int options,
+ isc_buffer_t *target);
+/*%<
+ * Convert the textual representation of a DNS name at source
+ * into uncompressed wire form stored in target.
+ *
+ * Notes:
+ * \li Relative domain names will have 'origin' appended to them
+ * unless 'origin' is NULL, in which case relative domain names
+ * will remain relative.
+ *
+ * \li If DNS_NAME_DOWNCASE is set in 'options', any uppercase letters
+ * in 'source' will be downcased when they are copied into 'target'.
+ *
+ * Requires:
+ *
+ * \li 'name' is a valid name.
+ *
+ * \li 'source' is a valid buffer.
+ *
+ * \li 'target' is a valid buffer or 'target' is NULL and 'name' has
+ * a dedicated buffer.
+ *
+ * Ensures:
+ *
+ * If result is success:
+ * \li If 'target' is not NULL, 'name' is attached to it.
+ *
+ * \li Uppercase letters are downcased in the copy iff
+ * DNS_NAME_DOWNCASE is set in 'options'.
+ *
+ * \li The current location in source is advanced, and the used space
+ * in target is updated.
+ *
+ * Result:
+ *\li #ISC_R_SUCCESS
+ *\li #DNS_R_EMPTYLABEL
+ *\li #DNS_R_LABELTOOLONG
+ *\li #DNS_R_BADESCAPE
+ *\li (#DNS_R_BADBITSTRING: should not be returned)
+ *\li (#DNS_R_BITSTRINGTOOLONG: should not be returned)
+ *\li #DNS_R_BADDOTTEDQUAD
+ *\li #ISC_R_NOSPACE
+ *\li #ISC_R_UNEXPECTEDEND
+ */
+
+isc_result_t
+dns_name_totext(dns_name_t *name, isc_boolean_t omit_final_dot,
+ isc_buffer_t *target);
+/*%<
+ * Convert 'name' into text format, storing the result in 'target'.
+ *
+ * Notes:
+ *\li If 'omit_final_dot' is true, then the final '.' in absolute
+ * names other than the root name will be omitted.
+ *
+ *\li If dns_name_countlabels == 0, the name will be "@", representing the
+ * current origin as described by RFC1035.
+ *
+ *\li The name is not NUL terminated.
+ *
+ * Requires:
+ *
+ *\li 'name' is a valid name
+ *
+ *\li 'target' is a valid buffer.
+ *
+ *\li if dns_name_isabsolute == FALSE, then omit_final_dot == FALSE
+ *
+ * Ensures:
+ *
+ *\li If the result is success:
+ * the used space in target is updated.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOSPACE
+ */
+
+#define DNS_NAME_MAXTEXT 1023
+/*%<
+ * The maximum length of the text representation of a domain
+ * name as generated by dns_name_totext(). This does not
+ * include space for a terminating NULL.
+ *
+ * This definition is conservative - the actual maximum
+ * is 1004, derived as follows:
+ *
+ * A backslash-decimal escaped character takes 4 bytes.
+ * A wire-encoded name can be up to 255 bytes and each
+ * label is one length byte + at most 63 bytes of data.
+ * Maximizing the label lengths gives us a name of
+ * three 63-octet labels, one 61-octet label, and the
+ * root label:
+ *
+ * 1 + 63 + 1 + 63 + 1 + 63 + 1 + 61 + 1 = 255
+ *
+ * When printed, this is (3 * 63 + 61) * 4
+ * bytes for the escaped label data + 4 bytes for the
+ * dot terminating each label = 1004 bytes total.
+ */
+
+isc_result_t
+dns_name_tofilenametext(dns_name_t *name, isc_boolean_t omit_final_dot,
+ isc_buffer_t *target);
+/*%<
+ * Convert 'name' into an alternate text format appropriate for filenames,
+ * storing the result in 'target'. The name data is downcased, guaranteeing
+ * that the filename does not depend on the case of the converted name.
+ *
+ * Notes:
+ *\li If 'omit_final_dot' is true, then the final '.' in absolute
+ * names other than the root name will be omitted.
+ *
+ *\li The name is not NUL terminated.
+ *
+ * Requires:
+ *
+ *\li 'name' is a valid absolute name
+ *
+ *\li 'target' is a valid buffer.
+ *
+ * Ensures:
+ *
+ *\li If the result is success:
+ * the used space in target is updated.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOSPACE
+ */
+
+isc_result_t
+dns_name_downcase(dns_name_t *source, dns_name_t *name,
+ isc_buffer_t *target);
+/*%<
+ * Downcase 'source'.
+ *
+ * Requires:
+ *
+ *\li 'source' and 'name' are valid names.
+ *
+ *\li If source == name, then
+ * 'source' must not be read-only
+ *
+ *\li Otherwise,
+ * 'target' is a valid buffer or 'target' is NULL and
+ * 'name' has a dedicated buffer.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOSPACE
+ *
+ * Note: if source == name, then the result will always be ISC_R_SUCCESS.
+ */
+
+isc_result_t
+dns_name_concatenate(dns_name_t *prefix, dns_name_t *suffix,
+ dns_name_t *name, isc_buffer_t *target);
+/*%<
+ * Concatenate 'prefix' and 'suffix'.
+ *
+ * Requires:
+ *
+ *\li 'prefix' is a valid name or NULL.
+ *
+ *\li 'suffix' is a valid name or NULL.
+ *
+ *\li 'name' is a valid name or NULL.
+ *
+ *\li 'target' is a valid buffer or 'target' is NULL and 'name' has
+ * a dedicated buffer.
+ *
+ *\li If 'prefix' is absolute, 'suffix' must be NULL or the empty name.
+ *
+ * Ensures:
+ *
+ *\li On success,
+ * If 'target' is not NULL and 'name' is not NULL, then 'name'
+ * is attached to it.
+ * The used space in target is updated.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOSPACE
+ *\li #DNS_R_NAMETOOLONG
+ */
+
+void
+dns_name_split(dns_name_t *name, unsigned int suffixlabels,
+ dns_name_t *prefix, dns_name_t *suffix);
+/*%<
+ *
+ * Split 'name' into two pieces on a label boundary.
+ *
+ * Notes:
+ * \li 'name' is split such that 'suffix' holds the most significant
+ * 'suffixlabels' labels. All other labels are stored in 'prefix'.
+ *
+ *\li Copying name data is avoided as much as possible, so 'prefix'
+ * and 'suffix' will end up pointing at the data for 'name'.
+ *
+ *\li It is legitimate to pass a 'prefix' or 'suffix' that has
+ * its name data stored someplace other than the dedicated buffer.
+ * This is useful to avoid name copying in the calling function.
+ *
+ *\li It is also legitimate to pass a 'prefix' or 'suffix' that is
+ * the same dns_name_t as 'name'.
+ *
+ * Requires:
+ *\li 'name' is a valid name.
+ *
+ *\li 'suffixlabels' cannot exceed the number of labels in 'name'.
+ *
+ * \li 'prefix' is a valid name or NULL, and cannot be read-only.
+ *
+ *\li 'suffix' is a valid name or NULL, and cannot be read-only.
+ *
+ *\li If non-NULL, 'prefix' and 'suffix' must have dedicated buffers.
+ *
+ *\li 'prefix' and 'suffix' cannot point to the same buffer.
+ *
+ * Ensures:
+ *
+ *\li On success:
+ * If 'prefix' is not NULL it will contain the least significant
+ * labels.
+ * If 'suffix' is not NULL it will contain the most significant
+ * labels. dns_name_countlabels(suffix) will be equal to
+ * suffixlabels.
+ *
+ *\li On failure:
+ * Either 'prefix' or 'suffix' is invalidated (depending
+ * on which one the problem was encountered with).
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS No worries. (This function should always success).
+ */
+
+isc_result_t
+dns_name_dup(const dns_name_t *source, isc_mem_t *mctx,
+ dns_name_t *target);
+/*%<
+ * Make 'target' a dynamically allocated copy of 'source'.
+ *
+ * Requires:
+ *
+ *\li 'source' is a valid non-empty name.
+ *
+ *\li 'target' is a valid name that is not read-only.
+ *
+ *\li 'mctx' is a valid memory context.
+ */
+
+isc_result_t
+dns_name_dupwithoffsets(dns_name_t *source, isc_mem_t *mctx,
+ dns_name_t *target);
+/*%<
+ * Make 'target' a read-only dynamically allocated copy of 'source'.
+ * 'target' will also have a dynamically allocated offsets table.
+ *
+ * Requires:
+ *
+ *\li 'source' is a valid non-empty name.
+ *
+ *\li 'target' is a valid name that is not read-only.
+ *
+ *\li 'target' has no offsets table.
+ *
+ *\li 'mctx' is a valid memory context.
+ */
+
+void
+dns_name_free(dns_name_t *name, isc_mem_t *mctx);
+/*%<
+ * Free 'name'.
+ *
+ * Requires:
+ *
+ *\li 'name' is a valid name created previously in 'mctx' by dns_name_dup().
+ *
+ *\li 'mctx' is a valid memory context.
+ *
+ * Ensures:
+ *
+ *\li All dynamic resources used by 'name' are freed and the name is
+ * invalidated.
+ */
+
+isc_result_t
+dns_name_digest(dns_name_t *name, dns_digestfunc_t digest, void *arg);
+/*%<
+ * Send 'name' in DNSSEC canonical form to 'digest'.
+ *
+ * Requires:
+ *
+ *\li 'name' is a valid name.
+ *
+ *\li 'digest' is a valid dns_digestfunc_t.
+ *
+ * Ensures:
+ *
+ *\li If successful, the DNSSEC canonical form of 'name' will have been
+ * sent to 'digest'.
+ *
+ *\li If digest() returns something other than ISC_R_SUCCESS, that result
+ * will be returned as the result of dns_name_digest().
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS
+ *
+ *\li Many other results are possible if not successful.
+ *
+ */
+
+isc_boolean_t
+dns_name_dynamic(dns_name_t *name);
+/*%<
+ * Returns whether there is dynamic memory associated with this name.
+ *
+ * Requires:
+ *
+ *\li 'name' is a valid name.
+ *
+ * Returns:
+ *
+ *\li 'ISC_TRUE' if the name is dynamic othewise 'ISC_FALSE'.
+ */
+
+isc_result_t
+dns_name_print(dns_name_t *name, FILE *stream);
+/*%<
+ * Print 'name' on 'stream'.
+ *
+ * Requires:
+ *
+ *\li 'name' is a valid name.
+ *
+ *\li 'stream' is a valid stream.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS
+ *
+ *\li Any error that dns_name_totext() can return.
+ */
+
+void
+dns_name_format(dns_name_t *name, char *cp, unsigned int size);
+/*%<
+ * Format 'name' as text appropriate for use in log messages.
+ *
+ * Store the formatted name at 'cp', writing no more than
+ * 'size' bytes. The resulting string is guaranteed to be
+ * null terminated.
+ *
+ * The formatted name will have a terminating dot only if it is
+ * the root.
+ *
+ * This function cannot fail, instead any errors are indicated
+ * in the returned text.
+ *
+ * Requires:
+ *
+ *\li 'name' is a valid name.
+ *
+ *\li 'cp' points a valid character array of size 'size'.
+ *
+ *\li 'size' > 0.
+ *
+ */
+
+isc_result_t
+dns_name_settotextfilter(dns_name_totextfilter_t proc);
+/*%<
+ * Set / clear a thread specific function 'proc' to be called at the
+ * end of dns_name_totext().
+ *
+ * Note: Under Windows you need to call "dns_name_settotextfilter(NULL);"
+ * prior to exiting the thread otherwise memory will be leaked.
+ * For other platforms, which are pthreads based, this is still a good
+ * idea but not required.
+ *
+ * Returns
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_UNEXPECTED
+ */
+
+#define DNS_NAME_FORMATSIZE (DNS_NAME_MAXTEXT + 1)
+/*%<
+ * Suggested size of buffer passed to dns_name_format().
+ * Includes space for the terminating NULL.
+ */
+
+isc_result_t
+dns_name_copy(dns_name_t *source, dns_name_t *dest, isc_buffer_t *target);
+/*%<
+ * Makes 'dest' refer to a copy of the name in 'source'. The data are
+ * either copied to 'target' or the dedicated buffer in 'dest'.
+ *
+ * Requires:
+ * \li 'source' is a valid name.
+ *
+ * \li 'dest' is an initialized name with a dedicated buffer.
+ *
+ * \li 'target' is NULL or an initialized buffer.
+ *
+ * \li Either dest has a dedicated buffer or target != NULL.
+ *
+ * Ensures:
+ *
+ *\li On success, the used space in target is updated.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOSPACE
+ */
+
+isc_boolean_t
+dns_name_ishostname(const dns_name_t *name, isc_boolean_t wildcard);
+/*%<
+ * Return if 'name' is a valid hostname. RFC 952 / RFC 1123.
+ * If 'wildcard' is ISC_TRUE then allow the first label of name to
+ * be a wildcard.
+ * The root is also accepted.
+ *
+ * Requires:
+ * 'name' to be valid.
+ */
+
+
+isc_boolean_t
+dns_name_ismailbox(const dns_name_t *name);
+/*%<
+ * Return if 'name' is a valid mailbox. RFC 821.
+ *
+ * Requires:
+ * \li 'name' to be valid.
+ */
+
+isc_boolean_t
+dns_name_internalwildcard(const dns_name_t *name);
+/*%<
+ * Return if 'name' contains a internal wildcard name.
+ *
+ * Requires:
+ * \li 'name' to be valid.
+ */
+
+void
+dns_name_destroy(void);
+/*%<
+ * Cleanup dns_name_settotextfilter() / dns_name_totext() state.
+ *
+ * This should be called as part of the final cleanup process.
+ *
+ * Note: dns_name_settotextfilter(NULL); should be called for all
+ * threads which have called dns_name_settotextfilter() with a
+ * non-NULL argument prior to calling dns_name_destroy();
+ */
+
+ISC_LANG_ENDDECLS
+
+/*
+ *** High Peformance Macros
+ ***/
+
+/*
+ * WARNING: Use of these macros by applications may require recompilation
+ * of the application in some situations where calling the function
+ * would not.
+ *
+ * WARNING: No assertion checking is done for these macros.
+ */
+
+#define DNS_NAME_INIT(n, o) \
+do { \
+ (n)->magic = DNS_NAME_MAGIC; \
+ (n)->ndata = NULL; \
+ (n)->length = 0; \
+ (n)->labels = 0; \
+ (n)->attributes = 0; \
+ (n)->offsets = (o); \
+ (n)->buffer = NULL; \
+ ISC_LINK_INIT((n), link); \
+ ISC_LIST_INIT((n)->list); \
+} while (0)
+
+#define DNS_NAME_RESET(n) \
+do { \
+ (n)->ndata = NULL; \
+ (n)->length = 0; \
+ (n)->labels = 0; \
+ (n)->attributes &= ~DNS_NAMEATTR_ABSOLUTE; \
+ if ((n)->buffer != NULL) \
+ isc_buffer_clear((n)->buffer); \
+} while (0)
+
+#define DNS_NAME_SETBUFFER(n, b) \
+ (n)->buffer = (b)
+
+#define DNS_NAME_ISABSOLUTE(n) \
+ (((n)->attributes & DNS_NAMEATTR_ABSOLUTE) != 0 ? ISC_TRUE : ISC_FALSE)
+
+#define DNS_NAME_COUNTLABELS(n) \
+ ((n)->labels)
+
+#define DNS_NAME_TOREGION(n, r) \
+do { \
+ (r)->base = (n)->ndata; \
+ (r)->length = (n)->length; \
+} while (0)
+
+#define DNS_NAME_SPLIT(n, l, p, s) \
+do { \
+ dns_name_t *_n = (n); \
+ dns_name_t *_p = (p); \
+ dns_name_t *_s = (s); \
+ unsigned int _l = (l); \
+ if (_p != NULL) \
+ dns_name_getlabelsequence(_n, 0, _n->labels - _l, _p); \
+ if (_s != NULL) \
+ dns_name_getlabelsequence(_n, _n->labels - _l, _l, _s); \
+} while (0)
+
+#ifdef DNS_NAME_USEINLINE
+
+#define dns_name_init(n, o) DNS_NAME_INIT(n, o)
+#define dns_name_reset(n) DNS_NAME_RESET(n)
+#define dns_name_setbuffer(n, b) DNS_NAME_SETBUFFER(n, b)
+#define dns_name_countlabels(n) DNS_NAME_COUNTLABELS(n)
+#define dns_name_isabsolute(n) DNS_NAME_ISABSOLUTE(n)
+#define dns_name_toregion(n, r) DNS_NAME_TOREGION(n, r)
+#define dns_name_split(n, l, p, s) DNS_NAME_SPLIT(n, l, p, s)
+
+#endif /* DNS_NAME_USEINLINE */
+
+#endif /* DNS_NAME_H */
diff --git a/lib/dns/include/dns/ncache.h b/lib/dns/include/dns/ncache.h
new file mode 100644
index 0000000..a818fe6
--- /dev/null
+++ b/lib/dns/include/dns/ncache.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: ncache.h,v 1.25 2008/09/25 04:02:39 tbox Exp $ */
+
+#ifndef DNS_NCACHE_H
+#define DNS_NCACHE_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/ncache.h
+ *\brief
+ * DNS Ncache
+ *
+ * XXX TBS XXX
+ *
+ * MP:
+ *\li The caller must ensure any required synchronization.
+ *
+ * Reliability:
+ *\li No anticipated impact.
+ *
+ * Resources:
+ *\li TBS
+ *
+ * Security:
+ *\li No anticipated impact.
+ *
+ * Standards:
+ *\li RFC2308
+ */
+
+#include <isc/lang.h>
+#include <isc/stdtime.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/*%
+ * _OMITDNSSEC:
+ * Omit DNSSEC records when rendering.
+ */
+#define DNS_NCACHETOWIRE_OMITDNSSEC 0x0001
+
+isc_result_t
+dns_ncache_add(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
+ dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t maxttl,
+ dns_rdataset_t *addedrdataset);
+isc_result_t
+dns_ncache_addoptout(dns_message_t *message, dns_db_t *cache,
+ dns_dbnode_t *node, dns_rdatatype_t covers,
+ isc_stdtime_t now, dns_ttl_t maxttl,
+ isc_boolean_t optout, dns_rdataset_t *addedrdataset);
+/*%<
+ * Convert the authority data from 'message' into a negative cache
+ * rdataset, and store it in 'cache' at 'node' with a TTL limited to
+ * 'maxttl'.
+ *
+ * The 'covers' argument is the RR type whose nonexistence we are caching,
+ * or dns_rdatatype_any when caching a NXDOMAIN response.
+ *
+ * 'optout' indicates a DNS_RATASETATTR_OPTOUT should be set.
+ *
+ * Note:
+ *\li If 'addedrdataset' is not NULL, then it will be attached to the added
+ * rdataset. See dns_db_addrdataset() for more details.
+ *
+ * Requires:
+ *\li 'message' is a valid message with a properly formatting negative cache
+ * authority section.
+ *
+ *\li The requirements of dns_db_addrdataset() apply to 'cache', 'node',
+ * 'now', and 'addedrdataset'.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOSPACE
+ *
+ *\li Any result code of dns_db_addrdataset() is a possible result code
+ * of dns_ncache_add().
+ */
+
+isc_result_t
+dns_ncache_towire(dns_rdataset_t *rdataset, dns_compress_t *cctx,
+ isc_buffer_t *target, unsigned int options,
+ unsigned int *countp);
+/*%<
+ * Convert the negative caching rdataset 'rdataset' to wire format,
+ * compressing names as specified in 'cctx', and storing the result in
+ * 'target'. If 'omit_dnssec' is set, DNSSEC records will not
+ * be added to 'target'.
+ *
+ * Notes:
+ *\li The number of RRs added to target will be added to *countp.
+ *
+ * Requires:
+ *\li 'rdataset' is a valid negative caching rdataset.
+ *
+ *\li 'rdataset' is not empty.
+ *
+ *\li 'countp' is a valid pointer.
+ *
+ * Ensures:
+ *\li On a return of ISC_R_SUCCESS, 'target' contains a wire format
+ * for the data contained in 'rdataset'. Any error return leaves
+ * the buffer unchanged.
+ *
+ *\li *countp has been incremented by the number of RRs added to
+ * target.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS - all ok
+ *\li #ISC_R_NOSPACE - 'target' doesn't have enough room
+ *
+ *\li Any error returned by dns_rdata_towire(), dns_rdataset_next(),
+ * dns_name_towire().
+ */
+
+isc_result_t
+dns_ncache_getrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name,
+ dns_rdatatype_t type, dns_rdataset_t *rdataset);
+/*%<
+ * Search the negative caching rdataset for an rdataset with the
+ * specified name and type.
+ *
+ * Requires:
+ *\li 'ncacherdataset' is a valid negative caching rdataset.
+ *
+ *\li 'ncacherdataset' is not empty.
+ *
+ *\li 'name' is a valid name.
+ *
+ *\li 'type' is not SIG, or a meta-RR type.
+ *
+ *\li 'rdataset' is a valid disassociated rdataset.
+ *
+ * Ensures:
+ *\li On a return of ISC_R_SUCCESS, 'rdataset' is bound to the found
+ * rdataset.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS - the rdataset was found.
+ *\li #ISC_R_NOTFOUND - the rdataset was not found.
+ *
+ */
+
+void
+dns_ncache_current(dns_rdataset_t *ncacherdataset, dns_name_t *found,
+ dns_rdataset_t *rdataset);
+
+/*%<
+ * Extract the current rdataset and name from a ncache entry.
+ *
+ * Requires:
+ * \li 'ncacherdataset' to be valid and to be a negative cache entry
+ * \li 'found' to be valid.
+ * \li 'rdataset' to be unassociated.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_NCACHE_H */
diff --git a/lib/dns/include/dns/nsec.h b/lib/dns/include/dns/nsec.h
new file mode 100644
index 0000000..335a463
--- /dev/null
+++ b/lib/dns/include/dns/nsec.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001, 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: nsec.h,v 1.12 2008/09/25 04:02:39 tbox Exp $ */
+
+#ifndef DNS_NSEC_H
+#define DNS_NSEC_H 1
+
+/*! \file dns/nsec.h */
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+#include <dns/name.h>
+
+#define DNS_NSEC_BUFFERSIZE (DNS_NAME_MAXWIRE + 8192 + 512)
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_nsec_buildrdata(dns_db_t *db, dns_dbversion_t *version,
+ dns_dbnode_t *node, dns_name_t *target,
+ unsigned char *buffer, dns_rdata_t *rdata);
+/*%<
+ * Build the rdata of a NSEC record.
+ *
+ * Requires:
+ *\li buffer Points to a temporary buffer of at least
+ * DNS_NSEC_BUFFERSIZE bytes.
+ *\li rdata Points to an initialized dns_rdata_t.
+ *
+ * Ensures:
+ * \li *rdata Contains a valid NSEC rdata. The 'data' member refers
+ * to 'buffer'.
+ */
+
+isc_result_t
+dns_nsec_build(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node,
+ dns_name_t *target, dns_ttl_t ttl);
+/*%<
+ * Build a NSEC record and add it to a database.
+ */
+
+isc_boolean_t
+dns_nsec_typepresent(dns_rdata_t *nsec, dns_rdatatype_t type);
+/*%<
+ * Determine if a type is marked as present in an NSEC record.
+ *
+ * Requires:
+ *\li 'nsec' points to a valid rdataset of type NSEC
+ */
+
+isc_result_t
+dns_nsec_nseconly(dns_db_t *db, dns_dbversion_t *version,
+ isc_boolean_t *answer);
+/*
+ * Report whether the DNSKEY RRset has a NSEC only algorithm. Unknown
+ * algorithms are assumed to support NSEC3.
+ *
+ * Requires:
+ * 'answer' to be non NULL.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_NSEC_H */
diff --git a/lib/dns/include/dns/nsec3.h b/lib/dns/include/dns/nsec3.h
new file mode 100644
index 0000000..5bf911a
--- /dev/null
+++ b/lib/dns/include/dns/nsec3.h
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2008 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 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.
+ */
+
+/* $Id: nsec3.h,v 1.5 2008/09/24 03:16:58 tbox Exp $ */
+
+#ifndef DNS_NSEC3_H
+#define DNS_NSEC3_H 1
+
+#include <isc/lang.h>
+#include <isc/iterated_hash.h>
+
+#include <dns/db.h>
+#include <dns/diff.h>
+#include <dns/name.h>
+#include <dns/rdatastruct.h>
+#include <dns/types.h>
+
+/*
+ * hash = 1, flags =1, iterations = 2, salt length = 1, salt = 255 (max)
+ * hash length = 1, hash = 255 (max), bitmap = 8192 + 512 (max)
+ */
+#define DNS_NSEC3_BUFFERSIZE (6 + 255 + 255 + 8192 + 512)
+/*
+ * hash = 1, flags = 1, iterations = 2, salt length = 1, salt = 255 (max)
+ */
+#define DNS_NSEC3PARAM_BUFFERSIZE (5 + 255)
+
+/*
+ * Test "unknown" algorithm. Is mapped to dns_hash_sha1.
+ */
+#define DNS_NSEC3_UNKNOWNALG 245U
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_nsec3_buildrdata(dns_db_t *db, dns_dbversion_t *version,
+ dns_dbnode_t *node, unsigned int hashalg,
+ unsigned int optin, unsigned int iterations,
+ const unsigned char *salt, size_t salt_length,
+ const unsigned char *nexthash, size_t hash_length,
+ unsigned char *buffer, dns_rdata_t *rdata);
+/*%<
+ * Build the rdata of a NSEC3 record for the data at 'node'.
+ * Note: 'node' is not the node where the NSEC3 record will be stored.
+ *
+ * Requires:
+ * buffer Points to a temporary buffer of at least
+ * DNS_NSEC_BUFFERSIZE bytes.
+ * rdata Points to an initialized dns_rdata_t.
+ *
+ * Ensures:
+ * *rdata Contains a valid NSEC3 rdata. The 'data' member refers
+ * to 'buffer'.
+ */
+
+isc_boolean_t
+dns_nsec3_typepresent(dns_rdata_t *nsec, dns_rdatatype_t type);
+/*%<
+ * Determine if a type is marked as present in an NSEC3 record.
+ *
+ * Requires:
+ * 'nsec' points to a valid rdataset of type NSEC3
+ */
+
+isc_result_t
+dns_nsec3_hashname(dns_fixedname_t *result,
+ unsigned char rethash[NSEC3_MAX_HASH_LENGTH],
+ size_t *hash_length, dns_name_t *name, dns_name_t *origin,
+ dns_hash_t hashalg, unsigned int iterations,
+ const unsigned char *salt, size_t saltlength);
+/*%<
+ * Make a hashed domain name from an unhashed one. If rethash is not NULL
+ * the raw hash is stored there.
+ */
+
+unsigned int
+dns_nsec3_hashlength(dns_hash_t hash);
+/*%<
+ * Return the length of the hash produced by the specified algorithm
+ * or zero when unknown.
+ */
+
+isc_boolean_t
+dns_nsec3_supportedhash(dns_hash_t hash);
+/*%<
+ * Return whether we support this hash algorithm or not.
+ */
+
+isc_result_t
+dns_nsec3_addnsec3(dns_db_t *db, dns_dbversion_t *version,
+ dns_name_t *name, const dns_rdata_nsec3param_t *nsec3param,
+ dns_ttl_t nsecttl, isc_boolean_t unsecure, dns_diff_t *diff);
+
+isc_result_t
+dns_nsec3_addnsec3s(dns_db_t *db, dns_dbversion_t *version,
+ dns_name_t *name, dns_ttl_t nsecttl,
+ isc_boolean_t unsecure, dns_diff_t *diff);
+/*%<
+ * Add NSEC3 records for 'name', recording the change in 'diff'.
+ * Adjust previous NSEC3 records, if any, to reflect the addition.
+ * The existing NSEC3 records are removed.
+ *
+ * dns_nsec3_addnsec3() will only add records to the chain identified by
+ * 'nsec3param'.
+ *
+ * 'unsecure' should be set to reflect if this is a potentially
+ * unsecure delegation (no DS record).
+ *
+ * dns_nsec3_addnsec3s() will examine the NSEC3PARAM RRset to determine which
+ * chains to be updated. NSEC3PARAM records with the DNS_NSEC3FLAG_CREATE
+ * will be preferentially choosen over NSEC3PARAM records without
+ * DNS_NSEC3FLAG_CREATE set. NSEC3PARAM records with DNS_NSEC3FLAG_REMOVE
+ * set will be ignored by dns_nsec3_addnsec3s(). If DNS_NSEC3FLAG_CREATE
+ * is set then the new NSEC3 will have OPTOUT set to match the that in the
+ * NSEC3PARAM record otherwise OPTOUT will be inherited from the previous
+ * record in the chain.
+ *
+ * Requires:
+ * 'db' to be valid.
+ * 'version' to be valid or NULL.
+ * 'name' to be valid.
+ * 'nsec3param' to be valid.
+ * 'diff' to be valid.
+ */
+
+isc_result_t
+dns_nsec3_delnsec3(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
+ const dns_rdata_nsec3param_t *nsec3param, dns_diff_t *diff);
+
+isc_result_t
+dns_nsec3_delnsec3s(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
+ dns_diff_t *diff);
+/*%<
+ * Remove NSEC3 records for 'name', recording the change in 'diff'.
+ * Adjust previous NSEC3 records, if any, to reflect the removal.
+ *
+ * dns_nsec3_delnsec3() performs the above for the chain identified by
+ * 'nsec3param'.
+ *
+ * dns_nsec3_delnsec3s() examines the NSEC3PARAM RRset in a similar manner
+ * to dns_nsec3_addnsec3s(). Unlike dns_nsec3_addnsec3s() updated NSEC3
+ * records have the OPTOUT flag preserved.
+ *
+ * Requires:
+ * 'db' to be valid.
+ * 'version' to be valid or NULL.
+ * 'name' to be valid.
+ * 'nsec3param' to be valid.
+ * 'diff' to be valid.
+ */
+
+isc_result_t
+dns_nsec3_active(dns_db_t *db, dns_dbversion_t *version,
+ isc_boolean_t complete, isc_boolean_t *answer);
+/*%<
+ * Check if there are any complete/to be built NSEC3 chains.
+ * If 'complete' is ISC_TRUE only complete chains will be recognised.
+ *
+ * Requires:
+ * 'db' to be valid.
+ * 'version' to be valid or NULL.
+ * 'answer' to be non NULL.
+ */
+
+isc_result_t
+dns_nsec3_maxiterations(dns_db_t *db, dns_dbversion_t *version,
+ isc_mem_t *mctx, unsigned int *iterationsp);
+/*%<
+ * Find the maximum permissible number of iterations allowed based on
+ * the key strength.
+ *
+ * Requires:
+ * 'db' to be valid.
+ * 'version' to be valid or NULL.
+ * 'mctx' to be valid.
+ * 'iterationsp' to be non NULL.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_NSEC3_H */
diff --git a/lib/dns/include/dns/opcode.h b/lib/dns/include/dns/opcode.h
new file mode 100644
index 0000000..368b2b2
--- /dev/null
+++ b/lib/dns/include/dns/opcode.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: opcode.h,v 1.8 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef DNS_OPCODE_H
+#define DNS_OPCODE_H 1
+
+/*! \file dns/opcode.h */
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t dns_opcode_totext(dns_opcode_t opcode, isc_buffer_t *target);
+/*%<
+ * Put a textual representation of error 'opcode' into 'target'.
+ *
+ * Requires:
+ *\li 'opcode' is a valid opcode.
+ *
+ *\li 'target' is a valid text buffer.
+ *
+ * Ensures:
+ *\li If the result is success:
+ * The used space in 'target' is updated.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS on success
+ *\li #ISC_R_NOSPACE target buffer is too small
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_OPCODE_H */
diff --git a/lib/dns/include/dns/order.h b/lib/dns/include/dns/order.h
new file mode 100644
index 0000000..85663c3
--- /dev/null
+++ b/lib/dns/include/dns/order.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: order.h,v 1.9 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef DNS_ORDER_H
+#define DNS_ORDER_H 1
+
+/*! \file dns/order.h */
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_order_create(isc_mem_t *mctx, dns_order_t **orderp);
+/*%<
+ * Create a order object.
+ *
+ * Requires:
+ * \li 'orderp' to be non NULL and '*orderp == NULL'.
+ *\li 'mctx' to be valid.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ *\li ISC_R_NOMEMORY
+ */
+
+isc_result_t
+dns_order_add(dns_order_t *order, dns_name_t *name,
+ dns_rdatatype_t rdtype, dns_rdataclass_t rdclass,
+ unsigned int mode);
+/*%<
+ * Add a entry to the end of the order list.
+ *
+ * Requires:
+ * \li 'order' to be valid.
+ *\li 'name' to be valid.
+ *\li 'mode' to be one of #DNS_RDATASERATTR_RANDOMIZE,
+ * #DNS_RDATASERATTR_RANDOMIZE or zero (#DNS_RDATASERATTR_CYCLIC).
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ */
+
+unsigned int
+dns_order_find(dns_order_t *order, dns_name_t *name,
+ dns_rdatatype_t rdtype, dns_rdataclass_t rdclass);
+/*%<
+ * Find the first matching entry on the list.
+ *
+ * Requires:
+ *\li 'order' to be valid.
+ *\li 'name' to be valid.
+ *
+ * Returns the mode set by dns_order_add() or zero.
+ */
+
+void
+dns_order_attach(dns_order_t *source, dns_order_t **target);
+/*%<
+ * Attach to the 'source' object.
+ *
+ * Requires:
+ * \li 'source' to be valid.
+ *\li 'target' to be non NULL and '*target == NULL'.
+ */
+
+void
+dns_order_detach(dns_order_t **orderp);
+/*%<
+ * Detach from the object. Clean up if last this was the last
+ * reference.
+ *
+ * Requires:
+ *\li '*orderp' to be valid.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_ORDER_H */
diff --git a/lib/dns/include/dns/peer.h b/lib/dns/include/dns/peer.h
new file mode 100644
index 0000000..c4b95c9
--- /dev/null
+++ b/lib/dns/include/dns/peer.h
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001, 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: peer.h,v 1.33 2008/04/03 06:09:05 tbox Exp $ */
+
+#ifndef DNS_PEER_H
+#define DNS_PEER_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/peer.h
+ * \brief
+ * Data structures for peers (e.g. a 'server' config file statement)
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <isc/lang.h>
+#include <isc/magic.h>
+#include <isc/netaddr.h>
+
+#include <dns/types.h>
+
+#define DNS_PEERLIST_MAGIC ISC_MAGIC('s','e','R','L')
+#define DNS_PEER_MAGIC ISC_MAGIC('S','E','r','v')
+
+#define DNS_PEERLIST_VALID(ptr) ISC_MAGIC_VALID(ptr, DNS_PEERLIST_MAGIC)
+#define DNS_PEER_VALID(ptr) ISC_MAGIC_VALID(ptr, DNS_PEER_MAGIC)
+
+/***
+ *** Types
+ ***/
+
+struct dns_peerlist {
+ unsigned int magic;
+ isc_uint32_t refs;
+
+ isc_mem_t *mem;
+
+ ISC_LIST(dns_peer_t) elements;
+};
+
+struct dns_peer {
+ unsigned int magic;
+ isc_uint32_t refs;
+
+ isc_mem_t *mem;
+
+ isc_netaddr_t address;
+ unsigned int prefixlen;
+ isc_boolean_t bogus;
+ dns_transfer_format_t transfer_format;
+ isc_uint32_t transfers;
+ isc_boolean_t support_ixfr;
+ isc_boolean_t provide_ixfr;
+ isc_boolean_t request_ixfr;
+ isc_boolean_t support_edns;
+ isc_boolean_t request_nsid;
+ dns_name_t *key;
+ isc_sockaddr_t *transfer_source;
+ isc_sockaddr_t *notify_source;
+ isc_sockaddr_t *query_source;
+ isc_uint16_t udpsize; /* recieve size */
+ isc_uint16_t maxudp; /* transmit size */
+
+ isc_uint32_t bitflags;
+
+ ISC_LINK(dns_peer_t) next;
+};
+
+/***
+ *** Functions
+ ***/
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_peerlist_new(isc_mem_t *mem, dns_peerlist_t **list);
+
+void
+dns_peerlist_attach(dns_peerlist_t *source, dns_peerlist_t **target);
+
+void
+dns_peerlist_detach(dns_peerlist_t **list);
+
+/*
+ * After return caller still holds a reference to peer.
+ */
+void
+dns_peerlist_addpeer(dns_peerlist_t *peers, dns_peer_t *peer);
+
+/*
+ * Ditto. */
+isc_result_t
+dns_peerlist_peerbyaddr(dns_peerlist_t *peers, isc_netaddr_t *addr,
+ dns_peer_t **retval);
+
+/*
+ * What he said.
+ */
+isc_result_t
+dns_peerlist_currpeer(dns_peerlist_t *peers, dns_peer_t **retval);
+
+isc_result_t
+dns_peer_new(isc_mem_t *mem, isc_netaddr_t *ipaddr, dns_peer_t **peer);
+
+isc_result_t
+dns_peer_newprefix(isc_mem_t *mem, isc_netaddr_t *ipaddr,
+ unsigned int prefixlen, dns_peer_t **peer);
+
+void
+dns_peer_attach(dns_peer_t *source, dns_peer_t **target);
+
+void
+dns_peer_detach(dns_peer_t **list);
+
+isc_result_t
+dns_peer_setbogus(dns_peer_t *peer, isc_boolean_t newval);
+
+isc_result_t
+dns_peer_getbogus(dns_peer_t *peer, isc_boolean_t *retval);
+
+isc_result_t
+dns_peer_setrequestixfr(dns_peer_t *peer, isc_boolean_t newval);
+
+isc_result_t
+dns_peer_getrequestixfr(dns_peer_t *peer, isc_boolean_t *retval);
+
+isc_result_t
+dns_peer_setprovideixfr(dns_peer_t *peer, isc_boolean_t newval);
+
+isc_result_t
+dns_peer_getprovideixfr(dns_peer_t *peer, isc_boolean_t *retval);
+
+isc_result_t
+dns_peer_setrequestnsid(dns_peer_t *peer, isc_boolean_t newval);
+
+isc_result_t
+dns_peer_getrequestnsid(dns_peer_t *peer, isc_boolean_t *retval);
+
+isc_result_t
+dns_peer_setsupportedns(dns_peer_t *peer, isc_boolean_t newval);
+
+isc_result_t
+dns_peer_getsupportedns(dns_peer_t *peer, isc_boolean_t *retval);
+
+isc_result_t
+dns_peer_settransfers(dns_peer_t *peer, isc_uint32_t newval);
+
+isc_result_t
+dns_peer_gettransfers(dns_peer_t *peer, isc_uint32_t *retval);
+
+isc_result_t
+dns_peer_settransferformat(dns_peer_t *peer, dns_transfer_format_t newval);
+
+isc_result_t
+dns_peer_gettransferformat(dns_peer_t *peer, dns_transfer_format_t *retval);
+
+isc_result_t
+dns_peer_setkeybycharp(dns_peer_t *peer, const char *keyval);
+
+isc_result_t
+dns_peer_getkey(dns_peer_t *peer, dns_name_t **retval);
+
+isc_result_t
+dns_peer_setkey(dns_peer_t *peer, dns_name_t **keyval);
+
+isc_result_t
+dns_peer_settransfersource(dns_peer_t *peer,
+ const isc_sockaddr_t *transfer_source);
+
+isc_result_t
+dns_peer_gettransfersource(dns_peer_t *peer, isc_sockaddr_t *transfer_source);
+
+isc_result_t
+dns_peer_setudpsize(dns_peer_t *peer, isc_uint16_t udpsize);
+
+isc_result_t
+dns_peer_getudpsize(dns_peer_t *peer, isc_uint16_t *udpsize);
+
+isc_result_t
+dns_peer_setmaxudp(dns_peer_t *peer, isc_uint16_t maxudp);
+
+isc_result_t
+dns_peer_getmaxudp(dns_peer_t *peer, isc_uint16_t *maxudp);
+
+isc_result_t
+dns_peer_setnotifysource(dns_peer_t *peer, const isc_sockaddr_t *notify_source);
+
+isc_result_t
+dns_peer_getnotifysource(dns_peer_t *peer, isc_sockaddr_t *notify_source);
+
+isc_result_t
+dns_peer_setquerysource(dns_peer_t *peer, const isc_sockaddr_t *query_source);
+
+isc_result_t
+dns_peer_getquerysource(dns_peer_t *peer, isc_sockaddr_t *query_source);
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_PEER_H */
diff --git a/lib/dns/include/dns/portlist.h b/lib/dns/include/dns/portlist.h
new file mode 100644
index 0000000..f76731a
--- /dev/null
+++ b/lib/dns/include/dns/portlist.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: portlist.h,v 1.9 2007/06/19 23:47:17 tbox Exp $ */
+
+/*! \file dns/portlist.h */
+
+#include <isc/lang.h>
+#include <isc/net.h>
+#include <isc/types.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_portlist_create(isc_mem_t *mctx, dns_portlist_t **portlistp);
+/*%<
+ * Create a port list.
+ *
+ * Requires:
+ *\li 'mctx' to be valid.
+ *\li 'portlistp' to be non NULL and '*portlistp' to be NULL;
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *\li #ISC_R_UNEXPECTED
+ */
+
+isc_result_t
+dns_portlist_add(dns_portlist_t *portlist, int af, in_port_t port);
+/*%<
+ * Add the given <port,af> tuple to the portlist.
+ *
+ * Requires:
+ *\li 'portlist' to be valid.
+ *\li 'af' to be AF_INET or AF_INET6
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ */
+
+void
+dns_portlist_remove(dns_portlist_t *portlist, int af, in_port_t port);
+/*%<
+ * Remove the given <port,af> tuple to the portlist.
+ *
+ * Requires:
+ *\li 'portlist' to be valid.
+ *\li 'af' to be AF_INET or AF_INET6
+ */
+
+isc_boolean_t
+dns_portlist_match(dns_portlist_t *portlist, int af, in_port_t port);
+/*%<
+ * Find the given <port,af> tuple to the portlist.
+ *
+ * Requires:
+ *\li 'portlist' to be valid.
+ *\li 'af' to be AF_INET or AF_INET6
+ *
+ * Returns
+ * \li #ISC_TRUE if the tuple is found, ISC_FALSE otherwise.
+ */
+
+void
+dns_portlist_attach(dns_portlist_t *portlist, dns_portlist_t **portlistp);
+/*%<
+ * Attach to a port list.
+ *
+ * Requires:
+ *\li 'portlist' to be valid.
+ *\li 'portlistp' to be non NULL and '*portlistp' to be NULL;
+ */
+
+void
+dns_portlist_detach(dns_portlist_t **portlistp);
+/*%<
+ * Detach from a port list.
+ *
+ * Requires:
+ *\li '*portlistp' to be valid.
+ */
+
+ISC_LANG_ENDDECLS
diff --git a/lib/dns/include/dns/rbt.h b/lib/dns/include/dns/rbt.h
new file mode 100644
index 0000000..06b55d5
--- /dev/null
+++ b/lib/dns/include/dns/rbt.h
@@ -0,0 +1,939 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rbt.h,v 1.71 2008/09/25 04:02:39 tbox Exp $ */
+
+#ifndef DNS_RBT_H
+#define DNS_RBT_H 1
+
+/*! \file dns/rbt.h */
+
+#include <isc/lang.h>
+#include <isc/magic.h>
+#include <isc/refcount.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+#define DNS_RBT_USEHASH 1
+
+/*@{*/
+/*%
+ * Option values for dns_rbt_findnode() and dns_rbt_findname().
+ * These are used to form a bitmask.
+ */
+#define DNS_RBTFIND_NOOPTIONS 0x00
+#define DNS_RBTFIND_EMPTYDATA 0x01
+#define DNS_RBTFIND_NOEXACT 0x02
+#define DNS_RBTFIND_NOPREDECESSOR 0x04
+/*@}*/
+
+#ifndef DNS_RBT_USEISCREFCOUNT
+#ifdef ISC_REFCOUNT_HAVEATOMIC
+#define DNS_RBT_USEISCREFCOUNT 1
+#endif
+#endif
+
+/*
+ * These should add up to 30.
+ */
+#define DNS_RBT_LOCKLENGTH 10
+#define DNS_RBT_REFLENGTH 20
+
+#define DNS_RBTNODE_MAGIC ISC_MAGIC('R','B','N','O')
+#if DNS_RBT_USEMAGIC
+#define DNS_RBTNODE_VALID(n) ISC_MAGIC_VALID(n, DNS_RBTNODE_MAGIC)
+#else
+#define DNS_RBTNODE_VALID(n) ISC_TRUE
+#endif
+
+/*%
+ * This is the structure that is used for each node in the red/black
+ * tree of trees. NOTE WELL: the implementation manages this as a variable
+ * length structure, with the actual wire-format name and other data
+ * appended to this structure. Allocating a contiguous block of memory for
+ * multiple dns_rbtnode structures will not work.
+ */
+typedef struct dns_rbtnode dns_rbtnode_t;
+struct dns_rbtnode {
+#if DNS_RBT_USEMAGIC
+ unsigned int magic;
+#endif
+ dns_rbtnode_t *parent;
+ dns_rbtnode_t *left;
+ dns_rbtnode_t *right;
+ dns_rbtnode_t *down;
+#ifdef DNS_RBT_USEHASH
+ dns_rbtnode_t *hashnext;
+#endif
+
+ /*%
+ * Used for LRU cache. This linked list is used to mark nodes which
+ * have no data any longer, but we cannot unlink at that exact moment
+ * because we did not or could not obtain a write lock on the tree.
+ */
+ ISC_LINK(dns_rbtnode_t) deadlink;
+
+ /*@{*/
+ /*!
+ * The following bitfields add up to a total bitwidth of 32.
+ * The range of values necessary for each item is indicated,
+ * but in the case of "attributes" the field is wider to accomodate
+ * possible future expansion. "offsetlen" could be one bit
+ * narrower by always adjusting its value by 1 to find the real
+ * offsetlen, but doing so does not gain anything (except perhaps
+ * another bit for "attributes", which doesn't yet need any more).
+ *
+ * In each case below the "range" indicated is what's _necessary_ for
+ * the bitfield to hold, not what it actually _can_ hold.
+ */
+ unsigned int is_root : 1; /*%< range is 0..1 */
+ unsigned int color : 1; /*%< range is 0..1 */
+ unsigned int find_callback : 1; /*%< range is 0..1 */
+ unsigned int attributes : 3; /*%< range is 0..2 */
+ unsigned int nsec3 : 1; /*%< range is 0..1 */
+ unsigned int namelen : 8; /*%< range is 1..255 */
+ unsigned int offsetlen : 8; /*%< range is 1..128 */
+ unsigned int padbytes : 9; /*%< range is 0..380 */
+ /*@}*/
+
+#ifdef DNS_RBT_USEHASH
+ unsigned int hashval;
+#endif
+
+ /*@{*/
+ /*!
+ * These values are used in the RBT DB implementation. The appropriate
+ * node lock must be held before accessing them.
+ */
+ void *data;
+ unsigned int dirty:1;
+ unsigned int wild:1;
+ unsigned int locknum:DNS_RBT_LOCKLENGTH;
+#ifndef DNS_RBT_USEISCREFCOUNT
+ unsigned int references:DNS_RBT_REFLENGTH;
+#else
+ isc_refcount_t references; /* note that this is not in the bitfield */
+#endif
+ /*@}*/
+};
+
+typedef isc_result_t (*dns_rbtfindcallback_t)(dns_rbtnode_t *node,
+ dns_name_t *name,
+ void *callback_arg);
+
+/*****
+ ***** Chain Info
+ *****/
+
+/*!
+ * A chain is used to keep track of the sequence of nodes to reach any given
+ * node from the root of the tree. Originally nodes did not have parent
+ * pointers in them (for memory usage reasons) so there was no way to find
+ * the path back to the root from any given node. Now that nodes have parent
+ * pointers, chains might be going away in a future release, though the
+ * movement functionality would remain.
+ *
+ * In any event, parent information, whether via parent pointers or chains, is
+ * necessary information for iterating through the tree or for basic internal
+ * tree maintenance issues (ie, the rotations that are done to rebalance the
+ * tree when a node is added). The obvious implication of this is that for a
+ * chain to remain valid, the tree has to be locked down against writes for the
+ * duration of the useful life of the chain, because additions or removals can
+ * change the path from the root to the node the chain has targetted.
+ *
+ * The dns_rbtnodechain_ functions _first, _last, _prev and _next all take
+ * dns_name_t parameters for the name and the origin, which can be NULL. If
+ * non-NULL, 'name' will end up pointing to the name data and offsets that are
+ * stored at the node (and thus it will be read-only), so it should be a
+ * regular dns_name_t that has been initialized with dns_name_init. When
+ * 'origin' is non-NULL, it will get the name of the origin stored in it, so it
+ * needs to have its own buffer space and offsets, which is most easily
+ * accomplished with a dns_fixedname_t. It is _not_ necessary to reinitialize
+ * either 'name' or 'origin' between calls to the chain functions.
+ *
+ * NOTE WELL: even though the name data at the root of the tree of trees will
+ * be absolute (typically just "."), it will will be made into a relative name
+ * with an origin of "." -- an empty name when the node is ".". This is
+ * because a common on operation on 'name' and 'origin' is to use
+ * dns_name_concatenate() on them to generate the complete name. An empty name
+ * can be detected when dns_name_countlabels == 0, and is printed by
+ * dns_name_totext()/dns_name_format() as "@", consistent with RFC1035's
+ * definition of "@" as the current origin.
+ *
+ * dns_rbtnodechain_current is similar to the _first, _last, _prev and _next
+ * functions but additionally can provide the node to which the chain points.
+ */
+
+/*%
+ * The number of level blocks to allocate at a time. Currently the maximum
+ * number of levels is allocated directly in the structure, but future
+ * revisions of this code might have a static initial block with dynamic
+ * growth. Allocating space for 256 levels when the tree is almost never that
+ * deep is wasteful, but it's not clear that it matters, since the waste is
+ * only 2MB for 1000 concurrently active chains on a system with 64-bit
+ * pointers.
+ */
+#define DNS_RBT_LEVELBLOCK 254
+
+typedef struct dns_rbtnodechain {
+ unsigned int magic;
+ isc_mem_t * mctx;
+ /*%
+ * The terminal node of the chain. It is not in levels[].
+ * This is ostensibly private ... but in a pinch it could be
+ * used tell that the chain points nowhere without needing to
+ * call dns_rbtnodechain_current().
+ */
+ dns_rbtnode_t * end;
+ /*%
+ * The maximum number of labels in a name is 128; bitstrings mean
+ * a conceptually very large number (which I have not bothered to
+ * compute) of logical levels because splitting can potentially occur
+ * at each bit. However, DNSSEC restricts the number of "logical"
+ * labels in a name to 255, meaning only 254 pointers are needed
+ * in the worst case.
+ */
+ dns_rbtnode_t * levels[DNS_RBT_LEVELBLOCK];
+ /*%
+ * level_count indicates how deep the chain points into the
+ * tree of trees, and is the index into the levels[] array.
+ * Thus, levels[level_count - 1] is the last level node stored.
+ * A chain that points to the top level of the tree of trees has
+ * a level_count of 0, the first level has a level_count of 1, and
+ * so on.
+ */
+ unsigned int level_count;
+ /*%
+ * level_matches tells how many levels matched above the node
+ * returned by dns_rbt_findnode(). A match (partial or exact) found
+ * in the first level thus results in level_matches being set to 1.
+ * This is used by the rbtdb to set the start point for a recursive
+ * search of superdomains until the RR it is looking for is found.
+ */
+ unsigned int level_matches;
+} dns_rbtnodechain_t;
+
+/*****
+ ***** Public interfaces.
+ *****/
+isc_result_t
+dns_rbt_create(isc_mem_t *mctx, void (*deleter)(void *, void *),
+ void *deleter_arg, dns_rbt_t **rbtp);
+/*%<
+ * Initialize a red-black tree of trees.
+ *
+ * Notes:
+ *\li The deleter argument, if non-null, points to a function that is
+ * responsible for cleaning up any memory associated with the data
+ * pointer of a node when the node is deleted. It is passed the
+ * deleted node's data pointer as its first argument and deleter_arg
+ * as its second argument.
+ *
+ * Requires:
+ * \li mctx is a pointer to a valid memory context.
+ *\li rbtp != NULL && *rbtp == NULL
+ *\li arg == NULL iff deleter == NULL
+ *
+ * Ensures:
+ *\li If result is ISC_R_SUCCESS:
+ * *rbtp points to a valid red-black tree manager
+ *
+ *\li If result is failure:
+ * *rbtp does not point to a valid red-black tree manager.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS Success
+ *\li #ISC_R_NOMEMORY Resource limit: Out of Memory
+ */
+
+isc_result_t
+dns_rbt_addname(dns_rbt_t *rbt, dns_name_t *name, void *data);
+/*%<
+ * Add 'name' to the tree of trees, associated with 'data'.
+ *
+ * Notes:
+ *\li 'data' is never required to be non-NULL, but specifying it
+ * when the name is added is faster than searching for 'name'
+ * again and then setting the data pointer. The lack of a data pointer
+ * for a node also has other ramifications regarding whether
+ * dns_rbt_findname considers a node to exist, or dns_rbt_deletename
+ * joins nodes.
+ *
+ * Requires:
+ *\li rbt is a valid rbt manager.
+ *\li dns_name_isabsolute(name) == TRUE
+ *
+ * Ensures:
+ *\li 'name' is not altered in any way.
+ *
+ *\li Any external references to nodes in the tree are unaffected by
+ * node splits that are necessary to insert the new name.
+ *
+ *\li If result is #ISC_R_SUCCESS:
+ * 'name' is findable in the red/black tree of trees in O(log N).
+ * The data pointer of the node for 'name' is set to 'data'.
+ *
+ *\li If result is #ISC_R_EXISTS or #ISC_R_NOSPACE:
+ * The tree of trees is unaltered.
+ *
+ *\li If result is #ISC_R_NOMEMORY:
+ * No guarantees.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS Success
+ *\li #ISC_R_EXISTS The name already exists with associated data.
+ *\li #ISC_R_NOSPACE The name had more logical labels than are allowed.
+ *\li #ISC_R_NOMEMORY Resource Limit: Out of Memory
+ */
+
+isc_result_t
+dns_rbt_addnode(dns_rbt_t *rbt, dns_name_t *name, dns_rbtnode_t **nodep);
+
+/*%<
+ * Just like dns_rbt_addname, but returns the address of the node.
+ *
+ * Requires:
+ *\li rbt is a valid rbt structure.
+ *\li dns_name_isabsolute(name) == TRUE
+ *\li nodep != NULL && *nodep == NULL
+ *
+ * Ensures:
+ *\li 'name' is not altered in any way.
+ *
+ *\li Any external references to nodes in the tree are unaffected by
+ * node splits that are necessary to insert the new name.
+ *
+ *\li If result is ISC_R_SUCCESS:
+ * 'name' is findable in the red/black tree of trees in O(log N).
+ * *nodep is the node that was added for 'name'.
+ *
+ *\li If result is ISC_R_EXISTS:
+ * The tree of trees is unaltered.
+ * *nodep is the existing node for 'name'.
+ *
+ *\li If result is ISC_R_NOMEMORY:
+ * No guarantees.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS Success
+ *\li #ISC_R_EXISTS The name already exists, possibly without data.
+ *\li #ISC_R_NOMEMORY Resource Limit: Out of Memory
+ */
+
+isc_result_t
+dns_rbt_findname(dns_rbt_t *rbt, dns_name_t *name, unsigned int options,
+ dns_name_t *foundname, void **data);
+/*%<
+ * Get the data pointer associated with 'name'.
+ *
+ * Notes:
+ *\li When #DNS_RBTFIND_NOEXACT is set, the closest matching superdomain is
+ * returned (also subject to #DNS_RBTFIND_EMPTYDATA), even when there is
+ * an exact match in the tree.
+ *
+ *\li A node that has no data is considered not to exist for this function,
+ * unless the #DNS_RBTFIND_EMPTYDATA option is set.
+ *
+ * Requires:
+ *\li rbt is a valid rbt manager.
+ *\li dns_name_isabsolute(name) == TRUE
+ *\li data != NULL && *data == NULL
+ *
+ * Ensures:
+ *\li 'name' and the tree are not altered in any way.
+ *
+ *\li If result is ISC_R_SUCCESS:
+ * *data is the data associated with 'name'.
+ *
+ *\li If result is DNS_R_PARTIALMATCH:
+ * *data is the data associated with the deepest superdomain
+ * of 'name' which has data.
+ *
+ *\li If result is ISC_R_NOTFOUND:
+ * Neither the name nor a superdomain was found with data.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS Success
+ *\li #DNS_R_PARTIALMATCH Superdomain found with data
+ *\li #ISC_R_NOTFOUND No match
+ *\li #ISC_R_NOSPACE Concatenating nodes to form foundname failed
+ */
+
+isc_result_t
+dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
+ dns_rbtnode_t **node, dns_rbtnodechain_t *chain,
+ unsigned int options, dns_rbtfindcallback_t callback,
+ void *callback_arg);
+/*%<
+ * Find the node for 'name'.
+ *
+ * Notes:
+ *\li A node that has no data is considered not to exist for this function,
+ * unless the DNS_RBTFIND_EMPTYDATA option is set. This applies to both
+ * exact matches and partial matches.
+ *
+ *\li If the chain parameter is non-NULL, then the path through the tree
+ * to the DNSSEC predecessor of the searched for name is maintained,
+ * unless the DNS_RBTFIND_NOPREDECESSOR or DNS_RBTFIND_NOEXACT option
+ * is used. (For more details on those options, see below.)
+ *
+ *\li If there is no predecessor, then the chain will point to nowhere, as
+ * indicated by chain->end being NULL or dns_rbtnodechain_current
+ * returning ISC_R_NOTFOUND. Note that in a normal Internet DNS RBT
+ * there will always be a predecessor for all names except the root
+ * name, because '.' will exist and '.' is the predecessor of
+ * everything. But you can certainly construct a trivial tree and a
+ * search for it that has no predecessor.
+ *
+ *\li Within the chain structure, the 'levels' member of the structure holds
+ * the root node of each level except the first.
+ *
+ *\li The 'level_count' of the chain indicates how deep the chain to the
+ * predecessor name is, as an index into the 'levels[]' array. It does
+ * not count name elements, per se, but only levels of the tree of trees,
+ * the distinction arrising because multiple labels from a name can be
+ * stored on only one level. It is also does not include the level
+ * that has the node, since that level is not stored in levels[].
+ *
+ *\li The chain's 'level_matches' is not directly related to the predecessor.
+ * It is the number of levels above the level of the found 'node',
+ * regardless of whether it was a partial match or exact match. When
+ * the node is found in the top level tree, or no node is found at all,
+ * level_matches is 0.
+ *
+ *\li When DNS_RBTFIND_NOEXACT is set, the closest matching superdomain is
+ * returned (also subject to DNS_RBTFIND_EMPTYDATA), even when
+ * there is an exact match in the tree. In this case, the chain
+ * will not point to the DNSSEC predecessor, but will instead point
+ * to the exact match, if there was any. Thus the preceding paragraphs
+ * should have "exact match" substituted for "predecessor" to describe
+ * how the various elements of the chain are set. This was done to
+ * ensure that the chain's state was sane, and to prevent problems that
+ * occurred when running the predecessor location code under conditions
+ * it was not designed for. It is not clear *where* the chain should
+ * point when DNS_RBTFIND_NOEXACT is set, so if you end up using a chain
+ * with this option because you want a particular node, let us know
+ * where you want the chain pointed, so this can be made more firm.
+ *
+ * Requires:
+ *\li rbt is a valid rbt manager.
+ *\li dns_name_isabsolute(name) == TRUE.
+ *\li node != NULL && *node == NULL.
+ *\li #DNS_RBTFIND_NOEXACT and DNS_RBTFIND_NOPREDECESSOR are mutally
+ * exclusive.
+ *
+ * Ensures:
+ *\li 'name' and the tree are not altered in any way.
+ *
+ *\li If result is ISC_R_SUCCESS:
+ *\verbatim
+ * *node is the terminal node for 'name'.
+
+ * 'foundname' and 'name' represent the same name (though not
+ * the same memory).
+
+ * 'chain' points to the DNSSEC predecessor, if any, of 'name'.
+ *
+ * chain->level_matches and chain->level_count are equal.
+ *\endverbatim
+ *
+ * If result is DNS_R_PARTIALMATCH:
+ *\verbatim
+ * *node is the data associated with the deepest superdomain
+ * of 'name' which has data.
+ *
+ * 'foundname' is the name of deepest superdomain (which has
+ * data, unless the DNS_RBTFIND_EMPTYDATA option is set).
+ *
+ * 'chain' points to the DNSSEC predecessor, if any, of 'name'.
+ *\endverbatim
+ *
+ *\li If result is ISC_R_NOTFOUND:
+ *\verbatim
+ * Neither the name nor a superdomain was found. *node is NULL.
+ *
+ * 'chain' points to the DNSSEC predecessor, if any, of 'name'.
+ *
+ * chain->level_matches is 0.
+ *\endverbatim
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS Success
+ *\li #DNS_R_PARTIALMATCH Superdomain found with data
+ *\li #ISC_R_NOTFOUND No match, or superdomain with no data
+ *\li #ISC_R_NOSPACE Concatenating nodes to form foundname failed
+ */
+
+isc_result_t
+dns_rbt_deletename(dns_rbt_t *rbt, dns_name_t *name, isc_boolean_t recurse);
+/*%<
+ * Delete 'name' from the tree of trees.
+ *
+ * Notes:
+ *\li When 'name' is removed, if recurse is ISC_TRUE then all of its
+ * subnames are removed too.
+ *
+ * Requires:
+ *\li rbt is a valid rbt manager.
+ *\li dns_name_isabsolute(name) == TRUE
+ *
+ * Ensures:
+ *\li 'name' is not altered in any way.
+ *
+ *\li Does NOT ensure that any external references to nodes in the tree
+ * are unaffected by node joins.
+ *
+ *\li If result is ISC_R_SUCCESS:
+ * 'name' does not appear in the tree with data; however,
+ * the node for the name might still exist which can be
+ * found with dns_rbt_findnode (but not dns_rbt_findname).
+ *
+ *\li If result is ISC_R_NOTFOUND:
+ * 'name' does not appear in the tree with data, because
+ * it did not appear in the tree before the function was called.
+ *
+ *\li If result is something else:
+ * See result codes for dns_rbt_findnode (if it fails, the
+ * node is not deleted) or dns_rbt_deletenode (if it fails,
+ * the node is deleted, but the tree is not optimized when
+ * it could have been).
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS Success
+ *\li #ISC_R_NOTFOUND No match
+ *\li something_else Any return code from dns_rbt_findnode except
+ * DNS_R_PARTIALMATCH (which causes ISC_R_NOTFOUND
+ * to be returned instead), and any code from
+ * dns_rbt_deletenode.
+ */
+
+isc_result_t
+dns_rbt_deletenode(dns_rbt_t *rbt, dns_rbtnode_t *node, isc_boolean_t recurse);
+/*%<
+ * Delete 'node' from the tree of trees.
+ *
+ * Notes:
+ *\li When 'node' is removed, if recurse is ISC_TRUE then all nodes
+ * in levels down from it are removed too.
+ *
+ * Requires:
+ *\li rbt is a valid rbt manager.
+ *\li node != NULL.
+ *
+ * Ensures:
+ *\li Does NOT ensure that any external references to nodes in the tree
+ * are unaffected by node joins.
+ *
+ *\li If result is ISC_R_SUCCESS:
+ * 'node' does not appear in the tree with data; however,
+ * the node might still exist if it serves as a pointer to
+ * a lower tree level as long as 'recurse' was false, hence
+ * the node could can be found with dns_rbt_findnode whem
+ * that function's empty_data_ok parameter is true.
+ *
+ *\li If result is ISC_R_NOMEMORY or ISC_R_NOSPACE:
+ * The node was deleted, but the tree structure was not
+ * optimized.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS Success
+ *\li #ISC_R_NOMEMORY Resource Limit: Out of Memory when joining nodes.
+ *\li #ISC_R_NOSPACE dns_name_concatenate failed when joining nodes.
+ */
+
+void
+dns_rbt_namefromnode(dns_rbtnode_t *node, dns_name_t *name);
+/*%<
+ * Convert the sequence of labels stored at 'node' into a 'name'.
+ *
+ * Notes:
+ *\li This function does not return the full name, from the root, but
+ * just the labels at the indicated node.
+ *
+ *\li The name data pointed to by 'name' is the information stored
+ * in the node, not a copy. Altering the data at this pointer
+ * will likely cause grief.
+ *
+ * Requires:
+ * \li name->offsets == NULL
+ *
+ * Ensures:
+ * \li 'name' is DNS_NAMEATTR_READONLY.
+ *
+ * \li 'name' will point directly to the labels stored after the
+ * dns_rbtnode_t struct.
+ *
+ * \li 'name' will have offsets that also point to the information stored
+ * as part of the node.
+ */
+
+isc_result_t
+dns_rbt_fullnamefromnode(dns_rbtnode_t *node, dns_name_t *name);
+/*%<
+ * Like dns_rbt_namefromnode, but returns the full name from the root.
+ *
+ * Notes:
+ * \li Unlike dns_rbt_namefromnode, the name will not point directly
+ * to node data. Rather, dns_name_concatenate will be used to copy
+ * the name data from each node into the 'name' argument.
+ *
+ * Requires:
+ * \li name != NULL
+ * \li name has a dedicated buffer.
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li ISC_R_NOSPACE (possible via dns_name_concatenate)
+ * \li DNS_R_NAMETOOLONG (possible via dns_name_concatenate)
+ */
+
+char *
+dns_rbt_formatnodename(dns_rbtnode_t *node, char *printname,
+ unsigned int size);
+/*%<
+ * Format the full name of a node for printing, using dns_name_format().
+ *
+ * Notes:
+ * \li 'size' is the length of the printname buffer. This should be
+ * DNS_NAME_FORMATSIZE or larger.
+ *
+ * Requires:
+ * \li node and printname are not NULL.
+ *
+ * Returns:
+ * \li The 'printname' pointer.
+ */
+
+unsigned int
+dns_rbt_nodecount(dns_rbt_t *rbt);
+/*%<
+ * Obtain the number of nodes in the tree of trees.
+ *
+ * Requires:
+ * \li rbt is a valid rbt manager.
+ */
+
+void
+dns_rbt_destroy(dns_rbt_t **rbtp);
+isc_result_t
+dns_rbt_destroy2(dns_rbt_t **rbtp, unsigned int quantum);
+/*%<
+ * Stop working with a red-black tree of trees.
+ * If 'quantum' is zero then the entire tree will be destroyed.
+ * If 'quantum' is non zero then up to 'quantum' nodes will be destroyed
+ * allowing the rbt to be incrementally destroyed by repeated calls to
+ * dns_rbt_destroy2(). Once dns_rbt_destroy2() has been called no other
+ * operations than dns_rbt_destroy()/dns_rbt_destroy2() should be
+ * performed on the tree of trees.
+ *
+ * Requires:
+ * \li *rbt is a valid rbt manager.
+ *
+ * Ensures on ISC_R_SUCCESS:
+ * \li All space allocated by the RBT library has been returned.
+ *
+ * \li *rbt is invalidated as an rbt manager.
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li ISC_R_QUOTA if 'quantum' nodes have been destroyed.
+ */
+
+void
+dns_rbt_printall(dns_rbt_t *rbt);
+/*%<
+ * Print an ASCII representation of the internal structure of the red-black
+ * tree of trees.
+ *
+ * Notes:
+ * \li The name stored at each node, along with the node's color, is printed.
+ * Then the down pointer, left and right pointers are displayed
+ * recursively in turn. NULL down pointers are silently omitted;
+ * NULL left and right pointers are printed.
+ */
+
+/*****
+ ***** Chain Functions
+ *****/
+
+void
+dns_rbtnodechain_init(dns_rbtnodechain_t *chain, isc_mem_t *mctx);
+/*%<
+ * Initialize 'chain'.
+ *
+ * Requires:
+ *\li 'chain' is a valid pointer.
+ *
+ *\li 'mctx' is a valid memory context.
+ *
+ * Ensures:
+ *\li 'chain' is suitable for use.
+ */
+
+void
+dns_rbtnodechain_reset(dns_rbtnodechain_t *chain);
+/*%<
+ * Free any dynamic storage associated with 'chain', and then reinitialize
+ * 'chain'.
+ *
+ * Requires:
+ *\li 'chain' is a valid pointer.
+ *
+ * Ensures:
+ *\li 'chain' is suitable for use, and uses no dynamic storage.
+ */
+
+void
+dns_rbtnodechain_invalidate(dns_rbtnodechain_t *chain);
+/*%<
+ * Free any dynamic storage associated with 'chain', and then invalidates it.
+ *
+ * Notes:
+ *\li Future calls to any dns_rbtnodechain_ function will need to call
+ * dns_rbtnodechain_init on the chain first (except, of course,
+ * dns_rbtnodechain_init itself).
+ *
+ * Requires:
+ *\li 'chain' is a valid chain.
+ *
+ * Ensures:
+ *\li 'chain' is no longer suitable for use, and uses no dynamic storage.
+ */
+
+isc_result_t
+dns_rbtnodechain_current(dns_rbtnodechain_t *chain, dns_name_t *name,
+ dns_name_t *origin, dns_rbtnode_t **node);
+/*%<
+ * Provide the name, origin and node to which the chain is currently pointed.
+ *
+ * Notes:
+ *\li The tree need not have be locked against additions for the chain
+ * to remain valid, however there are no guarantees if any deletion
+ * has been made since the chain was established.
+ *
+ * Requires:
+ *\li 'chain' is a valid chain.
+ *
+ * Ensures:
+ *\li 'node', if non-NULL, is the node to which the chain was pointed
+ * by dns_rbt_findnode, dns_rbtnodechain_first or dns_rbtnodechain_last.
+ * If none were called for the chain since it was initialized or reset,
+ * or if the was no predecessor to the name searched for with
+ * dns_rbt_findnode, then '*node' is NULL and ISC_R_NOTFOUND is returned.
+ *
+ *\li 'name', if non-NULL, is the name stored at the terminal level of
+ * the chain. This is typically a single label, like the "www" of
+ * "www.isc.org", but need not be so. At the root of the tree of trees,
+ * if the node is "." then 'name' is ".", otherwise it is relative to ".".
+ * (Minimalist and atypical case: if the tree has just the name
+ * "isc.org." then the root node's stored name is "isc.org." but 'name'
+ * will be "isc.org".)
+ *
+ *\li 'origin', if non-NULL, is the sequence of labels in the levels
+ * above the terminal level, such as "isc.org." in the above example.
+ * 'origin' is always "." for the root node.
+ *
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS name, origin & node were successfully set.
+ *\li #ISC_R_NOTFOUND The chain does not point to any node.
+ *\li &lt;something_else> Any error return from dns_name_concatenate.
+ */
+
+isc_result_t
+dns_rbtnodechain_first(dns_rbtnodechain_t *chain, dns_rbt_t *rbt,
+ dns_name_t *name, dns_name_t *origin);
+/*%<
+ * Set the chain to the lexically first node in the tree of trees.
+ *
+ * Notes:
+ *\li By the definition of ordering for DNS names, the root of the tree of
+ * trees is the very first node, since everything else in the megatree
+ * uses it as a common suffix.
+ *
+ * Requires:
+ *\li 'chain' is a valid chain.
+ *\li 'rbt' is a valid rbt manager.
+ *
+ * Ensures:
+ *\li The chain points to the very first node of the tree.
+ *
+ *\li 'name' and 'origin', if non-NULL, are set as described for
+ * dns_rbtnodechain_current. Thus 'origin' will always be ".".
+ *
+ * Returns:
+ *\li #DNS_R_NEWORIGIN The name & origin were successfully set.
+ *\li &lt;something_else> Any error result from dns_rbtnodechain_current.
+ */
+
+isc_result_t
+dns_rbtnodechain_last(dns_rbtnodechain_t *chain, dns_rbt_t *rbt,
+ dns_name_t *name, dns_name_t *origin);
+/*%<
+ * Set the chain to the lexically last node in the tree of trees.
+ *
+ * Requires:
+ *\li 'chain' is a valid chain.
+ *\li 'rbt' is a valid rbt manager.
+ *
+ * Ensures:
+ *\li The chain points to the very last node of the tree.
+ *
+ *\li 'name' and 'origin', if non-NULL, are set as described for
+ * dns_rbtnodechain_current.
+ *
+ * Returns:
+ *\li #DNS_R_NEWORIGIN The name & origin were successfully set.
+ *\li #ISC_R_NOMEMORY Resource Limit: Out of Memory building chain.
+ *\li &lt;something_else> Any error result from dns_name_concatenate.
+ */
+
+isc_result_t
+dns_rbtnodechain_prev(dns_rbtnodechain_t *chain, dns_name_t *name,
+ dns_name_t *origin);
+/*%<
+ * Adjusts chain to point the DNSSEC predecessor of the name to which it
+ * is currently pointed.
+ *
+ * Requires:
+ *\li 'chain' is a valid chain.
+ *\li 'chain' has been pointed somewhere in the tree with dns_rbt_findnode,
+ * dns_rbtnodechain_first or dns_rbtnodechain_last -- and remember that
+ * dns_rbt_findnode is not guaranteed to point the chain somewhere,
+ * since there may have been no predecessor to the searched for name.
+ *
+ * Ensures:
+ *\li The chain is pointed to the predecessor of its current target.
+ *
+ *\li 'name' and 'origin', if non-NULL, are set as described for
+ * dns_rbtnodechain_current.
+ *
+ *\li 'origin' is only if a new origin was found.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS The predecessor was found and 'name' was set.
+ *\li #DNS_R_NEWORIGIN The predecessor was found with a different
+ * origin and 'name' and 'origin' were set.
+ *\li #ISC_R_NOMORE There was no predecessor.
+ *\li &lt;something_else> Any error result from dns_rbtnodechain_current.
+ */
+
+isc_result_t
+dns_rbtnodechain_next(dns_rbtnodechain_t *chain, dns_name_t *name,
+ dns_name_t *origin);
+/*%<
+ * Adjusts chain to point the DNSSEC successor of the name to which it
+ * is currently pointed.
+ *
+ * Requires:
+ *\li 'chain' is a valid chain.
+ *\li 'chain' has been pointed somewhere in the tree with dns_rbt_findnode,
+ * dns_rbtnodechain_first or dns_rbtnodechain_last -- and remember that
+ * dns_rbt_findnode is not guaranteed to point the chain somewhere,
+ * since there may have been no predecessor to the searched for name.
+ *
+ * Ensures:
+ *\li The chain is pointed to the successor of its current target.
+ *
+ *\li 'name' and 'origin', if non-NULL, are set as described for
+ * dns_rbtnodechain_current.
+ *
+ *\li 'origin' is only if a new origin was found.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS The successor was found and 'name' was set.
+ *\li #DNS_R_NEWORIGIN The successor was found with a different
+ * origin and 'name' and 'origin' were set.
+ *\li #ISC_R_NOMORE There was no successor.
+ *\li &lt;something_else> Any error result from dns_name_concatenate.
+ */
+
+isc_result_t
+dns_rbtnodechain_down(dns_rbtnodechain_t *chain, dns_name_t *name,
+ dns_name_t *origin);
+/*%<
+ * Decend down if possible.
+ */
+
+isc_result_t
+dns_rbtnodechain_nextflat(dns_rbtnodechain_t *chain, dns_name_t *name);
+/*%<
+ * Find the next node at the current depth in DNSSEC order.
+ */
+
+/*
+ * Wrapper macros for manipulating the rbtnode reference counter:
+ * Since we selectively use isc_refcount_t for the reference counter of
+ * a rbtnode, operations on the counter depend on the actual type of it.
+ * The following macros provide a common interface to these operations,
+ * hiding the back-end. The usage is the same as that of isc_refcount_xxx().
+ */
+#ifdef DNS_RBT_USEISCREFCOUNT
+#define dns_rbtnode_refinit(node, n) \
+ do { \
+ isc_refcount_init(&(node)->references, (n)); \
+ } while (0)
+#define dns_rbtnode_refdestroy(node) \
+ do { \
+ isc_refcount_destroy(&(node)->references); \
+ } while (0)
+#define dns_rbtnode_refcurrent(node) \
+ isc_refcount_current(&(node)->references)
+#define dns_rbtnode_refincrement0(node, refs) \
+ do { \
+ isc_refcount_increment0(&(node)->references, (refs)); \
+ } while (0)
+#define dns_rbtnode_refincrement(node, refs) \
+ do { \
+ isc_refcount_increment(&(node)->references, (refs)); \
+ } while (0)
+#define dns_rbtnode_refdecrement(node, refs) \
+ do { \
+ isc_refcount_decrement(&(node)->references, (refs)); \
+ } while (0)
+#else /* DNS_RBT_USEISCREFCOUNT */
+#define dns_rbtnode_refinit(node, n) ((node)->references = (n))
+#define dns_rbtnode_refdestroy(node) (REQUIRE((node)->references == 0))
+#define dns_rbtnode_refcurrent(node) ((node)->references)
+#define dns_rbtnode_refincrement0(node, refs) \
+ do { \
+ unsigned int *_tmp = (unsigned int *)(refs); \
+ (node)->references++; \
+ if ((_tmp) != NULL) \
+ (*_tmp) = (node)->references; \
+ } while (0)
+#define dns_rbtnode_refincrement(node, refs) \
+ do { \
+ REQUIRE((node)->references > 0); \
+ (node)->references++; \
+ if ((refs) != NULL) \
+ (*refs) = (node)->references; \
+ } while (0)
+#define dns_rbtnode_refdecrement(node, refs) \
+ do { \
+ REQUIRE((node)->references > 0); \
+ (node)->references--; \
+ if ((refs) != NULL) \
+ (*refs) = (node)->references; \
+ } while (0)
+#endif /* DNS_RBT_USEISCREFCOUNT */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_RBT_H */
diff --git a/lib/dns/include/dns/rcode.h b/lib/dns/include/dns/rcode.h
new file mode 100644
index 0000000..94e831b
--- /dev/null
+++ b/lib/dns/include/dns/rcode.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rcode.h,v 1.21 2008/09/25 04:02:39 tbox Exp $ */
+
+#ifndef DNS_RCODE_H
+#define DNS_RCODE_H 1
+
+/*! \file dns/rcode.h */
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t dns_rcode_fromtext(dns_rcode_t *rcodep, isc_textregion_t *source);
+/*%<
+ * Convert the text 'source' refers to into a DNS error value.
+ *
+ * Requires:
+ *\li 'rcodep' is a valid pointer.
+ *
+ *\li 'source' is a valid text region.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS on success
+ *\li #DNS_R_UNKNOWN type is unknown
+ */
+
+isc_result_t dns_rcode_totext(dns_rcode_t rcode, isc_buffer_t *target);
+/*%<
+ * Put a textual representation of error 'rcode' into 'target'.
+ *
+ * Requires:
+ *\li 'rcode' is a valid rcode.
+ *
+ *\li 'target' is a valid text buffer.
+ *
+ * Ensures:
+ *\li If the result is success:
+ * The used space in 'target' is updated.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS on success
+ *\li #ISC_R_NOSPACE target buffer is too small
+ */
+
+isc_result_t dns_tsigrcode_fromtext(dns_rcode_t *rcodep,
+ isc_textregion_t *source);
+/*%<
+ * Convert the text 'source' refers to into a TSIG/TKEY error value.
+ *
+ * Requires:
+ *\li 'rcodep' is a valid pointer.
+ *
+ *\li 'source' is a valid text region.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS on success
+ *\li #DNS_R_UNKNOWN type is unknown
+ */
+
+isc_result_t dns_tsigrcode_totext(dns_rcode_t rcode, isc_buffer_t *target);
+/*%<
+ * Put a textual representation of TSIG/TKEY error 'rcode' into 'target'.
+ *
+ * Requires:
+ *\li 'rcode' is a valid TSIG/TKEY error code.
+ *
+ *\li 'target' is a valid text buffer.
+ *
+ * Ensures:
+ *\li If the result is success:
+ * The used space in 'target' is updated.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS on success
+ *\li #ISC_R_NOSPACE target buffer is too small
+ */
+
+isc_result_t
+dns_hashalg_fromtext(unsigned char *hashalg, isc_textregion_t *source);
+/*%<
+ * Convert the text 'source' refers to into a has algorithm value.
+ *
+ * Requires:
+ *\li 'hashalg' is a valid pointer.
+ *
+ *\li 'source' is a valid text region.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS on success
+ *\li #DNS_R_UNKNOWN type is unknown
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_RCODE_H */
diff --git a/lib/dns/include/dns/rdata.h b/lib/dns/include/dns/rdata.h
new file mode 100644
index 0000000..6ea1850
--- /dev/null
+++ b/lib/dns/include/dns/rdata.h
@@ -0,0 +1,702 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rdata.h,v 1.70 2008/04/02 02:37:42 marka Exp $ */
+
+#ifndef DNS_RDATA_H
+#define DNS_RDATA_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/rdata.h
+ * \brief
+ * Provides facilities for manipulating DNS rdata, including conversions to
+ * and from wire format and text format.
+ *
+ * Given the large amount of rdata possible in a nameserver, it was important
+ * to come up with a very efficient way of storing rdata, but at the same
+ * time allow it to be manipulated.
+ *
+ * The decision was to store rdata in uncompressed wire format,
+ * and not to make it a fully abstracted object; i.e. certain parts of the
+ * server know rdata is stored that way. This saves a lot of memory, and
+ * makes adding rdata to messages easy. Having much of the server know
+ * the representation would be perilous, and we certainly don't want each
+ * user of rdata to be manipulating such a low-level structure. This is
+ * where the rdata module comes in. The module allows rdata handles to be
+ * created and attached to uncompressed wire format regions. All rdata
+ * operations and conversions are done through these handles.
+ *
+ * Implementation Notes:
+ *
+ *\li The routines in this module are expected to be synthesized by the
+ * build process from a set of source files, one per rdata type. For
+ * portability, it's probably best that the building be done by a C
+ * program. Adding a new rdata type will be a simple matter of adding
+ * a file to a directory and rebuilding the server. *All* knowlege of
+ * the format of a particular rdata type is in this file.
+ *
+ * MP:
+ *\li Clients of this module must impose any required synchronization.
+ *
+ * Reliability:
+ *\li This module deals with low-level byte streams. Errors in any of
+ * the functions are likely to crash the server or corrupt memory.
+ *
+ *\li Rdata is typed, and the caller must know what type of rdata it has.
+ * A caller that gets this wrong could crash the server.
+ *
+ *\li The fromstruct() and tostruct() routines use a void * pointer to
+ * represent the structure. The caller must ensure that it passes a
+ * pointer to the appropriate type, or the server could crash or memory
+ * could be corrupted.
+ *
+ * Resources:
+ *\li None.
+ *
+ * Security:
+ *
+ *\li *** WARNING ***
+ * dns_rdata_fromwire() deals with raw network data. An error in
+ * this routine could result in the failure or hijacking of the server.
+ *
+ * Standards:
+ *\li RFC1035
+ *\li Draft EDNS0 (0)
+ *\li Draft EDNS1 (0)
+ *\li Draft Binary Labels (2)
+ *\li Draft Local Compression (1)
+ *\li Various RFCs for particular types; these will be documented in the
+ * sources files of the types.
+ *
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+#include <dns/name.h>
+
+ISC_LANG_BEGINDECLS
+
+
+/***
+ *** Types
+ ***/
+
+/*%
+ ***** An 'rdata' is a handle to a binary region. The handle has an RR
+ ***** class and type, and the data in the binary region is in the format
+ ***** of the given class and type.
+ *****/
+/*%
+ * Clients are strongly discouraged from using this type directly, with
+ * the exception of the 'link' field which may be used directly for whatever
+ * purpose the client desires.
+ */
+struct dns_rdata {
+ unsigned char * data;
+ unsigned int length;
+ dns_rdataclass_t rdclass;
+ dns_rdatatype_t type;
+ unsigned int flags;
+ ISC_LINK(dns_rdata_t) link;
+};
+
+#define DNS_RDATA_INIT { NULL, 0, 0, 0, 0, {(void*)(-1), (void *)(-1)}}
+
+#define DNS_RDATA_UPDATE 0x0001 /*%< update pseudo record. */
+#define DNS_RDATA_OFFLINE 0x0002 /*%< RRSIG has a offline key. */
+
+/*
+ * Flags affecting rdata formatting style. Flags 0xFFFF0000
+ * are used by masterfile-level formatting and defined elsewhere.
+ * See additional comments at dns_rdata_tofmttext().
+ */
+
+/*% Split the rdata into multiple lines to try to keep it
+ within the "width". */
+#define DNS_STYLEFLAG_MULTILINE 0x00000001U
+
+/*% Output explanatory comments. */
+#define DNS_STYLEFLAG_COMMENT 0x00000002U
+
+#define DNS_RDATA_DOWNCASE DNS_NAME_DOWNCASE
+#define DNS_RDATA_CHECKNAMES DNS_NAME_CHECKNAMES
+#define DNS_RDATA_CHECKNAMESFAIL DNS_NAME_CHECKNAMESFAIL
+#define DNS_RDATA_CHECKREVERSE DNS_NAME_CHECKREVERSE
+#define DNS_RDATA_CHECKMX DNS_NAME_CHECKMX
+#define DNS_RDATA_CHECKMXFAIL DNS_NAME_CHECKMXFAIL
+
+/***
+ *** Initialization
+ ***/
+
+void
+dns_rdata_init(dns_rdata_t *rdata);
+/*%<
+ * Make 'rdata' empty.
+ *
+ * Requires:
+ * 'rdata' is a valid rdata (i.e. not NULL, points to a struct dns_rdata)
+ */
+
+void
+dns_rdata_reset(dns_rdata_t *rdata);
+/*%<
+ * Make 'rdata' empty.
+ *
+ * Requires:
+ *\li 'rdata' is a previously initialized rdata and is not linked.
+ */
+
+void
+dns_rdata_clone(const dns_rdata_t *src, dns_rdata_t *target);
+/*%<
+ * Clone 'target' from 'src'.
+ *
+ * Requires:
+ *\li 'src' to be initialized.
+ *\li 'target' to be initialized.
+ */
+
+/***
+ *** Comparisons
+ ***/
+
+int
+dns_rdata_compare(const dns_rdata_t *rdata1, const dns_rdata_t *rdata2);
+/*%<
+ * Determine the relative ordering under the DNSSEC order relation of
+ * 'rdata1' and 'rdata2'.
+ *
+ * Requires:
+ *
+ *\li 'rdata1' is a valid, non-empty rdata
+ *
+ *\li 'rdata2' is a valid, non-empty rdata
+ *
+ * Returns:
+ *\li < 0 'rdata1' is less than 'rdata2'
+ *\li 0 'rdata1' is equal to 'rdata2'
+ *\li > 0 'rdata1' is greater than 'rdata2'
+ */
+
+/***
+ *** Conversions
+ ***/
+
+void
+dns_rdata_fromregion(dns_rdata_t *rdata, dns_rdataclass_t rdclass,
+ dns_rdatatype_t type, isc_region_t *r);
+/*%<
+ * Make 'rdata' refer to region 'r'.
+ *
+ * Requires:
+ *
+ *\li The data in 'r' is properly formatted for whatever type it is.
+ */
+
+void
+dns_rdata_toregion(const dns_rdata_t *rdata, isc_region_t *r);
+/*%<
+ * Make 'r' refer to 'rdata'.
+ */
+
+isc_result_t
+dns_rdata_fromwire(dns_rdata_t *rdata, dns_rdataclass_t rdclass,
+ dns_rdatatype_t type, isc_buffer_t *source,
+ dns_decompress_t *dctx, unsigned int options,
+ isc_buffer_t *target);
+/*%<
+ * Copy the possibly-compressed rdata at source into the target region.
+ *
+ * Notes:
+ *\li Name decompression policy is controlled by 'dctx'.
+ *
+ * 'options'
+ *\li DNS_RDATA_DOWNCASE downcase domain names when they are copied
+ * into target.
+ *
+ * Requires:
+ *
+ *\li 'rdclass' and 'type' are valid.
+ *
+ *\li 'source' is a valid buffer, and the active region of 'source'
+ * references the rdata to be processed.
+ *
+ *\li 'target' is a valid buffer.
+ *
+ *\li 'dctx' is a valid decompression context.
+ *
+ * Ensures,
+ * if result is success:
+ * \li If 'rdata' is not NULL, it is attached to the target.
+ * \li The conditions dns_name_fromwire() ensures for names hold
+ * for all names in the rdata.
+ * \li The current location in source is advanced, and the used space
+ * in target is updated.
+ *
+ * Result:
+ *\li Success
+ *\li Any non-success status from dns_name_fromwire()
+ *\li Various 'Bad Form' class failures depending on class and type
+ *\li Bad Form: Input too short
+ *\li Resource Limit: Not enough space
+ */
+
+isc_result_t
+dns_rdata_towire(dns_rdata_t *rdata, dns_compress_t *cctx,
+ isc_buffer_t *target);
+/*%<
+ * Convert 'rdata' into wire format, compressing it as specified by the
+ * compression context 'cctx', and storing the result in 'target'.
+ *
+ * Notes:
+ *\li If the compression context allows global compression, then the
+ * global compression table may be updated.
+ *
+ * Requires:
+ *\li 'rdata' is a valid, non-empty rdata
+ *
+ *\li target is a valid buffer
+ *
+ *\li Any offsets specified in a global compression table are valid
+ * for target.
+ *
+ * Ensures,
+ * if the result is success:
+ * \li The used space in target is updated.
+ *
+ * Returns:
+ *\li Success
+ *\li Any non-success status from dns_name_towire()
+ *\li Resource Limit: Not enough space
+ */
+
+isc_result_t
+dns_rdata_fromtext(dns_rdata_t *rdata, dns_rdataclass_t rdclass,
+ dns_rdatatype_t type, isc_lex_t *lexer, dns_name_t *origin,
+ unsigned int options, isc_mem_t *mctx,
+ isc_buffer_t *target, dns_rdatacallbacks_t *callbacks);
+/*%<
+ * Convert the textual representation of a DNS rdata into uncompressed wire
+ * form stored in the target region. Tokens constituting the text of the rdata
+ * are taken from 'lexer'.
+ *
+ * Notes:
+ *\li Relative domain names in the rdata will have 'origin' appended to them.
+ * A NULL origin implies "origin == dns_rootname".
+ *
+ *
+ * 'options'
+ *\li DNS_RDATA_DOWNCASE downcase domain names when they are copied
+ * into target.
+ *\li DNS_RDATA_CHECKNAMES perform checknames checks.
+ *\li DNS_RDATA_CHECKNAMESFAIL fail if the checknames check fail. If
+ * not set a warning will be issued.
+ *\li DNS_RDATA_CHECKREVERSE this should set if the owner name ends
+ * in IP6.ARPA, IP6.INT or IN-ADDR.ARPA.
+ *
+ * Requires:
+ *
+ *\li 'rdclass' and 'type' are valid.
+ *
+ *\li 'lexer' is a valid isc_lex_t.
+ *
+ *\li 'mctx' is a valid isc_mem_t.
+ *
+ *\li 'target' is a valid region.
+ *
+ *\li 'origin' if non NULL it must be absolute.
+ *
+ *\li 'callbacks' to be NULL or callbacks->warn and callbacks->error be
+ * initialized.
+ *
+ * Ensures,
+ * if result is success:
+ *\li If 'rdata' is not NULL, it is attached to the target.
+
+ *\li The conditions dns_name_fromtext() ensures for names hold
+ * for all names in the rdata.
+
+ *\li The used space in target is updated.
+ *
+ * Result:
+ *\li Success
+ *\li Translated result codes from isc_lex_gettoken
+ *\li Various 'Bad Form' class failures depending on class and type
+ *\li Bad Form: Input too short
+ *\li Resource Limit: Not enough space
+ *\li Resource Limit: Not enough memory
+ */
+
+isc_result_t
+dns_rdata_totext(dns_rdata_t *rdata, dns_name_t *origin, isc_buffer_t *target);
+/*%<
+ * Convert 'rdata' into text format, storing the result in 'target'.
+ * The text will consist of a single line, with fields separated by
+ * single spaces.
+ *
+ * Notes:
+ *\li If 'origin' is not NULL, then any names in the rdata that are
+ * subdomains of 'origin' will be made relative it.
+ *
+ *\li XXX Do we *really* want to support 'origin'? I'm inclined towards "no"
+ * at the moment.
+ *
+ * Requires:
+ *
+ *\li 'rdata' is a valid, non-empty rdata
+ *
+ *\li 'origin' is NULL, or is a valid name
+ *
+ *\li 'target' is a valid text buffer
+ *
+ * Ensures,
+ * if the result is success:
+ *
+ * \li The used space in target is updated.
+ *
+ * Returns:
+ *\li Success
+ *\li Any non-success status from dns_name_totext()
+ *\li Resource Limit: Not enough space
+ */
+
+isc_result_t
+dns_rdata_tofmttext(dns_rdata_t *rdata, dns_name_t *origin, unsigned int flags,
+ unsigned int width, char *linebreak, isc_buffer_t *target);
+/*%<
+ * Like dns_rdata_totext, but do formatted output suitable for
+ * database dumps. This is intended for use by dns_db_dump();
+ * library users are discouraged from calling it directly.
+ *
+ * If (flags & #DNS_STYLEFLAG_MULTILINE) != 0, attempt to stay
+ * within 'width' by breaking the text into multiple lines.
+ * The string 'linebreak' is inserted between lines, and parentheses
+ * are added when necessary. Because RRs contain unbreakable elements
+ * such as domain names whose length is variable, unpredictable, and
+ * potentially large, there is no guarantee that the lines will
+ * not exceed 'width' anyway.
+ *
+ * If (flags & #DNS_STYLEFLAG_MULTILINE) == 0, the rdata is always
+ * printed as a single line, and no parentheses are used.
+ * The 'width' and 'linebreak' arguments are ignored.
+ *
+ * If (flags & #DNS_STYLEFLAG_COMMENT) != 0, output explanatory
+ * comments next to things like the SOA timer fields. Some
+ * comments (e.g., the SOA ones) are only printed when multiline
+ * output is selected.
+ */
+
+isc_result_t
+dns_rdata_fromstruct(dns_rdata_t *rdata, dns_rdataclass_t rdclass,
+ dns_rdatatype_t type, void *source, isc_buffer_t *target);
+/*%<
+ * Convert the C structure representation of an rdata into uncompressed wire
+ * format in 'target'.
+ *
+ * XXX Should we have a 'size' parameter as a sanity check on target?
+ *
+ * Requires:
+ *
+ *\li 'rdclass' and 'type' are valid.
+ *
+ *\li 'source' points to a valid C struct for the class and type.
+ *
+ *\li 'target' is a valid buffer.
+ *
+ *\li All structure pointers to memory blocks should be NULL if their
+ * corresponding length values are zero.
+ *
+ * Ensures,
+ * if result is success:
+ * \li If 'rdata' is not NULL, it is attached to the target.
+ *
+ * \li The used space in 'target' is updated.
+ *
+ * Result:
+ *\li Success
+ *\li Various 'Bad Form' class failures depending on class and type
+ *\li Resource Limit: Not enough space
+ */
+
+isc_result_t
+dns_rdata_tostruct(dns_rdata_t *rdata, void *target, isc_mem_t *mctx);
+/*%<
+ * Convert an rdata into its C structure representation.
+ *
+ * If 'mctx' is NULL then 'rdata' must persist while 'target' is being used.
+ *
+ * If 'mctx' is non NULL then memory will be allocated if required.
+ *
+ * Requires:
+ *
+ *\li 'rdata' is a valid, non-empty rdata.
+ *
+ *\li 'target' to point to a valid pointer for the type and class.
+ *
+ * Result:
+ *\li Success
+ *\li Resource Limit: Not enough memory
+ */
+
+void
+dns_rdata_freestruct(void *source);
+/*%<
+ * Free dynamic memory attached to 'source' (if any).
+ *
+ * Requires:
+ *
+ *\li 'source' to point to the structure previously filled in by
+ * dns_rdata_tostruct().
+ */
+
+isc_boolean_t
+dns_rdatatype_ismeta(dns_rdatatype_t type);
+/*%<
+ * Return true iff the rdata type 'type' is a meta-type
+ * like ANY or AXFR.
+ */
+
+isc_boolean_t
+dns_rdatatype_issingleton(dns_rdatatype_t type);
+/*%<
+ * Return true iff the rdata type 'type' is a singleton type,
+ * like CNAME or SOA.
+ *
+ * Requires:
+ * \li 'type' is a valid rdata type.
+ *
+ */
+
+isc_boolean_t
+dns_rdataclass_ismeta(dns_rdataclass_t rdclass);
+/*%<
+ * Return true iff the rdata class 'rdclass' is a meta-class
+ * like ANY or NONE.
+ */
+
+isc_boolean_t
+dns_rdatatype_isdnssec(dns_rdatatype_t type);
+/*%<
+ * Return true iff 'type' is one of the DNSSEC
+ * rdata types that may exist alongside a CNAME record.
+ *
+ * Requires:
+ * \li 'type' is a valid rdata type.
+ */
+
+isc_boolean_t
+dns_rdatatype_iszonecutauth(dns_rdatatype_t type);
+/*%<
+ * Return true iff rdata of type 'type' is considered authoritative
+ * data (not glue) in the NSEC chain when it occurs in the parent zone
+ * at a zone cut.
+ *
+ * Requires:
+ * \li 'type' is a valid rdata type.
+ *
+ */
+
+isc_boolean_t
+dns_rdatatype_isknown(dns_rdatatype_t type);
+/*%<
+ * Return true iff the rdata type 'type' is known.
+ *
+ * Requires:
+ * \li 'type' is a valid rdata type.
+ *
+ */
+
+
+isc_result_t
+dns_rdata_additionaldata(dns_rdata_t *rdata, dns_additionaldatafunc_t add,
+ void *arg);
+/*%<
+ * Call 'add' for each name and type from 'rdata' which is subject to
+ * additional section processing.
+ *
+ * Requires:
+ *
+ *\li 'rdata' is a valid, non-empty rdata.
+ *
+ *\li 'add' is a valid dns_additionalfunc_t.
+ *
+ * Ensures:
+ *
+ *\li If successful, then add() will have been called for each name
+ * and type subject to additional section processing.
+ *
+ *\li If add() returns something other than #ISC_R_SUCCESS, that result
+ * will be returned as the result of dns_rdata_additionaldata().
+ *
+ * Returns:
+ *
+ *\li ISC_R_SUCCESS
+ *
+ *\li Many other results are possible if not successful.
+ */
+
+isc_result_t
+dns_rdata_digest(dns_rdata_t *rdata, dns_digestfunc_t digest, void *arg);
+/*%<
+ * Send 'rdata' in DNSSEC canonical form to 'digest'.
+ *
+ * Note:
+ *\li 'digest' may be called more than once by dns_rdata_digest(). The
+ * concatenation of all the regions, in the order they were given
+ * to 'digest', will be the DNSSEC canonical form of 'rdata'.
+ *
+ * Requires:
+ *
+ *\li 'rdata' is a valid, non-empty rdata.
+ *
+ *\li 'digest' is a valid dns_digestfunc_t.
+ *
+ * Ensures:
+ *
+ *\li If successful, then all of the rdata's data has been sent, in
+ * DNSSEC canonical form, to 'digest'.
+ *
+ *\li If digest() returns something other than ISC_R_SUCCESS, that result
+ * will be returned as the result of dns_rdata_digest().
+ *
+ * Returns:
+ *
+ *\li ISC_R_SUCCESS
+ *
+ *\li Many other results are possible if not successful.
+ */
+
+isc_boolean_t
+dns_rdatatype_questiononly(dns_rdatatype_t type);
+/*%<
+ * Return true iff rdata of type 'type' can only appear in the question
+ * section of a properly formatted message.
+ *
+ * Requires:
+ * \li 'type' is a valid rdata type.
+ *
+ */
+
+isc_boolean_t
+dns_rdatatype_notquestion(dns_rdatatype_t type);
+/*%<
+ * Return true iff rdata of type 'type' can not appear in the question
+ * section of a properly formatted message.
+ *
+ * Requires:
+ * \li 'type' is a valid rdata type.
+ *
+ */
+
+isc_boolean_t
+dns_rdatatype_atparent(dns_rdatatype_t type);
+/*%<
+ * Return true iff rdata of type 'type' should appear at the parent of
+ * a zone cut.
+ *
+ * Requires:
+ * \li 'type' is a valid rdata type.
+ *
+ */
+
+unsigned int
+dns_rdatatype_attributes(dns_rdatatype_t rdtype);
+/*%<
+ * Return attributes for the given type.
+ *
+ * Requires:
+ *\li 'rdtype' are known.
+ *
+ * Returns:
+ *\li a bitmask consisting of the following flags.
+ */
+
+/*% only one may exist for a name */
+#define DNS_RDATATYPEATTR_SINGLETON 0x00000001U
+/*% requires no other data be present */
+#define DNS_RDATATYPEATTR_EXCLUSIVE 0x00000002U
+/*% Is a meta type */
+#define DNS_RDATATYPEATTR_META 0x00000004U
+/*% Is a DNSSEC type, like RRSIG or NSEC */
+#define DNS_RDATATYPEATTR_DNSSEC 0x00000008U
+/*% Is a zone cut authority type */
+#define DNS_RDATATYPEATTR_ZONECUTAUTH 0x00000010U
+/*% Is reserved (unusable) */
+#define DNS_RDATATYPEATTR_RESERVED 0x00000020U
+/*% Is an unknown type */
+#define DNS_RDATATYPEATTR_UNKNOWN 0x00000040U
+/*% Is META, and can only be in a question section */
+#define DNS_RDATATYPEATTR_QUESTIONONLY 0x00000080U
+/*% is META, and can NOT be in a question section */
+#define DNS_RDATATYPEATTR_NOTQUESTION 0x00000100U
+/*% Is present at zone cuts in the parent, not the child */
+#define DNS_RDATATYPEATTR_ATPARENT 0x00000200U
+
+dns_rdatatype_t
+dns_rdata_covers(dns_rdata_t *rdata);
+/*%<
+ * Return the rdatatype that this type covers.
+ *
+ * Requires:
+ *\li 'rdata' is a valid, non-empty rdata.
+ *
+ *\li 'rdata' is a type that covers other rdata types.
+ *
+ * Returns:
+ *\li The type covered.
+ */
+
+isc_boolean_t
+dns_rdata_checkowner(dns_name_t* name, dns_rdataclass_t rdclass,
+ dns_rdatatype_t type, isc_boolean_t wildcard);
+/*
+ * Returns whether this is a valid ownername for this <type,class>.
+ * If wildcard is true allow the first label to be a wildcard if
+ * appropriate.
+ *
+ * Requires:
+ * 'name' is a valid name.
+ */
+
+isc_boolean_t
+dns_rdata_checknames(dns_rdata_t *rdata, dns_name_t *owner, dns_name_t *bad);
+/*
+ * Returns whether 'rdata' contains valid domain names. The checks are
+ * sensitive to the owner name.
+ *
+ * If 'bad' is non-NULL and a domain name fails the check the
+ * the offending name will be return in 'bad' by cloning from
+ * the 'rdata' contents.
+ *
+ * Requires:
+ * 'rdata' to be valid.
+ * 'owner' to be valid.
+ * 'bad' to be NULL or valid.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_RDATA_H */
diff --git a/lib/dns/include/dns/rdataclass.h b/lib/dns/include/dns/rdataclass.h
new file mode 100644
index 0000000..786eb6a
--- /dev/null
+++ b/lib/dns/include/dns/rdataclass.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rdataclass.h,v 1.24 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef DNS_RDATACLASS_H
+#define DNS_RDATACLASS_H 1
+
+/*! \file dns/rdataclass.h */
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_rdataclass_fromtext(dns_rdataclass_t *classp, isc_textregion_t *source);
+/*%<
+ * Convert the text 'source' refers to into a DNS class.
+ *
+ * Requires:
+ *\li 'classp' is a valid pointer.
+ *
+ *\li 'source' is a valid text region.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS on success
+ *\li #DNS_R_UNKNOWN class is unknown
+ */
+
+isc_result_t
+dns_rdataclass_totext(dns_rdataclass_t rdclass, isc_buffer_t *target);
+/*%<
+ * Put a textual representation of class 'rdclass' into 'target'.
+ *
+ * Requires:
+ *\li 'rdclass' is a valid class.
+ *
+ *\li 'target' is a valid text buffer.
+ *
+ * Ensures,
+ * if the result is success:
+ *\li The used space in 'target' is updated.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS on success
+ *\li #ISC_R_NOSPACE target buffer is too small
+ */
+
+void
+dns_rdataclass_format(dns_rdataclass_t rdclass,
+ char *array, unsigned int size);
+/*%<
+ * Format a human-readable representation of the class 'rdclass'
+ * into the character array 'array', which is of size 'size'.
+ * The resulting string is guaranteed to be null-terminated.
+ */
+
+#define DNS_RDATACLASS_FORMATSIZE sizeof("CLASS65535")
+/*%<
+ * Minimum size of array to pass to dns_rdataclass_format().
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_RDATACLASS_H */
diff --git a/lib/dns/include/dns/rdatalist.h b/lib/dns/include/dns/rdatalist.h
new file mode 100644
index 0000000..57debc3
--- /dev/null
+++ b/lib/dns/include/dns/rdatalist.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rdatalist.h,v 1.22 2008/04/03 06:09:05 tbox Exp $ */
+
+#ifndef DNS_RDATALIST_H
+#define DNS_RDATALIST_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/rdatalist.h
+ * \brief
+ * A DNS rdatalist is a list of rdata of a common type and class.
+ *
+ * MP:
+ *\li Clients of this module must impose any required synchronization.
+ *
+ * Reliability:
+ *\li No anticipated impact.
+ *
+ * Resources:
+ *\li TBS
+ *
+ * Security:
+ *\li No anticipated impact.
+ *
+ * Standards:
+ *\li None.
+ */
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+/*%
+ * Clients may use this type directly.
+ */
+struct dns_rdatalist {
+ dns_rdataclass_t rdclass;
+ dns_rdatatype_t type;
+ dns_rdatatype_t covers;
+ dns_ttl_t ttl;
+ ISC_LIST(dns_rdata_t) rdata;
+ ISC_LINK(dns_rdatalist_t) link;
+};
+
+ISC_LANG_BEGINDECLS
+
+void
+dns_rdatalist_init(dns_rdatalist_t *rdatalist);
+/*%<
+ * Initialize rdatalist.
+ *
+ * Ensures:
+ *\li All fields of rdatalist have been initialized to their default
+ * values.
+ */
+
+isc_result_t
+dns_rdatalist_tordataset(dns_rdatalist_t *rdatalist,
+ dns_rdataset_t *rdataset);
+/*%<
+ * Make 'rdataset' refer to the rdata in 'rdatalist'.
+ *
+ * Note:
+ *\li The caller must ensure that 'rdatalist' remains valid and unchanged
+ * while 'rdataset' is associated with it.
+ *
+ * Requires:
+ *
+ *\li 'rdatalist' is a valid rdatalist.
+ *
+ *\li 'rdataset' is a valid rdataset that is not currently associated with
+ * any rdata.
+ *
+ * Ensures,
+ * on success,
+ *
+ *\li 'rdataset' is associated with the rdata in rdatalist.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ */
+
+isc_result_t
+dns_rdatalist_fromrdataset(dns_rdataset_t *rdataset,
+ dns_rdatalist_t **rdatalist);
+/*%<
+ * Point 'rdatalist' to the rdatalist in 'rdataset'.
+ *
+ * Requires:
+ *
+ *\li 'rdatalist' is a pointer to a NULL dns_rdatalist_t pointer.
+ *
+ *\li 'rdataset' is a valid rdataset associated with an rdatalist.
+ *
+ * Ensures,
+ * on success,
+ *
+ *\li 'rdatalist' is pointed to the rdatalist in rdataset.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_RDATALIST_H */
diff --git a/lib/dns/include/dns/rdataset.h b/lib/dns/include/dns/rdataset.h
new file mode 100644
index 0000000..bfb6cd4
--- /dev/null
+++ b/lib/dns/include/dns/rdataset.h
@@ -0,0 +1,639 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rdataset.h,v 1.65 2008/09/24 02:46:23 marka Exp $ */
+
+#ifndef DNS_RDATASET_H
+#define DNS_RDATASET_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/rdataset.h
+ * \brief
+ * A DNS rdataset is a handle that can be associated with a collection of
+ * rdata all having a common owner name, class, and type.
+ *
+ * The dns_rdataset_t type is like a "virtual class". To actually use
+ * rdatasets, an implementation of the method suite (e.g. "slabbed rdata") is
+ * required.
+ *
+ * XXX &lt;more&gt; XXX
+ *
+ * MP:
+ *\li Clients of this module must impose any required synchronization.
+ *
+ * Reliability:
+ *\li No anticipated impact.
+ *
+ * Resources:
+ *\li TBS
+ *
+ * Security:
+ *\li No anticipated impact.
+ *
+ * Standards:
+ *\li None.
+ */
+
+#include <isc/lang.h>
+#include <isc/magic.h>
+#include <isc/stdtime.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+typedef enum {
+ dns_rdatasetadditional_fromauth,
+ dns_rdatasetadditional_fromcache,
+ dns_rdatasetadditional_fromglue
+} dns_rdatasetadditional_t;
+
+typedef struct dns_rdatasetmethods {
+ void (*disassociate)(dns_rdataset_t *rdataset);
+ isc_result_t (*first)(dns_rdataset_t *rdataset);
+ isc_result_t (*next)(dns_rdataset_t *rdataset);
+ void (*current)(dns_rdataset_t *rdataset,
+ dns_rdata_t *rdata);
+ void (*clone)(dns_rdataset_t *source,
+ dns_rdataset_t *target);
+ unsigned int (*count)(dns_rdataset_t *rdataset);
+ isc_result_t (*addnoqname)(dns_rdataset_t *rdataset,
+ dns_name_t *name);
+ isc_result_t (*getnoqname)(dns_rdataset_t *rdataset,
+ dns_name_t *name,
+ dns_rdataset_t *neg,
+ dns_rdataset_t *negsig);
+ isc_result_t (*addclosest)(dns_rdataset_t *rdataset,
+ dns_name_t *name);
+ isc_result_t (*getclosest)(dns_rdataset_t *rdataset,
+ dns_name_t *name,
+ dns_rdataset_t *neg,
+ dns_rdataset_t *negsig);
+ isc_result_t (*getadditional)(dns_rdataset_t *rdataset,
+ dns_rdatasetadditional_t type,
+ dns_rdatatype_t qtype,
+ dns_acache_t *acache,
+ dns_zone_t **zonep,
+ dns_db_t **dbp,
+ dns_dbversion_t **versionp,
+ dns_dbnode_t **nodep,
+ dns_name_t *fname,
+ dns_message_t *msg,
+ isc_stdtime_t now);
+ isc_result_t (*setadditional)(dns_rdataset_t *rdataset,
+ dns_rdatasetadditional_t type,
+ dns_rdatatype_t qtype,
+ dns_acache_t *acache,
+ dns_zone_t *zone,
+ dns_db_t *db,
+ dns_dbversion_t *version,
+ dns_dbnode_t *node,
+ dns_name_t *fname);
+ isc_result_t (*putadditional)(dns_acache_t *acache,
+ dns_rdataset_t *rdataset,
+ dns_rdatasetadditional_t type,
+ dns_rdatatype_t qtype);
+} dns_rdatasetmethods_t;
+
+#define DNS_RDATASET_MAGIC ISC_MAGIC('D','N','S','R')
+#define DNS_RDATASET_VALID(set) ISC_MAGIC_VALID(set, DNS_RDATASET_MAGIC)
+
+/*%
+ * Direct use of this structure by clients is strongly discouraged, except
+ * for the 'link' field which may be used however the client wishes. The
+ * 'private', 'current', and 'index' fields MUST NOT be changed by clients.
+ * rdataset implementations may change any of the fields.
+ */
+struct dns_rdataset {
+ unsigned int magic; /* XXX ? */
+ dns_rdatasetmethods_t * methods;
+ ISC_LINK(dns_rdataset_t) link;
+ /*
+ * XXX do we need these, or should they be retrieved by methods?
+ * Leaning towards the latter, since they are not frequently required
+ * once you have the rdataset.
+ */
+ dns_rdataclass_t rdclass;
+ dns_rdatatype_t type;
+ dns_ttl_t ttl;
+ dns_trust_t trust;
+ dns_rdatatype_t covers;
+ /*
+ * attributes
+ */
+ unsigned int attributes;
+ /*%
+ * the counter provides the starting point in the "cyclic" order.
+ * The value ISC_UINT32_MAX has a special meaning of "picking up a
+ * random value." in order to take care of databases that do not
+ * increment the counter.
+ */
+ isc_uint32_t count;
+ /*
+ * This RRSIG RRset should be re-generated around this time.
+ * Only valid if DNS_RDATASETATTR_RESIGN is set in attributes.
+ */
+ isc_stdtime_t resign;
+ /*@{*/
+ /*%
+ * These are for use by the rdataset implementation, and MUST NOT
+ * be changed by clients.
+ */
+ void * private1;
+ void * private2;
+ void * private3;
+ unsigned int privateuint4;
+ void * private5;
+ void * private6;
+ void * private7;
+ /*@}*/
+
+};
+
+/*!
+ * \def DNS_RDATASETATTR_RENDERED
+ * Used by message.c to indicate that the rdataset was rendered.
+ *
+ * \def DNS_RDATASETATTR_TTLADJUSTED
+ * Used by message.c to indicate that the rdataset's rdata had differing
+ * TTL values, and the rdataset->ttl holds the smallest.
+ *
+ * \def DNS_RDATASETATTR_LOADORDER
+ * Output the RRset in load order.
+ */
+
+#define DNS_RDATASETATTR_QUESTION 0x00000001
+#define DNS_RDATASETATTR_RENDERED 0x00000002 /*%< Used by message.c */
+#define DNS_RDATASETATTR_ANSWERED 0x00000004 /*%< Used by server. */
+#define DNS_RDATASETATTR_CACHE 0x00000008 /*%< Used by resolver. */
+#define DNS_RDATASETATTR_ANSWER 0x00000010 /*%< Used by resolver. */
+#define DNS_RDATASETATTR_ANSWERSIG 0x00000020 /*%< Used by resolver. */
+#define DNS_RDATASETATTR_EXTERNAL 0x00000040 /*%< Used by resolver. */
+#define DNS_RDATASETATTR_NCACHE 0x00000080 /*%< Used by resolver. */
+#define DNS_RDATASETATTR_CHAINING 0x00000100 /*%< Used by resolver. */
+#define DNS_RDATASETATTR_TTLADJUSTED 0x00000200 /*%< Used by message.c */
+#define DNS_RDATASETATTR_FIXEDORDER 0x00000400
+#define DNS_RDATASETATTR_RANDOMIZE 0x00000800
+#define DNS_RDATASETATTR_CHASE 0x00001000 /*%< Used by resolver. */
+#define DNS_RDATASETATTR_NXDOMAIN 0x00002000
+#define DNS_RDATASETATTR_NOQNAME 0x00004000
+#define DNS_RDATASETATTR_CHECKNAMES 0x00008000 /*%< Used by resolver. */
+#define DNS_RDATASETATTR_REQUIREDGLUE 0x00010000
+#define DNS_RDATASETATTR_LOADORDER 0x00020000
+#define DNS_RDATASETATTR_RESIGN 0x00040000
+#define DNS_RDATASETATTR_CLOSEST 0x00080000
+#define DNS_RDATASETATTR_OPTOUT 0x00100000 /*%< OPTOUT proof */
+
+/*%
+ * _OMITDNSSEC:
+ * Omit DNSSEC records when rendering ncache records.
+ */
+#define DNS_RDATASETTOWIRE_OMITDNSSEC 0x0001
+
+void
+dns_rdataset_init(dns_rdataset_t *rdataset);
+/*%<
+ * Make 'rdataset' a valid, disassociated rdataset.
+ *
+ * Requires:
+ *\li 'rdataset' is not NULL.
+ *
+ * Ensures:
+ *\li 'rdataset' is a valid, disassociated rdataset.
+ */
+
+void
+dns_rdataset_invalidate(dns_rdataset_t *rdataset);
+/*%<
+ * Invalidate 'rdataset'.
+ *
+ * Requires:
+ *\li 'rdataset' is a valid, disassociated rdataset.
+ *
+ * Ensures:
+ *\li If assertion checking is enabled, future attempts to use 'rdataset'
+ * without initializing it will cause an assertion failure.
+ */
+
+void
+dns_rdataset_disassociate(dns_rdataset_t *rdataset);
+/*%<
+ * Disassociate 'rdataset' from its rdata, allowing it to be reused.
+ *
+ * Notes:
+ *\li The client must ensure it has no references to rdata in the rdataset
+ * before disassociating.
+ *
+ * Requires:
+ *\li 'rdataset' is a valid, associated rdataset.
+ *
+ * Ensures:
+ *\li 'rdataset' is a valid, disassociated rdataset.
+ */
+
+isc_boolean_t
+dns_rdataset_isassociated(dns_rdataset_t *rdataset);
+/*%<
+ * Is 'rdataset' associated?
+ *
+ * Requires:
+ *\li 'rdataset' is a valid rdataset.
+ *
+ * Returns:
+ *\li #ISC_TRUE 'rdataset' is associated.
+ *\li #ISC_FALSE 'rdataset' is not associated.
+ */
+
+void
+dns_rdataset_makequestion(dns_rdataset_t *rdataset, dns_rdataclass_t rdclass,
+ dns_rdatatype_t type);
+/*%<
+ * Make 'rdataset' a valid, associated, question rdataset, with a
+ * question class of 'rdclass' and type 'type'.
+ *
+ * Notes:
+ *\li Question rdatasets have a class and type, but no rdata.
+ *
+ * Requires:
+ *\li 'rdataset' is a valid, disassociated rdataset.
+ *
+ * Ensures:
+ *\li 'rdataset' is a valid, associated, question rdataset.
+ */
+
+void
+dns_rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target);
+/*%<
+ * Make 'target' refer to the same rdataset as 'source'.
+ *
+ * Requires:
+ *\li 'source' is a valid, associated rdataset.
+ *
+ *\li 'target' is a valid, dissociated rdataset.
+ *
+ * Ensures:
+ *\li 'target' references the same rdataset as 'source'.
+ */
+
+unsigned int
+dns_rdataset_count(dns_rdataset_t *rdataset);
+/*%<
+ * Return the number of records in 'rdataset'.
+ *
+ * Requires:
+ *\li 'rdataset' is a valid, associated rdataset.
+ *
+ * Returns:
+ *\li The number of records in 'rdataset'.
+ */
+
+isc_result_t
+dns_rdataset_first(dns_rdataset_t *rdataset);
+/*%<
+ * Move the rdata cursor to the first rdata in the rdataset (if any).
+ *
+ * Requires:
+ *\li 'rdataset' is a valid, associated rdataset.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMORE There are no rdata in the set.
+ */
+
+isc_result_t
+dns_rdataset_next(dns_rdataset_t *rdataset);
+/*%<
+ * Move the rdata cursor to the next rdata in the rdataset (if any).
+ *
+ * Requires:
+ *\li 'rdataset' is a valid, associated rdataset.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMORE There are no more rdata in the set.
+ */
+
+void
+dns_rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata);
+/*%<
+ * Make 'rdata' refer to the current rdata.
+ *
+ * Notes:
+ *
+ *\li The data returned in 'rdata' is valid for the life of the
+ * rdataset; in particular, subsequent changes in the cursor position
+ * do not invalidate 'rdata'.
+ *
+ * Requires:
+ *\li 'rdataset' is a valid, associated rdataset.
+ *
+ *\li The rdata cursor of 'rdataset' is at a valid location (i.e. the
+ * result of last call to a cursor movement command was ISC_R_SUCCESS).
+ *
+ * Ensures:
+ *\li 'rdata' refers to the rdata at the rdata cursor location of
+ *\li 'rdataset'.
+ */
+
+isc_result_t
+dns_rdataset_totext(dns_rdataset_t *rdataset,
+ dns_name_t *owner_name,
+ isc_boolean_t omit_final_dot,
+ isc_boolean_t question,
+ isc_buffer_t *target);
+/*%<
+ * Convert 'rdataset' to text format, storing the result in 'target'.
+ *
+ * Notes:
+ *\li The rdata cursor position will be changed.
+ *
+ *\li The 'question' flag should normally be #ISC_FALSE. If it is
+ * #ISC_TRUE, the TTL and rdata fields are not printed. This is
+ * for use when printing an rdata representing a question section.
+ *
+ *\li This interface is deprecated; use dns_master_rdatasettottext()
+ * and/or dns_master_questiontotext() instead.
+ *
+ * Requires:
+ *\li 'rdataset' is a valid rdataset.
+ *
+ *\li 'rdataset' is not empty.
+ */
+
+isc_result_t
+dns_rdataset_towire(dns_rdataset_t *rdataset,
+ dns_name_t *owner_name,
+ dns_compress_t *cctx,
+ isc_buffer_t *target,
+ unsigned int options,
+ unsigned int *countp);
+/*%<
+ * Convert 'rdataset' to wire format, compressing names as specified
+ * in 'cctx', and storing the result in 'target'.
+ *
+ * Notes:
+ *\li The rdata cursor position will be changed.
+ *
+ *\li The number of RRs added to target will be added to *countp.
+ *
+ * Requires:
+ *\li 'rdataset' is a valid rdataset.
+ *
+ *\li 'rdataset' is not empty.
+ *
+ *\li 'countp' is a valid pointer.
+ *
+ * Ensures:
+ *\li On a return of ISC_R_SUCCESS, 'target' contains a wire format
+ * for the data contained in 'rdataset'. Any error return leaves
+ * the buffer unchanged.
+ *
+ *\li *countp has been incremented by the number of RRs added to
+ * target.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS - all ok
+ *\li #ISC_R_NOSPACE - 'target' doesn't have enough room
+ *
+ *\li Any error returned by dns_rdata_towire(), dns_rdataset_next(),
+ * dns_name_towire().
+ */
+
+isc_result_t
+dns_rdataset_towiresorted(dns_rdataset_t *rdataset,
+ const dns_name_t *owner_name,
+ dns_compress_t *cctx,
+ isc_buffer_t *target,
+ dns_rdatasetorderfunc_t order,
+ const void *order_arg,
+ unsigned int options,
+ unsigned int *countp);
+/*%<
+ * Like dns_rdataset_towire(), but sorting the rdatasets according to
+ * the integer value returned by 'order' when called witih the rdataset
+ * and 'order_arg' as arguments.
+ *
+ * Requires:
+ *\li All the requirements of dns_rdataset_towire(), and
+ * that order_arg is NULL if and only if order is NULL.
+ */
+
+isc_result_t
+dns_rdataset_towirepartial(dns_rdataset_t *rdataset,
+ const dns_name_t *owner_name,
+ dns_compress_t *cctx,
+ isc_buffer_t *target,
+ dns_rdatasetorderfunc_t order,
+ const void *order_arg,
+ unsigned int options,
+ unsigned int *countp,
+ void **state);
+/*%<
+ * Like dns_rdataset_towiresorted() except that a partial rdataset
+ * may be written.
+ *
+ * Requires:
+ *\li All the requirements of dns_rdataset_towiresorted().
+ * If 'state' is non NULL then the current position in the
+ * rdataset will be remembered if the rdataset in not
+ * completely written and should be passed on on subsequent
+ * calls (NOT CURRENTLY IMPLEMENTED).
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS if all of the records were written.
+ *\li #ISC_R_NOSPACE if unable to fit in all of the records. *countp
+ * will be updated to reflect the number of records
+ * written.
+ */
+
+isc_result_t
+dns_rdataset_additionaldata(dns_rdataset_t *rdataset,
+ dns_additionaldatafunc_t add, void *arg);
+/*%<
+ * For each rdata in rdataset, call 'add' for each name and type in the
+ * rdata which is subject to additional section processing.
+ *
+ * Requires:
+ *
+ *\li 'rdataset' is a valid, non-question rdataset.
+ *
+ *\li 'add' is a valid dns_additionaldatafunc_t
+ *
+ * Ensures:
+ *
+ *\li If successful, dns_rdata_additionaldata() will have been called for
+ * each rdata in 'rdataset'.
+ *
+ *\li If a call to dns_rdata_additionaldata() is not successful, the
+ * result returned will be the result of dns_rdataset_additionaldata().
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS
+ *
+ *\li Any error that dns_rdata_additionaldata() can return.
+ */
+
+isc_result_t
+dns_rdataset_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name,
+ dns_rdataset_t *neg, dns_rdataset_t *negsig);
+/*%<
+ * Return the noqname proof for this record.
+ *
+ * Requires:
+ *\li 'rdataset' to be valid and #DNS_RDATASETATTR_NOQNAME to be set.
+ *\li 'name' to be valid.
+ *\li 'neg' and 'negsig' to be valid and not associated.
+ */
+
+isc_result_t
+dns_rdataset_addnoqname(dns_rdataset_t *rdataset, dns_name_t *name);
+/*%<
+ * Associate a noqname proof with this record.
+ * Sets #DNS_RDATASETATTR_NOQNAME if successful.
+ * Adjusts the 'rdataset->ttl' to minimum of the 'rdataset->ttl' and
+ * the 'nsec'/'nsec3' and 'rrsig(nsec)'/'rrsig(nsec3)' ttl.
+ *
+ * Requires:
+ *\li 'rdataset' to be valid and #DNS_RDATASETATTR_NOQNAME to be set.
+ *\li 'name' to be valid and have NSEC or NSEC3 and associated RRSIG
+ * rdatasets.
+ */
+
+isc_result_t
+dns_rdataset_getclosest(dns_rdataset_t *rdataset, dns_name_t *name,
+ dns_rdataset_t *nsec, dns_rdataset_t *nsecsig);
+/*%<
+ * Return the closest encloser for this record.
+ *
+ * Requires:
+ *\li 'rdataset' to be valid and #DNS_RDATASETATTR_CLOSEST to be set.
+ *\li 'name' to be valid.
+ *\li 'nsec' and 'nsecsig' to be valid and not associated.
+ */
+
+isc_result_t
+dns_rdataset_addclosest(dns_rdataset_t *rdataset, dns_name_t *name);
+/*%<
+ * Associate a closest encloset proof with this record.
+ * Sets #DNS_RDATASETATTR_CLOSEST if successful.
+ * Adjusts the 'rdataset->ttl' to minimum of the 'rdataset->ttl' and
+ * the 'nsec' and 'rrsig(nsec)' ttl.
+ *
+ * Requires:
+ *\li 'rdataset' to be valid and #DNS_RDATASETATTR_CLOSEST to be set.
+ *\li 'name' to be valid and have NSEC3 and RRSIG(NSEC3) rdatasets.
+ */
+
+isc_result_t
+dns_rdataset_getadditional(dns_rdataset_t *rdataset,
+ dns_rdatasetadditional_t type,
+ dns_rdatatype_t qtype,
+ dns_acache_t *acache,
+ dns_zone_t **zonep,
+ dns_db_t **dbp,
+ dns_dbversion_t **versionp,
+ dns_dbnode_t **nodep,
+ dns_name_t *fname,
+ dns_message_t *msg,
+ isc_stdtime_t now);
+/*%<
+ * Get cached additional information from the DB node for a particular
+ * 'rdataset.' 'type' is one of dns_rdatasetadditional_fromauth,
+ * dns_rdatasetadditional_fromcache, and dns_rdatasetadditional_fromglue,
+ * which specifies the origin of the information. 'qtype' is intended to
+ * be used for specifying a particular rdata type in the cached information.
+ *
+ * Requires:
+ * \li 'rdataset' is a valid rdataset.
+ * \li 'acache' can be NULL, in which case this function will simply return
+ * ISC_R_FAILURE.
+ * \li For the other pointers, see dns_acache_getentry().
+ *
+ * Ensures:
+ * \li See dns_acache_getentry().
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_FAILURE - additional information caching is not supported.
+ * \li #ISC_R_NOTFOUND - the corresponding DB node has not cached additional
+ * information for 'rdataset.'
+ * \li Any error that dns_acache_getentry() can return.
+ */
+
+isc_result_t
+dns_rdataset_setadditional(dns_rdataset_t *rdataset,
+ dns_rdatasetadditional_t type,
+ dns_rdatatype_t qtype,
+ dns_acache_t *acache,
+ dns_zone_t *zone,
+ dns_db_t *db,
+ dns_dbversion_t *version,
+ dns_dbnode_t *node,
+ dns_name_t *fname);
+/*%<
+ * Set cached additional information to the DB node for a particular
+ * 'rdataset.' See dns_rdataset_getadditional for the semantics of 'type'
+ * and 'qtype'.
+ *
+ * Requires:
+ * \li 'rdataset' is a valid rdataset.
+ * \li 'acache' can be NULL, in which case this function will simply return
+ * ISC_R_FAILURE.
+ * \li For the other pointers, see dns_acache_setentry().
+ *
+ * Ensures:
+ * \li See dns_acache_setentry().
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_FAILURE - additional information caching is not supported.
+ * \li #ISC_R_NOMEMORY
+ * \li Any error that dns_acache_setentry() can return.
+ */
+
+isc_result_t
+dns_rdataset_putadditional(dns_acache_t *acache,
+ dns_rdataset_t *rdataset,
+ dns_rdatasetadditional_t type,
+ dns_rdatatype_t qtype);
+/*%<
+ * Discard cached additional information stored in the DB node for a particular
+ * 'rdataset.' See dns_rdataset_getadditional for the semantics of 'type'
+ * and 'qtype'.
+ *
+ * Requires:
+ * \li 'rdataset' is a valid rdataset.
+ * \li 'acache' can be NULL, in which case this function will simply return
+ * ISC_R_FAILURE.
+ *
+ * Ensures:
+ * \li See dns_acache_cancelentry().
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_FAILURE - additional information caching is not supported.
+ * \li #ISC_R_NOTFOUND - the corresponding DB node has not cached additional
+ * information for 'rdataset.'
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_RDATASET_H */
diff --git a/lib/dns/include/dns/rdatasetiter.h b/lib/dns/include/dns/rdatasetiter.h
new file mode 100644
index 0000000..dcde367
--- /dev/null
+++ b/lib/dns/include/dns/rdatasetiter.h
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rdatasetiter.h,v 1.21 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef DNS_RDATASETITER_H
+#define DNS_RDATASETITER_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/rdatasetiter.h
+ * \brief
+ * The DNS Rdataset Iterator interface allows iteration of all of the
+ * rdatasets at a node.
+ *
+ * The dns_rdatasetiter_t type is like a "virtual class". To actually use
+ * it, an implementation of the class is required. This implementation is
+ * supplied by the database.
+ *
+ * It is the client's responsibility to call dns_rdataset_disassociate()
+ * on all rdatasets returned.
+ *
+ * XXX more XXX
+ *
+ * MP:
+ *\li The iterator itself is not locked. The caller must ensure
+ * synchronization.
+ *
+ *\li The iterator methods ensure appropriate database locking.
+ *
+ * Reliability:
+ *\li No anticipated impact.
+ *
+ * Resources:
+ *\li TBS
+ *
+ * Security:
+ *\li No anticipated impact.
+ *
+ * Standards:
+ *\li None.
+ */
+
+/*****
+ ***** Imports
+ *****/
+
+#include <isc/lang.h>
+#include <isc/magic.h>
+#include <isc/stdtime.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/*****
+ ***** Types
+ *****/
+
+typedef struct dns_rdatasetitermethods {
+ void (*destroy)(dns_rdatasetiter_t **iteratorp);
+ isc_result_t (*first)(dns_rdatasetiter_t *iterator);
+ isc_result_t (*next)(dns_rdatasetiter_t *iterator);
+ void (*current)(dns_rdatasetiter_t *iterator,
+ dns_rdataset_t *rdataset);
+} dns_rdatasetitermethods_t;
+
+#define DNS_RDATASETITER_MAGIC ISC_MAGIC('D','N','S','i')
+#define DNS_RDATASETITER_VALID(i) ISC_MAGIC_VALID(i, DNS_RDATASETITER_MAGIC)
+
+/*%
+ * This structure is actually just the common prefix of a DNS db
+ * implementation's version of a dns_rdatasetiter_t.
+ * \brief
+ * Direct use of this structure by clients is forbidden. DB implementations
+ * may change the structure. 'magic' must be #DNS_RDATASETITER_MAGIC for
+ * any of the dns_rdatasetiter routines to work. DB implementations must
+ * maintain all DB rdataset iterator invariants.
+ */
+struct dns_rdatasetiter {
+ /* Unlocked. */
+ unsigned int magic;
+ dns_rdatasetitermethods_t * methods;
+ dns_db_t * db;
+ dns_dbnode_t * node;
+ dns_dbversion_t * version;
+ isc_stdtime_t now;
+};
+
+void
+dns_rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp);
+/*%<
+ * Destroy '*iteratorp'.
+ *
+ * Requires:
+ *
+ *\li '*iteratorp' is a valid iterator.
+ *
+ * Ensures:
+ *
+ *\li All resources used by the iterator are freed.
+ *
+ *\li *iteratorp == NULL.
+ */
+
+isc_result_t
+dns_rdatasetiter_first(dns_rdatasetiter_t *iterator);
+/*%<
+ * Move the rdataset cursor to the first rdataset at the node (if any).
+ *
+ * Requires:
+ *\li 'iterator' is a valid iterator.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ *\li ISC_R_NOMORE There are no rdatasets at the node.
+ *
+ *\li Other results are possible, depending on the DB implementation.
+ */
+
+isc_result_t
+dns_rdatasetiter_next(dns_rdatasetiter_t *iterator);
+/*%<
+ * Move the rdataset cursor to the next rdataset at the node (if any).
+ *
+ * Requires:
+ *\li 'iterator' is a valid iterator.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ *\li ISC_R_NOMORE There are no more rdatasets at the
+ * node.
+ *
+ *\li Other results are possible, depending on the DB implementation.
+ */
+
+void
+dns_rdatasetiter_current(dns_rdatasetiter_t *iterator,
+ dns_rdataset_t *rdataset);
+/*%<
+ * Return the current rdataset.
+ *
+ * Requires:
+ *\li 'iterator' is a valid iterator.
+ *
+ *\li 'rdataset' is a valid, disassociated rdataset.
+ *
+ *\li The rdataset cursor of 'iterator' is at a valid location (i.e. the
+ * result of last call to a cursor movement command was #ISC_R_SUCCESS).
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_RDATASETITER_H */
diff --git a/lib/dns/include/dns/rdataslab.h b/lib/dns/include/dns/rdataslab.h
new file mode 100644
index 0000000..3ac44b8
--- /dev/null
+++ b/lib/dns/include/dns/rdataslab.h
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rdataslab.h,v 1.33 2008/04/01 23:47:10 tbox Exp $ */
+
+#ifndef DNS_RDATASLAB_H
+#define DNS_RDATASLAB_H 1
+
+/*! \file dns/rdataslab.h
+ * \brief
+ * Implements storage of rdatasets into slabs of memory.
+ *
+ * MP:
+ *\li Clients of this module must impose any required synchronization.
+ *
+ * Reliability:
+ *\li This module deals with low-level byte streams. Errors in any of
+ * the functions are likely to crash the server or corrupt memory.
+ *
+ *\li If the caller passes invalid memory references, these functions are
+ * likely to crash the server or corrupt memory.
+ *
+ * Resources:
+ *\li None.
+ *
+ * Security:
+ *\li None.
+ *
+ * Standards:
+ *\li None.
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+#define DNS_RDATASLAB_FORCE 0x1
+#define DNS_RDATASLAB_EXACT 0x2
+
+#define DNS_RDATASLAB_OFFLINE 0x01 /* RRSIG is for offline DNSKEY */
+#define DNS_RDATASLAB_WARNMASK 0x0E /*%< RRSIG(DNSKEY) expired
+ * warnings number mask. */
+#define DNS_RDATASLAB_WARNSHIFT 1 /*%< How many bits to shift to find
+ * remaining expired warning number. */
+
+
+/***
+ *** Functions
+ ***/
+
+isc_result_t
+dns_rdataslab_fromrdataset(dns_rdataset_t *rdataset, isc_mem_t *mctx,
+ isc_region_t *region, unsigned int reservelen);
+/*%<
+ * Slabify a rdataset. The slab area will be allocated and returned
+ * in 'region'.
+ *
+ * Requires:
+ *\li 'rdataset' is valid.
+ *
+ * Ensures:
+ *\li 'region' will have base pointing to the start of allocated memory,
+ * with the slabified region beginning at region->base + reservelen.
+ * region->length contains the total length allocated.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS - successful completion
+ *\li ISC_R_NOMEMORY - no memory.
+ *\li XXX others
+ */
+
+void
+dns_rdataslab_tordataset(unsigned char *slab, unsigned int reservelen,
+ dns_rdataclass_t rdclass, dns_rdatatype_t rdtype,
+ dns_rdatatype_t covers, dns_ttl_t ttl,
+ dns_rdataset_t *rdataset);
+/*%<
+ * Construct an rdataset from a slab.
+ *
+ * Requires:
+ *\li 'slab' points to a slab.
+ *\li 'rdataset' is disassociated.
+ *
+ * Ensures:
+ *\li 'rdataset' is associated and points to a valid rdataest.
+ */
+unsigned int
+dns_rdataslab_size(unsigned char *slab, unsigned int reservelen);
+/*%<
+ * Return the total size of an rdataslab.
+ *
+ * Requires:
+ *\li 'slab' points to a slab.
+ *
+ * Returns:
+ *\li The number of bytes in the slab, including the reservelen.
+ */
+
+isc_result_t
+dns_rdataslab_merge(unsigned char *oslab, unsigned char *nslab,
+ unsigned int reservelen, isc_mem_t *mctx,
+ dns_rdataclass_t rdclass, dns_rdatatype_t type,
+ unsigned int flags, unsigned char **tslabp);
+/*%<
+ * Merge 'oslab' and 'nslab'.
+ */
+
+isc_result_t
+dns_rdataslab_subtract(unsigned char *mslab, unsigned char *sslab,
+ unsigned int reservelen, isc_mem_t *mctx,
+ dns_rdataclass_t rdclass, dns_rdatatype_t type,
+ unsigned int flags, unsigned char **tslabp);
+/*%<
+ * Subtract 'sslab' from 'mslab'. If 'exact' is true then all elements
+ * of 'sslab' must exist in 'mslab'.
+ *
+ * XXX
+ * valid flags are DNS_RDATASLAB_EXACT
+ */
+
+isc_boolean_t
+dns_rdataslab_equal(unsigned char *slab1, unsigned char *slab2,
+ unsigned int reservelen);
+/*%<
+ * Compare two rdataslabs for equality. This does _not_ do a full
+ * DNSSEC comparison.
+ *
+ * Requires:
+ *\li 'slab1' and 'slab2' point to slabs.
+ *
+ * Returns:
+ *\li ISC_TRUE if the slabs are equal, ISC_FALSE otherwise.
+ */
+isc_boolean_t
+dns_rdataslab_equalx(unsigned char *slab1, unsigned char *slab2,
+ unsigned int reservelen, dns_rdataclass_t rdclass,
+ dns_rdatatype_t type);
+/*%<
+ * Compare two rdataslabs for DNSSEC equality.
+ *
+ * Requires:
+ *\li 'slab1' and 'slab2' point to slabs.
+ *
+ * Returns:
+ *\li ISC_TRUE if the slabs are equal, #ISC_FALSE otherwise.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_RDATASLAB_H */
diff --git a/lib/dns/include/dns/rdatatype.h b/lib/dns/include/dns/rdatatype.h
new file mode 100644
index 0000000..ba9a92c
--- /dev/null
+++ b/lib/dns/include/dns/rdatatype.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rdatatype.h,v 1.26 2008/09/25 04:02:39 tbox Exp $ */
+
+#ifndef DNS_RDATATYPE_H
+#define DNS_RDATATYPE_H 1
+
+/*! \file dns/rdatatype.h */
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_rdatatype_fromtext(dns_rdatatype_t *typep, isc_textregion_t *source);
+/*%<
+ * Convert the text 'source' refers to into a DNS rdata type.
+ *
+ * Requires:
+ *\li 'typep' is a valid pointer.
+ *
+ *\li 'source' is a valid text region.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS on success
+ *\li DNS_R_UNKNOWN type is unknown
+ */
+
+isc_result_t
+dns_rdatatype_totext(dns_rdatatype_t type, isc_buffer_t *target);
+/*%<
+ * Put a textual representation of type 'type' into 'target'.
+ *
+ * Requires:
+ *\li 'type' is a valid type.
+ *
+ *\li 'target' is a valid text buffer.
+ *
+ * Ensures,
+ * if the result is success:
+ *\li The used space in 'target' is updated.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS on success
+ *\li #ISC_R_NOSPACE target buffer is too small
+ */
+
+void
+dns_rdatatype_format(dns_rdatatype_t rdtype,
+ char *array, unsigned int size);
+/*%<
+ * Format a human-readable representation of the type 'rdtype'
+ * into the character array 'array', which is of size 'size'.
+ * The resulting string is guaranteed to be null-terminated.
+ */
+
+#define DNS_RDATATYPE_FORMATSIZE sizeof("NSEC3PARAM")
+
+/*%<
+ * Minimum size of array to pass to dns_rdatatype_format().
+ * May need to be adjusted if a new RR type with a very long
+ * name is defined.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_RDATATYPE_H */
diff --git a/lib/dns/include/dns/request.h b/lib/dns/include/dns/request.h
new file mode 100644
index 0000000..9f115d0
--- /dev/null
+++ b/lib/dns/include/dns/request.h
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: request.h,v 1.27 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef DNS_REQUEST_H
+#define DNS_REQUEST_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/request.h
+ *
+ * \brief
+ * The request module provides simple request/response services useful for
+ * sending SOA queries, DNS Notify messages, and dynamic update requests.
+ *
+ * MP:
+ *\li The module ensures appropriate synchronization of data structures it
+ * creates and manipulates.
+ *
+ * Resources:
+ *\li TBS
+ *
+ * Security:
+ *\li No anticipated impact.
+ */
+
+#include <isc/lang.h>
+#include <isc/event.h>
+
+#include <dns/types.h>
+
+#define DNS_REQUESTOPT_TCP 0x00000001U
+
+typedef struct dns_requestevent {
+ ISC_EVENT_COMMON(struct dns_requestevent);
+ isc_result_t result;
+ dns_request_t *request;
+} dns_requestevent_t;
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_requestmgr_create(isc_mem_t *mctx, isc_timermgr_t *timermgr,
+ isc_socketmgr_t *socketmgr, isc_taskmgr_t *taskmgr,
+ dns_dispatchmgr_t *dispatchmgr,
+ dns_dispatch_t *dispatchv4, dns_dispatch_t *dispatchv6,
+ dns_requestmgr_t **requestmgrp);
+/*%<
+ * Create a request manager.
+ *
+ * Requires:
+ *
+ *\li 'mctx' is a valid memory context.
+ *
+ *\li 'timermgr' is a valid timer manager.
+ *
+ *\li 'socketmgr' is a valid socket manager.
+ *
+ *\li 'taskmgr' is a valid task manager.
+ *
+ *\li 'dispatchv4' is a valid dispatcher with an IPv4 UDP socket, or is NULL.
+ *
+ *\li 'dispatchv6' is a valid dispatcher with an IPv6 UDP socket, or is NULL.
+ *
+ *\li requestmgrp != NULL && *requestmgrp == NULL
+ *
+ * Ensures:
+ *
+ *\li On success, *requestmgrp is a valid request manager.
+ *
+ * Returns:
+ *
+ *\li ISC_R_SUCCESS
+ *
+ *\li Any other result indicates failure.
+ */
+
+void
+dns_requestmgr_whenshutdown(dns_requestmgr_t *requestmgr, isc_task_t *task,
+ isc_event_t **eventp);
+/*%<
+ * Send '*eventp' to 'task' when 'requestmgr' has completed shutdown.
+ *
+ * Notes:
+ *
+ *\li It is not safe to detach the last reference to 'requestmgr' until
+ * shutdown is complete.
+ *
+ * Requires:
+ *
+ *\li 'requestmgr' is a valid request manager.
+ *
+ *\li 'task' is a valid task.
+ *
+ *\li *eventp is a valid event.
+ *
+ * Ensures:
+ *
+ *\li *eventp == NULL.
+ */
+
+void
+dns_requestmgr_shutdown(dns_requestmgr_t *requestmgr);
+/*%<
+ * Start the shutdown process for 'requestmgr'.
+ *
+ * Notes:
+ *
+ *\li This call has no effect if the request manager is already shutting
+ * down.
+ *
+ * Requires:
+ *
+ *\li 'requestmgr' is a valid requestmgr.
+ */
+
+void
+dns_requestmgr_attach(dns_requestmgr_t *source, dns_requestmgr_t **targetp);
+/*%<
+ * Attach to the request manager. dns_requestmgr_shutdown() must not
+ * have been called on 'source' prior to calling dns_requestmgr_attach().
+ *
+ * Requires:
+ *
+ *\li 'source' is a valid requestmgr.
+ *
+ *\li 'targetp' to be non NULL and '*targetp' to be NULL.
+ */
+
+void
+dns_requestmgr_detach(dns_requestmgr_t **requestmgrp);
+/*%<
+ * Detach from the given requestmgr. If this is the final detach
+ * requestmgr will be destroyed. dns_requestmgr_shutdown() must
+ * be called before the final detach.
+ *
+ * Requires:
+ *
+ *\li '*requestmgrp' is a valid requestmgr.
+ *
+ * Ensures:
+ *\li '*requestmgrp' is NULL.
+ */
+
+isc_result_t
+dns_request_create(dns_requestmgr_t *requestmgr, dns_message_t *message,
+ isc_sockaddr_t *address, unsigned int options,
+ dns_tsigkey_t *key,
+ unsigned int timeout, isc_task_t *task,
+ isc_taskaction_t action, void *arg,
+ dns_request_t **requestp);
+/*%<
+ * Create and send a request.
+ *
+ * Notes:
+ *
+ *\li 'message' will be rendered and sent to 'address'. If the
+ * #DNS_REQUESTOPT_TCP option is set, TCP will be used. The request
+ * will timeout after 'timeout' seconds.
+ *
+ *\li When the request completes, successfully, due to a timeout, or
+ * because it was canceled, a completion event will be sent to 'task'.
+ *
+ * Requires:
+ *
+ *\li 'message' is a valid DNS message.
+ *
+ *\li 'address' is a valid sockaddr.
+ *
+ *\li 'timeout' > 0
+ *
+ *\li 'task' is a valid task.
+ *
+ *\li requestp != NULL && *requestp == NULL
+ */
+
+/*% See dns_request_createvia3() */
+isc_result_t
+dns_request_createvia(dns_requestmgr_t *requestmgr, dns_message_t *message,
+ isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
+ unsigned int options, dns_tsigkey_t *key,
+ unsigned int timeout, isc_task_t *task,
+ isc_taskaction_t action, void *arg,
+ dns_request_t **requestp);
+
+/*% See dns_request_createvia3() */
+isc_result_t
+dns_request_createvia2(dns_requestmgr_t *requestmgr, dns_message_t *message,
+ isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
+ unsigned int options, dns_tsigkey_t *key,
+ unsigned int timeout, unsigned int udptimeout,
+ isc_task_t *task, isc_taskaction_t action, void *arg,
+ dns_request_t **requestp);
+
+isc_result_t
+dns_request_createvia3(dns_requestmgr_t *requestmgr, dns_message_t *message,
+ isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
+ unsigned int options, dns_tsigkey_t *key,
+ unsigned int timeout, unsigned int udptimeout,
+ unsigned int udpretries, isc_task_t *task,
+ isc_taskaction_t action, void *arg,
+ dns_request_t **requestp);
+/*%<
+ * Create and send a request.
+ *
+ * Notes:
+ *
+ *\li 'message' will be rendered and sent to 'address'. If the
+ * #DNS_REQUESTOPT_TCP option is set, TCP will be used. The request
+ * will timeout after 'timeout' seconds. UDP requests will be resent
+ * at 'udptimeout' intervals if non-zero or 'udpretries' is non-zero.
+ *
+ *\li When the request completes, successfully, due to a timeout, or
+ * because it was canceled, a completion event will be sent to 'task'.
+ *
+ * Requires:
+ *
+ *\li 'message' is a valid DNS message.
+ *
+ *\li 'dstaddr' is a valid sockaddr.
+ *
+ *\li 'srcaddr' is a valid sockaddr or NULL.
+ *
+ *\li 'srcaddr' and 'dstaddr' are the same protocol family.
+ *
+ *\li 'timeout' > 0
+ *
+ *\li 'task' is a valid task.
+ *
+ *\li requestp != NULL && *requestp == NULL
+ */
+
+/*% See dns_request_createraw3() */
+isc_result_t
+dns_request_createraw(dns_requestmgr_t *requestmgr, isc_buffer_t *msgbuf,
+ isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
+ unsigned int options, unsigned int timeout,
+ isc_task_t *task, isc_taskaction_t action, void *arg,
+ dns_request_t **requestp);
+
+/*% See dns_request_createraw3() */
+isc_result_t
+dns_request_createraw2(dns_requestmgr_t *requestmgr, isc_buffer_t *msgbuf,
+ isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
+ unsigned int options, unsigned int timeout,
+ unsigned int udptimeout, isc_task_t *task,
+ isc_taskaction_t action, void *arg,
+ dns_request_t **requestp);
+
+isc_result_t
+dns_request_createraw3(dns_requestmgr_t *requestmgr, isc_buffer_t *msgbuf,
+ isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
+ unsigned int options, unsigned int timeout,
+ unsigned int udptimeout, unsigned int udpretries,
+ isc_task_t *task, isc_taskaction_t action, void *arg,
+ dns_request_t **requestp);
+/*!<
+ * \brief Create and send a request.
+ *
+ * Notes:
+ *
+ *\li 'msgbuf' will be sent to 'destaddr' after setting the id. If the
+ * #DNS_REQUESTOPT_TCP option is set, TCP will be used. The request
+ * will timeout after 'timeout' seconds. UDP requests will be resent
+ * at 'udptimeout' intervals if non-zero or if 'udpretries' is not zero.
+ *
+ *\li When the request completes, successfully, due to a timeout, or
+ * because it was canceled, a completion event will be sent to 'task'.
+ *
+ * Requires:
+ *
+ *\li 'msgbuf' is a valid DNS message in compressed wire format.
+ *
+ *\li 'destaddr' is a valid sockaddr.
+ *
+ *\li 'srcaddr' is a valid sockaddr or NULL.
+ *
+ *\li 'srcaddr' and 'dstaddr' are the same protocol family.
+ *
+ *\li 'timeout' > 0
+ *
+ *\li 'task' is a valid task.
+ *
+ *\li requestp != NULL && *requestp == NULL
+ */
+
+void
+dns_request_cancel(dns_request_t *request);
+/*%<
+ * Cancel 'request'.
+ *
+ * Requires:
+ *
+ *\li 'request' is a valid request.
+ *
+ * Ensures:
+ *
+ *\li If the completion event for 'request' has not yet been sent, it
+ * will be sent, and the result code will be ISC_R_CANCELED.
+ */
+
+isc_result_t
+dns_request_getresponse(dns_request_t *request, dns_message_t *message,
+ unsigned int options);
+/*%<
+ * Get the response to 'request' by filling in 'message'.
+ *
+ * 'options' is passed to dns_message_parse(). See dns_message_parse()
+ * for more details.
+ *
+ * Requires:
+ *
+ *\li 'request' is a valid request for which the caller has received the
+ * completion event.
+ *
+ *\li The result code of the completion event was #ISC_R_SUCCESS.
+ *
+ * Returns:
+ *
+ *\li ISC_R_SUCCESS
+ *
+ *\li Any result that dns_message_parse() can return.
+ */
+
+isc_boolean_t
+dns_request_usedtcp(dns_request_t *request);
+/*%<
+ * Return whether this query used TCP or not. Setting #DNS_REQUESTOPT_TCP
+ * in the call to dns_request_create() will cause the function to return
+ * #ISC_TRUE, othewise the result is based on the query message size.
+ *
+ * Requires:
+ *\li 'request' is a valid request.
+ *
+ * Returns:
+ *\li ISC_TRUE if TCP was used.
+ *\li ISC_FALSE if UDP was used.
+ */
+
+void
+dns_request_destroy(dns_request_t **requestp);
+/*%<
+ * Destroy 'request'.
+ *
+ * Requires:
+ *
+ *\li 'request' is a valid request for which the caller has received the
+ * completion event.
+ *
+ * Ensures:
+ *
+ *\li *requestp == NULL
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_REQUEST_H */
diff --git a/lib/dns/include/dns/resolver.h b/lib/dns/include/dns/resolver.h
new file mode 100644
index 0000000..2318e20
--- /dev/null
+++ b/lib/dns/include/dns/resolver.h
@@ -0,0 +1,481 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001, 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: resolver.h,v 1.60 2008/09/08 05:41:22 marka Exp $ */
+
+#ifndef DNS_RESOLVER_H
+#define DNS_RESOLVER_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/resolver.h
+ *
+ * \brief
+ * This is the BIND 9 resolver, the module responsible for resolving DNS
+ * requests by iteratively querying authoritative servers and following
+ * referrals. This is a "full resolver", not to be confused with
+ * the stub resolvers most people associate with the word "resolver".
+ * The full resolver is part of the caching name server or resolver
+ * daemon the stub resolver talks to.
+ *
+ * MP:
+ *\li The module ensures appropriate synchronization of data structures it
+ * creates and manipulates.
+ *
+ * Reliability:
+ *\li No anticipated impact.
+ *
+ * Resources:
+ *\li TBS
+ *
+ * Security:
+ *\li No anticipated impact.
+ *
+ * Standards:
+ *\li RFCs: 1034, 1035, 2181, TBS
+ *\li Drafts: TBS
+ */
+
+#include <isc/lang.h>
+#include <isc/socket.h>
+
+#include <dns/types.h>
+#include <dns/fixedname.h>
+
+ISC_LANG_BEGINDECLS
+
+/*%
+ * A dns_fetchevent_t is sent when a 'fetch' completes. Any of 'db',
+ * 'node', 'rdataset', and 'sigrdataset' may be bound. It is the
+ * receiver's responsibility to detach before freeing the event.
+ * \brief
+ * 'rdataset', 'sigrdataset', 'client' and 'id' are the values that were
+ * supplied when dns_resolver_createfetch() was called. They are returned
+ * to the caller so that they may be freed.
+ */
+typedef struct dns_fetchevent {
+ ISC_EVENT_COMMON(struct dns_fetchevent);
+ dns_fetch_t * fetch;
+ isc_result_t result;
+ dns_rdatatype_t qtype;
+ dns_db_t * db;
+ dns_dbnode_t * node;
+ dns_rdataset_t * rdataset;
+ dns_rdataset_t * sigrdataset;
+ dns_fixedname_t foundname;
+ isc_sockaddr_t * client;
+ dns_messageid_t id;
+} dns_fetchevent_t;
+
+/*
+ * Options that modify how a 'fetch' is done.
+ */
+#define DNS_FETCHOPT_TCP 0x01 /*%< Use TCP. */
+#define DNS_FETCHOPT_UNSHARED 0x02 /*%< See below. */
+#define DNS_FETCHOPT_RECURSIVE 0x04 /*%< Set RD? */
+#define DNS_FETCHOPT_NOEDNS0 0x08 /*%< Do not use EDNS. */
+#define DNS_FETCHOPT_FORWARDONLY 0x10 /*%< Only use forwarders. */
+#define DNS_FETCHOPT_NOVALIDATE 0x20 /*%< Disable validation. */
+#define DNS_FETCHOPT_EDNS512 0x40 /*%< Advertise a 512 byte
+ UDP buffer. */
+#define DNS_FETCHOPT_WANTNSID 0x80 /*%< Request NSID */
+
+#define DNS_FETCHOPT_EDNSVERSIONSET 0x00800000
+#define DNS_FETCHOPT_EDNSVERSIONMASK 0xff000000
+#define DNS_FETCHOPT_EDNSVERSIONSHIFT 24
+
+/*
+ * XXXRTH Should this API be made semi-private? (I.e.
+ * _dns_resolver_create()).
+ */
+
+#define DNS_RESOLVER_CHECKNAMES 0x01
+#define DNS_RESOLVER_CHECKNAMESFAIL 0x02
+
+isc_result_t
+dns_resolver_create(dns_view_t *view,
+ isc_taskmgr_t *taskmgr, unsigned int ntasks,
+ isc_socketmgr_t *socketmgr,
+ isc_timermgr_t *timermgr,
+ unsigned int options,
+ dns_dispatchmgr_t *dispatchmgr,
+ dns_dispatch_t *dispatchv4,
+ dns_dispatch_t *dispatchv6,
+ dns_resolver_t **resp);
+
+/*%<
+ * Create a resolver.
+ *
+ * Notes:
+ *
+ *\li Generally, applications should not create a resolver directly, but
+ * should instead call dns_view_createresolver().
+ *
+ * Requires:
+ *
+ *\li 'view' is a valid view.
+ *
+ *\li 'taskmgr' is a valid task manager.
+ *
+ *\li 'ntasks' > 0.
+ *
+ *\li 'socketmgr' is a valid socket manager.
+ *
+ *\li 'timermgr' is a valid timer manager.
+ *
+ *\li 'dispatchv4' is a valid dispatcher with an IPv4 UDP socket, or is NULL.
+ *
+ *\li 'dispatchv6' is a valid dispatcher with an IPv6 UDP socket, or is NULL.
+ *
+ *\li resp != NULL && *resp == NULL.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS On success.
+ *
+ *\li Anything else Failure.
+ */
+
+void
+dns_resolver_freeze(dns_resolver_t *res);
+/*%<
+ * Freeze resolver.
+ *
+ * Notes:
+ *
+ *\li Certain configuration changes cannot be made after the resolver
+ * is frozen. Fetches cannot be created until the resolver is frozen.
+ *
+ * Requires:
+ *
+ *\li 'res' is a valid, unfrozen resolver.
+ *
+ * Ensures:
+ *
+ *\li 'res' is frozen.
+ */
+
+void
+dns_resolver_prime(dns_resolver_t *res);
+/*%<
+ * Prime resolver.
+ *
+ * Notes:
+ *
+ *\li Resolvers which have a forwarding policy other than dns_fwdpolicy_only
+ * need to be primed with the root nameservers, otherwise the root
+ * nameserver hints data may be used indefinitely. This function requests
+ * that the resolver start a priming fetch, if it isn't already priming.
+ *
+ * Requires:
+ *
+ *\li 'res' is a valid, frozen resolver.
+ */
+
+
+void
+dns_resolver_whenshutdown(dns_resolver_t *res, isc_task_t *task,
+ isc_event_t **eventp);
+/*%<
+ * Send '*eventp' to 'task' when 'res' has completed shutdown.
+ *
+ * Notes:
+ *
+ *\li It is not safe to detach the last reference to 'res' until
+ * shutdown is complete.
+ *
+ * Requires:
+ *
+ *\li 'res' is a valid resolver.
+ *
+ *\li 'task' is a valid task.
+ *
+ *\li *eventp is a valid event.
+ *
+ * Ensures:
+ *
+ *\li *eventp == NULL.
+ */
+
+void
+dns_resolver_shutdown(dns_resolver_t *res);
+/*%<
+ * Start the shutdown process for 'res'.
+ *
+ * Notes:
+ *
+ *\li This call has no effect if the resolver is already shutting down.
+ *
+ * Requires:
+ *
+ *\li 'res' is a valid resolver.
+ */
+
+void
+dns_resolver_attach(dns_resolver_t *source, dns_resolver_t **targetp);
+
+void
+dns_resolver_detach(dns_resolver_t **resp);
+
+isc_result_t
+dns_resolver_createfetch(dns_resolver_t *res, dns_name_t *name,
+ dns_rdatatype_t type,
+ dns_name_t *domain, dns_rdataset_t *nameservers,
+ dns_forwarders_t *forwarders,
+ unsigned int options, isc_task_t *task,
+ isc_taskaction_t action, void *arg,
+ dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset,
+ dns_fetch_t **fetchp);
+
+isc_result_t
+dns_resolver_createfetch2(dns_resolver_t *res, dns_name_t *name,
+ dns_rdatatype_t type,
+ dns_name_t *domain, dns_rdataset_t *nameservers,
+ dns_forwarders_t *forwarders,
+ isc_sockaddr_t *client, isc_uint16_t id,
+ unsigned int options, isc_task_t *task,
+ isc_taskaction_t action, void *arg,
+ dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset,
+ dns_fetch_t **fetchp);
+/*%<
+ * Recurse to answer a question.
+ *
+ * Notes:
+ *
+ *\li This call starts a query for 'name', type 'type'.
+ *
+ *\li The 'domain' is a parent domain of 'name' for which
+ * a set of name servers 'nameservers' is known. If no
+ * such name server information is available, set
+ * 'domain' and 'nameservers' to NULL.
+ *
+ *\li 'forwarders' is unimplemented, and subject to change when
+ * we figure out how selective forwarding will work.
+ *
+ *\li When the fetch completes (successfully or otherwise), a
+ * #DNS_EVENT_FETCHDONE event with action 'action' and arg 'arg' will be
+ * posted to 'task'.
+ *
+ *\li The values of 'rdataset' and 'sigrdataset' will be returned in
+ * the FETCHDONE event.
+ *
+ *\li 'client' and 'id' are used for duplicate query detection. '*client'
+ * must remain stable until after 'action' has been called or
+ * dns_resolver_cancelfetch() is called.
+ *
+ * Requires:
+ *
+ *\li 'res' is a valid resolver that has been frozen.
+ *
+ *\li 'name' is a valid name.
+ *
+ *\li 'type' is not a meta type other than ANY.
+ *
+ *\li 'domain' is a valid name or NULL.
+ *
+ *\li 'nameservers' is a valid NS rdataset (whose owner name is 'domain')
+ * iff. 'domain' is not NULL.
+ *
+ *\li 'forwarders' is NULL.
+ *
+ *\li 'client' is a valid sockaddr or NULL.
+ *
+ *\li 'options' contains valid options.
+ *
+ *\li 'rdataset' is a valid, disassociated rdataset.
+ *
+ *\li 'sigrdataset' is NULL, or is a valid, disassociated rdataset.
+ *
+ *\li fetchp != NULL && *fetchp == NULL.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS Success
+ *\li #DNS_R_DUPLICATE
+ *\li #DNS_R_DROP
+ *
+ *\li Many other values are possible, all of which indicate failure.
+ */
+
+void
+dns_resolver_cancelfetch(dns_fetch_t *fetch);
+/*%<
+ * Cancel 'fetch'.
+ *
+ * Notes:
+ *
+ *\li If 'fetch' has not completed, post its FETCHDONE event with a
+ * result code of #ISC_R_CANCELED.
+ *
+ * Requires:
+ *
+ *\li 'fetch' is a valid fetch.
+ */
+
+void
+dns_resolver_destroyfetch(dns_fetch_t **fetchp);
+/*%<
+ * Destroy 'fetch'.
+ *
+ * Requires:
+ *
+ *\li '*fetchp' is a valid fetch.
+ *
+ *\li The caller has received the FETCHDONE event (either because the
+ * fetch completed or because dns_resolver_cancelfetch() was called).
+ *
+ * Ensures:
+ *
+ *\li *fetchp == NULL.
+ */
+
+dns_dispatchmgr_t *
+dns_resolver_dispatchmgr(dns_resolver_t *resolver);
+
+dns_dispatch_t *
+dns_resolver_dispatchv4(dns_resolver_t *resolver);
+
+dns_dispatch_t *
+dns_resolver_dispatchv6(dns_resolver_t *resolver);
+
+isc_socketmgr_t *
+dns_resolver_socketmgr(dns_resolver_t *resolver);
+
+isc_taskmgr_t *
+dns_resolver_taskmgr(dns_resolver_t *resolver);
+
+isc_uint32_t
+dns_resolver_getlamettl(dns_resolver_t *resolver);
+/*%<
+ * Get the resolver's lame-ttl. zero => no lame processing.
+ *
+ * Requires:
+ *\li 'resolver' to be valid.
+ */
+
+void
+dns_resolver_setlamettl(dns_resolver_t *resolver, isc_uint32_t lame_ttl);
+/*%<
+ * Set the resolver's lame-ttl. zero => no lame processing.
+ *
+ * Requires:
+ *\li 'resolver' to be valid.
+ */
+
+unsigned int
+dns_resolver_nrunning(dns_resolver_t *resolver);
+/*%<
+ * Return the number of currently running resolutions in this
+ * resolver. This is may be less than the number of outstanding
+ * fetches due to multiple identical fetches, or more than the
+ * number of of outstanding fetches due to the fact that resolution
+ * can continue even though a fetch has been canceled.
+ */
+
+isc_result_t
+dns_resolver_addalternate(dns_resolver_t *resolver, isc_sockaddr_t *alt,
+ dns_name_t *name, in_port_t port);
+/*%<
+ * Add alternate addresses to be tried in the event that the nameservers
+ * for a zone are not available in the address families supported by the
+ * operating system.
+ *
+ * Require:
+ * \li only one of 'name' or 'alt' to be valid.
+ */
+
+void
+dns_resolver_setudpsize(dns_resolver_t *resolver, isc_uint16_t udpsize);
+/*%<
+ * Set the EDNS UDP buffer size advertised by the server.
+ */
+
+isc_uint16_t
+dns_resolver_getudpsize(dns_resolver_t *resolver);
+/*%<
+ * Get the current EDNS UDP buffer size.
+ */
+
+void
+dns_resolver_reset_algorithms(dns_resolver_t *resolver);
+/*%<
+ * Clear the disabled DNSSEC algorithms.
+ */
+
+isc_result_t
+dns_resolver_disable_algorithm(dns_resolver_t *resolver, dns_name_t *name,
+ unsigned int alg);
+/*%<
+ * Mark the give DNSSEC algorithm as disabled and below 'name'.
+ * Valid algorithms are less than 256.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_RANGE
+ *\li #ISC_R_NOMEMORY
+ */
+
+isc_boolean_t
+dns_resolver_algorithm_supported(dns_resolver_t *resolver, dns_name_t *name,
+ unsigned int alg);
+/*%<
+ * Check if the given algorithm is supported by this resolver.
+ * This checks if the algorithm has been disabled via
+ * dns_resolver_disable_algorithm() then the underlying
+ * crypto libraries if not specifically disabled.
+ */
+
+isc_boolean_t
+dns_resolver_digest_supported(dns_resolver_t *resolver, unsigned int digest_type);
+/*%<
+ * Is this digest type supported.
+ */
+
+void
+dns_resolver_resetmustbesecure(dns_resolver_t *resolver);
+
+isc_result_t
+dns_resolver_setmustbesecure(dns_resolver_t *resolver, dns_name_t *name,
+ isc_boolean_t value);
+
+isc_boolean_t
+dns_resolver_getmustbesecure(dns_resolver_t *resolver, dns_name_t *name);
+
+void
+dns_resolver_setclientsperquery(dns_resolver_t *resolver,
+ isc_uint32_t min, isc_uint32_t max);
+
+void
+dns_resolver_getclientsperquery(dns_resolver_t *resolver, isc_uint32_t *cur,
+ isc_uint32_t *min, isc_uint32_t *max);
+
+isc_boolean_t
+dns_resolver_getzeronosoattl(dns_resolver_t *resolver);
+
+void
+dns_resolver_setzeronosoattl(dns_resolver_t *resolver, isc_boolean_t state);
+
+unsigned int
+dns_resolver_getoptions(dns_resolver_t *resolver);
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_RESOLVER_H */
diff --git a/lib/dns/include/dns/result.h b/lib/dns/include/dns/result.h
new file mode 100644
index 0000000..ed29bcd
--- /dev/null
+++ b/lib/dns/include/dns/result.h
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: result.h,v 1.116 2008/09/25 04:02:39 tbox Exp $ */
+
+#ifndef DNS_RESULT_H
+#define DNS_RESULT_H 1
+
+/*! \file dns/result.h */
+
+#include <isc/lang.h>
+#include <isc/resultclass.h>
+
+#include <dns/types.h>
+
+/*
+ * Nothing in this file truly depends on <isc/result.h>, but the
+ * DNS result codes are considered to be publicly derived from
+ * the ISC result codes, so including this file buys you the ISC_R_
+ * namespace too.
+ */
+#include <isc/result.h> /* Contractual promise. */
+
+/*
+ * DNS library result codes
+ */
+#define DNS_R_LABELTOOLONG (ISC_RESULTCLASS_DNS + 0)
+#define DNS_R_BADESCAPE (ISC_RESULTCLASS_DNS + 1)
+/*
+ * Since we dropped the support of bitstring labels, deprecate the related
+ * result codes too.
+
+#define DNS_R_BADBITSTRING (ISC_RESULTCLASS_DNS + 2)
+#define DNS_R_BITSTRINGTOOLONG (ISC_RESULTCLASS_DNS + 3)
+*/
+#define DNS_R_EMPTYLABEL (ISC_RESULTCLASS_DNS + 4)
+#define DNS_R_BADDOTTEDQUAD (ISC_RESULTCLASS_DNS + 5)
+#define DNS_R_INVALIDNS (ISC_RESULTCLASS_DNS + 6)
+#define DNS_R_UNKNOWN (ISC_RESULTCLASS_DNS + 7)
+#define DNS_R_BADLABELTYPE (ISC_RESULTCLASS_DNS + 8)
+#define DNS_R_BADPOINTER (ISC_RESULTCLASS_DNS + 9)
+#define DNS_R_TOOMANYHOPS (ISC_RESULTCLASS_DNS + 10)
+#define DNS_R_DISALLOWED (ISC_RESULTCLASS_DNS + 11)
+#define DNS_R_EXTRATOKEN (ISC_RESULTCLASS_DNS + 12)
+#define DNS_R_EXTRADATA (ISC_RESULTCLASS_DNS + 13)
+#define DNS_R_TEXTTOOLONG (ISC_RESULTCLASS_DNS + 14)
+#define DNS_R_NOTZONETOP (ISC_RESULTCLASS_DNS + 15)
+#define DNS_R_SYNTAX (ISC_RESULTCLASS_DNS + 16)
+#define DNS_R_BADCKSUM (ISC_RESULTCLASS_DNS + 17)
+#define DNS_R_BADAAAA (ISC_RESULTCLASS_DNS + 18)
+#define DNS_R_NOOWNER (ISC_RESULTCLASS_DNS + 19)
+#define DNS_R_NOTTL (ISC_RESULTCLASS_DNS + 20)
+#define DNS_R_BADCLASS (ISC_RESULTCLASS_DNS + 21)
+#define DNS_R_NAMETOOLONG (ISC_RESULTCLASS_DNS + 22)
+#define DNS_R_PARTIALMATCH (ISC_RESULTCLASS_DNS + 23)
+#define DNS_R_NEWORIGIN (ISC_RESULTCLASS_DNS + 24)
+#define DNS_R_UNCHANGED (ISC_RESULTCLASS_DNS + 25)
+#define DNS_R_BADTTL (ISC_RESULTCLASS_DNS + 26)
+#define DNS_R_NOREDATA (ISC_RESULTCLASS_DNS + 27)
+#define DNS_R_CONTINUE (ISC_RESULTCLASS_DNS + 28)
+#define DNS_R_DELEGATION (ISC_RESULTCLASS_DNS + 29)
+#define DNS_R_GLUE (ISC_RESULTCLASS_DNS + 30)
+#define DNS_R_DNAME (ISC_RESULTCLASS_DNS + 31)
+#define DNS_R_CNAME (ISC_RESULTCLASS_DNS + 32)
+#define DNS_R_BADDB (ISC_RESULTCLASS_DNS + 33)
+#define DNS_R_ZONECUT (ISC_RESULTCLASS_DNS + 34)
+#define DNS_R_BADZONE (ISC_RESULTCLASS_DNS + 35)
+#define DNS_R_MOREDATA (ISC_RESULTCLASS_DNS + 36)
+#define DNS_R_UPTODATE (ISC_RESULTCLASS_DNS + 37)
+#define DNS_R_TSIGVERIFYFAILURE (ISC_RESULTCLASS_DNS + 38)
+#define DNS_R_TSIGERRORSET (ISC_RESULTCLASS_DNS + 39)
+#define DNS_R_SIGINVALID (ISC_RESULTCLASS_DNS + 40)
+#define DNS_R_SIGEXPIRED (ISC_RESULTCLASS_DNS + 41)
+#define DNS_R_SIGFUTURE (ISC_RESULTCLASS_DNS + 42)
+#define DNS_R_KEYUNAUTHORIZED (ISC_RESULTCLASS_DNS + 43)
+#define DNS_R_INVALIDTIME (ISC_RESULTCLASS_DNS + 44)
+#define DNS_R_EXPECTEDTSIG (ISC_RESULTCLASS_DNS + 45)
+#define DNS_R_UNEXPECTEDTSIG (ISC_RESULTCLASS_DNS + 46)
+#define DNS_R_INVALIDTKEY (ISC_RESULTCLASS_DNS + 47)
+#define DNS_R_HINT (ISC_RESULTCLASS_DNS + 48)
+#define DNS_R_DROP (ISC_RESULTCLASS_DNS + 49)
+#define DNS_R_NOTLOADED (ISC_RESULTCLASS_DNS + 50)
+#define DNS_R_NCACHENXDOMAIN (ISC_RESULTCLASS_DNS + 51)
+#define DNS_R_NCACHENXRRSET (ISC_RESULTCLASS_DNS + 52)
+#define DNS_R_WAIT (ISC_RESULTCLASS_DNS + 53)
+#define DNS_R_NOTVERIFIEDYET (ISC_RESULTCLASS_DNS + 54)
+#define DNS_R_NOIDENTITY (ISC_RESULTCLASS_DNS + 55)
+#define DNS_R_NOJOURNAL (ISC_RESULTCLASS_DNS + 56)
+#define DNS_R_ALIAS (ISC_RESULTCLASS_DNS + 57)
+#define DNS_R_USETCP (ISC_RESULTCLASS_DNS + 58)
+#define DNS_R_NOVALIDSIG (ISC_RESULTCLASS_DNS + 59)
+#define DNS_R_NOVALIDNSEC (ISC_RESULTCLASS_DNS + 60)
+#define DNS_R_NOTINSECURE (ISC_RESULTCLASS_DNS + 61)
+#define DNS_R_UNKNOWNSERVICE (ISC_RESULTCLASS_DNS + 62)
+#define DNS_R_RECOVERABLE (ISC_RESULTCLASS_DNS + 63)
+#define DNS_R_UNKNOWNOPT (ISC_RESULTCLASS_DNS + 64)
+#define DNS_R_UNEXPECTEDID (ISC_RESULTCLASS_DNS + 65)
+#define DNS_R_SEENINCLUDE (ISC_RESULTCLASS_DNS + 66)
+#define DNS_R_NOTEXACT (ISC_RESULTCLASS_DNS + 67)
+#define DNS_R_BLACKHOLED (ISC_RESULTCLASS_DNS + 68)
+#define DNS_R_BADALG (ISC_RESULTCLASS_DNS + 69)
+#define DNS_R_METATYPE (ISC_RESULTCLASS_DNS + 70)
+#define DNS_R_CNAMEANDOTHER (ISC_RESULTCLASS_DNS + 71)
+#define DNS_R_SINGLETON (ISC_RESULTCLASS_DNS + 72)
+#define DNS_R_HINTNXRRSET (ISC_RESULTCLASS_DNS + 73)
+#define DNS_R_NOMASTERFILE (ISC_RESULTCLASS_DNS + 74)
+#define DNS_R_UNKNOWNPROTO (ISC_RESULTCLASS_DNS + 75)
+#define DNS_R_CLOCKSKEW (ISC_RESULTCLASS_DNS + 76)
+#define DNS_R_BADIXFR (ISC_RESULTCLASS_DNS + 77)
+#define DNS_R_NOTAUTHORITATIVE (ISC_RESULTCLASS_DNS + 78)
+#define DNS_R_NOVALIDKEY (ISC_RESULTCLASS_DNS + 79)
+#define DNS_R_OBSOLETE (ISC_RESULTCLASS_DNS + 80)
+#define DNS_R_FROZEN (ISC_RESULTCLASS_DNS + 81)
+#define DNS_R_UNKNOWNFLAG (ISC_RESULTCLASS_DNS + 82)
+#define DNS_R_EXPECTEDRESPONSE (ISC_RESULTCLASS_DNS + 83)
+#define DNS_R_NOVALIDDS (ISC_RESULTCLASS_DNS + 84)
+#define DNS_R_NSISADDRESS (ISC_RESULTCLASS_DNS + 85)
+#define DNS_R_REMOTEFORMERR (ISC_RESULTCLASS_DNS + 86)
+#define DNS_R_TRUNCATEDTCP (ISC_RESULTCLASS_DNS + 87)
+#define DNS_R_LAME (ISC_RESULTCLASS_DNS + 88)
+#define DNS_R_UNEXPECTEDRCODE (ISC_RESULTCLASS_DNS + 89)
+#define DNS_R_UNEXPECTEDOPCODE (ISC_RESULTCLASS_DNS + 90)
+#define DNS_R_CHASEDSSERVERS (ISC_RESULTCLASS_DNS + 91)
+#define DNS_R_EMPTYNAME (ISC_RESULTCLASS_DNS + 92)
+#define DNS_R_EMPTYWILD (ISC_RESULTCLASS_DNS + 93)
+#define DNS_R_BADBITMAP (ISC_RESULTCLASS_DNS + 94)
+#define DNS_R_FROMWILDCARD (ISC_RESULTCLASS_DNS + 95)
+#define DNS_R_BADOWNERNAME (ISC_RESULTCLASS_DNS + 96)
+#define DNS_R_BADNAME (ISC_RESULTCLASS_DNS + 97)
+#define DNS_R_DYNAMIC (ISC_RESULTCLASS_DNS + 98)
+#define DNS_R_UNKNOWNCOMMAND (ISC_RESULTCLASS_DNS + 99)
+#define DNS_R_MUSTBESECURE (ISC_RESULTCLASS_DNS + 100)
+#define DNS_R_COVERINGNSEC (ISC_RESULTCLASS_DNS + 101)
+#define DNS_R_MXISADDRESS (ISC_RESULTCLASS_DNS + 102)
+#define DNS_R_DUPLICATE (ISC_RESULTCLASS_DNS + 103)
+#define DNS_R_INVALIDNSEC3 (ISC_RESULTCLASS_DNS + 104)
+
+#define DNS_R_NRESULTS 105 /*%< Number of results */
+
+/*
+ * DNS wire format rcodes.
+ *
+ * By making these their own class we can easily convert them into the
+ * wire-format rcode value simply by masking off the resultclass.
+ */
+#define DNS_R_NOERROR (ISC_RESULTCLASS_DNSRCODE + 0)
+#define DNS_R_FORMERR (ISC_RESULTCLASS_DNSRCODE + 1)
+#define DNS_R_SERVFAIL (ISC_RESULTCLASS_DNSRCODE + 2)
+#define DNS_R_NXDOMAIN (ISC_RESULTCLASS_DNSRCODE + 3)
+#define DNS_R_NOTIMP (ISC_RESULTCLASS_DNSRCODE + 4)
+#define DNS_R_REFUSED (ISC_RESULTCLASS_DNSRCODE + 5)
+#define DNS_R_YXDOMAIN (ISC_RESULTCLASS_DNSRCODE + 6)
+#define DNS_R_YXRRSET (ISC_RESULTCLASS_DNSRCODE + 7)
+#define DNS_R_NXRRSET (ISC_RESULTCLASS_DNSRCODE + 8)
+#define DNS_R_NOTAUTH (ISC_RESULTCLASS_DNSRCODE + 9)
+#define DNS_R_NOTZONE (ISC_RESULTCLASS_DNSRCODE + 10)
+#define DNS_R_BADVERS (ISC_RESULTCLASS_DNSRCODE + 16)
+
+#define DNS_R_NRCODERESULTS 17 /*%< Number of rcode results */
+
+#define DNS_RESULT_ISRCODE(result) \
+ (ISC_RESULTCLASS_INCLASS(ISC_RESULTCLASS_DNSRCODE, (result)))
+
+ISC_LANG_BEGINDECLS
+
+const char *
+dns_result_totext(isc_result_t);
+
+void
+dns_result_register(void);
+
+dns_rcode_t
+dns_result_torcode(isc_result_t result);
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_RESULT_H */
diff --git a/lib/dns/include/dns/rootns.h b/lib/dns/include/dns/rootns.h
new file mode 100644
index 0000000..6da3f79
--- /dev/null
+++ b/lib/dns/include/dns/rootns.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rootns.h,v 1.16 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef DNS_ROOTNS_H
+#define DNS_ROOTNS_H 1
+
+/*! \file dns/rootns.h */
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_rootns_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
+ const char *filename, dns_db_t **target);
+
+void
+dns_root_checkhints(dns_view_t *view, dns_db_t *hints, dns_db_t *db);
+/*
+ * Reports differences between hints and the real roots.
+ *
+ * Requires view, hints and (cache) db to be valid.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_ROOTNS_H */
diff --git a/lib/dns/include/dns/sdb.h b/lib/dns/include/dns/sdb.h
new file mode 100644
index 0000000..3914295
--- /dev/null
+++ b/lib/dns/include/dns/sdb.h
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: sdb.h,v 1.21 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef DNS_SDB_H
+#define DNS_SDB_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/sdb.h
+ * \brief
+ * Simple database API.
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+/***
+ *** Types
+ ***/
+
+/*%
+ * A simple database. This is an opaque type.
+ */
+typedef struct dns_sdb dns_sdb_t;
+
+/*%
+ * A simple database lookup in progress. This is an opaque type.
+ */
+typedef struct dns_sdblookup dns_sdblookup_t;
+
+/*%
+ * A simple database traversal in progress. This is an opaque type.
+ */
+typedef struct dns_sdballnodes dns_sdballnodes_t;
+
+typedef isc_result_t
+(*dns_sdblookupfunc_t)(const char *zone, const char *name, void *dbdata,
+ dns_sdblookup_t *);
+
+typedef isc_result_t
+(*dns_sdbauthorityfunc_t)(const char *zone, void *dbdata, dns_sdblookup_t *);
+
+typedef isc_result_t
+(*dns_sdballnodesfunc_t)(const char *zone, void *dbdata,
+ dns_sdballnodes_t *allnodes);
+
+typedef isc_result_t
+(*dns_sdbcreatefunc_t)(const char *zone, int argc, char **argv,
+ void *driverdata, void **dbdata);
+
+typedef void
+(*dns_sdbdestroyfunc_t)(const char *zone, void *driverdata, void **dbdata);
+
+
+typedef struct dns_sdbmethods {
+ dns_sdblookupfunc_t lookup;
+ dns_sdbauthorityfunc_t authority;
+ dns_sdballnodesfunc_t allnodes;
+ dns_sdbcreatefunc_t create;
+ dns_sdbdestroyfunc_t destroy;
+} dns_sdbmethods_t;
+
+/***
+ *** Functions
+ ***/
+
+ISC_LANG_BEGINDECLS
+
+#define DNS_SDBFLAG_RELATIVEOWNER 0x00000001U
+#define DNS_SDBFLAG_RELATIVERDATA 0x00000002U
+#define DNS_SDBFLAG_THREADSAFE 0x00000004U
+
+isc_result_t
+dns_sdb_register(const char *drivername, const dns_sdbmethods_t *methods,
+ void *driverdata, unsigned int flags, isc_mem_t *mctx,
+ dns_sdbimplementation_t **sdbimp);
+/*%<
+ * Register a simple database driver for the database type 'drivername',
+ * implemented by the functions in '*methods'.
+ *
+ * sdbimp must point to a NULL dns_sdbimplementation_t pointer. That is,
+ * sdbimp != NULL && *sdbimp == NULL. It will be assigned a value that
+ * will later be used to identify the driver when deregistering it.
+ *
+ * The name server will perform lookups in the database by calling the
+ * function 'lookup', passing it a printable zone name 'zone', a printable
+ * domain name 'name', and a copy of the argument 'dbdata' that
+ * was potentially returned by the create function. The 'dns_sdblookup_t'
+ * argument to 'lookup' and 'authority' is an opaque pointer to be passed to
+ * ns_sdb_putrr().
+ *
+ * The lookup function returns the lookup results to the name server
+ * by calling ns_sdb_putrr() once for each record found. On success,
+ * the return value of the lookup function should be ISC_R_SUCCESS.
+ * If the domain name 'name' does not exist, the lookup function should
+ * ISC_R_NOTFOUND. Any other return value is treated as an error.
+ *
+ * Lookups at the zone apex will cause the server to also call the
+ * function 'authority' (if non-NULL), which must provide an SOA record
+ * and NS records for the zone by calling ns_sdb_putrr() once for each of
+ * these records. The 'authority' function may be NULL if invoking
+ * the 'lookup' function on the zone apex will return SOA and NS records.
+ *
+ * The allnodes function, if non-NULL, fills in an opaque structure to be
+ * used by a database iterator. This allows the zone to be transferred.
+ * This may use a considerable amount of memory for large zones, and the
+ * zone transfer may not be fully RFC1035 compliant if the zone is
+ * frequently changed.
+ *
+ * The create function will be called for each zone configured
+ * into the name server using this database type. It can be used
+ * to create a "database object" containg zone specific data,
+ * which can make use of the database arguments specified in the
+ * name server configuration.
+ *
+ * The destroy function will be called to free the database object
+ * when its zone is destroyed.
+ *
+ * The create and destroy functions may be NULL.
+ *
+ * If flags includes DNS_SDBFLAG_RELATIVEOWNER, the lookup and authority
+ * functions will be called with relative names rather than absolute names.
+ * The string "@" represents the zone apex in this case.
+ *
+ * If flags includes DNS_SDBFLAG_RELATIVERDATA, the rdata strings may
+ * include relative names. Otherwise, all names in the rdata string must
+ * be absolute. Be aware that if relative names are allowed, any
+ * absolute names must contain a trailing dot.
+ *
+ * If flags includes DNS_SDBFLAG_THREADSAFE, the driver must be able to
+ * handle multiple lookups in parallel. Otherwise, calls into the driver
+ * are serialized.
+ */
+
+void
+dns_sdb_unregister(dns_sdbimplementation_t **sdbimp);
+/*%<
+ * Removes the simple database driver from the list of registered database
+ * types. There must be no active databases of this type when this function
+ * is called.
+ */
+
+/*% See dns_sdb_putradata() */
+isc_result_t
+dns_sdb_putrr(dns_sdblookup_t *lookup, const char *type, dns_ttl_t ttl,
+ const char *data);
+isc_result_t
+dns_sdb_putrdata(dns_sdblookup_t *lookup, dns_rdatatype_t type, dns_ttl_t ttl,
+ const unsigned char *rdata, unsigned int rdlen);
+/*%<
+ * Add a single resource record to the lookup structure to be
+ * returned in the query response. dns_sdb_putrr() takes the
+ * resource record in master file text format as a null-terminated
+ * string, and dns_sdb_putrdata() takes the raw RDATA in
+ * uncompressed wire format.
+ */
+
+/*% See dns_sdb_putnamerdata() */
+isc_result_t
+dns_sdb_putnamedrr(dns_sdballnodes_t *allnodes, const char *name,
+ const char *type, dns_ttl_t ttl, const char *data);
+isc_result_t
+dns_sdb_putnamedrdata(dns_sdballnodes_t *allnodes, const char *name,
+ dns_rdatatype_t type, dns_ttl_t ttl,
+ const void *rdata, unsigned int rdlen);
+/*%<
+ * Add a single resource record to the allnodes structure to be
+ * included in a zone transfer response, in text or wire
+ * format as above.
+ */
+
+isc_result_t
+dns_sdb_putsoa(dns_sdblookup_t *lookup, const char *mname, const char *rname,
+ isc_uint32_t serial);
+/*%<
+ * This function may optionally be called from the 'authority' callback
+ * to simplify construction of the SOA record for 'zone'. It will
+ * provide a SOA listing 'mname' as as the master server and 'rname' as
+ * the responsible person mailbox. It is the responsibility of the
+ * driver to increment the serial number between responses if necessary.
+ * All other SOA fields will have reasonable default values.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_SDB_H */
diff --git a/lib/dns/include/dns/sdlz.h b/lib/dns/include/dns/sdlz.h
new file mode 100644
index 0000000..6f14e6f
--- /dev/null
+++ b/lib/dns/include/dns/sdlz.h
@@ -0,0 +1,266 @@
+/*
+ * Portions Copyright (C) 2005-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ *
+ * 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 STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET 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.
+ *
+ * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was
+ * conceived and contributed by Rob Butler.
+ *
+ * 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 ROB BUTLER
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * ROB BUTLER 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.
+ */
+
+/* $Id: sdlz.h,v 1.7 2007/06/19 23:47:17 tbox Exp $ */
+
+/*! \file dns/sdlz.h */
+
+#ifndef SDLZ_H
+#define SDLZ_H 1
+
+#include <dns/dlz.h>
+
+ISC_LANG_BEGINDECLS
+
+#define DNS_SDLZFLAG_THREADSAFE 0x00000001U
+#define DNS_SDLZFLAG_RELATIVEOWNER 0x00000002U
+#define DNS_SDLZFLAG_RELATIVERDATA 0x00000004U
+
+ /* A simple DLZ database. */
+typedef struct dns_sdlz_db dns_sdlz_db_t;
+
+ /* A simple DLZ database lookup in progress. */
+typedef struct dns_sdlzlookup dns_sdlzlookup_t;
+
+ /* A simple DLZ database traversal in progress. */
+typedef struct dns_sdlzallnodes dns_sdlzallnodes_t;
+
+
+typedef isc_result_t
+(*dns_sdlzallnodesfunc_t)(const char *zone, void *driverarg, void *dbdata,
+ dns_sdlzallnodes_t *allnodes);
+
+/*%<
+ * Method prototype. Drivers implementing the SDLZ interface may
+ * supply an all nodes method. This method is called when the DNS
+ * server is performing a zone transfer query, after the allow zone
+ * transfer method has been called. This method is only called if the
+ * allow zone transfer method returned ISC_R_SUCCESS. This method and
+ * the allow zone transfer method are both required for zone transfers
+ * to be supported. If the driver generates data dynamically (instead
+ * of searching in a database for it) it should not implement this
+ * function as a zone transfer would be meaningless. A SDLZ driver
+ * does not have to implement an all nodes method.
+ */
+
+typedef isc_result_t
+(*dns_sdlzallowzonexfr_t)(void *driverarg, void *dbdata, const char *name,
+ const char *client);
+
+/*%<
+ * Method prototype. Drivers implementing the SDLZ interface may
+ * supply an allow zone transfer method. This method is called when
+ * the DNS server is performing a zone transfer query, before the all
+ * nodes method can be called. This method and the all node method
+ * are both required for zone transfers to be supported. If the
+ * driver generates data dynamically (instead of searching in a
+ * database for it) it should not implement this function as a zone
+ * transfer would be meaningless. A SDLZ driver does not have to
+ * implement an allow zone transfer method.
+ *
+ * This method should return ISC_R_SUCCESS if the zone is supported by
+ * the database and a zone transfer is allowed for the specified
+ * client. If the zone is supported by the database, but zone
+ * transfers are not allowed for the specified client this method
+ * should return ISC_R_NOPERM.. Lastly the method should return
+ * ISC_R_NOTFOUND if the zone is not supported by the database. If an
+ * error occurs it should return a result code indicating the type of
+ * error.
+ */
+
+typedef isc_result_t
+(*dns_sdlzauthorityfunc_t)(const char *zone, void *driverarg, void *dbdata,
+ dns_sdlzlookup_t *lookup);
+
+/*%<
+ * Method prototype. Drivers implementing the SDLZ interface may
+ * supply an authority method. This method is called when the DNS
+ * server is performing a query, after both the find zone and lookup
+ * methods have been called. This method is required if the lookup
+ * function does not supply authority information for the dns
+ * record. A SDLZ driver does not have to implement an authority
+ * method.
+ */
+
+typedef isc_result_t
+(*dns_sdlzcreate_t)(const char *dlzname, unsigned int argc, char *argv[],
+ void *driverarg, void **dbdata);
+
+/*%<
+ * Method prototype. Drivers implementing the SDLZ interface may
+ * supply a create method. This method is called when the DNS server
+ * is starting up and creating drivers for use later. A SDLZ driver
+ * does not have to implement a create method.
+ */
+
+typedef void
+(*dns_sdlzdestroy_t)(void *driverarg, void *dbdata);
+
+/*%<
+ * Method prototype. Drivers implementing the SDLZ interface may
+ * supply a destroy method. This method is called when the DNS server
+ * is shuting down and no longer needs the driver. A SDLZ driver does
+ * not have to implement a destroy method.
+ */
+
+typedef isc_result_t
+(*dns_sdlzfindzone_t)(void *driverarg, void *dbdata, const char *name);
+
+/*%<
+ * Method prototype. Drivers implementing the SDLZ interface MUST
+ * supply a find zone method. This method is called when the DNS
+ * server is performing a query to to determine if 'name' is a
+ * supported dns zone. The find zone method will be called with the
+ * longest possible name first, and continue to be called with
+ * successively shorter domain names, until any of the following
+ * occur:
+ *
+ * \li 1) the function returns (ISC_R_SUCCESS) indicating a zone name
+ * match.
+ *
+ * \li 2) a problem occurs, and the functions returns anything other than
+ * (ISC_R_NOTFOUND)
+ *
+ * \li 3) we run out of domain name labels. I.E. we have tried the
+ * shortest domain name
+ *
+ * \li 4) the number of labels in the domain name is less than min_lables
+ * for dns_dlzfindzone
+ *
+ * The driver's find zone method should return ISC_R_SUCCESS if the
+ * zone is supported by the database. Otherwise it should return
+ * ISC_R_NOTFOUND, if the zone is not supported. If an error occurs
+ * it should return a result code indicating the type of error.
+ */
+
+typedef isc_result_t
+(*dns_sdlzlookupfunc_t)(const char *zone, const char *name, void *driverarg,
+ void *dbdata, dns_sdlzlookup_t *lookup);
+
+/*%<
+ * Method prototype. Drivers implementing the SDLZ interface MUST
+ * supply a lookup method. This method is called when the DNS server
+ * is performing a query, after the find zone and before any other
+ * methods have been called. This function returns record DNS record
+ * information using the dns_sdlz_putrr and dns_sdlz_putsoa functions.
+ * If this function supplies authority information for the DNS record
+ * the authority method is not required. If it does not, the
+ * authority function is required. A SDLZ driver must implement a
+ * lookup method.
+ */
+
+typedef struct dns_sdlzmethods {
+ dns_sdlzcreate_t create;
+ dns_sdlzdestroy_t destroy;
+ dns_sdlzfindzone_t findzone;
+ dns_sdlzlookupfunc_t lookup;
+ dns_sdlzauthorityfunc_t authority;
+ dns_sdlzallnodesfunc_t allnodes;
+ dns_sdlzallowzonexfr_t allowzonexfr;
+} dns_sdlzmethods_t;
+
+isc_result_t
+dns_sdlzregister(const char *drivername, const dns_sdlzmethods_t *methods,
+ void *driverarg, unsigned int flags, isc_mem_t *mctx,
+ dns_sdlzimplementation_t **sdlzimp);
+/*%<
+ * Register a dynamically loadable zones (dlz) driver for the database
+ * type 'drivername', implemented by the functions in '*methods'.
+ *
+ * sdlzimp must point to a NULL dns_sdlzimplementation_t pointer.
+ * That is, sdlzimp != NULL && *sdlzimp == NULL. It will be assigned
+ * a value that will later be used to identify the driver when
+ * deregistering it.
+ */
+
+void
+dns_sdlzunregister(dns_sdlzimplementation_t **sdlzimp);
+
+/*%<
+ * Removes the sdlz driver from the list of registered sdlz drivers.
+ * There must be no active sdlz drivers of this type when this
+ * function is called.
+ */
+
+isc_result_t
+dns_sdlz_putnamedrr(dns_sdlzallnodes_t *allnodes, const char *name,
+ const char *type, dns_ttl_t ttl, const char *data);
+/*%<
+ * Add a single resource record to the allnodes structure to be later
+ * parsed into a zone transfer response.
+ */
+
+isc_result_t
+dns_sdlz_putrr(dns_sdlzlookup_t *lookup, const char *type, dns_ttl_t ttl,
+ const char *data);
+/*%<
+ * Add a single resource record to the lookup structure to be later
+ * parsed into a query response.
+ */
+
+isc_result_t
+dns_sdlz_putsoa(dns_sdlzlookup_t *lookup, const char *mname, const char *rname,
+ isc_uint32_t serial);
+/*%<
+ * This function may optionally be called from the 'authority'
+ * callback to simplify construction of the SOA record for 'zone'. It
+ * will provide a SOA listing 'mname' as as the master server and
+ * 'rname' as the responsible person mailbox. It is the
+ * responsibility of the driver to increment the serial number between
+ * responses if necessary. All other SOA fields will have reasonable
+ * default values.
+ */
+
+
+ISC_LANG_ENDDECLS
+
+#endif /* SDLZ_H */
diff --git a/lib/dns/include/dns/secalg.h b/lib/dns/include/dns/secalg.h
new file mode 100644
index 0000000..2e4fe3e
--- /dev/null
+++ b/lib/dns/include/dns/secalg.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: secalg.h,v 1.19 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef DNS_SECALG_H
+#define DNS_SECALG_H 1
+
+/*! \file dns/secalg.h */
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_secalg_fromtext(dns_secalg_t *secalgp, isc_textregion_t *source);
+/*%<
+ * Convert the text 'source' refers to into a DNSSEC security algorithm value.
+ * The text may contain either a mnemonic algorithm name or a decimal algorithm
+ * number.
+ *
+ * Requires:
+ *\li 'secalgp' is a valid pointer.
+ *
+ *\li 'source' is a valid text region.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS on success
+ *\li ISC_R_RANGE numeric type is out of range
+ *\li DNS_R_UNKNOWN mnemonic type is unknown
+ */
+
+isc_result_t
+dns_secalg_totext(dns_secalg_t secalg, isc_buffer_t *target);
+/*%<
+ * Put a textual representation of the DNSSEC security algorithm 'secalg'
+ * into 'target'.
+ *
+ * Requires:
+ *\li 'secalg' is a valid secalg.
+ *
+ *\li 'target' is a valid text buffer.
+ *
+ * Ensures,
+ * if the result is success:
+ *\li The used space in 'target' is updated.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS on success
+ *\li ISC_R_NOSPACE target buffer is too small
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_SECALG_H */
diff --git a/lib/dns/include/dns/secproto.h b/lib/dns/include/dns/secproto.h
new file mode 100644
index 0000000..b9179c0
--- /dev/null
+++ b/lib/dns/include/dns/secproto.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: secproto.h,v 1.16 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef DNS_SECPROTO_H
+#define DNS_SECPROTO_H 1
+
+/*! \file dns/secproto.h */
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_secproto_fromtext(dns_secproto_t *secprotop, isc_textregion_t *source);
+/*%<
+ * Convert the text 'source' refers to into a DNSSEC security protocol value.
+ * The text may contain either a mnemonic protocol name or a decimal protocol
+ * number.
+ *
+ * Requires:
+ *\li 'secprotop' is a valid pointer.
+ *
+ *\li 'source' is a valid text region.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS on success
+ *\li ISC_R_RANGE numeric type is out of range
+ *\li DNS_R_UNKNOWN mnemonic type is unknown
+ */
+
+isc_result_t
+dns_secproto_totext(dns_secproto_t secproto, isc_buffer_t *target);
+/*%<
+ * Put a textual representation of the DNSSEC security protocol 'secproto'
+ * into 'target'.
+ *
+ * Requires:
+ *\li 'secproto' is a valid secproto.
+ *
+ *\li 'target' is a valid text buffer.
+ *
+ * Ensures,
+ * if the result is success:
+ * \li The used space in 'target' is updated.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS on success
+ *\li ISC_R_NOSPACE target buffer is too small
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_SECPROTO_H */
diff --git a/lib/dns/include/dns/soa.h b/lib/dns/include/dns/soa.h
new file mode 100644
index 0000000..bb56365
--- /dev/null
+++ b/lib/dns/include/dns/soa.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: soa.h,v 1.9 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef DNS_SOA_H
+#define DNS_SOA_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/soa.h
+ * \brief
+ * SOA utilities.
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_uint32_t
+dns_soa_getserial(dns_rdata_t *rdata);
+isc_uint32_t
+dns_soa_getrefresh(dns_rdata_t *rdata);
+isc_uint32_t
+dns_soa_getretry(dns_rdata_t *rdata);
+isc_uint32_t
+dns_soa_getexpire(dns_rdata_t *rdata);
+isc_uint32_t
+dns_soa_getminimum(dns_rdata_t *rdata);
+/*
+ * Extract an integer field from the rdata of a SOA record.
+ *
+ * Requires:
+ * rdata refers to the rdata of a well-formed SOA record.
+ */
+
+void
+dns_soa_setserial(isc_uint32_t val, dns_rdata_t *rdata);
+void
+dns_soa_setrefresh(isc_uint32_t val, dns_rdata_t *rdata);
+void
+dns_soa_setretry(isc_uint32_t val, dns_rdata_t *rdata);
+void
+dns_soa_setexpire(isc_uint32_t val, dns_rdata_t *rdata);
+void
+dns_soa_setminimum(isc_uint32_t val, dns_rdata_t *rdata);
+/*
+ * Change an integer field of a SOA record by modifying the
+ * rdata in-place.
+ *
+ * Requires:
+ * rdata refers to the rdata of a well-formed SOA record.
+ */
+
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_SOA_H */
diff --git a/lib/dns/include/dns/ssu.h b/lib/dns/include/dns/ssu.h
new file mode 100644
index 0000000..f013bd0
--- /dev/null
+++ b/lib/dns/include/dns/ssu.h
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001, 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: ssu.h,v 1.24 2008/01/18 23:46:58 tbox Exp $ */
+
+#ifndef DNS_SSU_H
+#define DNS_SSU_H 1
+
+/*! \file dns/ssu.h */
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+#define DNS_SSUMATCHTYPE_NAME 0
+#define DNS_SSUMATCHTYPE_SUBDOMAIN 1
+#define DNS_SSUMATCHTYPE_WILDCARD 2
+#define DNS_SSUMATCHTYPE_SELF 3
+#define DNS_SSUMATCHTYPE_SELFSUB 4
+#define DNS_SSUMATCHTYPE_SELFWILD 5
+#define DNS_SSUMATCHTYPE_SELFKRB5 6
+#define DNS_SSUMATCHTYPE_SELFMS 7
+#define DNS_SSUMATCHTYPE_SUBDOMAINMS 8
+#define DNS_SSUMATCHTYPE_SUBDOMAINKRB5 9
+#define DNS_SSUMATCHTYPE_TCPSELF 10
+#define DNS_SSUMATCHTYPE_6TO4SELF 11
+#define DNS_SSUMATCHTYPE_MAX 11 /* max value */
+
+isc_result_t
+dns_ssutable_create(isc_mem_t *mctx, dns_ssutable_t **table);
+/*%<
+ * Creates a table that will be used to store simple-secure-update rules.
+ * Note: all locking must be provided by the client.
+ *
+ * Requires:
+ *\li 'mctx' is a valid memory context
+ *\li 'table' is not NULL, and '*table' is NULL
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ *\li ISC_R_NOMEMORY
+ */
+
+void
+dns_ssutable_attach(dns_ssutable_t *source, dns_ssutable_t **targetp);
+/*%<
+ * Attach '*targetp' to 'source'.
+ *
+ * Requires:
+ *\li 'source' is a valid SSU table
+ *\li 'targetp' points to a NULL dns_ssutable_t *.
+ *
+ * Ensures:
+ *\li *targetp is attached to source.
+ */
+
+void
+dns_ssutable_detach(dns_ssutable_t **tablep);
+/*%<
+ * Detach '*tablep' from its simple-secure-update rule table.
+ *
+ * Requires:
+ *\li 'tablep' points to a valid dns_ssutable_t
+ *
+ * Ensures:
+ *\li *tablep is NULL
+ *\li If '*tablep' is the last reference to the SSU table, all
+ * resources used by the table will be freed.
+ */
+
+isc_result_t
+dns_ssutable_addrule(dns_ssutable_t *table, isc_boolean_t grant,
+ dns_name_t *identity, unsigned int matchtype,
+ dns_name_t *name, unsigned int ntypes,
+ dns_rdatatype_t *types);
+/*%<
+ * Adds a new rule to a simple-secure-update rule table. The rule
+ * either grants or denies update privileges of an identity (or set of
+ * identities) to modify a name (or set of names) or certain types present
+ * at that name.
+ *
+ * Notes:
+ *\li If 'matchtype' is of SELF type, this rule only matches if the
+ * name to be updated matches the signing identity.
+ *
+ *\li If 'ntypes' is 0, this rule applies to all types except
+ * NS, SOA, RRSIG, and NSEC.
+ *
+ *\li If 'types' includes ANY, this rule applies to all types
+ * except NSEC.
+ *
+ * Requires:
+ *\li 'table' is a valid SSU table
+ *\li 'identity' is a valid absolute name
+ *\li 'matchtype' must be one of the defined constants.
+ *\li 'name' is a valid absolute name
+ *\li If 'ntypes' > 0, 'types' must not be NULL
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ *\li ISC_R_NOMEMORY
+ */
+
+isc_boolean_t
+dns_ssutable_checkrules(dns_ssutable_t *table, dns_name_t *signer,
+ dns_name_t *name, isc_netaddr_t *tcpaddr,
+ dns_rdatatype_t type);
+/*%<
+ * Checks that the attempted update of (name, type) is allowed according
+ * to the rules specified in the simple-secure-update rule table. If
+ * no rules are matched, access is denied.
+ *
+ * Notes:
+ * 'tcpaddr' should only be set if the request received
+ * via TCP. This provides a weak assurance that the
+ * request was not spoofed. 'tcpaddr' is to to validate
+ * DNS_SSUMATCHTYPE_TCPSELF and DNS_SSUMATCHTYPE_6TO4SELF
+ * rules.
+ *
+ * For DNS_SSUMATCHTYPE_TCPSELF the addresses are mapped to
+ * the standard reverse names under IN-ADDR.ARPA and IP6.ARPA.
+ * RFC 1035, Section 3.5, "IN-ADDR.ARPA domain" and RFC 3596,
+ * Section 2.5, "IP6.ARPA Domain".
+ *
+ * For DNS_SSUMATCHTYPE_6TO4SELF, IPv4 address are converted
+ * to a 6to4 prefix (48 bits) per the rules in RFC 3056. Only
+ * the top 48 bits of the IPv6 address are mapped to the reverse
+ * name. This is independent of whether the most significant 16
+ * bits match 2002::/16, assigned for 6to4 prefixes, or not.
+ *
+ * Requires:
+ *\li 'table' is a valid SSU table
+ *\li 'signer' is NULL or a valid absolute name
+ *\li 'tcpaddr' is NULL or a valid network address.
+ *\li 'name' is a valid absolute name
+ */
+
+
+/*% Accessor functions to extract rule components */
+isc_boolean_t dns_ssurule_isgrant(const dns_ssurule_t *rule);
+/*% Accessor functions to extract rule components */
+dns_name_t * dns_ssurule_identity(const dns_ssurule_t *rule);
+/*% Accessor functions to extract rule components */
+unsigned int dns_ssurule_matchtype(const dns_ssurule_t *rule);
+/*% Accessor functions to extract rule components */
+dns_name_t * dns_ssurule_name(const dns_ssurule_t *rule);
+/*% Accessor functions to extract rule components */
+unsigned int dns_ssurule_types(const dns_ssurule_t *rule,
+ dns_rdatatype_t **types);
+
+isc_result_t dns_ssutable_firstrule(const dns_ssutable_t *table,
+ dns_ssurule_t **rule);
+/*%<
+ * Initiates a rule iterator. There is no need to maintain any state.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMORE
+ */
+
+isc_result_t dns_ssutable_nextrule(dns_ssurule_t *rule,
+ dns_ssurule_t **nextrule);
+/*%<
+ * Returns the next rule in the table.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMORE
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_SSU_H */
diff --git a/lib/dns/include/dns/stats.h b/lib/dns/include/dns/stats.h
new file mode 100644
index 0000000..8f8e38a
--- /dev/null
+++ b/lib/dns/include/dns/stats.h
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: stats.h,v 1.18 2008/09/08 05:59:11 marka Exp $ */
+
+#ifndef DNS_STATS_H
+#define DNS_STATS_H 1
+
+/*! \file dns/stats.h */
+
+#include <dns/types.h>
+
+/*%
+ * Statistics counters. Used as dns_statscounter_t values.
+ */
+enum {
+ /*%
+ * Resolver statistics counters.
+ */
+ dns_resstatscounter_queryv4 = 0,
+ dns_resstatscounter_queryv6 = 1,
+ dns_resstatscounter_responsev4 = 2,
+ dns_resstatscounter_responsev6 = 3,
+ dns_resstatscounter_nxdomain = 4,
+ dns_resstatscounter_servfail = 5,
+ dns_resstatscounter_formerr = 6,
+ dns_resstatscounter_othererror = 7,
+ dns_resstatscounter_edns0fail = 8,
+ dns_resstatscounter_mismatch = 9,
+ dns_resstatscounter_truncated = 10,
+ dns_resstatscounter_lame = 11,
+ dns_resstatscounter_retry = 12,
+ dns_resstatscounter_gluefetchv4 = 13,
+ dns_resstatscounter_gluefetchv6 = 14,
+ dns_resstatscounter_gluefetchv4fail = 15,
+ dns_resstatscounter_gluefetchv6fail = 16,
+ dns_resstatscounter_val = 17,
+ dns_resstatscounter_valsuccess = 18,
+ dns_resstatscounter_valnegsuccess = 19,
+ dns_resstatscounter_valfail = 20,
+
+ dns_resstatscounter_max = 21,
+
+ /*%
+ * Zone statistics counters.
+ */
+ dns_zonestatscounter_notifyoutv4 = 0,
+ dns_zonestatscounter_notifyoutv6 = 1,
+ dns_zonestatscounter_notifyinv4 = 2,
+ dns_zonestatscounter_notifyinv6 = 3,
+ dns_zonestatscounter_notifyrej = 4,
+ dns_zonestatscounter_soaoutv4 = 5,
+ dns_zonestatscounter_soaoutv6 = 6,
+ dns_zonestatscounter_axfrreqv4 = 7,
+ dns_zonestatscounter_axfrreqv6 = 8,
+ dns_zonestatscounter_ixfrreqv4 = 9,
+ dns_zonestatscounter_ixfrreqv6 = 10,
+ dns_zonestatscounter_xfrsuccess = 11,
+ dns_zonestatscounter_xfrfail = 12,
+
+ dns_zonestatscounter_max = 13,
+
+ /*%
+ * Query statistics counters (obsolete).
+ */
+ dns_statscounter_success = 0, /*%< Successful lookup */
+ dns_statscounter_referral = 1, /*%< Referral result */
+ dns_statscounter_nxrrset = 2, /*%< NXRRSET result */
+ dns_statscounter_nxdomain = 3, /*%< NXDOMAIN result */
+ dns_statscounter_recursion = 4, /*%< Recursion was used */
+ dns_statscounter_failure = 5, /*%< Some other failure */
+ dns_statscounter_duplicate = 6, /*%< Duplicate query */
+ dns_statscounter_dropped = 7 /*%< Duplicate query (dropped) */
+};
+
+#define DNS_STATS_NCOUNTERS 8
+
+/*%<
+ * Flag(s) for dns_xxxstats_dump().
+ */
+#define DNS_STATSDUMP_VERBOSE 0x00000001 /*%< dump 0-value counters */
+
+/*%<
+ * (Obsoleted)
+ */
+LIBDNS_EXTERNAL_DATA extern const char *dns_statscounter_names[];
+
+/*%
+ * Attributes for statistics counters of RRset and Rdatatype types.
+ *
+ * _OTHERTYPE
+ * The rdata type is not explicitly supported and the corresponding counter
+ * is counted for other such types, too. When this attribute is set,
+ * the base type is of no use.
+ *
+ * _NXRRSET
+ * RRset type counters only. Indicates the RRset is non existent.
+ *
+ * _NXDOMAIN
+ * RRset type counters only. Indicates a non existent name. When this
+ * attribute is set, the base type is of no use.
+ */
+#define DNS_RDATASTATSTYPE_ATTR_OTHERTYPE 0x0001
+#define DNS_RDATASTATSTYPE_ATTR_NXRRSET 0x0002
+#define DNS_RDATASTATSTYPE_ATTR_NXDOMAIN 0x0004
+
+/*%<
+ * Conversion macros among dns_rdatatype_t, attributes and dns_statscounter_t.
+ */
+#define DNS_RDATASTATSTYPE_BASE(type) ((dns_rdatatype_t)((type) & 0xFFFF))
+#define DNS_RDATASTATSTYPE_ATTR(type) ((type) >> 16)
+#define DNS_RDATASTATSTYPE_VALUE(b, a) (((a) << 16) | (b))
+
+/*%<
+ * Types of dump callbacks.
+ */
+typedef void (*dns_generalstats_dumper_t)(dns_statscounter_t, isc_uint64_t,
+ void *);
+typedef void (*dns_rdatatypestats_dumper_t)(dns_rdatastatstype_t, isc_uint64_t,
+ void *);
+typedef void (*dns_opcodestats_dumper_t)(dns_opcode_t, isc_uint64_t, void *);
+
+isc_result_t
+dns_generalstats_create(isc_mem_t *mctx, dns_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
+ */
+
+isc_result_t
+dns_rdatatypestats_create(isc_mem_t *mctx, dns_stats_t **statsp);
+/*%<
+ * Create a statistics counter structure per rdatatype.
+ *
+ * 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
+ */
+
+isc_result_t
+dns_rdatasetstats_create(isc_mem_t *mctx, dns_stats_t **statsp);
+/*%<
+ * Create a statistics counter structure per RRset.
+ *
+ * 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
+ */
+
+isc_result_t
+dns_opcodestats_create(isc_mem_t *mctx, dns_stats_t **statsp);
+/*%<
+ * Create a statistics counter structure per opcode.
+ *
+ * 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
+dns_stats_attach(dns_stats_t *stats, dns_stats_t **statsp);
+/*%<
+ * Attach to a statistics set.
+ *
+ * Requires:
+ *\li 'stats' is a valid dns_stats_t.
+ *
+ *\li 'statsp' != NULL && '*statsp' == NULL
+ */
+
+void
+dns_stats_detach(dns_stats_t **statsp);
+/*%<
+ * Detaches from the statistics set.
+ *
+ * Requires:
+ *\li 'statsp' != NULL and '*statsp' is a valid dns_stats_t.
+ */
+
+void
+dns_generalstats_increment(dns_stats_t *stats, dns_statscounter_t counter);
+/*%<
+ * Increment the counter-th counter of stats.
+ *
+ * Requires:
+ *\li 'stats' is a valid dns_stats_t created by dns_generalstats_create().
+ *
+ *\li counter is less than the maximum available ID for the stats specified
+ * on creation.
+ */
+
+void
+dns_rdatatypestats_increment(dns_stats_t *stats, dns_rdatatype_t type);
+/*%<
+ * Increment the statistics counter for 'type'.
+ *
+ * Requires:
+ *\li 'stats' is a valid dns_stats_t created by dns_rdatatypestats_create().
+ */
+
+void
+dns_rdatasetstats_increment(dns_stats_t *stats, dns_rdatastatstype_t rrsettype);
+/*%<
+ * Increment the statistics counter for 'rrsettype'.
+ *
+ * Requires:
+ *\li 'stats' is a valid dns_stats_t created by dns_rdatasetstats_create().
+ */
+
+void
+dns_rdatasetstats_decrement(dns_stats_t *stats, dns_rdatastatstype_t rrsettype);
+/*%<
+ * Decrement the statistics counter for 'rrsettype'.
+ *
+ * Requires:
+ *\li 'stats' is a valid dns_stats_t created by dns_rdatasetstats_create().
+ */
+
+void
+dns_opcodestats_increment(dns_stats_t *stats, dns_opcode_t code);
+/*%<
+ * Increment the statistics counter for 'code'.
+ *
+ * Requires:
+ *\li 'stats' is a valid dns_stats_t created by dns_opcodestats_create().
+ */
+
+void
+dns_generalstats_dump(dns_stats_t *stats, dns_generalstats_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 DNS_STATSDUMP_VERBOSE flag, even such counters are dumped.
+ *
+ * Requires:
+ *\li 'stats' is a valid dns_stats_t created by dns_generalstats_create().
+ */
+
+void
+dns_rdatatypestats_dump(dns_stats_t *stats, dns_rdatatypestats_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 the corresponding type in the form of
+ * dns_rdatastatstype_t, the current counter value and the given argument
+ * arg. By default counters that have a value of 0 is skipped; if options has
+ * the DNS_STATSDUMP_VERBOSE flag, even such counters are dumped.
+ *
+ * Requires:
+ *\li 'stats' is a valid dns_stats_t created by dns_generalstats_create().
+ */
+
+void
+dns_rdatasetstats_dump(dns_stats_t *stats, dns_rdatatypestats_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 the corresponding type in the form of
+ * dns_rdatastatstype_t, the current counter value and the given argument
+ * arg. By default counters that have a value of 0 is skipped; if options has
+ * the DNS_STATSDUMP_VERBOSE flag, even such counters are dumped.
+ *
+ * Requires:
+ *\li 'stats' is a valid dns_stats_t created by dns_generalstats_create().
+ */
+
+void
+dns_opcodestats_dump(dns_stats_t *stats, dns_opcodestats_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 the corresponding opcode, the current
+ * counter value and the given argument arg. By default counters that have a
+ * value of 0 is skipped; if options has the DNS_STATSDUMP_VERBOSE flag, even
+ * such counters are dumped.
+ *
+ * Requires:
+ *\li 'stats' is a valid dns_stats_t created by dns_generalstats_create().
+ */
+
+isc_result_t
+dns_stats_alloccounters(isc_mem_t *mctx, isc_uint64_t **ctrp);
+/*%<
+ * Allocate an array of query statistics counters from the memory
+ * context 'mctx'.
+ *
+ * This function is obsoleted. Use dns_xxxstats_create() instead.
+ */
+
+void
+dns_stats_freecounters(isc_mem_t *mctx, isc_uint64_t **ctrp);
+/*%<
+ * Free an array of query statistics counters allocated from the memory
+ * context 'mctx'.
+ *
+ * This function is obsoleted. Use dns_stats_destroy() instead.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_STATS_H */
diff --git a/lib/dns/include/dns/tcpmsg.h b/lib/dns/include/dns/tcpmsg.h
new file mode 100644
index 0000000..fe83c53
--- /dev/null
+++ b/lib/dns/include/dns/tcpmsg.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: tcpmsg.h,v 1.22 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef DNS_TCPMSG_H
+#define DNS_TCPMSG_H 1
+
+/*! \file dns/tcpmsg.h */
+
+#include <isc/buffer.h>
+#include <isc/lang.h>
+#include <isc/socket.h>
+
+typedef struct dns_tcpmsg {
+ /* private (don't touch!) */
+ unsigned int magic;
+ isc_uint16_t size;
+ isc_buffer_t buffer;
+ unsigned int maxsize;
+ isc_mem_t *mctx;
+ isc_socket_t *sock;
+ isc_task_t *task;
+ isc_taskaction_t action;
+ void *arg;
+ isc_event_t event;
+ /* public (read-only) */
+ isc_result_t result;
+ isc_sockaddr_t address;
+} dns_tcpmsg_t;
+
+ISC_LANG_BEGINDECLS
+
+void
+dns_tcpmsg_init(isc_mem_t *mctx, isc_socket_t *sock, dns_tcpmsg_t *tcpmsg);
+/*%<
+ * Associate a tcp message state with a given memory context and
+ * TCP socket.
+ *
+ * Requires:
+ *
+ *\li "mctx" and "sock" be non-NULL and valid types.
+ *
+ *\li "sock" be a read/write TCP socket.
+ *
+ *\li "tcpmsg" be non-NULL and an uninitialized or invalidated structure.
+ *
+ * Ensures:
+ *
+ *\li "tcpmsg" is a valid structure.
+ */
+
+void
+dns_tcpmsg_setmaxsize(dns_tcpmsg_t *tcpmsg, unsigned int maxsize);
+/*%<
+ * Set the maximum packet size to "maxsize"
+ *
+ * Requires:
+ *
+ *\li "tcpmsg" be valid.
+ *
+ *\li 512 <= "maxsize" <= 65536
+ */
+
+isc_result_t
+dns_tcpmsg_readmessage(dns_tcpmsg_t *tcpmsg,
+ isc_task_t *task, isc_taskaction_t action, void *arg);
+/*%<
+ * Schedule an event to be delivered when a DNS message is readable, or
+ * when an error occurs on the socket.
+ *
+ * Requires:
+ *
+ *\li "tcpmsg" be valid.
+ *
+ *\li "task", "taskaction", and "arg" be valid.
+ *
+ * Returns:
+ *
+ *\li ISC_R_SUCCESS -- no error
+ *\li Anything that the isc_socket_recv() call can return. XXXMLG
+ *
+ * Notes:
+ *
+ *\li The event delivered is a fully generic event. It will contain no
+ * actual data. The sender will be a pointer to the dns_tcpmsg_t.
+ * The result code inside that structure should be checked to see
+ * what the final result was.
+ */
+
+void
+dns_tcpmsg_cancelread(dns_tcpmsg_t *tcpmsg);
+/*%<
+ * Cancel a readmessage() call. The event will still be posted with a
+ * CANCELED result code.
+ *
+ * Requires:
+ *
+ *\li "tcpmsg" be valid.
+ */
+
+void
+dns_tcpmsg_keepbuffer(dns_tcpmsg_t *tcpmsg, isc_buffer_t *buffer);
+/*%<
+ * If a dns buffer is to be kept between calls, this function marks the
+ * internal state-machine buffer as invalid, and copies all the contents
+ * of the state into "buffer".
+ *
+ * Requires:
+ *
+ *\li "tcpmsg" be valid.
+ *
+ *\li "buffer" be non-NULL.
+ */
+
+void
+dns_tcpmsg_invalidate(dns_tcpmsg_t *tcpmsg);
+/*%<
+ * Clean up all allocated state, and invalidate the structure.
+ *
+ * Requires:
+ *
+ *\li "tcpmsg" be valid.
+ *
+ * Ensures:
+ *
+ *\li "tcpmsg" is invalidated and disassociated with all memory contexts,
+ * sockets, etc.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_TCPMSG_H */
diff --git a/lib/dns/include/dns/time.h b/lib/dns/include/dns/time.h
new file mode 100644
index 0000000..5b47d11
--- /dev/null
+++ b/lib/dns/include/dns/time.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: time.h,v 1.17 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef DNS_TIME_H
+#define DNS_TIME_H 1
+
+/*! \file dns/time.h */
+
+/***
+ *** Imports
+ ***/
+
+#include <isc/buffer.h>
+#include <isc/lang.h>
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Functions
+ ***/
+
+isc_result_t
+dns_time64_fromtext(const char *source, isc_int64_t *target);
+/*%<
+ * Convert a date and time in YYYYMMDDHHMMSS text format at 'source'
+ * into to a 64-bit count of seconds since Jan 1 1970 0:00 GMT.
+ * Store the count at 'target'.
+ */
+
+isc_result_t
+dns_time32_fromtext(const char *source, isc_uint32_t *target);
+/*%<
+ * Like dns_time64_fromtext, but returns the second count modulo 2^32
+ * as per RFC2535.
+ */
+
+
+isc_result_t
+dns_time64_totext(isc_int64_t value, isc_buffer_t *target);
+/*%<
+ * Convert a 64-bit count of seconds since Jan 1 1970 0:00 GMT into
+ * a YYYYMMDDHHMMSS text representation and append it to 'target'.
+ */
+
+isc_result_t
+dns_time32_totext(isc_uint32_t value, isc_buffer_t *target);
+/*%<
+ * Like dns_time64_totext, but for a 32-bit cyclic time value.
+ * Of those dates whose counts of seconds since Jan 1 1970 0:00 GMT
+ * are congruent with 'value' modulo 2^32, the one closest to the
+ * current date is chosen.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_TIME_H */
diff --git a/lib/dns/include/dns/timer.h b/lib/dns/include/dns/timer.h
new file mode 100644
index 0000000..48d6d56
--- /dev/null
+++ b/lib/dns/include/dns/timer.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: timer.h,v 1.9 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef DNS_TIMER_H
+#define DNS_TIMER_H 1
+
+/*! \file dns/timer.h */
+
+/***
+ *** Imports
+ ***/
+
+#include <isc/buffer.h>
+#include <isc/lang.h>
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Functions
+ ***/
+
+isc_result_t
+dns_timer_setidle(isc_timer_t *timer, unsigned int maxtime,
+ unsigned int idletime, isc_boolean_t purge);
+/*%<
+ * Convenience function for setting up simple, one-second-granularity
+ * idle timers as used by zone transfers.
+ * \brief
+ * Set the timer 'timer' to go off after 'idletime' seconds of inactivity,
+ * or after 'maxtime' at the very latest. Events are purged iff
+ * 'purge' is ISC_TRUE.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_TIMER_H */
diff --git a/lib/dns/include/dns/tkey.h b/lib/dns/include/dns/tkey.h
new file mode 100644
index 0000000..1eecc98
--- /dev/null
+++ b/lib/dns/include/dns/tkey.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: tkey.h,v 1.26 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef DNS_TKEY_H
+#define DNS_TKEY_H 1
+
+/*! \file dns/tkey.h */
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+#include <dst/dst.h>
+#include <dst/gssapi.h>
+
+ISC_LANG_BEGINDECLS
+
+/* Key agreement modes */
+#define DNS_TKEYMODE_SERVERASSIGNED 1
+#define DNS_TKEYMODE_DIFFIEHELLMAN 2
+#define DNS_TKEYMODE_GSSAPI 3
+#define DNS_TKEYMODE_RESOLVERASSIGNED 4
+#define DNS_TKEYMODE_DELETE 5
+
+struct dns_tkeyctx {
+ dst_key_t *dhkey;
+ dns_name_t *domain;
+ gss_cred_id_t gsscred;
+ isc_mem_t *mctx;
+ isc_entropy_t *ectx;
+};
+
+isc_result_t
+dns_tkeyctx_create(isc_mem_t *mctx, isc_entropy_t *ectx,
+ dns_tkeyctx_t **tctxp);
+/*%<
+ * Create an empty TKEY context.
+ *
+ * Requires:
+ *\li 'mctx' is not NULL
+ *\li 'tctx' is not NULL
+ *\li '*tctx' is NULL
+ *
+ * Returns
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *\li return codes from dns_name_fromtext()
+ */
+
+void
+dns_tkeyctx_destroy(dns_tkeyctx_t **tctxp);
+/*%<
+ * Frees all data associated with the TKEY context
+ *
+ * Requires:
+ *\li 'tctx' is not NULL
+ *\li '*tctx' is not NULL
+ */
+
+isc_result_t
+dns_tkey_processquery(dns_message_t *msg, dns_tkeyctx_t *tctx,
+ dns_tsig_keyring_t *ring);
+/*%<
+ * Processes a query containing a TKEY record, adding or deleting TSIG
+ * keys if necessary, and modifies the message to contain the response.
+ *
+ * Requires:
+ *\li 'msg' is a valid message
+ *\li 'tctx' is a valid TKEY context
+ *\li 'ring' is a valid TSIG keyring
+ *
+ * Returns
+ *\li #ISC_R_SUCCESS msg was updated (the TKEY operation succeeded,
+ * or msg now includes a TKEY with an error set)
+ * DNS_R_FORMERR the packet was malformed (missing a TKEY
+ * or KEY).
+ *\li other An error occurred while processing the message
+ */
+
+isc_result_t
+dns_tkey_builddhquery(dns_message_t *msg, dst_key_t *key, dns_name_t *name,
+ dns_name_t *algorithm, isc_buffer_t *nonce,
+ isc_uint32_t lifetime);
+/*%<
+ * Builds a query containing a TKEY that will generate a shared
+ * secret using a Diffie-Hellman key exchange. The shared key
+ * will be of the specified algorithm (only DNS_TSIG_HMACMD5_NAME
+ * is supported), and will be named either 'name',
+ * 'name' + server chosen domain, or random data + server chosen domain
+ * if 'name' == dns_rootname. If nonce is not NULL, it supplies
+ * random data used in the shared secret computation. The key is
+ * requested to have the specified lifetime (in seconds)
+ *
+ *
+ * Requires:
+ *\li 'msg' is a valid message
+ *\li 'key' is a valid Diffie Hellman dst key
+ *\li 'name' is a valid name
+ *\li 'algorithm' is a valid name
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS msg was successfully updated to include the
+ * query to be sent
+ *\li other an error occurred while building the message
+ */
+
+isc_result_t
+dns_tkey_buildgssquery(dns_message_t *msg, dns_name_t *name, dns_name_t *gname,
+ isc_buffer_t *intoken, isc_uint32_t lifetime,
+ gss_ctx_id_t *context, isc_boolean_t win2k);
+/*%<
+ * Builds a query containing a TKEY that will generate a GSSAPI context.
+ * The key is requested to have the specified lifetime (in seconds).
+ *
+ * Requires:
+ *\li 'msg' is a valid message
+ *\li 'name' is a valid name
+ *\li 'gname' is a valid name
+ *\li 'context' is a pointer to a valid gss_ctx_id_t
+ * (which may have the value GSS_C_NO_CONTEXT)
+ *\li 'win2k' when true says to turn on some hacks to work
+ * with the non-standard GSS-TSIG of Windows 2000
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS msg was successfully updated to include the
+ * query to be sent
+ *\li other an error occurred while building the message
+ */
+
+
+isc_result_t
+dns_tkey_builddeletequery(dns_message_t *msg, dns_tsigkey_t *key);
+/*%<
+ * Builds a query containing a TKEY record that will delete the
+ * specified shared secret from the server.
+ *
+ * Requires:
+ *\li 'msg' is a valid message
+ *\li 'key' is a valid TSIG key
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS msg was successfully updated to include the
+ * query to be sent
+ *\li other an error occurred while building the message
+ */
+
+isc_result_t
+dns_tkey_processdhresponse(dns_message_t *qmsg, dns_message_t *rmsg,
+ dst_key_t *key, isc_buffer_t *nonce,
+ dns_tsigkey_t **outkey, dns_tsig_keyring_t *ring);
+/*%<
+ * Processes a response to a query containing a TKEY that was
+ * designed to generate a shared secret using a Diffie-Hellman key
+ * exchange. If the query was successful, a new shared key
+ * is created and added to the list of shared keys.
+ *
+ * Requires:
+ *\li 'qmsg' is a valid message (the query)
+ *\li 'rmsg' is a valid message (the response)
+ *\li 'key' is a valid Diffie Hellman dst key
+ *\li 'outkey' is either NULL or a pointer to NULL
+ *\li 'ring' is a valid keyring or NULL
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS the shared key was successfully added
+ *\li #ISC_R_NOTFOUND an error occurred while looking for a
+ * component of the query or response
+ */
+
+isc_result_t
+dns_tkey_processgssresponse(dns_message_t *qmsg, dns_message_t *rmsg,
+ dns_name_t *gname, gss_ctx_id_t *context,
+ isc_buffer_t *outtoken, dns_tsigkey_t **outkey,
+ dns_tsig_keyring_t *ring);
+/*%<
+ * XXX
+ */
+
+isc_result_t
+dns_tkey_processdeleteresponse(dns_message_t *qmsg, dns_message_t *rmsg,
+ dns_tsig_keyring_t *ring);
+/*%<
+ * Processes a response to a query containing a TKEY that was
+ * designed to delete a shared secret. If the query was successful,
+ * the shared key is deleted from the list of shared keys.
+ *
+ * Requires:
+ *\li 'qmsg' is a valid message (the query)
+ *\li 'rmsg' is a valid message (the response)
+ *\li 'ring' is not NULL
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS the shared key was successfully deleted
+ *\li #ISC_R_NOTFOUND an error occurred while looking for a
+ * component of the query or response
+ */
+
+
+isc_result_t
+dns_tkey_gssnegotiate(dns_message_t *qmsg, dns_message_t *rmsg,
+ dns_name_t *server, gss_ctx_id_t *context,
+ dns_tsigkey_t **outkey, dns_tsig_keyring_t *ring,
+ isc_boolean_t win2k);
+
+/*
+ * Client side negotiation of GSS-TSIG. Process the respsonse
+ * to a TKEY, and establish a TSIG key if negotiation was successful.
+ * Build a response to the input TKEY message. Can take multiple
+ * calls to successfully establish the context.
+ *
+ * Requires:
+ * 'qmsg' is a valid message, the original TKEY request;
+ * it will be filled with the new message to send
+ * 'rmsg' is a valid message, the incoming TKEY message
+ * 'server' is the server name
+ * 'context' is the input context handle
+ * 'outkey' receives the established key, if non-NULL;
+ * if non-NULL must point to NULL
+ * 'ring' is the keyring in which to establish the key,
+ * or NULL
+ * 'win2k' when true says to turn on some hacks to work
+ * with the non-standard GSS-TSIG of Windows 2000
+ *
+ * Returns:
+ * ISC_R_SUCCESS context was successfully established
+ * ISC_R_NOTFOUND couldn't find a needed part of the query
+ * or response
+ * DNS_R_CONTINUE additional context negotiation is required;
+ * send the new qmsg to the server
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_TKEY_H */
diff --git a/lib/dns/include/dns/tsig.h b/lib/dns/include/dns/tsig.h
new file mode 100644
index 0000000..e8c0e2c
--- /dev/null
+++ b/lib/dns/include/dns/tsig.h
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: tsig.h,v 1.51 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef DNS_TSIG_H
+#define DNS_TSIG_H 1
+
+/*! \file dns/tsig.h */
+
+#include <isc/lang.h>
+#include <isc/refcount.h>
+#include <isc/rwlock.h>
+#include <isc/stdtime.h>
+
+#include <dns/types.h>
+#include <dns/name.h>
+
+#include <dst/dst.h>
+
+/*
+ * Algorithms.
+ */
+LIBDNS_EXTERNAL_DATA extern dns_name_t *dns_tsig_hmacmd5_name;
+#define DNS_TSIG_HMACMD5_NAME dns_tsig_hmacmd5_name
+LIBDNS_EXTERNAL_DATA extern dns_name_t *dns_tsig_gssapi_name;
+#define DNS_TSIG_GSSAPI_NAME dns_tsig_gssapi_name
+LIBDNS_EXTERNAL_DATA extern dns_name_t *dns_tsig_gssapims_name;
+#define DNS_TSIG_GSSAPIMS_NAME dns_tsig_gssapims_name
+LIBDNS_EXTERNAL_DATA extern dns_name_t *dns_tsig_hmacsha1_name;
+#define DNS_TSIG_HMACSHA1_NAME dns_tsig_hmacsha1_name
+LIBDNS_EXTERNAL_DATA extern dns_name_t *dns_tsig_hmacsha224_name;
+#define DNS_TSIG_HMACSHA224_NAME dns_tsig_hmacsha224_name
+LIBDNS_EXTERNAL_DATA extern dns_name_t *dns_tsig_hmacsha256_name;
+#define DNS_TSIG_HMACSHA256_NAME dns_tsig_hmacsha256_name
+LIBDNS_EXTERNAL_DATA extern dns_name_t *dns_tsig_hmacsha384_name;
+#define DNS_TSIG_HMACSHA384_NAME dns_tsig_hmacsha384_name
+LIBDNS_EXTERNAL_DATA extern dns_name_t *dns_tsig_hmacsha512_name;
+#define DNS_TSIG_HMACSHA512_NAME dns_tsig_hmacsha512_name
+
+/*%
+ * Default fudge value.
+ */
+#define DNS_TSIG_FUDGE 300
+
+struct dns_tsig_keyring {
+ dns_rbt_t *keys;
+ unsigned int writecount;
+ isc_rwlock_t lock;
+ isc_mem_t *mctx;
+};
+
+struct dns_tsigkey {
+ /* Unlocked */
+ unsigned int magic; /*%< Magic number. */
+ isc_mem_t *mctx;
+ dst_key_t *key; /*%< Key */
+ dns_name_t name; /*%< Key name */
+ dns_name_t *algorithm; /*%< Algorithm name */
+ dns_name_t *creator; /*%< name that created secret */
+ isc_boolean_t generated; /*%< was this generated? */
+ isc_stdtime_t inception; /*%< start of validity period */
+ isc_stdtime_t expire; /*%< end of validity period */
+ dns_tsig_keyring_t *ring; /*%< the enclosing keyring */
+ isc_refcount_t refs; /*%< reference counter */
+};
+
+#define dns_tsigkey_identity(tsigkey) \
+ ((tsigkey) == NULL ? NULL : \
+ (tsigkey)->generated ? ((tsigkey)->creator) : \
+ (&((tsigkey)->name)))
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_tsigkey_create(dns_name_t *name, dns_name_t *algorithm,
+ unsigned char *secret, int length, isc_boolean_t generated,
+ dns_name_t *creator, isc_stdtime_t inception,
+ isc_stdtime_t expire, isc_mem_t *mctx,
+ dns_tsig_keyring_t *ring, dns_tsigkey_t **key);
+
+isc_result_t
+dns_tsigkey_createfromkey(dns_name_t *name, dns_name_t *algorithm,
+ dst_key_t *dstkey, isc_boolean_t generated,
+ dns_name_t *creator, isc_stdtime_t inception,
+ isc_stdtime_t expire, isc_mem_t *mctx,
+ dns_tsig_keyring_t *ring, dns_tsigkey_t **key);
+/*%<
+ * Creates a tsig key structure and saves it in the keyring. If key is
+ * not NULL, *key will contain a copy of the key. The keys validity
+ * period is specified by (inception, expire), and will not expire if
+ * inception == expire. If the key was generated, the creating identity,
+ * if there is one, should be in the creator parameter. Specifying an
+ * unimplemented algorithm will cause failure only if dstkey != NULL; this
+ * allows a transient key with an invalid algorithm to exist long enough
+ * to generate a BADKEY response.
+ *
+ * Requires:
+ *\li 'name' is a valid dns_name_t
+ *\li 'algorithm' is a valid dns_name_t
+ *\li 'secret' is a valid pointer
+ *\li 'length' is an integer >= 0
+ *\li 'key' is a valid dst key or NULL
+ *\li 'creator' points to a valid dns_name_t or is NULL
+ *\li 'mctx' is a valid memory context
+ *\li 'ring' is a valid TSIG keyring or NULL
+ *\li 'key' or '*key' must be NULL
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_EXISTS - a key with this name already exists
+ *\li #ISC_R_NOTIMPLEMENTED - algorithm is not implemented
+ *\li #ISC_R_NOMEMORY
+ */
+
+void
+dns_tsigkey_attach(dns_tsigkey_t *source, dns_tsigkey_t **targetp);
+/*%<
+ * Attach '*targetp' to 'source'.
+ *
+ * Requires:
+ *\li 'key' is a valid TSIG key
+ *
+ * Ensures:
+ *\li *targetp is attached to source.
+ */
+
+void
+dns_tsigkey_detach(dns_tsigkey_t **keyp);
+/*%<
+ * Detaches from the tsig key structure pointed to by '*key'.
+ *
+ * Requires:
+ *\li 'keyp' is not NULL and '*keyp' is a valid TSIG key
+ *
+ * Ensures:
+ *\li 'keyp' points to NULL
+ */
+
+void
+dns_tsigkey_setdeleted(dns_tsigkey_t *key);
+/*%<
+ * Prevents this key from being used again. It will be deleted when
+ * no references exist.
+ *
+ * Requires:
+ *\li 'key' is a valid TSIG key on a keyring
+ */
+
+isc_result_t
+dns_tsig_sign(dns_message_t *msg);
+/*%<
+ * Generates a TSIG record for this message
+ *
+ * Requires:
+ *\li 'msg' is a valid message
+ *\li 'msg->tsigkey' is a valid TSIG key
+ *\li 'msg->tsig' is NULL
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *\li #ISC_R_NOSPACE
+ *\li #DNS_R_EXPECTEDTSIG
+ * - this is a response & msg->querytsig is NULL
+ */
+
+isc_result_t
+dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg,
+ dns_tsig_keyring_t *ring1, dns_tsig_keyring_t *ring2);
+/*%<
+ * Verifies the TSIG record in this message
+ *
+ * Requires:
+ *\li 'source' is a valid buffer containing the unparsed message
+ *\li 'msg' is a valid message
+ *\li 'msg->tsigkey' is a valid TSIG key if this is a response
+ *\li 'msg->tsig' is NULL
+ *\li 'msg->querytsig' is not NULL if this is a response
+ *\li 'ring1' and 'ring2' are each either a valid keyring or NULL
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *\li #DNS_R_EXPECTEDTSIG - A TSIG was expected but not seen
+ *\li #DNS_R_UNEXPECTEDTSIG - A TSIG was seen but not expected
+ *\li #DNS_R_TSIGERRORSET - the TSIG verified but ->error was set
+ * and this is a query
+ *\li #DNS_R_CLOCKSKEW - the TSIG failed to verify because of
+ * the time was out of the allowed range.
+ *\li #DNS_R_TSIGVERIFYFAILURE - the TSIG failed to verify
+ *\li #DNS_R_EXPECTEDRESPONSE - the message was set over TCP and
+ * should have been a response,
+ * but was not.
+ */
+
+isc_result_t
+dns_tsigkey_find(dns_tsigkey_t **tsigkey, dns_name_t *name,
+ dns_name_t *algorithm, dns_tsig_keyring_t *ring);
+/*%<
+ * Returns the TSIG key corresponding to this name and (possibly)
+ * algorithm. Also increments the key's reference counter.
+ *
+ * Requires:
+ *\li 'tsigkey' is not NULL
+ *\li '*tsigkey' is NULL
+ *\li 'name' is a valid dns_name_t
+ *\li 'algorithm' is a valid dns_name_t or NULL
+ *\li 'ring' is a valid keyring
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOTFOUND
+ */
+
+
+isc_result_t
+dns_tsigkeyring_create(isc_mem_t *mctx, dns_tsig_keyring_t **ringp);
+/*%<
+ * Create an empty TSIG key ring.
+ *
+ * Requires:
+ *\li 'mctx' is not NULL
+ *\li 'ringp' is not NULL, and '*ringp' is NULL
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ */
+
+
+void
+dns_tsigkeyring_destroy(dns_tsig_keyring_t **ringp);
+/*%<
+ * Destroy a TSIG key ring.
+ *
+ * Requires:
+ *\li 'ringp' is not NULL
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_TSIG_H */
diff --git a/lib/dns/include/dns/ttl.h b/lib/dns/include/dns/ttl.h
new file mode 100644
index 0000000..c252518
--- /dev/null
+++ b/lib/dns/include/dns/ttl.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: ttl.h,v 1.19 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef DNS_TTL_H
+#define DNS_TTL_H 1
+
+/*! \file dns/ttl.h */
+
+/***
+ *** Imports
+ ***/
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Functions
+ ***/
+
+isc_result_t
+dns_ttl_totext(isc_uint32_t src, isc_boolean_t verbose,
+ isc_buffer_t *target);
+/*%<
+ * Output a TTL or other time interval in a human-readable form.
+ * The time interval is given as a count of seconds in 'src'.
+ * The text representation is appended to 'target'.
+ *
+ * If 'verbose' is ISC_FALSE, use the terse BIND 8 style, like "1w2d3h4m5s".
+ *
+ * If 'verbose' is ISC_TRUE, use a verbose style like the SOA comments
+ * in "dig", like "1 week 2 days 3 hours 4 minutes 5 seconds".
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li ISC_R_NOSPACE
+ */
+
+isc_result_t
+dns_counter_fromtext(isc_textregion_t *source, isc_uint32_t *ttl);
+/*%<
+ * Converts a counter from either a plain number or a BIND 8 style value.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ *\li DNS_R_SYNTAX
+ */
+
+isc_result_t
+dns_ttl_fromtext(isc_textregion_t *source, isc_uint32_t *ttl);
+/*%<
+ * Converts a ttl from either a plain number or a BIND 8 style value.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ *\li DNS_R_BADTTL
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_TTL_H */
diff --git a/lib/dns/include/dns/types.h b/lib/dns/include/dns/types.h
new file mode 100644
index 0000000..5223397
--- /dev/null
+++ b/lib/dns/include/dns/types.h
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: types.h,v 1.130 2008/09/24 02:46:23 marka Exp $ */
+
+#ifndef DNS_TYPES_H
+#define DNS_TYPES_H 1
+
+/*! \file dns/types.h
+ * \brief
+ * Including this file gives you type declarations suitable for use in
+ * .h files, which lets us avoid circular type reference problems.
+ * \brief
+ * To actually use a type or get declarations of its methods, you must
+ * include the appropriate .h file too.
+ */
+
+#include <isc/types.h>
+
+typedef struct dns_acache dns_acache_t;
+typedef struct dns_acacheentry dns_acacheentry_t;
+typedef struct dns_acachestats dns_acachestats_t;
+typedef struct dns_acl dns_acl_t;
+typedef struct dns_aclelement dns_aclelement_t;
+typedef struct dns_aclenv dns_aclenv_t;
+typedef struct dns_adb dns_adb_t;
+typedef struct dns_adbaddrinfo dns_adbaddrinfo_t;
+typedef ISC_LIST(dns_adbaddrinfo_t) dns_adbaddrinfolist_t;
+typedef struct dns_adbentry dns_adbentry_t;
+typedef struct dns_adbfind dns_adbfind_t;
+typedef ISC_LIST(dns_adbfind_t) dns_adbfindlist_t;
+typedef struct dns_byaddr dns_byaddr_t;
+typedef struct dns_cache dns_cache_t;
+typedef isc_uint16_t dns_cert_t;
+typedef struct dns_compress dns_compress_t;
+typedef struct dns_db dns_db_t;
+typedef struct dns_dbimplementation dns_dbimplementation_t;
+typedef struct dns_dbiterator dns_dbiterator_t;
+typedef void dns_dbload_t;
+typedef void dns_dbnode_t;
+typedef struct dns_dbtable dns_dbtable_t;
+typedef void dns_dbversion_t;
+typedef struct dns_dlzimplementation dns_dlzimplementation_t;
+typedef struct dns_dlzdb dns_dlzdb_t;
+typedef struct dns_sdlzimplementation dns_sdlzimplementation_t;
+typedef struct dns_decompress dns_decompress_t;
+typedef struct dns_dispatch dns_dispatch_t;
+typedef struct dns_dispatchevent dns_dispatchevent_t;
+typedef struct dns_dispatchlist dns_dispatchlist_t;
+typedef struct dns_dispatchmgr dns_dispatchmgr_t;
+typedef struct dns_dispentry dns_dispentry_t;
+typedef struct dns_dumpctx dns_dumpctx_t;
+typedef struct dns_fetch dns_fetch_t;
+typedef struct dns_fixedname dns_fixedname_t;
+typedef struct dns_forwarders dns_forwarders_t;
+typedef struct dns_fwdtable dns_fwdtable_t;
+typedef struct dns_iptable dns_iptable_t;
+typedef isc_uint32_t dns_iterations_t;
+typedef isc_uint16_t dns_keyflags_t;
+typedef struct dns_keynode dns_keynode_t;
+typedef struct dns_keytable dns_keytable_t;
+typedef isc_uint16_t dns_keytag_t;
+typedef struct dns_loadctx dns_loadctx_t;
+typedef struct dns_loadmgr dns_loadmgr_t;
+typedef struct dns_message dns_message_t;
+typedef isc_uint16_t dns_messageid_t;
+typedef isc_region_t dns_label_t;
+typedef struct dns_lookup dns_lookup_t;
+typedef struct dns_name dns_name_t;
+typedef ISC_LIST(dns_name_t) dns_namelist_t;
+typedef isc_uint16_t dns_opcode_t;
+typedef unsigned char dns_offsets_t[128];
+typedef struct dns_order dns_order_t;
+typedef struct dns_peer dns_peer_t;
+typedef struct dns_peerlist dns_peerlist_t;
+typedef struct dns_portlist dns_portlist_t;
+typedef struct dns_rbt dns_rbt_t;
+typedef isc_uint16_t dns_rcode_t;
+typedef struct dns_rdata dns_rdata_t;
+typedef struct dns_rdatacallbacks dns_rdatacallbacks_t;
+typedef isc_uint16_t dns_rdataclass_t;
+typedef struct dns_rdatalist dns_rdatalist_t;
+typedef struct dns_rdataset dns_rdataset_t;
+typedef ISC_LIST(dns_rdataset_t) dns_rdatasetlist_t;
+typedef struct dns_rdatasetiter dns_rdatasetiter_t;
+typedef isc_uint16_t dns_rdatatype_t;
+typedef struct dns_request dns_request_t;
+typedef struct dns_requestmgr dns_requestmgr_t;
+typedef struct dns_resolver dns_resolver_t;
+typedef struct dns_sdbimplementation dns_sdbimplementation_t;
+typedef isc_uint8_t dns_secalg_t;
+typedef isc_uint8_t dns_secproto_t;
+typedef struct dns_signature dns_signature_t;
+typedef struct dns_ssurule dns_ssurule_t;
+typedef struct dns_ssutable dns_ssutable_t;
+typedef struct dns_stats dns_stats_t;
+typedef int dns_statscounter_t;
+typedef isc_uint32_t dns_rdatastatstype_t;
+typedef struct dns_tkeyctx dns_tkeyctx_t;
+typedef isc_uint16_t dns_trust_t;
+typedef struct dns_tsig_keyring dns_tsig_keyring_t;
+typedef struct dns_tsigkey dns_tsigkey_t;
+typedef isc_uint32_t dns_ttl_t;
+typedef struct dns_validator dns_validator_t;
+typedef struct dns_view dns_view_t;
+typedef ISC_LIST(dns_view_t) dns_viewlist_t;
+typedef struct dns_zone dns_zone_t;
+typedef ISC_LIST(dns_zone_t) dns_zonelist_t;
+typedef struct dns_zonemgr dns_zonemgr_t;
+typedef struct dns_zt dns_zt_t;
+
+/*
+ * If we are not using GSSAPI, define the types we use as opaque types here.
+ */
+#ifndef GSSAPI
+typedef struct not_defined_gss_cred_id *gss_cred_id_t;
+typedef struct not_defined_gss_ctx *gss_ctx_id_t;
+#endif
+typedef struct dst_gssapi_signverifyctx dst_gssapi_signverifyctx_t;
+
+typedef enum {
+ dns_hash_sha1 = 1
+} dns_hash_t;
+
+typedef enum {
+ dns_fwdpolicy_none = 0,
+ dns_fwdpolicy_first = 1,
+ dns_fwdpolicy_only = 2
+} dns_fwdpolicy_t;
+
+typedef enum {
+ dns_namereln_none = 0,
+ dns_namereln_contains = 1,
+ dns_namereln_subdomain = 2,
+ dns_namereln_equal = 3,
+ dns_namereln_commonancestor = 4
+} dns_namereln_t;
+
+typedef enum {
+ dns_one_answer, dns_many_answers
+} dns_transfer_format_t;
+
+typedef enum {
+ dns_dbtype_zone = 0, dns_dbtype_cache = 1, dns_dbtype_stub = 3
+} dns_dbtype_t;
+
+typedef enum {
+ dns_notifytype_no = 0,
+ dns_notifytype_yes = 1,
+ dns_notifytype_explicit = 2,
+ dns_notifytype_masteronly = 3
+} dns_notifytype_t;
+
+typedef enum {
+ dns_dialuptype_no = 0,
+ dns_dialuptype_yes = 1,
+ dns_dialuptype_notify = 2,
+ dns_dialuptype_notifypassive = 3,
+ dns_dialuptype_refresh = 4,
+ dns_dialuptype_passive = 5
+} dns_dialuptype_t;
+
+typedef enum {
+ dns_masterformat_none = 0,
+ dns_masterformat_text = 1,
+ dns_masterformat_raw = 2
+} dns_masterformat_t;
+
+/*
+ * These are generated by gen.c.
+ */
+#include <dns/enumtype.h> /* Provides dns_rdatatype_t. */
+#include <dns/enumclass.h> /* Provides dns_rdataclass_t. */
+
+/*%
+ * rcodes.
+ */
+enum {
+ /*
+ * Standard rcodes.
+ */
+ dns_rcode_noerror = 0,
+#define dns_rcode_noerror ((dns_rcode_t)dns_rcode_noerror)
+ dns_rcode_formerr = 1,
+#define dns_rcode_formerr ((dns_rcode_t)dns_rcode_formerr)
+ dns_rcode_servfail = 2,
+#define dns_rcode_servfail ((dns_rcode_t)dns_rcode_servfail)
+ dns_rcode_nxdomain = 3,
+#define dns_rcode_nxdomain ((dns_rcode_t)dns_rcode_nxdomain)
+ dns_rcode_notimp = 4,
+#define dns_rcode_notimp ((dns_rcode_t)dns_rcode_notimp)
+ dns_rcode_refused = 5,
+#define dns_rcode_refused ((dns_rcode_t)dns_rcode_refused)
+ dns_rcode_yxdomain = 6,
+#define dns_rcode_yxdomain ((dns_rcode_t)dns_rcode_yxdomain)
+ dns_rcode_yxrrset = 7,
+#define dns_rcode_yxrrset ((dns_rcode_t)dns_rcode_yxrrset)
+ dns_rcode_nxrrset = 8,
+#define dns_rcode_nxrrset ((dns_rcode_t)dns_rcode_nxrrset)
+ dns_rcode_notauth = 9,
+#define dns_rcode_notauth ((dns_rcode_t)dns_rcode_notauth)
+ dns_rcode_notzone = 10,
+#define dns_rcode_notzone ((dns_rcode_t)dns_rcode_notzone)
+ /*
+ * Extended rcodes.
+ */
+ dns_rcode_badvers = 16
+#define dns_rcode_badvers ((dns_rcode_t)dns_rcode_badvers)
+};
+
+/*%
+ * TSIG errors.
+ */
+enum {
+ dns_tsigerror_badsig = 16,
+ dns_tsigerror_badkey = 17,
+ dns_tsigerror_badtime = 18,
+ dns_tsigerror_badmode = 19,
+ dns_tsigerror_badname = 20,
+ dns_tsigerror_badalg = 21,
+ dns_tsigerror_badtrunc = 22
+};
+
+/*%
+ * Opcodes.
+ */
+enum {
+ dns_opcode_query = 0,
+#define dns_opcode_query ((dns_opcode_t)dns_opcode_query)
+ dns_opcode_iquery = 1,
+#define dns_opcode_iquery ((dns_opcode_t)dns_opcode_iquery)
+ dns_opcode_status = 2,
+#define dns_opcode_status ((dns_opcode_t)dns_opcode_status)
+ dns_opcode_notify = 4,
+#define dns_opcode_notify ((dns_opcode_t)dns_opcode_notify)
+ dns_opcode_update = 5 /* dynamic update */
+#define dns_opcode_update ((dns_opcode_t)dns_opcode_update)
+};
+
+/*%
+ * Trust levels. Must be kept in sync with trustnames[] in masterdump.c.
+ */
+enum {
+ /* Sentinel value; no data should have this trust level. */
+ dns_trust_none = 0,
+#define dns_trust_none ((dns_trust_t)dns_trust_none)
+
+ /*% Subject to DNSSEC validation but has not yet been validated */
+ dns_trust_pending = 1,
+#define dns_trust_pending ((dns_trust_t)dns_trust_pending)
+
+ /*% Received in the additional section of a response. */
+ dns_trust_additional = 2,
+#define dns_trust_additional ((dns_trust_t)dns_trust_additional)
+
+ /* Received in a referral response. */
+ dns_trust_glue = 3,
+#define dns_trust_glue ((dns_trust_t)dns_trust_glue)
+
+ /* Answser from a non-authoritative server */
+ dns_trust_answer = 4,
+#define dns_trust_answer ((dns_trust_t)dns_trust_answer)
+
+ /* Received in the authority section as part of an
+ authoritative response */
+ dns_trust_authauthority = 5,
+#define dns_trust_authauthority ((dns_trust_t)dns_trust_authauthority)
+
+ /* Answser from an authoritative server */
+ dns_trust_authanswer = 6,
+#define dns_trust_authanswer ((dns_trust_t)dns_trust_authanswer)
+
+ /* Successfully DNSSEC validated */
+ dns_trust_secure = 7,
+#define dns_trust_secure ((dns_trust_t)dns_trust_secure)
+
+ /* This server is authoritative */
+ dns_trust_ultimate = 8
+#define dns_trust_ultimate ((dns_trust_t)dns_trust_ultimate)
+};
+
+/*%
+ * Name checking severites.
+ */
+typedef enum {
+ dns_severity_ignore,
+ dns_severity_warn,
+ dns_severity_fail
+} dns_severity_t;
+
+/*
+ * Functions.
+ */
+typedef void
+(*dns_dumpdonefunc_t)(void *, isc_result_t);
+
+typedef void
+(*dns_loaddonefunc_t)(void *, isc_result_t);
+
+typedef isc_result_t
+(*dns_addrdatasetfunc_t)(void *, dns_name_t *, dns_rdataset_t *);
+
+typedef isc_result_t
+(*dns_additionaldatafunc_t)(void *, dns_name_t *, dns_rdatatype_t);
+
+typedef isc_result_t
+(*dns_digestfunc_t)(void *, isc_region_t *);
+
+typedef void
+(*dns_xfrindone_t)(dns_zone_t *, isc_result_t);
+
+typedef void
+(*dns_updatecallback_t)(void *, isc_result_t, dns_message_t *);
+
+typedef int
+(*dns_rdatasetorderfunc_t)(const dns_rdata_t *, const void *);
+
+typedef isc_boolean_t
+(*dns_checkmxfunc_t)(dns_zone_t *, dns_name_t *, dns_name_t *);
+
+typedef isc_boolean_t
+(*dns_checksrvfunc_t)(dns_zone_t *, dns_name_t *, dns_name_t *);
+
+typedef isc_boolean_t
+(*dns_checknsfunc_t)(dns_zone_t *, dns_name_t *, dns_name_t *,
+ dns_rdataset_t *, dns_rdataset_t *);
+
+typedef isc_boolean_t
+(*dns_isselffunc_t)(dns_view_t *, dns_tsigkey_t *, isc_sockaddr_t *,
+ isc_sockaddr_t *, dns_rdataclass_t, void *);
+
+#endif /* DNS_TYPES_H */
diff --git a/lib/dns/include/dns/validator.h b/lib/dns/include/dns/validator.h
new file mode 100644
index 0000000..0a2417b
--- /dev/null
+++ b/lib/dns/include/dns/validator.h
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: validator.h,v 1.41 2008/09/25 04:02:39 tbox Exp $ */
+
+#ifndef DNS_VALIDATOR_H
+#define DNS_VALIDATOR_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/validator.h
+ *
+ * \brief
+ * DNS Validator
+ * This is the BIND 9 validator, the module responsible for validating the
+ * rdatasets and negative responses (messages). It makes use of zones in
+ * the view and may fetch RRset to complete trust chains. It implements
+ * DNSSEC as specified in RFC 4033, 4034 and 4035.
+ *
+ * It can also optionally implement ISC's DNSSEC look-aside validation.
+ *
+ * Correct operation is critical to preventing spoofed answers from secure
+ * zones being accepted.
+ *
+ * MP:
+ *\li The module ensures appropriate synchronization of data structures it
+ * creates and manipulates.
+ *
+ * Reliability:
+ *\li No anticipated impact.
+ *
+ * Resources:
+ *\li TBS
+ *
+ * Security:
+ *\li No anticipated impact.
+ *
+ * Standards:
+ *\li RFCs: 1034, 1035, 2181, 4033, 4034, 4035.
+ */
+
+#include <isc/lang.h>
+#include <isc/event.h>
+#include <isc/mutex.h>
+
+#include <dns/fixedname.h>
+#include <dns/types.h>
+#include <dns/rdataset.h>
+#include <dns/rdatastruct.h> /* for dns_rdata_rrsig_t */
+
+#include <dst/dst.h>
+
+/*%
+ * A dns_validatorevent_t is sent when a 'validation' completes.
+ * \brief
+ * 'name', 'rdataset', 'sigrdataset', and 'message' are the values that were
+ * supplied when dns_validator_create() was called. They are returned to the
+ * caller so that they may be freed.
+ *
+ * If the RESULT is ISC_R_SUCCESS and the answer is secure then
+ * proofs[] will contain the the names of the NSEC records that hold the
+ * various proofs. Note the same name may appear multiple times.
+ */
+typedef struct dns_validatorevent {
+ ISC_EVENT_COMMON(struct dns_validatorevent);
+ dns_validator_t * validator;
+ isc_result_t result;
+ /*
+ * Name and type of the response to be validated.
+ */
+ dns_name_t * name;
+ dns_rdatatype_t type;
+ /*
+ * Rdata and RRSIG (if any) for positive responses.
+ */
+ dns_rdataset_t * rdataset;
+ dns_rdataset_t * sigrdataset;
+ /*
+ * The full response. Required for negative responses.
+ * Also required for positive wildcard responses.
+ */
+ dns_message_t * message;
+ /*
+ * Proofs to be cached.
+ */
+ dns_name_t * proofs[4];
+ /*
+ * Optout proof seen.
+ */
+ isc_boolean_t optout;
+} dns_validatorevent_t;
+
+#define DNS_VALIDATOR_NOQNAMEPROOF 0
+#define DNS_VALIDATOR_NODATAPROOF 1
+#define DNS_VALIDATOR_NOWILDCARDPROOF 2
+#define DNS_VALIDATOR_CLOSESTENCLOSER 3
+
+/*%
+ * A validator object represents a validation in progress.
+ * \brief
+ * Clients are strongly discouraged from using this type directly, with
+ * the exception of the 'link' field, which may be used directly for
+ * whatever purpose the client desires.
+ */
+struct dns_validator {
+ /* Unlocked. */
+ unsigned int magic;
+ isc_mutex_t lock;
+ dns_view_t * view;
+ /* Locked by lock. */
+ unsigned int options;
+ unsigned int attributes;
+ dns_validatorevent_t * event;
+ dns_fetch_t * fetch;
+ dns_validator_t * subvalidator;
+ dns_validator_t * parent;
+ dns_keytable_t * keytable;
+ dns_keynode_t * keynode;
+ dst_key_t * key;
+ dns_rdata_rrsig_t * siginfo;
+ isc_task_t * task;
+ isc_taskaction_t action;
+ void * arg;
+ unsigned int labels;
+ dns_rdataset_t * currentset;
+ isc_boolean_t seensig;
+ dns_rdataset_t * keyset;
+ dns_rdataset_t * dsset;
+ dns_rdataset_t * soaset;
+ dns_rdataset_t * nsecset;
+ dns_rdataset_t * nsec3set;
+ dns_name_t * soaname;
+ dns_rdataset_t frdataset;
+ dns_rdataset_t fsigrdataset;
+ dns_fixedname_t fname;
+ dns_fixedname_t wild;
+ dns_fixedname_t nearest;
+ dns_fixedname_t closest;
+ ISC_LINK(dns_validator_t) link;
+ dns_rdataset_t dlv;
+ dns_fixedname_t dlvsep;
+ isc_boolean_t havedlvsep;
+ isc_boolean_t mustbesecure;
+ unsigned int dlvlabels;
+ unsigned int depth;
+};
+
+/*%
+ * dns_validator_create() options.
+ */
+#define DNS_VALIDATOR_DLV 1U
+#define DNS_VALIDATOR_DEFER 2U
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
+ dns_message_t *message, unsigned int options,
+ isc_task_t *task, isc_taskaction_t action, void *arg,
+ dns_validator_t **validatorp);
+/*%<
+ * Start a DNSSEC validation.
+ *
+ * This validates a response to the question given by
+ * 'name' and 'type'.
+ *
+ * To validate a positive response, the response data is
+ * given by 'rdataset' and 'sigrdataset'. If 'sigrdataset'
+ * is NULL, the data is presumed insecure and an attempt
+ * is made to prove its insecurity by finding the appropriate
+ * null key.
+ *
+ * The complete response message may be given in 'message',
+ * to make available any authority section NSECs that may be
+ * needed for validation of a response resulting from a
+ * wildcard expansion (though no such wildcard validation
+ * is implemented yet). If the complete response message
+ * is not available, 'message' is NULL.
+ *
+ * To validate a negative response, the complete negative response
+ * message is given in 'message'. The 'rdataset', and
+ * 'sigrdataset' arguments must be NULL, but the 'name' and 'type'
+ * arguments must be provided.
+ *
+ * The validation is performed in the context of 'view'.
+ *
+ * When the validation finishes, a dns_validatorevent_t with
+ * the given 'action' and 'arg' are sent to 'task'.
+ * Its 'result' field will be ISC_R_SUCCESS iff the
+ * response was successfully proven to be either secure or
+ * part of a known insecure domain.
+ *
+ * options:
+ * If DNS_VALIDATOR_DLV is set the caller knows there is not a
+ * trusted key and the validator should immediately attempt to validate
+ * the answer by looking for a appopriate DLV RRset.
+ */
+
+void
+dns_validator_send(dns_validator_t *validator);
+/*%<
+ * Send a deferred validation request
+ *
+ * Requires:
+ * 'validator' to points to a valid DNSSEC validator.
+ */
+
+void
+dns_validator_cancel(dns_validator_t *validator);
+/*%<
+ * Cancel a DNSSEC validation in progress.
+ *
+ * Requires:
+ *\li 'validator' points to a valid DNSSEC validator, which
+ * may or may not already have completed.
+ *
+ * Ensures:
+ *\li It the validator has not already sent its completion
+ * event, it will send it with result code ISC_R_CANCELED.
+ */
+
+void
+dns_validator_destroy(dns_validator_t **validatorp);
+/*%<
+ * Destroy a DNSSEC validator.
+ *
+ * Requires:
+ *\li '*validatorp' points to a valid DNSSEC validator.
+ * \li The validator must have completed and sent its completion
+ * event.
+ *
+ * Ensures:
+ *\li All resources used by the validator are freed.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_VALIDATOR_H */
diff --git a/lib/dns/include/dns/version.h b/lib/dns/include/dns/version.h
new file mode 100644
index 0000000..2a33dcf
--- /dev/null
+++ b/lib/dns/include/dns/version.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: version.h,v 1.9 2007/06/19 23:47:17 tbox Exp $ */
+
+/*! \file dns/version.h */
+
+#include <isc/platform.h>
+
+LIBDNS_EXTERNAL_DATA extern const char dns_version[];
+
+LIBDNS_EXTERNAL_DATA extern const unsigned int dns_libinterface;
+LIBDNS_EXTERNAL_DATA extern const unsigned int dns_librevision;
+LIBDNS_EXTERNAL_DATA extern const unsigned int dns_libage;
diff --git a/lib/dns/include/dns/view.h b/lib/dns/include/dns/view.h
new file mode 100644
index 0000000..8329523
--- /dev/null
+++ b/lib/dns/include/dns/view.h
@@ -0,0 +1,879 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: view.h,v 1.111 2008/05/21 23:17:21 each Exp $ */
+
+#ifndef DNS_VIEW_H
+#define DNS_VIEW_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/view.h
+ * \brief
+ * DNS View
+ *
+ * A "view" is a DNS namespace, together with an optional resolver and a
+ * forwarding policy. A "DNS namespace" is a (possibly empty) set of
+ * authoritative zones together with an optional cache and optional
+ * "hints" information.
+ *
+ * Views start out "unfrozen". In this state, core attributes like
+ * the cache, set of zones, and forwarding policy may be set. While
+ * "unfrozen", the caller (e.g. nameserver configuration loading
+ * code), must ensure exclusive access to the view. When the view is
+ * "frozen", the core attributes become immutable, and the view module
+ * will ensure synchronization. Freezing allows the view's core attributes
+ * to be accessed without locking.
+ *
+ * MP:
+ *\li Before the view is frozen, the caller must ensure synchronization.
+ *
+ *\li After the view is frozen, the module guarantees appropriate
+ * synchronization of any data structures it creates and manipulates.
+ *
+ * Reliability:
+ *\li No anticipated impact.
+ *
+ * Resources:
+ *\li TBS
+ *
+ * Security:
+ *\li No anticipated impact.
+ *
+ * Standards:
+ *\li None.
+ */
+
+#include <stdio.h>
+
+#include <isc/lang.h>
+#include <isc/magic.h>
+#include <isc/event.h>
+#include <isc/mutex.h>
+#include <isc/net.h>
+#include <isc/refcount.h>
+#include <isc/rwlock.h>
+#include <isc/stdtime.h>
+
+#include <dns/acl.h>
+#include <dns/fixedname.h>
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+struct dns_view {
+ /* Unlocked. */
+ unsigned int magic;
+ isc_mem_t * mctx;
+ dns_rdataclass_t rdclass;
+ char * name;
+ dns_zt_t * zonetable;
+ dns_dlzdb_t * dlzdatabase;
+ dns_resolver_t * resolver;
+ dns_adb_t * adb;
+ dns_requestmgr_t * requestmgr;
+ dns_acache_t * acache;
+ dns_cache_t * cache;
+ dns_db_t * cachedb;
+ dns_db_t * hints;
+ dns_keytable_t * secroots;
+ dns_keytable_t * trustedkeys;
+ isc_mutex_t lock;
+ isc_boolean_t frozen;
+ isc_task_t * task;
+ isc_event_t resevent;
+ isc_event_t adbevent;
+ isc_event_t reqevent;
+ dns_stats_t * resstats;
+ dns_stats_t * resquerystats;
+
+ /* Configurable data. */
+ dns_tsig_keyring_t * statickeys;
+ dns_tsig_keyring_t * dynamickeys;
+ dns_peerlist_t * peers;
+ dns_order_t * order;
+ dns_fwdtable_t * fwdtable;
+ isc_boolean_t recursion;
+ isc_boolean_t auth_nxdomain;
+ isc_boolean_t additionalfromcache;
+ isc_boolean_t additionalfromauth;
+ isc_boolean_t minimalresponses;
+ isc_boolean_t enablednssec;
+ isc_boolean_t enablevalidation;
+ isc_boolean_t acceptexpired;
+ dns_transfer_format_t transfer_format;
+ dns_acl_t * queryacl;
+ dns_acl_t * queryonacl;
+ dns_acl_t * recursionacl;
+ dns_acl_t * recursiononacl;
+ dns_acl_t * sortlist;
+ dns_acl_t * notifyacl;
+ dns_acl_t * transferacl;
+ dns_acl_t * updateacl;
+ dns_acl_t * upfwdacl;
+ isc_boolean_t requestixfr;
+ isc_boolean_t provideixfr;
+ isc_boolean_t requestnsid;
+ dns_ttl_t maxcachettl;
+ dns_ttl_t maxncachettl;
+ in_port_t dstport;
+ dns_aclenv_t aclenv;
+ dns_rdatatype_t preferred_glue;
+ isc_boolean_t flush;
+ dns_namelist_t * delonly;
+ isc_boolean_t rootdelonly;
+ dns_namelist_t * rootexclude;
+ isc_boolean_t checknames;
+ dns_name_t * dlv;
+ dns_fixedname_t dlv_fixed;
+ isc_uint16_t maxudp;
+
+ /*
+ * Configurable data for server use only,
+ * locked by server configuration lock.
+ */
+ dns_acl_t * matchclients;
+ dns_acl_t * matchdestinations;
+ isc_boolean_t matchrecursiveonly;
+
+ /* Locked by themselves. */
+ isc_refcount_t references;
+
+ /* Locked by lock. */
+ unsigned int weakrefs;
+ unsigned int attributes;
+ /* Under owner's locking control. */
+ ISC_LINK(struct dns_view) link;
+};
+
+#define DNS_VIEW_MAGIC ISC_MAGIC('V','i','e','w')
+#define DNS_VIEW_VALID(view) ISC_MAGIC_VALID(view, DNS_VIEW_MAGIC)
+
+#define DNS_VIEWATTR_RESSHUTDOWN 0x01
+#define DNS_VIEWATTR_ADBSHUTDOWN 0x02
+#define DNS_VIEWATTR_REQSHUTDOWN 0x04
+
+isc_result_t
+dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
+ const char *name, dns_view_t **viewp);
+/*%<
+ * Create a view.
+ *
+ * Notes:
+ *
+ *\li The newly created view has no cache, no resolver, and an empty
+ * zone table. The view is not frozen.
+ *
+ * Requires:
+ *
+ *\li 'mctx' is a valid memory context.
+ *
+ *\li 'rdclass' is a valid class.
+ *
+ *\li 'name' is a valid C string.
+ *
+ *\li viewp != NULL && *viewp == NULL
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *
+ *\li Other errors are possible.
+ */
+
+void
+dns_view_attach(dns_view_t *source, dns_view_t **targetp);
+/*%<
+ * Attach '*targetp' to 'source'.
+ *
+ * Requires:
+ *
+ *\li 'source' is a valid, frozen view.
+ *
+ *\li 'targetp' points to a NULL dns_view_t *.
+ *
+ * Ensures:
+ *
+ *\li *targetp is attached to source.
+ *
+ *\li While *targetp is attached, the view will not shut down.
+ */
+
+void
+dns_view_detach(dns_view_t **viewp);
+/*%<
+ * Detach '*viewp' from its view.
+ *
+ * Requires:
+ *
+ *\li 'viewp' points to a valid dns_view_t *
+ *
+ * Ensures:
+ *
+ *\li *viewp is NULL.
+ */
+
+void
+dns_view_flushanddetach(dns_view_t **viewp);
+/*%<
+ * Detach '*viewp' from its view. If this was the last reference
+ * uncommited changed in zones will be flushed to disk.
+ *
+ * Requires:
+ *
+ *\li 'viewp' points to a valid dns_view_t *
+ *
+ * Ensures:
+ *
+ *\li *viewp is NULL.
+ */
+
+void
+dns_view_weakattach(dns_view_t *source, dns_view_t **targetp);
+/*%<
+ * Weakly attach '*targetp' to 'source'.
+ *
+ * Requires:
+ *
+ *\li 'source' is a valid, frozen view.
+ *
+ *\li 'targetp' points to a NULL dns_view_t *.
+ *
+ * Ensures:
+ *
+ *\li *targetp is attached to source.
+ *
+ * \li While *targetp is attached, the view will not be freed.
+ */
+
+void
+dns_view_weakdetach(dns_view_t **targetp);
+/*%<
+ * Detach '*viewp' from its view.
+ *
+ * Requires:
+ *
+ *\li 'viewp' points to a valid dns_view_t *.
+ *
+ * Ensures:
+ *
+ *\li *viewp is NULL.
+ */
+
+isc_result_t
+dns_view_createresolver(dns_view_t *view,
+ isc_taskmgr_t *taskmgr, unsigned int ntasks,
+ isc_socketmgr_t *socketmgr,
+ isc_timermgr_t *timermgr,
+ unsigned int options,
+ dns_dispatchmgr_t *dispatchmgr,
+ dns_dispatch_t *dispatchv4,
+ dns_dispatch_t *dispatchv6);
+/*%<
+ * Create a resolver and address database for the view.
+ *
+ * Requires:
+ *
+ *\li 'view' is a valid, unfrozen view.
+ *
+ *\li 'view' does not have a resolver already.
+ *
+ *\li The requirements of dns_resolver_create() apply to 'taskmgr',
+ * 'ntasks', 'socketmgr', 'timermgr', 'options', 'dispatchv4', and
+ * 'dispatchv6'.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS
+ *
+ *\li Any error that dns_resolver_create() can return.
+ */
+
+void
+dns_view_setcache(dns_view_t *view, dns_cache_t *cache);
+/*%<
+ * Set the view's cache database.
+ *
+ * Requires:
+ *
+ *\li 'view' is a valid, unfrozen view.
+ *
+ *\li 'cache' is a valid cache.
+ *
+ * Ensures:
+ *
+ * \li The cache of 'view' is 'cached.
+ *
+ *\li If this is not the first call to dns_view_setcache() for this
+ * view, then previously set cache is detached.
+ */
+
+void
+dns_view_sethints(dns_view_t *view, dns_db_t *hints);
+/*%<
+ * Set the view's hints database.
+ *
+ * Requires:
+ *
+ *\li 'view' is a valid, unfrozen view, whose hints database has not been
+ * set.
+ *
+ *\li 'hints' is a valid zone database.
+ *
+ * Ensures:
+ *
+ * \li The hints database of 'view' is 'hints'.
+ */
+
+void
+dns_view_setkeyring(dns_view_t *view, dns_tsig_keyring_t *ring);
+/*%<
+ * Set the view's static TSIG keys
+ *
+ * Requires:
+ *
+ * \li 'view' is a valid, unfrozen view, whose static TSIG keyring has not
+ * been set.
+ *
+ *\li 'ring' is a valid TSIG keyring
+ *
+ * Ensures:
+ *
+ *\li The static TSIG keyring of 'view' is 'ring'.
+ */
+
+void
+dns_view_setdstport(dns_view_t *view, in_port_t dstport);
+/*%<
+ * Set the view's destination port. This is the port to
+ * which outgoing queries are sent. The default is 53,
+ * the standard DNS port.
+ *
+ * Requires:
+ *
+ *\li 'view' is a valid view.
+ *
+ *\li 'dstport' is a valid TCP/UDP port number.
+ *
+ * Ensures:
+ *\li External name servers will be assumed to be listning
+ * on 'dstport'. For servers whose address has already
+ * obtained obtained at the time of the call, the view may
+ * continue to use the previously set port until the address
+ * times out from the view's address database.
+ */
+
+
+isc_result_t
+dns_view_addzone(dns_view_t *view, dns_zone_t *zone);
+/*%<
+ * Add zone 'zone' to 'view'.
+ *
+ * Requires:
+ *
+ *\li 'view' is a valid, unfrozen view.
+ *
+ *\li 'zone' is a valid zone.
+ */
+
+void
+dns_view_freeze(dns_view_t *view);
+/*%<
+ * Freeze view.
+ *
+ * Requires:
+ *
+ *\li 'view' is a valid, unfrozen view.
+ *
+ * Ensures:
+ *
+ *\li 'view' is frozen.
+ */
+
+isc_result_t
+dns_view_find(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
+ isc_stdtime_t now, unsigned int options, isc_boolean_t use_hints,
+ dns_db_t **dbp, dns_dbnode_t **nodep, dns_name_t *foundname,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset);
+/*%<
+ * Find an rdataset whose owner name is 'name', and whose type is
+ * 'type'.
+ *
+ * Notes:
+ *
+ *\li See the description of dns_db_find() for information about 'options'.
+ * If the caller sets #DNS_DBFIND_GLUEOK, it must ensure that 'name'
+ * and 'type' are appropriate for glue retrieval.
+ *
+ *\li If 'now' is zero, then the current time will be used.
+ *
+ *\li If 'use_hints' is ISC_TRUE, and the view has a hints database, then
+ * it will be searched last. If the answer is found in the hints
+ * database, the result code will be DNS_R_HINT. If the name is found
+ * in the hints database but not the type, the result code will be
+ * #DNS_R_HINTNXRRSET.
+ *
+ *\li 'foundname' must meet the requirements of dns_db_find().
+ *
+ *\li If 'sigrdataset' is not NULL, and there is a SIG rdataset which
+ * covers 'type', then 'sigrdataset' will be bound to it.
+ *
+ * Requires:
+ *
+ *\li 'view' is a valid, frozen view.
+ *
+ *\li 'name' is valid name.
+ *
+ *\li 'type' is a valid dns_rdatatype_t, and is not a meta query type
+ * except dns_rdatatype_any.
+ *
+ *\li dbp == NULL || *dbp == NULL
+ *
+ *\li nodep == NULL || *nodep == NULL. If nodep != NULL, dbp != NULL.
+ *
+ *\li 'foundname' is a valid name with a dedicated buffer or NULL.
+ *
+ *\li 'rdataset' is a valid, disassociated rdataset.
+ *
+ *\li 'sigrdataset' is NULL, or is a valid, disassociated rdataset.
+ *
+ * Ensures:
+ *
+ *\li In successful cases, 'rdataset', and possibly 'sigrdataset', are
+ * bound to the found data.
+ *
+ *\li If dbp != NULL, it points to the database containing the data.
+ *
+ *\li If nodep != NULL, it points to the database node containing the data.
+ *
+ *\li If foundname != NULL, it contains the full name of the found data.
+ *
+ * Returns:
+ *
+ *\li Any result that dns_db_find() can return, with the exception of
+ * #DNS_R_DELEGATION.
+ */
+
+isc_result_t
+dns_view_simplefind(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
+ isc_stdtime_t now, unsigned int options,
+ isc_boolean_t use_hints,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset);
+/*%<
+ * Find an rdataset whose owner name is 'name', and whose type is
+ * 'type'.
+ *
+ * Notes:
+ *
+ *\li This routine is appropriate for simple, exact-match queries of the
+ * view. 'name' must be a canonical name; there is no DNAME or CNAME
+ * processing.
+ *
+ *\li See the description of dns_db_find() for information about 'options'.
+ * If the caller sets DNS_DBFIND_GLUEOK, it must ensure that 'name'
+ * and 'type' are appropriate for glue retrieval.
+ *
+ *\li If 'now' is zero, then the current time will be used.
+ *
+ *\li If 'use_hints' is ISC_TRUE, and the view has a hints database, then
+ * it will be searched last. If the answer is found in the hints
+ * database, the result code will be DNS_R_HINT. If the name is found
+ * in the hints database but not the type, the result code will be
+ * DNS_R_HINTNXRRSET.
+ *
+ *\li If 'sigrdataset' is not NULL, and there is a SIG rdataset which
+ * covers 'type', then 'sigrdataset' will be bound to it.
+ *
+ * Requires:
+ *
+ *\li 'view' is a valid, frozen view.
+ *
+ *\li 'name' is valid name.
+ *
+ *\li 'type' is a valid dns_rdatatype_t, and is not a meta query type
+ * (e.g. dns_rdatatype_any), or dns_rdatatype_rrsig.
+ *
+ *\li 'rdataset' is a valid, disassociated rdataset.
+ *
+ *\li 'sigrdataset' is NULL, or is a valid, disassociated rdataset.
+ *
+ * Ensures:
+ *
+ *\li In successful cases, 'rdataset', and possibly 'sigrdataset', are
+ * bound to the found data.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS Success; result is desired type.
+ *\li DNS_R_GLUE Success; result is glue.
+ *\li DNS_R_HINT Success; result is a hint.
+ *\li DNS_R_NCACHENXDOMAIN Success; result is a ncache entry.
+ *\li DNS_R_NCACHENXRRSET Success; result is a ncache entry.
+ *\li DNS_R_NXDOMAIN The name does not exist.
+ *\li DNS_R_NXRRSET The rrset does not exist.
+ *\li #ISC_R_NOTFOUND No matching data found,
+ * or an error occurred.
+ */
+
+/*% See dns_view_findzonecut2() */
+isc_result_t
+dns_view_findzonecut(dns_view_t *view, dns_name_t *name, dns_name_t *fname,
+ isc_stdtime_t now, unsigned int options,
+ isc_boolean_t use_hints,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset);
+
+isc_result_t
+dns_view_findzonecut2(dns_view_t *view, dns_name_t *name, dns_name_t *fname,
+ isc_stdtime_t now, unsigned int options,
+ isc_boolean_t use_hints, isc_boolean_t use_cache,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset);
+/*%<
+ * Find the best known zonecut containing 'name'.
+ *
+ * This uses local authority, cache, and optionally hints data.
+ * No external queries are performed.
+ *
+ * Notes:
+ *
+ *\li If 'now' is zero, then the current time will be used.
+ *
+ *\li If 'use_hints' is ISC_TRUE, and the view has a hints database, then
+ * it will be searched last.
+ *
+ *\li If 'use_cache' is ISC_TRUE, and the view has a cache, then it will be
+ * searched.
+ *
+ *\li If 'sigrdataset' is not NULL, and there is a SIG rdataset which
+ * covers 'type', then 'sigrdataset' will be bound to it.
+ *
+ *\li If the DNS_DBFIND_NOEXACT option is set, then the zonecut returned
+ * (if any) will be the deepest known ancestor of 'name'.
+ *
+ * Requires:
+ *
+ *\li 'view' is a valid, frozen view.
+ *
+ *\li 'name' is valid name.
+ *
+ *\li 'rdataset' is a valid, disassociated rdataset.
+ *
+ *\li 'sigrdataset' is NULL, or is a valid, disassociated rdataset.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS Success.
+ *
+ *\li Many other results are possible.
+ */
+
+isc_result_t
+dns_viewlist_find(dns_viewlist_t *list, const char *name,
+ dns_rdataclass_t rdclass, dns_view_t **viewp);
+/*%<
+ * Search for a view with name 'name' and class 'rdclass' in 'list'.
+ * If found, '*viewp' is (strongly) attached to it.
+ *
+ * Requires:
+ *
+ *\li 'viewp' points to a NULL dns_view_t *.
+ *
+ * Returns:
+ *
+ *\li #ISC_R_SUCCESS A matching view was found.
+ *\li #ISC_R_NOTFOUND No matching view was found.
+ */
+
+isc_result_t
+dns_viewlist_findzone(dns_viewlist_t *list, dns_name_t *name, isc_boolean_t allclasses,
+ dns_rdataclass_t rdclass, dns_zone_t **zonep);
+
+/*%<
+ * Search zone with 'name' in view with 'rdclass' in viewlist 'list'
+ * If found, zone is returned in *zonep. If allclasses is set rdclass is ignored
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS A matching zone was found.
+ *\li #ISC_R_NOTFOUND No matching zone was found.
+ */
+
+isc_result_t
+dns_view_findzone(dns_view_t *view, dns_name_t *name, dns_zone_t **zonep);
+/*%<
+ * Search for the zone 'name' in the zone table of 'view'.
+ * If found, 'zonep' is (strongly) attached to it. There
+ * are no partial matches.
+ *
+ * Requires:
+ *
+ *\li 'zonep' points to a NULL dns_zone_t *.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS A matching zone was found.
+ *\li #ISC_R_NOTFOUND No matching zone was found.
+ *\li others An error occurred.
+ */
+
+isc_result_t
+dns_view_load(dns_view_t *view, isc_boolean_t stop);
+
+isc_result_t
+dns_view_loadnew(dns_view_t *view, isc_boolean_t stop);
+/*%<
+ * Load zones attached to this view. dns_view_load() loads
+ * all zones whose master file has changed since the last
+ * load; dns_view_loadnew() loads only zones that have never
+ * been loaded.
+ *
+ * If 'stop' is ISC_TRUE, stop on the first error and return it.
+ * If 'stop' is ISC_FALSE, ignore errors.
+ *
+ * Requires:
+ *
+ *\li 'view' is valid.
+ */
+
+isc_result_t
+dns_view_gettsig(dns_view_t *view, dns_name_t *keyname,
+ dns_tsigkey_t **keyp);
+/*%<
+ * Find the TSIG key configured in 'view' with name 'keyname',
+ * if any.
+ *
+ * Reqires:
+ *\li keyp points to a NULL dns_tsigkey_t *.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS A key was found and '*keyp' now points to it.
+ *\li #ISC_R_NOTFOUND No key was found.
+ *\li others An error occurred.
+ */
+
+isc_result_t
+dns_view_getpeertsig(dns_view_t *view, isc_netaddr_t *peeraddr,
+ dns_tsigkey_t **keyp);
+/*%<
+ * Find the TSIG key configured in 'view' for the server whose
+ * address is 'peeraddr', if any.
+ *
+ * Reqires:
+ * keyp points to a NULL dns_tsigkey_t *.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS A key was found and '*keyp' now points to it.
+ *\li #ISC_R_NOTFOUND No key was found.
+ *\li others An error occurred.
+ */
+
+isc_result_t
+dns_view_checksig(dns_view_t *view, isc_buffer_t *source, dns_message_t *msg);
+/*%<
+ * Verifies the signature of a message.
+ *
+ * Requires:
+ *
+ *\li 'view' is a valid view.
+ *\li 'source' is a valid buffer containing the message
+ *\li 'msg' is a valid message
+ *
+ * Returns:
+ *\li see dns_tsig_verify()
+ */
+
+void
+dns_view_dialup(dns_view_t *view);
+/*%<
+ * Perform dialup-time maintenance on the zones of 'view'.
+ */
+
+isc_result_t
+dns_view_dumpdbtostream(dns_view_t *view, FILE *fp);
+/*%<
+ * Dump the current state of the view 'view' to the stream 'fp'
+ * for purposes of analysis or debugging.
+ *
+ * Currently the dumped state includes the view's cache; in the future
+ * it may also include other state such as the address database.
+ * It will not not include authoritative data since it is voluminous and
+ * easily obtainable by other means.
+ *
+ * Requires:
+ *
+ *\li 'view' is valid.
+ *
+ *\li 'fp' refers to a file open for writing.
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS The cache was successfully dumped.
+ * \li others An error occurred (see dns_master_dump)
+ */
+
+isc_result_t
+dns_view_flushcache(dns_view_t *view);
+/*%<
+ * Flush the view's cache (and ADB).
+ *
+ * Requires:
+ * 'view' is valid.
+ *
+ * No other tasks are executing.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ */
+
+isc_result_t
+dns_view_flushname(dns_view_t *view, dns_name_t *);
+/*%<
+ * Flush the given name from the view's cache (and ADB).
+ *
+ * Requires:
+ *\li 'view' is valid.
+ *\li 'name' is valid.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ * other returns are failures.
+ */
+
+isc_result_t
+dns_view_adddelegationonly(dns_view_t *view, dns_name_t *name);
+/*%<
+ * Add the given name to the delegation only table.
+ *
+ *
+ * Requires:
+ *\li 'view' is valid.
+ *\li 'name' is valid.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ */
+
+isc_result_t
+dns_view_excludedelegationonly(dns_view_t *view, dns_name_t *name);
+/*%<
+ * Add the given name to be excluded from the root-delegation-only.
+ *
+ *
+ * Requires:
+ *\li 'view' is valid.
+ *\li 'name' is valid.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ */
+
+isc_boolean_t
+dns_view_isdelegationonly(dns_view_t *view, dns_name_t *name);
+/*%<
+ * Check if 'name' is in the delegation only table or if
+ * rootdelonly is set that name is not being excluded.
+ *
+ * Requires:
+ *\li 'view' is valid.
+ *\li 'name' is valid.
+ *
+ * Returns:
+ *\li #ISC_TRUE if the name is is the table.
+ *\li #ISC_FALSE othewise.
+ */
+
+void
+dns_view_setrootdelonly(dns_view_t *view, isc_boolean_t value);
+/*%<
+ * Set the root delegation only flag.
+ *
+ * Requires:
+ *\li 'view' is valid.
+ */
+
+isc_boolean_t
+dns_view_getrootdelonly(dns_view_t *view);
+/*%<
+ * Get the root delegation only flag.
+ *
+ * Requires:
+ *\li 'view' is valid.
+ */
+
+isc_result_t
+dns_view_freezezones(dns_view_t *view, isc_boolean_t freeze);
+/*%<
+ * Freeze/thaw updates to master zones.
+ *
+ * Requires:
+ * \li 'view' is valid.
+ */
+
+void
+dns_view_setresstats(dns_view_t *view, dns_stats_t *stats);
+/*%<
+ * Set a general resolver statistics counter set 'stats' for 'view'.
+ *
+ * Requires:
+ * \li 'view' is valid and is not frozen.
+ *
+ *\li stats is a valid statistics supporting resolver statistics counters
+ * (see dns/stats.h).
+ */
+
+void
+dns_view_getresstats(dns_view_t *view, dns_stats_t **statsp);
+/*%<
+ * Get the general statistics counter set for 'view'. If a statistics set is
+ * set '*statsp' will be attached to the set; otherwise, '*statsp' will be
+ * untouched.
+ *
+ * Requires:
+ * \li 'view' is valid and is not frozen.
+ *
+ *\li 'statsp' != NULL && '*statsp' != NULL
+ */
+
+void
+dns_view_setresquerystats(dns_view_t *view, dns_stats_t *stats);
+/*%<
+ * Set a statistics counter set of rdata type, 'stats', for 'view'. Once the
+ * statistic set is installed, view's resolver will count outgoing queries
+ * per rdata type.
+ *
+ * Requires:
+ * \li 'view' is valid and is not frozen.
+ *
+ *\li stats is a valid statistics created by dns_rdatatypestats_create().
+ */
+
+void
+dns_view_getresquerystats(dns_view_t *view, dns_stats_t **statsp);
+/*%<
+ * Get the rdatatype statistics counter set for 'view'. If a statistics set is
+ * set '*statsp' will be attached to the set; otherwise, '*statsp' will be
+ * untouched.
+ *
+ * Requires:
+ * \li 'view' is valid and is not frozen.
+ *
+ *\li 'statsp' != NULL && '*statsp' != NULL
+ */
+
+#endif /* DNS_VIEW_H */
diff --git a/lib/dns/include/dns/xfrin.h b/lib/dns/include/dns/xfrin.h
new file mode 100644
index 0000000..9dd4dc9
--- /dev/null
+++ b/lib/dns/include/dns/xfrin.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001, 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: xfrin.h,v 1.28 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef DNS_XFRIN_H
+#define DNS_XFRIN_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file dns/xfrin.h
+ * \brief
+ * Incoming zone transfers (AXFR + IXFR).
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+/***
+ *** Types
+ ***/
+
+/*%
+ * A transfer in progress. This is an opaque type.
+ */
+typedef struct dns_xfrin_ctx dns_xfrin_ctx_t;
+
+/***
+ *** Functions
+ ***/
+
+ISC_LANG_BEGINDECLS
+
+/*% see dns_xfrin_create2() */
+isc_result_t
+dns_xfrin_create(dns_zone_t *zone, dns_rdatatype_t xfrtype,
+ isc_sockaddr_t *masteraddr, dns_tsigkey_t *tsigkey,
+ isc_mem_t *mctx, isc_timermgr_t *timermgr,
+ isc_socketmgr_t *socketmgr, isc_task_t *task,
+ dns_xfrindone_t done, dns_xfrin_ctx_t **xfrp);
+
+isc_result_t
+dns_xfrin_create2(dns_zone_t *zone, dns_rdatatype_t xfrtype,
+ isc_sockaddr_t *masteraddr, isc_sockaddr_t *sourceaddr,
+ dns_tsigkey_t *tsigkey, isc_mem_t *mctx,
+ isc_timermgr_t *timermgr, isc_socketmgr_t *socketmgr,
+ isc_task_t *task, dns_xfrindone_t done,
+ dns_xfrin_ctx_t **xfrp);
+/*%<
+ * Attempt to start an incoming zone transfer of 'zone'
+ * from 'masteraddr', creating a dns_xfrin_ctx_t object to
+ * manage it. Attach '*xfrp' to the newly created object.
+ *
+ * Iff ISC_R_SUCCESS is returned, '*done' is guaranteed to be
+ * called in the context of 'task', with 'zone' and a result
+ * code as arguments when the transfer finishes.
+ *
+ * Requires:
+ *\li 'xfrtype' is dns_rdatatype_axfr, dns_rdatatype_ixfr
+ * or dns_rdatatype_soa (soa query followed by axfr if
+ * serial is greater than current serial).
+ *
+ *\li If 'xfrtype' is dns_rdatatype_ixfr or dns_rdatatype_soa,
+ * the zone has a database.
+ */
+
+void
+dns_xfrin_shutdown(dns_xfrin_ctx_t *xfr);
+/*%<
+ * If the zone transfer 'xfr' has already finished,
+ * do nothing. Otherwise, abort it and cause it to call
+ * its done callback with a status of ISC_R_CANCELLED.
+ */
+
+void
+dns_xfrin_detach(dns_xfrin_ctx_t **xfrp);
+/*%<
+ * Detach a reference to a zone transfer object.
+ * Caller to maintain external locking if required.
+ */
+
+void
+dns_xfrin_attach(dns_xfrin_ctx_t *source, dns_xfrin_ctx_t **target);
+/*%<
+ * Caller to maintain external locking if required.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_XFRIN_H */
diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h
new file mode 100644
index 0000000..c6f4444
--- /dev/null
+++ b/lib/dns/include/dns/zone.h
@@ -0,0 +1,1736 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: zone.h,v 1.160 2008/09/24 02:46:23 marka Exp $ */
+
+#ifndef DNS_ZONE_H
+#define DNS_ZONE_H 1
+
+/*! \file dns/zone.h */
+
+/***
+ *** Imports
+ ***/
+
+#include <stdio.h>
+
+#include <isc/formatcheck.h>
+#include <isc/lang.h>
+#include <isc/rwlock.h>
+
+#include <dns/masterdump.h>
+#include <dns/rdatastruct.h>
+#include <dns/types.h>
+
+typedef enum {
+ dns_zone_none,
+ dns_zone_master,
+ dns_zone_slave,
+ dns_zone_stub
+} dns_zonetype_t;
+
+#define DNS_ZONEOPT_SERVERS 0x00000001U /*%< perform server checks */
+#define DNS_ZONEOPT_PARENTS 0x00000002U /*%< perform parent checks */
+#define DNS_ZONEOPT_CHILDREN 0x00000004U /*%< perform child checks */
+#define DNS_ZONEOPT_NOTIFY 0x00000008U /*%< perform NOTIFY */
+#define DNS_ZONEOPT_MANYERRORS 0x00000010U /*%< return many errors on load */
+#define DNS_ZONEOPT_IXFRFROMDIFFS 0x00000020U /*%< calculate differences */
+#define DNS_ZONEOPT_NOMERGE 0x00000040U /*%< don't merge journal */
+#define DNS_ZONEOPT_CHECKNS 0x00000080U /*%< check if NS's are addresses */
+#define DNS_ZONEOPT_FATALNS 0x00000100U /*%< DNS_ZONEOPT_CHECKNS is fatal */
+#define DNS_ZONEOPT_MULTIMASTER 0x00000200U /*%< this zone has multiple masters */
+#define DNS_ZONEOPT_USEALTXFRSRC 0x00000400U /*%< use alternate transfer sources */
+#define DNS_ZONEOPT_CHECKNAMES 0x00000800U /*%< check-names */
+#define DNS_ZONEOPT_CHECKNAMESFAIL 0x00001000U /*%< fatal check-name failures */
+#define DNS_ZONEOPT_CHECKWILDCARD 0x00002000U /*%< check for internal wildcards */
+#define DNS_ZONEOPT_CHECKMX 0x00004000U /*%< check-mx */
+#define DNS_ZONEOPT_CHECKMXFAIL 0x00008000U /*%< fatal check-mx failures */
+#define DNS_ZONEOPT_CHECKINTEGRITY 0x00010000U /*%< perform integrity checks */
+#define DNS_ZONEOPT_CHECKSIBLING 0x00020000U /*%< perform sibling glue checks */
+#define DNS_ZONEOPT_NOCHECKNS 0x00040000U /*%< disable IN NS address checks */
+#define DNS_ZONEOPT_WARNMXCNAME 0x00080000U /*%< warn on MX CNAME check */
+#define DNS_ZONEOPT_IGNOREMXCNAME 0x00100000U /*%< ignore MX CNAME check */
+#define DNS_ZONEOPT_WARNSRVCNAME 0x00200000U /*%< warn on SRV CNAME check */
+#define DNS_ZONEOPT_IGNORESRVCNAME 0x00400000U /*%< ignore SRV CNAME check */
+#define DNS_ZONEOPT_UPDATECHECKKSK 0x00800000U /*%< check dnskey KSK flag */
+#define DNS_ZONEOPT_TRYTCPREFRESH 0x01000000U /*%< try tcp refresh on udp failure */
+#define DNS_ZONEOPT_NOTIFYTOSOA 0x02000000U /*%< Notify the SOA MNAME */
+#define DNS_ZONEOPT_NSEC3TESTZONE 0x04000000U /*%< nsec3-test-zone */
+
+#ifndef NOMINUM_PUBLIC
+/*
+ * Nominum specific options build down.
+ */
+#define DNS_ZONEOPT_NOTIFYFORWARD 0x80000000U /* forward notify to master */
+#endif /* NOMINUM_PUBLIC */
+
+#ifndef DNS_ZONE_MINREFRESH
+#define DNS_ZONE_MINREFRESH 300 /*%< 5 minutes */
+#endif
+#ifndef DNS_ZONE_MAXREFRESH
+#define DNS_ZONE_MAXREFRESH 2419200 /*%< 4 weeks */
+#endif
+#ifndef DNS_ZONE_DEFAULTREFRESH
+#define DNS_ZONE_DEFAULTREFRESH 3600 /*%< 1 hour */
+#endif
+#ifndef DNS_ZONE_MINRETRY
+#define DNS_ZONE_MINRETRY 300 /*%< 5 minutes */
+#endif
+#ifndef DNS_ZONE_MAXRETRY
+#define DNS_ZONE_MAXRETRY 1209600 /*%< 2 weeks */
+#endif
+#ifndef DNS_ZONE_DEFAULTRETRY
+#define DNS_ZONE_DEFAULTRETRY 60 /*%< 1 minute, subject to
+ exponential backoff */
+#endif
+
+#define DNS_ZONESTATE_XFERRUNNING 1
+#define DNS_ZONESTATE_XFERDEFERRED 2
+#define DNS_ZONESTATE_SOAQUERY 3
+#define DNS_ZONESTATE_ANY 4
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Functions
+ ***/
+
+isc_result_t
+dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx);
+/*%<
+ * Creates a new empty zone and attach '*zonep' to it.
+ *
+ * Requires:
+ *\li 'zonep' to point to a NULL pointer.
+ *\li 'mctx' to be a valid memory context.
+ *
+ * Ensures:
+ *\li '*zonep' refers to a valid zone.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *\li #ISC_R_UNEXPECTED
+ */
+
+void
+dns_zone_setclass(dns_zone_t *zone, dns_rdataclass_t rdclass);
+/*%<
+ * Sets the class of a zone. This operation can only be performed
+ * once on a zone.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *\li dns_zone_setclass() not to have been called since the zone was
+ * created.
+ *\li 'rdclass' != dns_rdataclass_none.
+ */
+
+dns_rdataclass_t
+dns_zone_getclass(dns_zone_t *zone);
+/*%<
+ * Returns the current zone class.
+ *
+ * Requires:
+ *\li 'zone' to be a valid zone.
+ */
+
+isc_uint32_t
+dns_zone_getserial(dns_zone_t *zone);
+/*%<
+ * Returns the current serial number of the zone.
+ *
+ * Requires:
+ *\li 'zone' to be a valid zone.
+ */
+
+void
+dns_zone_settype(dns_zone_t *zone, dns_zonetype_t type);
+/*%<
+ * Sets the zone type. This operation can only be performed once on
+ * a zone.
+ *
+ * Requires:
+ *\li 'zone' to be a valid zone.
+ *\li dns_zone_settype() not to have been called since the zone was
+ * created.
+ *\li 'type' != dns_zone_none
+ */
+
+void
+dns_zone_setview(dns_zone_t *zone, dns_view_t *view);
+/*%<
+ * Associate the zone with a view.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ */
+
+dns_view_t *
+dns_zone_getview(dns_zone_t *zone);
+/*%<
+ * Returns the zone's associated view.
+ *
+ * Requires:
+ *\li 'zone' to be a valid zone.
+ */
+
+isc_result_t
+dns_zone_setorigin(dns_zone_t *zone, const dns_name_t *origin);
+/*%<
+ * Sets the zones origin to 'origin'.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *\li 'origin' to be non NULL.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ */
+
+dns_name_t *
+dns_zone_getorigin(dns_zone_t *zone);
+/*%<
+ * Returns the value of the origin.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ */
+
+isc_result_t
+dns_zone_setfile(dns_zone_t *zone, const char *file);
+
+isc_result_t
+dns_zone_setfile2(dns_zone_t *zone, const char *file,
+ dns_masterformat_t format);
+/*%<
+ * Sets the name of the master file in the format of 'format' from which
+ * the zone loads its database to 'file'.
+ *
+ * For zones that have no associated master file, 'file' will be NULL.
+ *
+ * For zones with persistent databases, the file name
+ * setting is ignored.
+ *
+ * dns_zone_setfile() is a backward-compatible form of
+ * dns_zone_setfile2(), which always specifies the
+ * dns_masterformat_text (RFC1035) format.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *
+ * Returns:
+ *\li #ISC_R_NOMEMORY
+ *\li #ISC_R_SUCCESS
+ */
+
+const char *
+dns_zone_getfile(dns_zone_t *zone);
+/*%<
+ * Gets the name of the zone's master file, if any.
+ *
+ * Requires:
+ *\li 'zone' to be valid initialised zone.
+ *
+ * Returns:
+ *\li Pointer to null-terminated file name, or NULL.
+ */
+
+isc_result_t
+dns_zone_load(dns_zone_t *zone);
+
+isc_result_t
+dns_zone_loadnew(dns_zone_t *zone);
+/*%<
+ * Cause the database to be loaded from its backing store.
+ * Confirm that the minimum requirements for the zone type are
+ * met, otherwise DNS_R_BADZONE is returned.
+ *
+ * dns_zone_loadnew() only loads zones that are not yet loaded.
+ * dns_zone_load() also loads zones that are already loaded and
+ * and whose master file has changed since the last load.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *
+ * Returns:
+ *\li #ISC_R_UNEXPECTED
+ *\li #ISC_R_SUCCESS
+ *\li DNS_R_CONTINUE Incremental load has been queued.
+ *\li DNS_R_UPTODATE The zone has already been loaded based on
+ * file system timestamps.
+ *\li DNS_R_BADZONE
+ *\li Any result value from dns_db_load().
+ */
+
+void
+dns_zone_attach(dns_zone_t *source, dns_zone_t **target);
+/*%<
+ * Attach '*target' to 'source' incrementing its external
+ * reference count.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *\li 'target' to be non NULL and '*target' to be NULL.
+ */
+
+void
+dns_zone_detach(dns_zone_t **zonep);
+/*%<
+ * Detach from a zone decrementing its external reference count.
+ * If this was the last external reference to the zone it will be
+ * shut down and eventually freed.
+ *
+ * Require:
+ *\li 'zonep' to point to a valid zone.
+ */
+
+void
+dns_zone_iattach(dns_zone_t *source, dns_zone_t **target);
+/*%<
+ * Attach '*target' to 'source' incrementing its internal
+ * reference count. This is intended for use by operations
+ * such as zone transfers that need to prevent the zone
+ * object from being freed but not from shutting down.
+ *
+ * Require:
+ *\li The caller is running in the context of the zone's task.
+ *\li 'zone' to be a valid zone.
+ *\li 'target' to be non NULL and '*target' to be NULL.
+ */
+
+void
+dns_zone_idetach(dns_zone_t **zonep);
+/*%<
+ * Detach from a zone decrementing its internal reference count.
+ * If there are no more internal or external references to the
+ * zone, it will be freed.
+ *
+ * Require:
+ *\li The caller is running in the context of the zone's task.
+ *\li 'zonep' to point to a valid zone.
+ */
+
+void
+dns_zone_setflag(dns_zone_t *zone, unsigned int flags, isc_boolean_t value);
+/*%<
+ * Sets ('value' == 'ISC_TRUE') / clears ('value' == 'IS_FALSE')
+ * zone flags. Valid flag bits are DNS_ZONE_F_*.
+ *
+ * Requires
+ *\li 'zone' to be a valid zone.
+ */
+
+isc_result_t
+dns_zone_getdb(dns_zone_t *zone, dns_db_t **dbp);
+/*%<
+ * Attach '*dbp' to the database to if it exists otherwise
+ * return DNS_R_NOTLOADED.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *\li 'dbp' to be != NULL && '*dbp' == NULL.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li DNS_R_NOTLOADED
+ */
+
+isc_result_t
+dns_zone_setdbtype(dns_zone_t *zone,
+ unsigned int dbargc, const char * const *dbargv);
+/*%<
+ * Sets the database type to dbargv[0] and database arguments
+ * to subsequent dbargv elements.
+ * 'db_type' is not checked to see if it is a valid database type.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *\li 'database' to be non NULL.
+ *\li 'dbargc' to be >= 1
+ *\li 'dbargv' to point to dbargc NULL-terminated strings
+ *
+ * Returns:
+ *\li #ISC_R_NOMEMORY
+ *\li #ISC_R_SUCCESS
+ */
+
+isc_result_t
+dns_zone_getdbtype(dns_zone_t *zone, char ***argv, isc_mem_t *mctx);
+/*%<
+ * Returns the current dbtype. isc_mem_free() should be used
+ * to free 'argv' after use.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *\li 'argv' to be non NULL and *argv to be NULL.
+ *\li 'mctx' to be valid.
+ *
+ * Returns:
+ *\li #ISC_R_NOMEMORY
+ *\li #ISC_R_SUCCESS
+ */
+
+void
+dns_zone_markdirty(dns_zone_t *zone);
+/*%<
+ * Mark a zone as 'dirty'.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ */
+
+void
+dns_zone_expire(dns_zone_t *zone);
+/*%<
+ * Mark the zone as expired. If the zone requires dumping cause it to
+ * be initiated. Set the refresh and retry intervals to there default
+ * values and unload the zone.
+ *
+ * Require
+ *\li 'zone' to be a valid zone.
+ */
+
+void
+dns_zone_refresh(dns_zone_t *zone);
+/*%<
+ * Initiate zone up to date checks. The zone must already be being
+ * managed.
+ *
+ * Require
+ *\li 'zone' to be a valid zone.
+ */
+
+isc_result_t
+dns_zone_flush(dns_zone_t *zone);
+/*%<
+ * Write the zone to database if there are uncommited changes.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ */
+
+isc_result_t
+dns_zone_dump(dns_zone_t *zone);
+/*%<
+ * Write the zone to database.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ */
+
+isc_result_t
+dns_zone_dumptostream(dns_zone_t *zone, FILE *fd);
+
+isc_result_t
+dns_zone_dumptostream2(dns_zone_t *zone, FILE *fd, dns_masterformat_t format,
+ const dns_master_style_t *style);
+/*%<
+ * Write the zone to stream 'fd' in the specified 'format'.
+ * If the 'format' is dns_masterformat_text (RFC1035), 'style' also
+ * specifies the file style (e.g., &dns_master_style_default).
+ *
+ * dns_zone_dumptostream() is a backward-compatible form of
+ * dns_zone_dumptostream2(), which always uses the dns_masterformat_text
+ * format and the dns_master_style_default style.
+ *
+ * Note that dns_zone_dumptostream2() is the most flexible form. It
+ * can also provide the functionality of dns_zone_fulldumptostream().
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *\li 'fd' to be a stream open for writing.
+ */
+
+isc_result_t
+dns_zone_fulldumptostream(dns_zone_t *zone, FILE *fd);
+/*%<
+ * The same as dns_zone_dumptostream, but dumps the zone with
+ * different dump settings (dns_master_style_full).
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *\li 'fd' to be a stream open for writing.
+ */
+
+void
+dns_zone_maintenance(dns_zone_t *zone);
+/*%<
+ * Perform regular maintenace on the zone. This is called as a
+ * result of a zone being managed.
+ *
+ * Require
+ *\li 'zone' to be a valid zone.
+ */
+
+isc_result_t
+dns_zone_setmasters(dns_zone_t *zone, const isc_sockaddr_t *masters,
+ isc_uint32_t count);
+isc_result_t
+dns_zone_setmasterswithkeys(dns_zone_t *zone,
+ const isc_sockaddr_t *masters,
+ dns_name_t **keynames,
+ isc_uint32_t count);
+/*%<
+ * Set the list of master servers for the zone.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *\li 'masters' array of isc_sockaddr_t with port set or NULL.
+ *\li 'count' the number of masters.
+ *\li 'keynames' array of dns_name_t's for tsig keys or NULL.
+ *
+ * \li dns_zone_setmasters() is just a wrapper to setmasterswithkeys(),
+ * passing NULL in the keynames field.
+ *
+ * \li If 'masters' is NULL then 'count' must be zero.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *\li Any result dns_name_dup() can return, if keynames!=NULL
+ */
+
+isc_result_t
+dns_zone_setalsonotify(dns_zone_t *zone, const isc_sockaddr_t *notify,
+ isc_uint32_t count);
+/*%<
+ * Set the list of additional servers to be notified when
+ * a zone changes. To clear the list use 'count = 0'.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *\li 'notify' to be non-NULL if count != 0.
+ *\li 'count' to be the number of notifyees.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ */
+
+void
+dns_zone_unload(dns_zone_t *zone);
+/*%<
+ * detach the database from the zone structure.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ */
+
+void
+dns_zone_setoption(dns_zone_t *zone, unsigned int option, isc_boolean_t value);
+/*%<
+ * Set given options on ('value' == ISC_TRUE) or off ('value' ==
+ * #ISC_FALSE).
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ */
+
+unsigned int
+dns_zone_getoptions(dns_zone_t *zone);
+/*%<
+ * Returns the current zone options.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ */
+
+void
+dns_zone_setminrefreshtime(dns_zone_t *zone, isc_uint32_t val);
+/*%<
+ * Set the minimum refresh time.
+ *
+ * Requires:
+ *\li 'zone' is valid.
+ *\li val > 0.
+ */
+
+void
+dns_zone_setmaxrefreshtime(dns_zone_t *zone, isc_uint32_t val);
+/*%<
+ * Set the maximum refresh time.
+ *
+ * Requires:
+ *\li 'zone' is valid.
+ *\li val > 0.
+ */
+
+void
+dns_zone_setminretrytime(dns_zone_t *zone, isc_uint32_t val);
+/*%<
+ * Set the minimum retry time.
+ *
+ * Requires:
+ *\li 'zone' is valid.
+ *\li val > 0.
+ */
+
+void
+dns_zone_setmaxretrytime(dns_zone_t *zone, isc_uint32_t val);
+/*%<
+ * Set the maximum retry time.
+ *
+ * Requires:
+ *\li 'zone' is valid.
+ * val > 0.
+ */
+
+isc_result_t
+dns_zone_setxfrsource4(dns_zone_t *zone, const isc_sockaddr_t *xfrsource);
+isc_result_t
+dns_zone_setaltxfrsource4(dns_zone_t *zone,
+ const isc_sockaddr_t *xfrsource);
+/*%<
+ * Set the source address to be used in IPv4 zone transfers.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *\li 'xfrsource' to contain the address.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ */
+
+isc_sockaddr_t *
+dns_zone_getxfrsource4(dns_zone_t *zone);
+isc_sockaddr_t *
+dns_zone_getaltxfrsource4(dns_zone_t *zone);
+/*%<
+ * Returns the source address set by a previous dns_zone_setxfrsource4
+ * call, or the default of inaddr_any, port 0.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ */
+
+isc_result_t
+dns_zone_setxfrsource6(dns_zone_t *zone, const isc_sockaddr_t *xfrsource);
+isc_result_t
+dns_zone_setaltxfrsource6(dns_zone_t *zone,
+ const isc_sockaddr_t *xfrsource);
+/*%<
+ * Set the source address to be used in IPv6 zone transfers.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *\li 'xfrsource' to contain the address.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ */
+
+isc_sockaddr_t *
+dns_zone_getxfrsource6(dns_zone_t *zone);
+isc_sockaddr_t *
+dns_zone_getaltxfrsource6(dns_zone_t *zone);
+/*%<
+ * Returns the source address set by a previous dns_zone_setxfrsource6
+ * call, or the default of in6addr_any, port 0.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ */
+
+isc_result_t
+dns_zone_setnotifysrc4(dns_zone_t *zone, const isc_sockaddr_t *notifysrc);
+/*%<
+ * Set the source address to be used with IPv4 NOTIFY messages.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *\li 'notifysrc' to contain the address.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ */
+
+isc_sockaddr_t *
+dns_zone_getnotifysrc4(dns_zone_t *zone);
+/*%<
+ * Returns the source address set by a previous dns_zone_setnotifysrc4
+ * call, or the default of inaddr_any, port 0.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ */
+
+isc_result_t
+dns_zone_setnotifysrc6(dns_zone_t *zone, const isc_sockaddr_t *notifysrc);
+/*%<
+ * Set the source address to be used with IPv6 NOTIFY messages.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *\li 'notifysrc' to contain the address.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ */
+
+isc_sockaddr_t *
+dns_zone_getnotifysrc6(dns_zone_t *zone);
+/*%<
+ * Returns the source address set by a previous dns_zone_setnotifysrc6
+ * call, or the default of in6addr_any, port 0.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ */
+
+void
+dns_zone_setnotifyacl(dns_zone_t *zone, dns_acl_t *acl);
+/*%<
+ * Sets the notify acl list for the zone.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *\li 'acl' to be a valid acl.
+ */
+
+void
+dns_zone_setqueryacl(dns_zone_t *zone, dns_acl_t *acl);
+/*%<
+ * Sets the query acl list for the zone.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *\li 'acl' to be a valid acl.
+ */
+
+void
+dns_zone_setqueryonacl(dns_zone_t *zone, dns_acl_t *acl);
+/*%<
+ * Sets the query-on acl list for the zone.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *\li 'acl' to be a valid acl.
+ */
+
+void
+dns_zone_setupdateacl(dns_zone_t *zone, dns_acl_t *acl);
+/*%<
+ * Sets the update acl list for the zone.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *\li 'acl' to be valid acl.
+ */
+
+void
+dns_zone_setforwardacl(dns_zone_t *zone, dns_acl_t *acl);
+/*%<
+ * Sets the forward unsigned updates acl list for the zone.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *\li 'acl' to be valid acl.
+ */
+
+void
+dns_zone_setxfracl(dns_zone_t *zone, dns_acl_t *acl);
+/*%<
+ * Sets the transfer acl list for the zone.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *\li 'acl' to be valid acl.
+ */
+
+dns_acl_t *
+dns_zone_getnotifyacl(dns_zone_t *zone);
+/*%<
+ * Returns the current notify acl or NULL.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *
+ * Returns:
+ *\li acl a pointer to the acl.
+ *\li NULL
+ */
+
+dns_acl_t *
+dns_zone_getqueryacl(dns_zone_t *zone);
+/*%<
+ * Returns the current query acl or NULL.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *
+ * Returns:
+ *\li acl a pointer to the acl.
+ *\li NULL
+ */
+
+dns_acl_t *
+dns_zone_getqueryonacl(dns_zone_t *zone);
+/*%<
+ * Returns the current query-on acl or NULL.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *
+ * Returns:
+ *\li acl a pointer to the acl.
+ *\li NULL
+ */
+
+dns_acl_t *
+dns_zone_getupdateacl(dns_zone_t *zone);
+/*%<
+ * Returns the current update acl or NULL.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *
+ * Returns:
+ *\li acl a pointer to the acl.
+ *\li NULL
+ */
+
+dns_acl_t *
+dns_zone_getforwardacl(dns_zone_t *zone);
+/*%<
+ * Returns the current forward unsigned updates acl or NULL.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *
+ * Returns:
+ *\li acl a pointer to the acl.
+ *\li NULL
+ */
+
+dns_acl_t *
+dns_zone_getxfracl(dns_zone_t *zone);
+/*%<
+ * Returns the current transfer acl or NULL.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *
+ * Returns:
+ *\li acl a pointer to the acl.
+ *\li NULL
+ */
+
+void
+dns_zone_clearupdateacl(dns_zone_t *zone);
+/*%<
+ * Clear the current update acl.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ */
+
+void
+dns_zone_clearforwardacl(dns_zone_t *zone);
+/*%<
+ * Clear the current forward unsigned updates acl.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ */
+
+void
+dns_zone_clearnotifyacl(dns_zone_t *zone);
+/*%<
+ * Clear the current notify acl.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ */
+
+void
+dns_zone_clearqueryacl(dns_zone_t *zone);
+/*%<
+ * Clear the current query acl.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ */
+
+void
+dns_zone_clearqueryonacl(dns_zone_t *zone);
+/*%<
+ * Clear the current query-on acl.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ */
+
+void
+dns_zone_clearxfracl(dns_zone_t *zone);
+/*%<
+ * Clear the current transfer acl.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ */
+
+isc_boolean_t
+dns_zone_getupdatedisabled(dns_zone_t *zone);
+/*%<
+ * Return update disabled.
+ * Transient unless called when running in isc_task_exclusive() mode.
+ */
+
+void
+dns_zone_setupdatedisabled(dns_zone_t *zone, isc_boolean_t state);
+/*%<
+ * Set update disabled.
+ * Should only be called only when running in isc_task_exclusive() mode.
+ * Failure to do so may result in updates being committed after the
+ * call has been made.
+ */
+
+isc_boolean_t
+dns_zone_getzeronosoattl(dns_zone_t *zone);
+/*%<
+ * Return zero-no-soa-ttl status.
+ */
+
+void
+dns_zone_setzeronosoattl(dns_zone_t *zone, isc_boolean_t state);
+/*%<
+ * Set zero-no-soa-ttl status.
+ */
+
+void
+dns_zone_setchecknames(dns_zone_t *zone, dns_severity_t severity);
+/*%<
+ * Set the severity of name checking when loading a zone.
+ *
+ * Require:
+ * \li 'zone' to be a valid zone.
+ */
+
+dns_severity_t
+dns_zone_getchecknames(dns_zone_t *zone);
+/*%<
+ * Return the current severity of name checking.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ */
+
+void
+dns_zone_setjournalsize(dns_zone_t *zone, isc_int32_t size);
+/*%<
+ * Sets the journal size for the zone.
+ *
+ * Requires:
+ *\li 'zone' to be a valid zone.
+ */
+
+isc_int32_t
+dns_zone_getjournalsize(dns_zone_t *zone);
+/*%<
+ * Return the journal size as set with a previous call to
+ * dns_zone_setjournalsize().
+ *
+ * Requires:
+ *\li 'zone' to be a valid zone.
+ */
+
+isc_result_t
+dns_zone_notifyreceive(dns_zone_t *zone, isc_sockaddr_t *from,
+ dns_message_t *msg);
+/*%<
+ * Tell the zone that it has recieved a NOTIFY message from another
+ * server. This may cause some zone maintainence activity to occur.
+ *
+ * Requires:
+ *\li 'zone' to be a valid zone.
+ *\li '*from' to contain the address of the server from which 'msg'
+ * was recieved.
+ *\li 'msg' a message with opcode NOTIFY and qr clear.
+ *
+ * Returns:
+ *\li DNS_R_REFUSED
+ *\li DNS_R_NOTIMP
+ *\li DNS_R_FORMERR
+ *\li DNS_R_SUCCESS
+ */
+
+void
+dns_zone_setmaxxfrin(dns_zone_t *zone, isc_uint32_t maxxfrin);
+/*%<
+ * Set the maximum time (in seconds) that a zone transfer in (AXFR/IXFR)
+ * of this zone will use before being aborted.
+ *
+ * Requires:
+ * \li 'zone' to be valid initialised zone.
+ */
+
+isc_uint32_t
+dns_zone_getmaxxfrin(dns_zone_t *zone);
+/*%<
+ * Returns the maximum transfer time for this zone. This will be
+ * either the value set by the last call to dns_zone_setmaxxfrin() or
+ * the default value of 1 hour.
+ *
+ * Requires:
+ *\li 'zone' to be valid initialised zone.
+ */
+
+void
+dns_zone_setmaxxfrout(dns_zone_t *zone, isc_uint32_t maxxfrout);
+/*%<
+ * Set the maximum time (in seconds) that a zone transfer out (AXFR/IXFR)
+ * of this zone will use before being aborted.
+ *
+ * Requires:
+ * \li 'zone' to be valid initialised zone.
+ */
+
+isc_uint32_t
+dns_zone_getmaxxfrout(dns_zone_t *zone);
+/*%<
+ * Returns the maximum transfer time for this zone. This will be
+ * either the value set by the last call to dns_zone_setmaxxfrout() or
+ * the default value of 1 hour.
+ *
+ * Requires:
+ *\li 'zone' to be valid initialised zone.
+ */
+
+isc_result_t
+dns_zone_setjournal(dns_zone_t *zone, const char *journal);
+/*%<
+ * Sets the filename used for journaling updates / IXFR transfers.
+ * The default journal name is set by dns_zone_setfile() to be
+ * "file.jnl". If 'journal' is NULL, the zone will have no
+ * journal name.
+ *
+ * Requires:
+ *\li 'zone' to be a valid zone.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ */
+
+char *
+dns_zone_getjournal(dns_zone_t *zone);
+/*%<
+ * Returns the journal name associated with this zone.
+ * If no journal has been set this will be NULL.
+ *
+ * Requires:
+ *\li 'zone' to be valid initialised zone.
+ */
+
+dns_zonetype_t
+dns_zone_gettype(dns_zone_t *zone);
+/*%<
+ * Returns the type of the zone (master/slave/etc.)
+ *
+ * Requires:
+ *\li 'zone' to be valid initialised zone.
+ */
+
+void
+dns_zone_settask(dns_zone_t *zone, isc_task_t *task);
+/*%<
+ * Give a zone a task to work with. Any current task will be detached.
+ *
+ * Requires:
+ *\li 'zone' to be valid.
+ *\li 'task' to be valid.
+ */
+
+void
+dns_zone_gettask(dns_zone_t *zone, isc_task_t **target);
+/*%<
+ * Attach '*target' to the zone's task.
+ *
+ * Requires:
+ *\li 'zone' to be valid initialised zone.
+ *\li 'zone' to have a task.
+ *\li 'target' to be != NULL && '*target' == NULL.
+ */
+
+void
+dns_zone_notify(dns_zone_t *zone);
+/*%<
+ * Generate notify events for this zone.
+ *
+ * Requires:
+ *\li 'zone' to be a valid zone.
+ */
+
+isc_result_t
+dns_zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump);
+/*%<
+ * Replace the database of "zone" with a new database "db".
+ *
+ * If "dump" is ISC_TRUE, then the new zone contents are dumped
+ * into to the zone's master file for persistence. When replacing
+ * a zone database by one just loaded from a master file, set
+ * "dump" to ISC_FALSE to avoid a redunant redump of the data just
+ * loaded. Otherwise, it should be set to ISC_TRUE.
+ *
+ * If the "diff-on-reload" option is enabled in the configuration file,
+ * the differences between the old and the new database are added to the
+ * journal file, and the master file dump is postponed.
+ *
+ * Requires:
+ * \li 'zone' to be a valid zone.
+ *
+ * Returns:
+ * \li DNS_R_SUCCESS
+ * \li DNS_R_BADZONE zone failed basic consistancy checks:
+ * * a single SOA must exist
+ * * some NS records must exist.
+ * Others
+ */
+
+isc_uint32_t
+dns_zone_getidlein(dns_zone_t *zone);
+/*%<
+ * Requires:
+ * \li 'zone' to be a valid zone.
+ *
+ * Returns:
+ * \li number of seconds of idle time before we abort the transfer in.
+ */
+
+void
+dns_zone_setidlein(dns_zone_t *zone, isc_uint32_t idlein);
+/*%<
+ * \li Set the idle timeout for transfer the.
+ * \li Zero set the default value, 1 hour.
+ *
+ * Requires:
+ * \li 'zone' to be a valid zone.
+ */
+
+isc_uint32_t
+dns_zone_getidleout(dns_zone_t *zone);
+/*%<
+ *
+ * Requires:
+ * \li 'zone' to be a valid zone.
+ *
+ * Returns:
+ * \li number of seconds of idle time before we abort a transfer out.
+ */
+
+void
+dns_zone_setidleout(dns_zone_t *zone, isc_uint32_t idleout);
+/*%<
+ * \li Set the idle timeout for transfers out.
+ * \li Zero set the default value, 1 hour.
+ *
+ * Requires:
+ * \li 'zone' to be a valid zone.
+ */
+
+void
+dns_zone_getssutable(dns_zone_t *zone, dns_ssutable_t **table);
+/*%<
+ * Get the simple-secure-update policy table.
+ *
+ * Requires:
+ * \li 'zone' to be a valid zone.
+ */
+
+void
+dns_zone_setssutable(dns_zone_t *zone, dns_ssutable_t *table);
+/*%<
+ * Set / clear the simple-secure-update policy table.
+ *
+ * Requires:
+ * \li 'zone' to be a valid zone.
+ */
+
+isc_mem_t *
+dns_zone_getmctx(dns_zone_t *zone);
+/*%<
+ * Get the memory context of a zone.
+ *
+ * Requires:
+ * \li 'zone' to be a valid zone.
+ */
+
+dns_zonemgr_t *
+dns_zone_getmgr(dns_zone_t *zone);
+/*%<
+ * If 'zone' is managed return the zone manager otherwise NULL.
+ *
+ * Requires:
+ * \li 'zone' to be a valid zone.
+ */
+
+void
+dns_zone_setsigvalidityinterval(dns_zone_t *zone, isc_uint32_t interval);
+/*%<
+ * Set the zone's RRSIG validity interval. This is the length of time
+ * for which DNSSEC signatures created as a result of dynamic updates
+ * to secure zones will remain valid, in seconds.
+ *
+ * Requires:
+ * \li 'zone' to be a valid zone.
+ */
+
+isc_uint32_t
+dns_zone_getsigvalidityinterval(dns_zone_t *zone);
+/*%<
+ * Get the zone's RRSIG validity interval.
+ *
+ * Requires:
+ * \li 'zone' to be a valid zone.
+ */
+
+void
+dns_zone_setsigresigninginterval(dns_zone_t *zone, isc_uint32_t interval);
+/*%<
+ * Set the zone's RRSIG re-signing interval. A dynamic zone's RRSIG's
+ * will be re-signed 'interval' amount of time before they expire.
+ *
+ * Requires:
+ * \li 'zone' to be a valid zone.
+ */
+
+isc_uint32_t
+dns_zone_getsigresigninginterval(dns_zone_t *zone);
+/*%<
+ * Get the zone's RRSIG re-signing interval.
+ *
+ * Requires:
+ * \li 'zone' to be a valid zone.
+ */
+
+void
+dns_zone_setnotifytype(dns_zone_t *zone, dns_notifytype_t notifytype);
+/*%<
+ * Sets zone notify method to "notifytype"
+ */
+
+isc_result_t
+dns_zone_forwardupdate(dns_zone_t *zone, dns_message_t *msg,
+ dns_updatecallback_t callback, void *callback_arg);
+/*%<
+ * Forward 'msg' to each master in turn until we get an answer or we
+ * have exausted the list of masters. 'callback' will be called with
+ * ISC_R_SUCCESS if we get an answer and the returned message will be
+ * passed as 'answer_message', otherwise a non ISC_R_SUCCESS result code
+ * will be passed and answer_message will be NULL. The callback function
+ * is responsible for destroying 'answer_message'.
+ * (callback)(callback_arg, result, answer_message);
+ *
+ * Require:
+ *\li 'zone' to be valid
+ *\li 'msg' to be valid.
+ *\li 'callback' to be non NULL.
+ * Returns:
+ *\li #ISC_R_SUCCESS if the message has been forwarded,
+ *\li #ISC_R_NOMEMORY
+ *\li Others
+ */
+
+isc_result_t
+dns_zone_next(dns_zone_t *zone, dns_zone_t **next);
+/*%<
+ * Find the next zone in the list of managed zones.
+ *
+ * Requires:
+ *\li 'zone' to be valid
+ *\li The zone manager for the indicated zone MUST be locked
+ * by the caller. This is not checked.
+ *\li 'next' be non-NULL, and '*next' be NULL.
+ *
+ * Ensures:
+ *\li 'next' points to a valid zone (result ISC_R_SUCCESS) or to NULL
+ * (result ISC_R_NOMORE).
+ */
+
+
+
+isc_result_t
+dns_zone_first(dns_zonemgr_t *zmgr, dns_zone_t **first);
+/*%<
+ * Find the first zone in the list of managed zones.
+ *
+ * Requires:
+ *\li 'zonemgr' to be valid
+ *\li The zone manager for the indicated zone MUST be locked
+ * by the caller. This is not checked.
+ *\li 'first' be non-NULL, and '*first' be NULL
+ *
+ * Ensures:
+ *\li 'first' points to a valid zone (result ISC_R_SUCCESS) or to NULL
+ * (result ISC_R_NOMORE).
+ */
+
+isc_result_t
+dns_zone_setkeydirectory(dns_zone_t *zone, const char *directory);
+/*%<
+ * Sets the name of the directory where private keys used for
+ * online signing of dynamic zones are found.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *
+ * Returns:
+ *\li #ISC_R_NOMEMORY
+ *\li #ISC_R_SUCCESS
+ */
+
+const char *
+dns_zone_getkeydirectory(dns_zone_t *zone);
+/*%<
+ * Gets the name of the directory where private keys used for
+ * online signing of dynamic zones are found.
+ *
+ * Requires:
+ *\li 'zone' to be valid initialised zone.
+ *
+ * Returns:
+ * Pointer to null-terminated file name, or NULL.
+ */
+
+
+isc_result_t
+dns_zonemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
+ isc_timermgr_t *timermgr, isc_socketmgr_t *socketmgr,
+ dns_zonemgr_t **zmgrp);
+/*%<
+ * Create a zone manager.
+ *
+ * Requires:
+ *\li 'mctx' to be a valid memory context.
+ *\li 'taskmgr' to be a valid task manager.
+ *\li 'timermgr' to be a valid timer manager.
+ *\li 'zmgrp' to point to a NULL pointer.
+ */
+
+isc_result_t
+dns_zonemgr_managezone(dns_zonemgr_t *zmgr, dns_zone_t *zone);
+/*%<
+ * Bring the zone under control of a zone manager.
+ *
+ * Require:
+ *\li 'zmgr' to be a valid zone manager.
+ *\li 'zone' to be a valid zone.
+ */
+
+isc_result_t
+dns_zonemgr_forcemaint(dns_zonemgr_t *zmgr);
+/*%<
+ * Force zone maintenance of all zones managed by 'zmgr' at its
+ * earliest conveniene.
+ */
+
+void
+dns_zonemgr_resumexfrs(dns_zonemgr_t *zmgr);
+/*%<
+ * Attempt to start any stalled zone transfers.
+ */
+
+void
+dns_zonemgr_shutdown(dns_zonemgr_t *zmgr);
+/*%<
+ * Shut down the zone manager.
+ *
+ * Requires:
+ *\li 'zmgr' to be a valid zone manager.
+ */
+
+void
+dns_zonemgr_attach(dns_zonemgr_t *source, dns_zonemgr_t **target);
+/*%<
+ * Attach '*target' to 'source' incrementing its external
+ * reference count.
+ *
+ * Require:
+ *\li 'zone' to be a valid zone.
+ *\li 'target' to be non NULL and '*target' to be NULL.
+ */
+
+void
+dns_zonemgr_detach(dns_zonemgr_t **zmgrp);
+/*%<
+ * Detach from a zone manager.
+ *
+ * Requires:
+ *\li '*zmgrp' is a valid, non-NULL zone manager pointer.
+ *
+ * Ensures:
+ *\li '*zmgrp' is NULL.
+ */
+
+void
+dns_zonemgr_releasezone(dns_zonemgr_t *zmgr, dns_zone_t *zone);
+/*%<
+ * Release 'zone' from the managed by 'zmgr'. 'zmgr' is implicitly
+ * detached from 'zone'.
+ *
+ * Requires:
+ *\li 'zmgr' to be a valid zone manager.
+ *\li 'zone' to be a valid zone.
+ *\li 'zmgr' == 'zone->zmgr'
+ *
+ * Ensures:
+ *\li 'zone->zmgr' == NULL;
+ */
+
+void
+dns_zonemgr_settransfersin(dns_zonemgr_t *zmgr, isc_uint32_t value);
+/*%<
+ * Set the maximum number of simultaneous transfers in allowed by
+ * the zone manager.
+ *
+ * Requires:
+ *\li 'zmgr' to be a valid zone manager.
+ */
+
+isc_uint32_t
+dns_zonemgr_getttransfersin(dns_zonemgr_t *zmgr);
+/*%<
+ * Return the the maximum number of simultaneous transfers in allowed.
+ *
+ * Requires:
+ *\li 'zmgr' to be a valid zone manager.
+ */
+
+void
+dns_zonemgr_settransfersperns(dns_zonemgr_t *zmgr, isc_uint32_t value);
+/*%<
+ * Set the number of zone transfers allowed per nameserver.
+ *
+ * Requires:
+ *\li 'zmgr' to be a valid zone manager
+ */
+
+isc_uint32_t
+dns_zonemgr_getttransfersperns(dns_zonemgr_t *zmgr);
+/*%<
+ * Return the number of transfers allowed per nameserver.
+ *
+ * Requires:
+ *\li 'zmgr' to be a valid zone manager.
+ */
+
+void
+dns_zonemgr_setiolimit(dns_zonemgr_t *zmgr, isc_uint32_t iolimit);
+/*%<
+ * Set the number of simultaneous file descriptors available for
+ * reading and writing masterfiles.
+ *
+ * Requires:
+ *\li 'zmgr' to be a valid zone manager.
+ *\li 'iolimit' to be positive.
+ */
+
+isc_uint32_t
+dns_zonemgr_getiolimit(dns_zonemgr_t *zmgr);
+/*%<
+ * Get the number of simultaneous file descriptors available for
+ * reading and writing masterfiles.
+ *
+ * Requires:
+ *\li 'zmgr' to be a valid zone manager.
+ */
+
+void
+dns_zonemgr_setserialqueryrate(dns_zonemgr_t *zmgr, unsigned int value);
+/*%<
+ * Set the number of SOA queries sent per second.
+ *
+ * Requires:
+ *\li 'zmgr' to be a valid zone manager
+ */
+
+unsigned int
+dns_zonemgr_getserialqueryrate(dns_zonemgr_t *zmgr);
+/*%<
+ * Return the number of SOA queries sent per second.
+ *
+ * Requires:
+ *\li 'zmgr' to be a valid zone manager.
+ */
+
+unsigned int
+dns_zonemgr_getcount(dns_zonemgr_t *zmgr, int state);
+/*%<
+ * Returns the number of zones in the specified state.
+ *
+ * Requires:
+ *\li 'zmgr' to be a valid zone manager.
+ *\li 'state' to be a valid DNS_ZONESTATE_ constant.
+ */
+
+void
+dns_zonemgr_unreachableadd(dns_zonemgr_t *zmgr, isc_sockaddr_t *remote,
+ isc_sockaddr_t *local, isc_time_t *now);
+/*%<
+ * Add the pair of addresses to the unreachable cache.
+ *
+ * Requires:
+ *\li 'zmgr' to be a valid zone manager.
+ *\li 'remote' to be a valid sockaddr.
+ *\li 'local' to be a valid sockaddr.
+ */
+
+void
+dns_zone_forcereload(dns_zone_t *zone);
+/*%<
+ * Force a reload of specified zone.
+ *
+ * Requires:
+ *\li 'zone' to be a valid zone.
+ */
+
+isc_boolean_t
+dns_zone_isforced(dns_zone_t *zone);
+/*%<
+ * Check if the zone is waiting a forced reload.
+ *
+ * Requires:
+ * \li 'zone' to be a valid zone.
+ */
+
+isc_result_t
+dns_zone_setstatistics(dns_zone_t *zone, isc_boolean_t on);
+/*%<
+ * This function is obsoleted by dns_zone_setrequeststats().
+ */
+
+isc_uint64_t *
+dns_zone_getstatscounters(dns_zone_t *zone);
+/*%<
+ * This function is obsoleted by dns_zone_getrequeststats().
+ */
+
+void
+dns_zone_setstats(dns_zone_t *zone, dns_stats_t *stats);
+/*%<
+ * Set a general zone-maintenance statistics set 'stats' for 'zone'. This
+ * function is expected to be called only on zone creation (when necessary).
+ * Once installed, it cannot be removed or replaced. Also, there is no
+ * interface to get the installed stats from the zone; the caller must keep the
+ * stats to reference (e.g. dump) it later.
+ *
+ * Requires:
+ * \li 'zone' to be a valid zone and does not have a statistics set already
+ * installed.
+ *
+ *\li stats is a valid statistics supporting zone statistics counters
+ * (see dns/stats.h).
+ */
+
+void
+dns_zone_setrequeststats(dns_zone_t *zone, dns_stats_t *stats);
+/*%<
+ * Set an additional statistics set to zone. It is attached in the zone
+ * but is not counted in the zone module; only the caller updates the counters.
+ *
+ * Requires:
+ * \li 'zone' to be a valid zone.
+ *
+ *\li stats is a valid statistics.
+ */
+
+dns_stats_t *
+dns_zone_getrequeststats(dns_zone_t *zone);
+/*%<
+ * Get the additional statistics for zone, if one is installed.
+ *
+ * Requires:
+ * \li 'zone' to be a valid zone.
+ *
+ * Returns:
+ * \li when available, a pointer to the statistics set installed in zone;
+ * otherwise NULL.
+ */
+
+void
+dns_zone_dialup(dns_zone_t *zone);
+/*%<
+ * Perform dialup-time maintenance on 'zone'.
+ */
+
+void
+dns_zone_setdialup(dns_zone_t *zone, dns_dialuptype_t dialup);
+/*%<
+ * Set the dialup type of 'zone' to 'dialup'.
+ *
+ * Requires:
+ * \li 'zone' to be valid initialised zone.
+ *\li 'dialup' to be a valid dialup type.
+ */
+
+void
+dns_zone_log(dns_zone_t *zone, int level, const char *msg, ...)
+ ISC_FORMAT_PRINTF(3, 4);
+/*%<
+ * Log the message 'msg...' at 'level', including text that identifies
+ * the message as applying to 'zone'.
+ */
+
+void
+dns_zone_logc(dns_zone_t *zone, isc_logcategory_t *category, int level,
+ const char *msg, ...) ISC_FORMAT_PRINTF(4, 5);
+/*%<
+ * Log the message 'msg...' at 'level', including text that identifies
+ * the message as applying to 'zone'.
+ */
+
+void
+dns_zone_name(dns_zone_t *zone, char *buf, size_t len);
+/*%<
+ * Return the name of the zone with class and view.
+ *
+ * Requires:
+ *\li 'zone' to be valid.
+ *\li 'buf' to be non NULL.
+ */
+
+isc_result_t
+dns_zone_checknames(dns_zone_t *zone, dns_name_t *name, dns_rdata_t *rdata);
+/*%<
+ * Check if this record meets the check-names policy.
+ *
+ * Requires:
+ * 'zone' to be valid.
+ * 'name' to be valid.
+ * 'rdata' to be valid.
+ *
+ * Returns:
+ * DNS_R_SUCCESS passed checks.
+ * DNS_R_BADOWNERNAME failed ownername checks.
+ * DNS_R_BADNAME failed rdata checks.
+ */
+
+void
+dns_zone_setacache(dns_zone_t *zone, dns_acache_t *acache);
+/*%<
+ * Associate the zone with an additional cache.
+ *
+ * Require:
+ * 'zone' to be a valid zone.
+ * 'acache' to be a non NULL pointer.
+ *
+ * Ensures:
+ * 'zone' will have a reference to 'acache'
+ */
+
+void
+dns_zone_setcheckmx(dns_zone_t *zone, dns_checkmxfunc_t checkmx);
+/*%<
+ * Set the post load integrity callback function 'checkmx'.
+ * 'checkmx' will be called if the MX is not within the zone.
+ *
+ * Require:
+ * 'zone' to be a valid zone.
+ */
+
+void
+dns_zone_setchecksrv(dns_zone_t *zone, dns_checkmxfunc_t checksrv);
+/*%<
+ * Set the post load integrity callback function 'checksrv'.
+ * 'checksrv' will be called if the SRV TARGET is not within the zone.
+ *
+ * Require:
+ * 'zone' to be a valid zone.
+ */
+
+void
+dns_zone_setcheckns(dns_zone_t *zone, dns_checknsfunc_t checkns);
+/*%<
+ * Set the post load integrity callback function 'checkmx'.
+ * 'checkmx' will be called if the MX is not within the zone.
+ *
+ * Require:
+ * 'zone' to be a valid zone.
+ */
+
+void
+dns_zone_setnotifydelay(dns_zone_t *zone, isc_uint32_t delay);
+/*%<
+ * Set the minimum delay between sets of notify messages.
+ *
+ * Requires:
+ * 'zone' to be valid.
+ */
+
+isc_uint32_t
+dns_zone_getnotifydelay(dns_zone_t *zone);
+/*%<
+ * Get the minimum delay between sets of notify messages.
+ *
+ * Requires:
+ * 'zone' to be valid.
+ */
+
+void
+dns_zone_setisself(dns_zone_t *zone, dns_isselffunc_t isself, void *arg);
+/*%<
+ * Set the isself callback function and argument.
+ *
+ * isc_boolean_t
+ * isself(dns_view_t *myview, dns_tsigkey_t *mykey, isc_netaddr_t *srcaddr,
+ * isc_netaddr_t *destaddr, dns_rdataclass_t rdclass, void *arg);
+ *
+ * 'isself' returns ISC_TRUE if a non-recursive query from 'srcaddr' to
+ * 'destaddr' with optional key 'mykey' for class 'rdclass' would be
+ * delivered to 'myview'.
+ */
+
+void
+dns_zone_setnodes(dns_zone_t *zone, isc_uint32_t nodes);
+/*%<
+ * Set the number of nodes that will be checked per quantum.
+ */
+
+void
+dns_zone_setsignatures(dns_zone_t *zone, isc_uint32_t signatures);
+/*%<
+ * Set the number of signatures that will be generated per quantum.
+ */
+
+isc_result_t
+dns_zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm,
+ isc_uint16_t keyid, isc_boolean_t delete);
+/*%<
+ * Initiate/resume signing of the entire zone with the zone DNSKEY(s)
+ * that match the given algorithm and keyid.
+ */
+
+isc_result_t
+dns_zone_addnsec3chain(dns_zone_t *zone, dns_rdata_nsec3param_t *nsec3param);
+/*%<
+ * Incrementally add a NSEC3 chain that corresponds to 'nsec3param'.
+ */
+
+void
+dns_zone_setprivatetype(dns_zone_t *zone, dns_rdatatype_t type);
+dns_rdatatype_t
+dns_zone_getprivatetype(dns_zone_t *zone);
+/*
+ * Get/Set the private record type. It is expected that these interfaces
+ * will not be permanent.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_ZONE_H */
diff --git a/lib/dns/include/dns/zonekey.h b/lib/dns/include/dns/zonekey.h
new file mode 100644
index 0000000..d9ba862
--- /dev/null
+++ b/lib/dns/include/dns/zonekey.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: zonekey.h,v 1.10 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef DNS_ZONEKEY_H
+#define DNS_ZONEKEY_H 1
+
+/*! \file dns/zonekey.h */
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_boolean_t
+dns_zonekey_iszonekey(dns_rdata_t *keyrdata);
+/*%<
+ * Determines if the key record contained in the rdata is a zone key.
+ *
+ * Requires:
+ * 'keyrdata' is not NULL.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_ZONEKEY_H */
diff --git a/lib/dns/include/dns/zt.h b/lib/dns/include/dns/zt.h
new file mode 100644
index 0000000..6cfe3d3
--- /dev/null
+++ b/lib/dns/include/dns/zt.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: zt.h,v 1.38 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef DNS_ZT_H
+#define DNS_ZT_H 1
+
+/*! \file dns/zt.h */
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+#define DNS_ZTFIND_NOEXACT 0x01
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_zt_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, dns_zt_t **zt);
+/*%<
+ * Creates a new zone table.
+ *
+ * Requires:
+ * \li 'mctx' to be initialized.
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS on success.
+ * \li #ISC_R_NOMEMORY
+ */
+
+isc_result_t
+dns_zt_mount(dns_zt_t *zt, dns_zone_t *zone);
+/*%<
+ * Mounts the zone on the zone table.
+ *
+ * Requires:
+ * \li 'zt' to be valid
+ * \li 'zone' to be valid
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_EXISTS
+ * \li #ISC_R_NOSPACE
+ * \li #ISC_R_NOMEMORY
+ */
+
+isc_result_t
+dns_zt_unmount(dns_zt_t *zt, dns_zone_t *zone);
+/*%<
+ * Unmount the given zone from the table.
+ *
+ * Requires:
+ * 'zt' to be valid
+ * \li 'zone' to be valid
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS
+ * \li #ISC_R_NOTFOUND
+ * \li #ISC_R_NOMEMORY
+ */
+
+isc_result_t
+dns_zt_find(dns_zt_t *zt, dns_name_t *name, unsigned int options,
+ dns_name_t *foundname, dns_zone_t **zone);
+/*%<
+ * Find the best match for 'name' in 'zt'. If foundname is non NULL
+ * then the name of the zone found is returned.
+ *
+ * Notes:
+ * \li If the DNS_ZTFIND_NOEXACT is set, the best partial match (if any)
+ * to 'name' will be returned.
+ *
+ * Requires:
+ * \li 'zt' to be valid
+ * \li 'name' to be valid
+ * \li 'foundname' to be initialized and associated with a fixedname or NULL
+ * \li 'zone' to be non NULL and '*zone' to be NULL
+ *
+ * Returns:
+ * \li #ISC_R_SUCCESS
+ * \li #DNS_R_PARTIALMATCH
+ * \li #ISC_R_NOTFOUND
+ * \li #ISC_R_NOSPACE
+ */
+
+void
+dns_zt_detach(dns_zt_t **ztp);
+/*%<
+ * Detach the given zonetable, if the reference count goes to zero the
+ * zonetable will be freed. In either case 'ztp' is set to NULL.
+ *
+ * Requires:
+ * \li '*ztp' to be valid
+ */
+
+void
+dns_zt_flushanddetach(dns_zt_t **ztp);
+/*%<
+ * Detach the given zonetable, if the reference count goes to zero the
+ * zonetable will be flushed and then freed. In either case 'ztp' is
+ * set to NULL.
+ *
+ * Requires:
+ * \li '*ztp' to be valid
+ */
+
+void
+dns_zt_attach(dns_zt_t *zt, dns_zt_t **ztp);
+/*%<
+ * Attach 'zt' to '*ztp'.
+ *
+ * Requires:
+ * \li 'zt' to be valid
+ * \li '*ztp' to be NULL
+ */
+
+isc_result_t
+dns_zt_load(dns_zt_t *zt, isc_boolean_t stop);
+
+isc_result_t
+dns_zt_loadnew(dns_zt_t *zt, isc_boolean_t stop);
+/*%<
+ * Load all zones in the table. If 'stop' is ISC_TRUE,
+ * stop on the first error and return it. If 'stop'
+ * is ISC_FALSE, ignore errors.
+ *
+ * dns_zt_loadnew() only loads zones that are not yet loaded.
+ * dns_zt_load() also loads zones that are already loaded and
+ * and whose master file has changed since the last load.
+ *
+ * Requires:
+ * \li 'zt' to be valid
+ */
+
+isc_result_t
+dns_zt_freezezones(dns_zt_t *zt, isc_boolean_t freeze);
+/*%<
+ * Freeze/thaw updates to master zones.
+ * Any pending updates will be flushed.
+ * Zones will be reloaded on thaw.
+ */
+
+isc_result_t
+dns_zt_apply(dns_zt_t *zt, isc_boolean_t stop,
+ isc_result_t (*action)(dns_zone_t *, void *), void *uap);
+
+isc_result_t
+dns_zt_apply2(dns_zt_t *zt, isc_boolean_t stop, isc_result_t *sub,
+ isc_result_t (*action)(dns_zone_t *, void *), void *uap);
+/*%<
+ * Apply a given 'action' to all zone zones in the table.
+ * If 'stop' is 'ISC_TRUE' then walking the zone tree will stop if
+ * 'action' does not return ISC_R_SUCCESS.
+ *
+ * Requires:
+ * \li 'zt' to be valid.
+ * \li 'action' to be non NULL.
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS if action was applied to all nodes. If 'stop' is
+ * ISC_FALSE and 'sub' is non NULL then the first error (if any)
+ * reported by 'action' is returned in '*sub';
+ * any error code from 'action'.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_ZT_H */
diff --git a/lib/dns/include/dst/Makefile.in b/lib/dns/include/dst/Makefile.in
new file mode 100644
index 0000000..4ed4ec0
--- /dev/null
+++ b/lib/dns/include/dst/Makefile.in
@@ -0,0 +1,37 @@
+# Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 1998-2001 Internet Software Consortium.
+#
+# 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 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.
+
+# $Id: Makefile.in,v 1.4 2007/12/11 20:28:55 marka Exp $
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+top_srcdir = @top_srcdir@
+
+@BIND9_VERSION@
+
+HEADERS = dst.h gssapi.h lib.h result.h
+
+SUBDIRS =
+TARGETS =
+
+@BIND9_MAKE_RULES@
+
+installdirs:
+ $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${includedir}/dst
+
+install:: installdirs
+ for i in ${HEADERS}; do \
+ ${INSTALL_DATA} ${srcdir}/$$i ${DESTDIR}${includedir}/dst ; \
+ done
diff --git a/lib/dns/include/dst/dst.h b/lib/dns/include/dst/dst.h
new file mode 100644
index 0000000..702ad71
--- /dev/null
+++ b/lib/dns/include/dst/dst.h
@@ -0,0 +1,642 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: dst.h,v 1.12 2008/09/24 02:46:23 marka Exp $ */
+
+#ifndef DST_DST_H
+#define DST_DST_H 1
+
+/*! \file dst/dst.h */
+
+#include <isc/lang.h>
+
+#include <dns/types.h>
+
+#include <dst/gssapi.h>
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Types
+ ***/
+
+/*%
+ * The dst_key structure is opaque. Applications should use the accessor
+ * functions provided to retrieve key attributes. If an application needs
+ * to set attributes, new accessor functions will be written.
+ */
+
+typedef struct dst_key dst_key_t;
+typedef struct dst_context dst_context_t;
+
+/* DST algorithm codes */
+#define DST_ALG_UNKNOWN 0
+#define DST_ALG_RSAMD5 1
+#define DST_ALG_RSA DST_ALG_RSAMD5 /*%< backwards compatibility */
+#define DST_ALG_DH 2
+#define DST_ALG_DSA 3
+#define DST_ALG_ECC 4
+#define DST_ALG_RSASHA1 5
+#define DST_ALG_NSEC3DSA 6
+#define DST_ALG_NSEC3RSASHA1 7
+#define DST_ALG_HMACMD5 157
+#define DST_ALG_GSSAPI 160
+#define DST_ALG_HMACSHA1 161 /* XXXMPA */
+#define DST_ALG_HMACSHA224 162 /* XXXMPA */
+#define DST_ALG_HMACSHA256 163 /* XXXMPA */
+#define DST_ALG_HMACSHA384 164 /* XXXMPA */
+#define DST_ALG_HMACSHA512 165 /* XXXMPA */
+#define DST_ALG_PRIVATE 254
+#define DST_ALG_EXPAND 255
+#define DST_MAX_ALGS 255
+
+/*% A buffer of this size is large enough to hold any key */
+#define DST_KEY_MAXSIZE 1280
+
+/*%
+ * A buffer of this size is large enough to hold the textual representation
+ * of any key
+ */
+#define DST_KEY_MAXTEXTSIZE 2048
+
+/*% 'Type' for dst_read_key() */
+#define DST_TYPE_KEY 0x1000000 /* KEY key */
+#define DST_TYPE_PRIVATE 0x2000000
+#define DST_TYPE_PUBLIC 0x4000000
+
+/***
+ *** Functions
+ ***/
+
+isc_result_t
+dst_lib_init(isc_mem_t *mctx, isc_entropy_t *ectx, unsigned int eflags);
+/*%<
+ * Initializes the DST subsystem.
+ *
+ * Requires:
+ * \li "mctx" is a valid memory context
+ * \li "ectx" is a valid entropy context
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li ISC_R_NOMEMORY
+ *
+ * Ensures:
+ * \li DST is properly initialized.
+ */
+
+void
+dst_lib_destroy(void);
+/*%<
+ * Releases all resources allocated by DST.
+ */
+
+isc_boolean_t
+dst_algorithm_supported(unsigned int alg);
+/*%<
+ * Checks that a given algorithm is supported by DST.
+ *
+ * Returns:
+ * \li ISC_TRUE
+ * \li ISC_FALSE
+ */
+
+isc_result_t
+dst_context_create(dst_key_t *key, isc_mem_t *mctx, dst_context_t **dctxp);
+/*%<
+ * Creates a context to be used for a sign or verify operation.
+ *
+ * Requires:
+ * \li "key" is a valid key.
+ * \li "mctx" is a valid memory context.
+ * \li dctxp != NULL && *dctxp == NULL
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li ISC_R_NOMEMORY
+ *
+ * Ensures:
+ * \li *dctxp will contain a usable context.
+ */
+
+void
+dst_context_destroy(dst_context_t **dctxp);
+/*%<
+ * Destroys all memory associated with a context.
+ *
+ * Requires:
+ * \li *dctxp != NULL && *dctxp == NULL
+ *
+ * Ensures:
+ * \li *dctxp == NULL
+ */
+
+isc_result_t
+dst_context_adddata(dst_context_t *dctx, const isc_region_t *data);
+/*%<
+ * Incrementally adds data to the context to be used in a sign or verify
+ * operation.
+ *
+ * Requires:
+ * \li "dctx" is a valid context
+ * \li "data" is a valid region
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li DST_R_SIGNFAILURE
+ * \li all other errors indicate failure
+ */
+
+isc_result_t
+dst_context_sign(dst_context_t *dctx, isc_buffer_t *sig);
+/*%<
+ * Computes a signature using the data and key stored in the context.
+ *
+ * Requires:
+ * \li "dctx" is a valid context.
+ * \li "sig" is a valid buffer.
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li DST_R_VERIFYFAILURE
+ * \li all other errors indicate failure
+ *
+ * Ensures:
+ * \li "sig" will contain the signature
+ */
+
+isc_result_t
+dst_context_verify(dst_context_t *dctx, isc_region_t *sig);
+/*%<
+ * Verifies the signature using the data and key stored in the context.
+ *
+ * Requires:
+ * \li "dctx" is a valid context.
+ * \li "sig" is a valid region.
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li all other errors indicate failure
+ *
+ * Ensures:
+ * \li "sig" will contain the signature
+ */
+
+isc_result_t
+dst_key_computesecret(const dst_key_t *pub, const dst_key_t *priv,
+ isc_buffer_t *secret);
+/*%<
+ * Computes a shared secret from two (Diffie-Hellman) keys.
+ *
+ * Requires:
+ * \li "pub" is a valid key that can be used to derive a shared secret
+ * \li "priv" is a valid private key that can be used to derive a shared secret
+ * \li "secret" is a valid buffer
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li any other result indicates failure
+ *
+ * Ensures:
+ * \li If successful, secret will contain the derived shared secret.
+ */
+
+isc_result_t
+dst_key_fromfile(dns_name_t *name, dns_keytag_t id, unsigned int alg, int type,
+ const char *directory, isc_mem_t *mctx, dst_key_t **keyp);
+/*%<
+ * Reads a key from permanent storage. The key can either be a public or
+ * private key, and is specified by name, algorithm, and id. If a private key
+ * is specified, the public key must also be present. If directory is NULL,
+ * the current directory is assumed.
+ *
+ * Requires:
+ * \li "name" is a valid absolute dns name.
+ * \li "id" is a valid key tag identifier.
+ * \li "alg" is a supported key algorithm.
+ * \li "type" is DST_TYPE_PUBLIC, DST_TYPE_PRIVATE, or the bitwise union.
+ * DST_TYPE_KEY look for a KEY record otherwise DNSKEY
+ * \li "mctx" is a valid memory context.
+ * \li "keyp" is not NULL and "*keyp" is NULL.
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li any other result indicates failure
+ *
+ * Ensures:
+ * \li If successful, *keyp will contain a valid key.
+ */
+
+isc_result_t
+dst_key_fromnamedfile(const char *filename, int type, isc_mem_t *mctx,
+ dst_key_t **keyp);
+/*%<
+ * Reads a key from permanent storage. The key can either be a public or
+ * key, and is specified by filename. If a private key is specified, the
+ * public key must also be present.
+ *
+ * Requires:
+ * \li "filename" is not NULL
+ * \li "type" is DST_TYPE_PUBLIC, DST_TYPE_PRIVATE, or the bitwise union
+ * DST_TYPE_KEY look for a KEY record otherwise DNSKEY
+ * \li "mctx" is a valid memory context
+ * \li "keyp" is not NULL and "*keyp" is NULL.
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li any other result indicates failure
+ *
+ * Ensures:
+ * \li If successful, *keyp will contain a valid key.
+ */
+
+
+isc_result_t
+dst_key_read_public(const char *filename, int type,
+ isc_mem_t *mctx, dst_key_t **keyp);
+/*%<
+ * Reads a public key from permanent storage. The key must be a public key.
+ *
+ * Requires:
+ * \li "filename" is not NULL
+ * \li "type" is DST_TYPE_KEY look for a KEY record otherwise DNSKEY
+ * \li "mctx" is a valid memory context
+ * \li "keyp" is not NULL and "*keyp" is NULL.
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li DST_R_BADKEYTYPE if the key type is not the expected one
+ * \li ISC_R_UNEXPECTEDTOKEN if the file can not be parsed as a public key
+ * \li any other result indicates failure
+ *
+ * Ensures:
+ * \li If successful, *keyp will contain a valid key.
+ */
+
+isc_result_t
+dst_key_tofile(const dst_key_t *key, int type, const char *directory);
+/*%<
+ * Writes a key to permanent storage. The key can either be a public or
+ * private key. Public keys are written in DNS format and private keys
+ * are written as a set of base64 encoded values. If directory is NULL,
+ * the current directory is assumed.
+ *
+ * Requires:
+ * \li "key" is a valid key.
+ * \li "type" is DST_TYPE_PUBLIC, DST_TYPE_PRIVATE, or the bitwise union
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li any other result indicates failure
+ */
+
+isc_result_t
+dst_key_fromdns(dns_name_t *name, dns_rdataclass_t rdclass,
+ isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp);
+/*%<
+ * Converts a DNS KEY record into a DST key.
+ *
+ * Requires:
+ * \li "name" is a valid absolute dns name.
+ * \li "source" is a valid buffer. There must be at least 4 bytes available.
+ * \li "mctx" is a valid memory context.
+ * \li "keyp" is not NULL and "*keyp" is NULL.
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li any other result indicates failure
+ *
+ * Ensures:
+ * \li If successful, *keyp will contain a valid key, and the consumed
+ * pointer in data will be advanced.
+ */
+
+isc_result_t
+dst_key_todns(const dst_key_t *key, isc_buffer_t *target);
+/*%<
+ * Converts a DST key into a DNS KEY record.
+ *
+ * Requires:
+ * \li "key" is a valid key.
+ * \li "target" is a valid buffer. There must be at least 4 bytes unused.
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li any other result indicates failure
+ *
+ * Ensures:
+ * \li If successful, the used pointer in 'target' is advanced by at least 4.
+ */
+
+isc_result_t
+dst_key_frombuffer(dns_name_t *name, unsigned int alg,
+ unsigned int flags, unsigned int protocol,
+ dns_rdataclass_t rdclass,
+ isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp);
+/*%<
+ * Converts a buffer containing DNS KEY RDATA into a DST key.
+ *
+ * Requires:
+ *\li "name" is a valid absolute dns name.
+ *\li "alg" is a supported key algorithm.
+ *\li "source" is a valid buffer.
+ *\li "mctx" is a valid memory context.
+ *\li "keyp" is not NULL and "*keyp" is NULL.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ * \li any other result indicates failure
+ *
+ * Ensures:
+ *\li If successful, *keyp will contain a valid key, and the consumed
+ * pointer in source will be advanced.
+ */
+
+isc_result_t
+dst_key_tobuffer(const dst_key_t *key, isc_buffer_t *target);
+/*%<
+ * Converts a DST key into DNS KEY RDATA format.
+ *
+ * Requires:
+ *\li "key" is a valid key.
+ *\li "target" is a valid buffer.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ * \li any other result indicates failure
+ *
+ * Ensures:
+ *\li If successful, the used pointer in 'target' is advanced.
+ */
+
+isc_result_t
+dst_key_privatefrombuffer(dst_key_t *key, isc_buffer_t *buffer);
+/*%<
+ * Converts a public key into a private key, reading the private key
+ * information from the buffer. The buffer should contain the same data
+ * as the .private key file would.
+ *
+ * Requires:
+ *\li "key" is a valid public key.
+ *\li "buffer" is not NULL.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ * \li any other result indicates failure
+ *
+ * Ensures:
+ *\li If successful, key will contain a valid private key.
+ */
+
+gss_ctx_id_t
+dst_key_getgssctx(const dst_key_t *key);
+/*%<
+ * Returns the opaque key data.
+ * Be cautions when using this value unless you know what you are doing.
+ *
+ * Requires:
+ *\li "key" is not NULL.
+ *
+ * Returns:
+ *\li gssctx key data, possibly NULL.
+ */
+
+isc_result_t
+dst_key_fromgssapi(dns_name_t *name, gss_ctx_id_t gssctx, isc_mem_t *mctx,
+ dst_key_t **keyp);
+/*%<
+ * Converts a GSSAPI opaque context id into a DST key.
+ *
+ * Requires:
+ *\li "name" is a valid absolute dns name.
+ *\li "gssctx" is a GSSAPI context id.
+ *\li "mctx" is a valid memory context.
+ *\li "keyp" is not NULL and "*keyp" is NULL.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ * \li any other result indicates failure
+ *
+ * Ensures:
+ *\li If successful, *keyp will contain a valid key and be responsible for
+ * the context id.
+ */
+
+isc_result_t
+dst_key_fromlabel(dns_name_t *name, int alg, unsigned int flags,
+ unsigned int protocol, dns_rdataclass_t rdclass,
+ const char *engine, const char *label, const char *pin,
+ isc_mem_t *mctx, dst_key_t **keyp);
+
+isc_result_t
+dst_key_generate(dns_name_t *name, unsigned int alg,
+ unsigned int bits, unsigned int param,
+ unsigned int flags, unsigned int protocol,
+ dns_rdataclass_t rdclass,
+ isc_mem_t *mctx, dst_key_t **keyp);
+/*%<
+ * Generate a DST key (or keypair) with the supplied parameters. The
+ * interpretation of the "param" field depends on the algorithm:
+ * \code
+ * RSA: exponent
+ * 0 use exponent 3
+ * !0 use Fermat4 (2^16 + 1)
+ * DH: generator
+ * 0 default - use well known prime if bits == 768 or 1024,
+ * otherwise use 2 as the generator.
+ * !0 use this value as the generator.
+ * DSA: unused
+ * HMACMD5: entropy
+ * 0 default - require good entropy
+ * !0 lack of good entropy is ok
+ *\endcode
+ *
+ * Requires:
+ *\li "name" is a valid absolute dns name.
+ *\li "keyp" is not NULL and "*keyp" is NULL.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ * \li any other result indicates failure
+ *
+ * Ensures:
+ *\li If successful, *keyp will contain a valid key.
+ */
+
+isc_boolean_t
+dst_key_compare(const dst_key_t *key1, const dst_key_t *key2);
+/*%<
+ * Compares two DST keys.
+ *
+ * Requires:
+ *\li "key1" is a valid key.
+ *\li "key2" is a valid key.
+ *
+ * Returns:
+ *\li ISC_TRUE
+ * \li ISC_FALSE
+ */
+
+isc_boolean_t
+dst_key_paramcompare(const dst_key_t *key1, const dst_key_t *key2);
+/*%<
+ * Compares the parameters of two DST keys. This is used to determine if
+ * two (Diffie-Hellman) keys can be used to derive a shared secret.
+ *
+ * Requires:
+ *\li "key1" is a valid key.
+ *\li "key2" is a valid key.
+ *
+ * Returns:
+ *\li ISC_TRUE
+ * \li ISC_FALSE
+ */
+
+void
+dst_key_free(dst_key_t **keyp);
+/*%<
+ * Release all memory associated with the key.
+ *
+ * Requires:
+ *\li "keyp" is not NULL and "*keyp" is a valid key.
+ *
+ * Ensures:
+ *\li All memory associated with "*keyp" will be freed.
+ *\li *keyp == NULL
+ */
+
+/*%<
+ * Accessor functions to obtain key fields.
+ *
+ * Require:
+ *\li "key" is a valid key.
+ */
+dns_name_t *
+dst_key_name(const dst_key_t *key);
+
+unsigned int
+dst_key_size(const dst_key_t *key);
+
+unsigned int
+dst_key_proto(const dst_key_t *key);
+
+unsigned int
+dst_key_alg(const dst_key_t *key);
+
+isc_uint32_t
+dst_key_flags(const dst_key_t *key);
+
+dns_keytag_t
+dst_key_id(const dst_key_t *key);
+
+dns_rdataclass_t
+dst_key_class(const dst_key_t *key);
+
+isc_boolean_t
+dst_key_isprivate(const dst_key_t *key);
+
+isc_boolean_t
+dst_key_iszonekey(const dst_key_t *key);
+
+isc_boolean_t
+dst_key_isnullkey(const dst_key_t *key);
+
+isc_result_t
+dst_key_buildfilename(const dst_key_t *key, int type,
+ const char *directory, isc_buffer_t *out);
+/*%<
+ * Generates the filename used by dst to store the specified key.
+ * If directory is NULL, the current directory is assumed.
+ *
+ * Requires:
+ *\li "key" is a valid key
+ *\li "type" is either DST_TYPE_PUBLIC, DST_TYPE_PRIVATE, or 0 for no suffix.
+ *\li "out" is a valid buffer
+ *
+ * Ensures:
+ *\li the file name will be written to "out", and the used pointer will
+ * be advanced.
+ */
+
+isc_result_t
+dst_key_sigsize(const dst_key_t *key, unsigned int *n);
+/*%<
+ * Computes the size of a signature generated by the given key.
+ *
+ * Requires:
+ *\li "key" is a valid key.
+ *\li "n" is not NULL
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li DST_R_UNSUPPORTEDALG
+ *
+ * Ensures:
+ *\li "n" stores the size of a generated signature
+ */
+
+isc_result_t
+dst_key_secretsize(const dst_key_t *key, unsigned int *n);
+/*%<
+ * Computes the size of a shared secret generated by the given key.
+ *
+ * Requires:
+ *\li "key" is a valid key.
+ *\li "n" is not NULL
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li DST_R_UNSUPPORTEDALG
+ *
+ * Ensures:
+ *\li "n" stores the size of a generated shared secret
+ */
+
+isc_uint16_t
+dst_region_computeid(const isc_region_t *source, unsigned int alg);
+/*%<
+ * Computes the key id of the key stored in the provided region with the
+ * given algorithm.
+ *
+ * Requires:
+ *\li "source" contains a valid, non-NULL region.
+ *
+ * Returns:
+ *\li the key id
+ */
+
+isc_uint16_t
+dst_key_getbits(const dst_key_t *key);
+/*
+ * Get the number of digest bits required (0 == MAX).
+ *
+ * Requires:
+ * "key" is a valid key.
+ */
+
+void
+dst_key_setbits(dst_key_t *key, isc_uint16_t bits);
+/*
+ * Set the number of digest bits required (0 == MAX).
+ *
+ * Requires:
+ * "key" is a valid key.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DST_DST_H */
diff --git a/lib/dns/include/dst/gssapi.h b/lib/dns/include/dst/gssapi.h
new file mode 100644
index 0000000..7d1d487
--- /dev/null
+++ b/lib/dns/include/dst/gssapi.h
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: gssapi.h,v 1.9 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef DST_GSSAPI_H
+#define DST_GSSAPI_H 1
+
+/*! \file dst/gssapi.h */
+
+#include <isc/formatcheck.h>
+#include <isc/lang.h>
+#include <isc/platform.h>
+#include <isc/types.h>
+#include <dns/types.h>
+
+#ifdef GSSAPI
+#ifdef _WINDOWS
+/*
+ * MSVC does not like macros in #include lines.
+ */
+#include <gssapi/gssapi.h>
+#else
+#include ISC_PLATFORM_GSSAPIHEADER
+#endif
+#ifndef GSS_SPNEGO_MECHANISM
+#define GSS_SPNEGO_MECHANISM ((void*)0)
+#endif
+#endif
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Types
+ ***/
+
+/***
+ *** Functions
+ ***/
+
+isc_result_t
+dst_gssapi_acquirecred(dns_name_t *name, isc_boolean_t initiate,
+ gss_cred_id_t *cred);
+/*
+ * Acquires GSS credentials.
+ *
+ * Requires:
+ * 'name' is a valid name, preferably one known by the GSS provider
+ * 'initiate' indicates whether the credentials are for initiating or
+ * accepting contexts
+ * 'cred' is a pointer to NULL, which will be allocated with the
+ * credential handle. Call dst_gssapi_releasecred to free
+ * the memory.
+ *
+ * Returns:
+ * ISC_R_SUCCESS msg was successfully updated to include the
+ * query to be sent
+ * other an error occurred while building the message
+ */
+
+isc_result_t
+dst_gssapi_releasecred(gss_cred_id_t *cred);
+/*
+ * Releases GSS credentials. Calling this function does release the
+ * memory allocated for the credential in dst_gssapi_acquirecred()
+ *
+ * Requires:
+ * 'mctx' is a valid memory context
+ * 'cred' is a pointer to the credential to be released
+ *
+ * Returns:
+ * ISC_R_SUCCESS credential was released successfully
+ * other an error occurred while releaseing
+ * the credential
+ */
+
+isc_result_t
+dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken,
+ isc_buffer_t *outtoken, gss_ctx_id_t *gssctx);
+/*
+ * Initiates a GSS context.
+ *
+ * Requires:
+ * 'name' is a valid name, preferably one known by the GSS
+ * provider
+ * 'intoken' is a token received from the acceptor, or NULL if
+ * there isn't one
+ * 'outtoken' is a buffer to receive the token generated by
+ * gss_init_sec_context() to be sent to the acceptor
+ * 'context' is a pointer to a valid gss_ctx_id_t
+ * (which may have the value GSS_C_NO_CONTEXT)
+ *
+ * Returns:
+ * ISC_R_SUCCESS msg was successfully updated to include the
+ * query to be sent
+ * other an error occurred while building the message
+ */
+
+isc_result_t
+dst_gssapi_acceptctx(gss_cred_id_t cred,
+ isc_region_t *intoken, isc_buffer_t **outtoken,
+ gss_ctx_id_t *context, dns_name_t *principal,
+ isc_mem_t *mctx);
+/*
+ * Accepts a GSS context.
+ *
+ * Requires:
+ * 'mctx' is a valid memory context
+ * 'cred' is the acceptor's valid GSS credential handle
+ * 'intoken' is a token received from the initiator
+ * 'outtoken' is a pointer a buffer pointer used to return the token
+ * generated by gss_accept_sec_context() to be sent to the
+ * initiator
+ * 'context' is a valid pointer to receive the generated context handle.
+ * On the initial call, it should be a pointer to NULL, which
+ * will be allocated as a gss_ctx_id_t. Subsequent calls
+ * should pass in the handle generated on the first call.
+ * Call dst_gssapi_releasecred to delete the context and free
+ * the memory.
+ *
+ * Requires:
+ * 'outtoken' to != NULL && *outtoken == NULL.
+ *
+ * Returns:
+ * ISC_R_SUCCESS msg was successfully updated to include the
+ * query to be sent
+ * other an error occurred while building the message
+ */
+
+isc_result_t
+dst_gssapi_deletectx(isc_mem_t *mctx, gss_ctx_id_t *gssctx);
+/*
+ * Destroys a GSS context. This function deletes the context from the GSS
+ * provider and then frees the memory used by the context pointer.
+ *
+ * Requires:
+ * 'mctx' is a valid memory context
+ * 'context' is a valid GSS context
+ *
+ * Returns:
+ * ISC_R_SUCCESS
+ */
+
+
+void
+gss_log(int level, const char *fmt, ...)
+ISC_FORMAT_PRINTF(2, 3);
+/*
+ * Loging function for GSS.
+ *
+ * Requires
+ * 'level' is the log level to be used, as an integer
+ * 'fmt' is a printf format specifier
+ */
+
+char *
+gss_error_tostring(isc_uint32_t major, isc_uint32_t minor,
+ char *buf, size_t buflen);
+/*
+ * Render a GSS major status/minor status pair into a string
+ *
+ * Requires:
+ * 'major' is a GSS major status code
+ * 'minor' is a GSS minor status code
+ *
+ * Returns:
+ * A string containing the text representation of the error codes.
+ * Users should copy the string if they wish to keep it.
+ */
+
+isc_boolean_t
+dst_gssapi_identitymatchesrealmkrb5(dns_name_t *signer, dns_name_t *name,
+ dns_name_t *realm);
+/*
+ * Compare a "signer" (in the format of a Kerberos-format Kerberos5
+ * printipal: host/example.com@EXAMPLE.COM) to the realm name stored
+ * in "name" (which represents the realm name).
+ *
+ */
+
+isc_boolean_t
+dst_gssapi_identitymatchesrealmms(dns_name_t *signer, dns_name_t *name,
+ dns_name_t *realm);
+/*
+ * Compare a "signer" (in the format of a Kerberos-format Kerberos5
+ * printipal: host/example.com@EXAMPLE.COM) to the realm name stored
+ * in "name" (which represents the realm name).
+ *
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DST_GSSAPI_H */
diff --git a/lib/dns/include/dst/lib.h b/lib/dns/include/dst/lib.h
new file mode 100644
index 0000000..886575e
--- /dev/null
+++ b/lib/dns/include/dst/lib.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: lib.h,v 1.7 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef DST_LIB_H
+#define DST_LIB_H 1
+
+/*! \file dst/lib.h */
+
+#include <isc/types.h>
+#include <isc/lang.h>
+
+ISC_LANG_BEGINDECLS
+
+LIBDNS_EXTERNAL_DATA extern isc_msgcat_t *dst_msgcat;
+
+void
+dst_lib_initmsgcat(void);
+/*
+ * Initialize the DST library's message catalog, dst_msgcat, if it
+ * has not already been initialized.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DST_LIB_H */
diff --git a/lib/dns/include/dst/result.h b/lib/dns/include/dst/result.h
new file mode 100644
index 0000000..d77b72e
--- /dev/null
+++ b/lib/dns/include/dst/result.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: result.h,v 1.9 2008/04/01 23:47:10 tbox Exp $ */
+
+#ifndef DST_RESULT_H
+#define DST_RESULT_H 1
+
+/*! \file dst/result.h */
+
+#include <isc/lang.h>
+#include <isc/resultclass.h>
+
+/*
+ * Nothing in this file truly depends on <isc/result.h>, but the
+ * DST result codes are considered to be publicly derived from
+ * the ISC result codes, so including this file buys you the ISC_R_
+ * namespace too.
+ */
+#include <isc/result.h> /* Contractual promise. */
+
+#define DST_R_UNSUPPORTEDALG (ISC_RESULTCLASS_DST + 0)
+#define DST_R_OPENSSLFAILURE (ISC_RESULTCLASS_DST + 1)
+#define DST_R_NOCRYPTO (ISC_RESULTCLASS_DST + 2)
+#define DST_R_NULLKEY (ISC_RESULTCLASS_DST + 3)
+#define DST_R_INVALIDPUBLICKEY (ISC_RESULTCLASS_DST + 4)
+#define DST_R_INVALIDPRIVATEKEY (ISC_RESULTCLASS_DST + 5)
+/* 6 is unused */
+#define DST_R_WRITEERROR (ISC_RESULTCLASS_DST + 7)
+#define DST_R_INVALIDPARAM (ISC_RESULTCLASS_DST + 8)
+/* 9 is unused */
+/* 10 is unused */
+#define DST_R_SIGNFAILURE (ISC_RESULTCLASS_DST + 11)
+/* 12 is unused */
+/* 13 is unused */
+#define DST_R_VERIFYFAILURE (ISC_RESULTCLASS_DST + 14)
+#define DST_R_NOTPUBLICKEY (ISC_RESULTCLASS_DST + 15)
+#define DST_R_NOTPRIVATEKEY (ISC_RESULTCLASS_DST + 16)
+#define DST_R_KEYCANNOTCOMPUTESECRET (ISC_RESULTCLASS_DST + 17)
+#define DST_R_COMPUTESECRETFAILURE (ISC_RESULTCLASS_DST + 18)
+#define DST_R_NORANDOMNESS (ISC_RESULTCLASS_DST + 19)
+#define DST_R_BADKEYTYPE (ISC_RESULTCLASS_DST + 20)
+#define DST_R_NOENGINE (ISC_RESULTCLASS_DST + 21)
+
+#define DST_R_NRESULTS 22 /* Number of results */
+
+ISC_LANG_BEGINDECLS
+
+const char *
+dst_result_totext(isc_result_t);
+
+void
+dst_result_register(void);
+
+ISC_LANG_ENDDECLS
+
+#endif /* DST_RESULT_H */
diff --git a/lib/dns/iptable.c b/lib/dns/iptable.c
new file mode 100644
index 0000000..5e9c63f
--- /dev/null
+++ b/lib/dns/iptable.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 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.
+ */
+
+/* $Id: iptable.c,v 1.12 2008/09/26 21:12:02 each Exp $ */
+
+#include <isc/mem.h>
+#include <isc/radix.h>
+
+#include <dns/acl.h>
+
+static void destroy_iptable(dns_iptable_t *dtab);
+
+/*
+ * Create a new IP table and the underlying radix structure
+ */
+isc_result_t
+dns_iptable_create(isc_mem_t *mctx, dns_iptable_t **target) {
+ isc_result_t result;
+ dns_iptable_t *tab;
+
+ tab = isc_mem_get(mctx, sizeof(*tab));
+ if (tab == NULL)
+ return (ISC_R_NOMEMORY);
+ tab->mctx = mctx;
+ isc_refcount_init(&tab->refcount, 1);
+ tab->magic = DNS_IPTABLE_MAGIC;
+
+ result = isc_radix_create(mctx, &tab->radix, RADIX_MAXBITS);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ *target = tab;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ dns_iptable_detach(&tab);
+ return (result);
+}
+
+isc_boolean_t dns_iptable_neg = ISC_FALSE;
+isc_boolean_t dns_iptable_pos = ISC_TRUE;
+
+/*
+ * Add an IP prefix to an existing IP table
+ */
+isc_result_t
+dns_iptable_addprefix(dns_iptable_t *tab, isc_netaddr_t *addr,
+ isc_uint16_t bitlen, isc_boolean_t pos)
+{
+ isc_result_t result;
+ isc_prefix_t pfx;
+ isc_radix_node_t *node = NULL;
+ int family;
+
+ INSIST(DNS_IPTABLE_VALID(tab));
+ INSIST(tab->radix);
+
+ NETADDR_TO_PREFIX_T(addr, pfx, bitlen);
+
+ result = isc_radix_insert(tab->radix, &node, NULL, &pfx);
+ if (result != ISC_R_SUCCESS) {
+ isc_refcount_destroy(&pfx.refcount);
+ return(result);
+ }
+
+ /* If a node already contains data, don't overwrite it */
+ family = pfx.family;
+ if (family == AF_UNSPEC) {
+ /* "any" or "none" */
+ INSIST(pfx.bitlen == 0);
+ if (pos) {
+ if (node->data[0] == NULL)
+ node->data[0] = &dns_iptable_pos;
+ if (node->data[1] == NULL)
+ node->data[1] = &dns_iptable_pos;
+ } else {
+ if (node->data[0] == NULL)
+ node->data[0] = &dns_iptable_neg;
+ if (node->data[1] == NULL)
+ node->data[1] = &dns_iptable_neg;
+ }
+ } else {
+ /* any other prefix */
+ if (node->data[ISC_IS6(family)] == NULL) {
+ if (pos)
+ node->data[ISC_IS6(family)] = &dns_iptable_pos;
+ else
+ node->data[ISC_IS6(family)] = &dns_iptable_neg;
+ }
+ }
+
+ isc_refcount_destroy(&pfx.refcount);
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Merge one IP table into another one.
+ */
+isc_result_t
+dns_iptable_merge(dns_iptable_t *tab, dns_iptable_t *source, isc_boolean_t pos)
+{
+ isc_result_t result;
+ isc_radix_node_t *node, *new_node;
+ int max_node = 0;
+
+ RADIX_WALK (source->radix->head, node) {
+ new_node = NULL;
+ result = isc_radix_insert (tab->radix, &new_node, node, NULL);
+
+ if (result != ISC_R_SUCCESS)
+ return(result);
+
+ /*
+ * If we're negating a nested ACL, then we should
+ * reverse the sense of every node. However, this
+ * could lead to a negative node in a nested ACL
+ * becoming a positive match in the parent, which
+ * could be a security risk. To prevent this, we
+ * just leave the negative nodes negative.
+ */
+ if (!pos) {
+ if (node->data[0] &&
+ *(isc_boolean_t *) node->data[0] == ISC_TRUE)
+ new_node->data[0] = &dns_iptable_neg;
+
+ if (node->data[1] &&
+ *(isc_boolean_t *) node->data[1] == ISC_TRUE)
+ new_node->data[1] = &dns_iptable_neg;
+ }
+
+ if (node->node_num[0] > max_node)
+ max_node = node->node_num[0];
+ if (node->node_num[1] > max_node)
+ max_node = node->node_num[1];
+ } RADIX_WALK_END;
+
+ tab->radix->num_added_node += max_node;
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_iptable_attach(dns_iptable_t *source, dns_iptable_t **target) {
+ REQUIRE(DNS_IPTABLE_VALID(source));
+ isc_refcount_increment(&source->refcount, NULL);
+ *target = source;
+}
+
+void
+dns_iptable_detach(dns_iptable_t **tabp) {
+ dns_iptable_t *tab = *tabp;
+ unsigned int refs;
+ REQUIRE(DNS_IPTABLE_VALID(tab));
+ isc_refcount_decrement(&tab->refcount, &refs);
+ if (refs == 0)
+ destroy_iptable(tab);
+ *tabp = NULL;
+}
+
+static void
+destroy_iptable(dns_iptable_t *dtab) {
+
+ REQUIRE(DNS_IPTABLE_VALID(dtab));
+
+ if (dtab->radix != NULL) {
+ isc_radix_destroy(dtab->radix, NULL);
+ dtab->radix = NULL;
+ }
+
+ isc_refcount_destroy(&dtab->refcount);
+ dtab->magic = 0;
+ isc_mem_put(dtab->mctx, dtab, sizeof(*dtab));
+}
diff --git a/lib/dns/journal.c b/lib/dns/journal.c
new file mode 100644
index 0000000..e84ade3
--- /dev/null
+++ b/lib/dns/journal.c
@@ -0,0 +1,2217 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: journal.c,v 1.103 2008/09/25 02:01:45 marka Exp $ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <isc/file.h>
+#include <isc/mem.h>
+#include <isc/stdio.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/compress.h>
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/diff.h>
+#include <dns/fixedname.h>
+#include <dns/journal.h>
+#include <dns/log.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/result.h>
+#include <dns/soa.h>
+
+/*! \file
+ * \brief Journalling.
+ *
+ * A journal file consists of
+ *
+ * \li A fixed-size header of type journal_rawheader_t.
+ *
+ * \li The index. This is an unordered array of index entries
+ * of type journal_rawpos_t giving the locations
+ * of some arbitrary subset of the journal's addressable
+ * transactions. The index entries are used as hints to
+ * speed up the process of locating a transaction with a given
+ * serial number. Unused index entries have an "offset"
+ * field of zero. The size of the index can vary between
+ * journal files, but does not change during the lifetime
+ * of a file. The size can be zero.
+ *
+ * \li The journal data. This consists of one or more transactions.
+ * Each transaction begins with a transaction header of type
+ * journal_rawxhdr_t. The transaction header is followed by a
+ * sequence of RRs, similar in structure to an IXFR difference
+ * sequence (RFC1995). That is, the pre-transaction SOA,
+ * zero or more other deleted RRs, the post-transaction SOA,
+ * and zero or more other added RRs. Unlike in IXFR, each RR
+ * is prefixed with a 32-bit length.
+ *
+ * The journal data part grows as new transactions are
+ * appended to the file. Only those transactions
+ * whose serial number is current-(2^31-1) to current
+ * are considered "addressable" and may be pointed
+ * to from the header or index. They may be preceded
+ * by old transactions that are no longer addressable,
+ * and they may be followed by transactions that were
+ * appended to the journal but never committed by updating
+ * the "end" position in the header. The latter will
+ * be overwritten when new transactions are added.
+ */
+/*%
+ * When true, accept IXFR difference sequences where the
+ * SOA serial number does not change (BIND 8 sends such
+ * sequences).
+ */
+static isc_boolean_t bind8_compat = ISC_TRUE; /* XXX config */
+
+/**************************************************************************/
+/*
+ * Miscellaneous utilities.
+ */
+
+#define JOURNAL_COMMON_LOGARGS \
+ dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_JOURNAL
+
+#define JOURNAL_DEBUG_LOGARGS(n) \
+ JOURNAL_COMMON_LOGARGS, ISC_LOG_DEBUG(n)
+
+/*%
+ * It would be non-sensical (or at least obtuse) to use FAIL() with an
+ * ISC_R_SUCCESS code, but the test is there to keep the Solaris compiler
+ * from complaining about "end-of-loop code not reached".
+ */
+#define FAIL(code) \
+ do { result = (code); \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+
+#define CHECK(op) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+
+static isc_result_t index_to_disk(dns_journal_t *);
+
+static inline isc_uint32_t
+decode_uint32(unsigned char *p) {
+ return ((p[0] << 24) +
+ (p[1] << 16) +
+ (p[2] << 8) +
+ (p[3] << 0));
+}
+
+static inline void
+encode_uint32(isc_uint32_t val, unsigned char *p) {
+ p[0] = (isc_uint8_t)(val >> 24);
+ p[1] = (isc_uint8_t)(val >> 16);
+ p[2] = (isc_uint8_t)(val >> 8);
+ p[3] = (isc_uint8_t)(val >> 0);
+}
+
+isc_result_t
+dns_db_createsoatuple(dns_db_t *db, dns_dbversion_t *ver, isc_mem_t *mctx,
+ dns_diffop_t op, dns_difftuple_t **tp)
+{
+ isc_result_t result;
+ dns_dbnode_t *node;
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_name_t *zonename;
+
+ zonename = dns_db_origin(db);
+
+ node = NULL;
+ result = dns_db_findnode(db, zonename, ISC_FALSE, &node);
+ if (result != ISC_R_SUCCESS)
+ goto nonode;
+
+ dns_rdataset_init(&rdataset);
+ result = dns_db_findrdataset(db, node, ver, dns_rdatatype_soa, 0,
+ (isc_stdtime_t)0, &rdataset, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto freenode;
+
+ result = dns_rdataset_first(&rdataset);
+ if (result != ISC_R_SUCCESS)
+ goto freenode;
+
+ dns_rdataset_current(&rdataset, &rdata);
+
+ result = dns_difftuple_create(mctx, op, zonename, rdataset.ttl,
+ &rdata, tp);
+
+ dns_rdataset_disassociate(&rdataset);
+ dns_db_detachnode(db, &node);
+ return (ISC_R_SUCCESS);
+
+ freenode:
+ dns_db_detachnode(db, &node);
+ nonode:
+ UNEXPECTED_ERROR(__FILE__, __LINE__, "missing SOA");
+ return (result);
+}
+
+/* Journalling */
+
+/*%
+ * On-disk representation of a "pointer" to a journal entry.
+ * These are used in the journal header to locate the beginning
+ * and end of the journal, and in the journal index to locate
+ * other transactions.
+ */
+typedef struct {
+ unsigned char serial[4]; /*%< SOA serial before update. */
+ /*
+ * XXXRTH Should offset be 8 bytes?
+ * XXXDCL ... probably, since isc_offset_t is 8 bytes on many OSs.
+ * XXXAG ... but we will not be able to seek >2G anyway on many
+ * platforms as long as we are using fseek() rather
+ * than lseek().
+ */
+ unsigned char offset[4]; /*%< Offset from beginning of file. */
+} journal_rawpos_t;
+
+
+/*%
+ * The header is of a fixed size, with some spare room for future
+ * extensions.
+ */
+#define JOURNAL_HEADER_SIZE 64 /* Bytes. */
+
+/*%
+ * The on-disk representation of the journal header.
+ * All numbers are stored in big-endian order.
+ */
+typedef union {
+ struct {
+ /*% File format version ID. */
+ unsigned char format[16];
+ /*% Position of the first addressable transaction */
+ journal_rawpos_t begin;
+ /*% Position of the next (yet nonexistent) transaction. */
+ journal_rawpos_t end;
+ /*% Number of index entries following the header. */
+ unsigned char index_size[4];
+ } h;
+ /* Pad the header to a fixed size. */
+ unsigned char pad[JOURNAL_HEADER_SIZE];
+} journal_rawheader_t;
+
+/*%
+ * The on-disk representation of the transaction header.
+ * There is one of these at the beginning of each transaction.
+ */
+typedef struct {
+ unsigned char size[4]; /*%< In bytes, excluding header. */
+ unsigned char serial0[4]; /*%< SOA serial before update. */
+ unsigned char serial1[4]; /*%< SOA serial after update. */
+} journal_rawxhdr_t;
+
+/*%
+ * The on-disk representation of the RR header.
+ * There is one of these at the beginning of each RR.
+ */
+typedef struct {
+ unsigned char size[4]; /*%< In bytes, excluding header. */
+} journal_rawrrhdr_t;
+
+/*%
+ * The in-core representation of the journal header.
+ */
+typedef struct {
+ isc_uint32_t serial;
+ isc_offset_t offset;
+} journal_pos_t;
+
+#define POS_VALID(pos) ((pos).offset != 0)
+#define POS_INVALIDATE(pos) ((pos).offset = 0, (pos).serial = 0)
+
+typedef struct {
+ unsigned char format[16];
+ journal_pos_t begin;
+ journal_pos_t end;
+ isc_uint32_t index_size;
+} journal_header_t;
+
+/*%
+ * The in-core representation of the transaction header.
+ */
+
+typedef struct {
+ isc_uint32_t size;
+ isc_uint32_t serial0;
+ isc_uint32_t serial1;
+} journal_xhdr_t;
+
+/*%
+ * The in-core representation of the RR header.
+ */
+typedef struct {
+ isc_uint32_t size;
+} journal_rrhdr_t;
+
+
+/*%
+ * Initial contents to store in the header of a newly created
+ * journal file.
+ *
+ * The header starts with the magic string ";BIND LOG V9\n"
+ * to identify the file as a BIND 9 journal file. An ASCII
+ * identification string is used rather than a binary magic
+ * number to be consistent with BIND 8 (BIND 8 journal files
+ * are ASCII text files).
+ */
+
+static journal_header_t
+initial_journal_header = { ";BIND LOG V9\n", { 0, 0 }, { 0, 0 }, 0 };
+
+#define JOURNAL_EMPTY(h) ((h)->begin.offset == (h)->end.offset)
+
+typedef enum {
+ JOURNAL_STATE_INVALID,
+ JOURNAL_STATE_READ,
+ JOURNAL_STATE_WRITE,
+ JOURNAL_STATE_TRANSACTION
+} journal_state_t;
+
+struct dns_journal {
+ unsigned int magic; /*%< JOUR */
+ isc_mem_t *mctx; /*%< Memory context */
+ journal_state_t state;
+ const char *filename; /*%< Journal file name */
+ FILE * fp; /*%< File handle */
+ isc_offset_t offset; /*%< Current file offset */
+ journal_header_t header; /*%< In-core journal header */
+ unsigned char *rawindex; /*%< In-core buffer for journal index in on-disk format */
+ journal_pos_t *index; /*%< In-core journal index */
+
+ /*% Current transaction state (when writing). */
+ struct {
+ unsigned int n_soa; /*%< Number of SOAs seen */
+ journal_pos_t pos[2]; /*%< Begin/end position */
+ } x;
+
+ /*% Iteration state (when reading). */
+ struct {
+ /* These define the part of the journal we iterate over. */
+ journal_pos_t bpos; /*%< Position before first, */
+ journal_pos_t epos; /*%< and after last transaction */
+ /* The rest is iterator state. */
+ isc_uint32_t current_serial; /*%< Current SOA serial */
+ isc_buffer_t source; /*%< Data from disk */
+ isc_buffer_t target; /*%< Data from _fromwire check */
+ dns_decompress_t dctx; /*%< Dummy decompression ctx */
+ dns_name_t name; /*%< Current domain name */
+ dns_rdata_t rdata; /*%< Current rdata */
+ isc_uint32_t ttl; /*%< Current TTL */
+ unsigned int xsize; /*%< Size of transaction data */
+ unsigned int xpos; /*%< Current position in it */
+ isc_result_t result; /*%< Result of last call */
+ } it;
+};
+
+#define DNS_JOURNAL_MAGIC ISC_MAGIC('J', 'O', 'U', 'R')
+#define DNS_JOURNAL_VALID(t) ISC_MAGIC_VALID(t, DNS_JOURNAL_MAGIC)
+
+static void
+journal_pos_decode(journal_rawpos_t *raw, journal_pos_t *cooked) {
+ cooked->serial = decode_uint32(raw->serial);
+ cooked->offset = decode_uint32(raw->offset);
+}
+
+static void
+journal_pos_encode(journal_rawpos_t *raw, journal_pos_t *cooked) {
+ encode_uint32(cooked->serial, raw->serial);
+ encode_uint32(cooked->offset, raw->offset);
+}
+
+static void
+journal_header_decode(journal_rawheader_t *raw, journal_header_t *cooked) {
+ INSIST(sizeof(cooked->format) == sizeof(raw->h.format));
+ memcpy(cooked->format, raw->h.format, sizeof(cooked->format));
+ journal_pos_decode(&raw->h.begin, &cooked->begin);
+ journal_pos_decode(&raw->h.end, &cooked->end);
+ cooked->index_size = decode_uint32(raw->h.index_size);
+}
+
+static void
+journal_header_encode(journal_header_t *cooked, journal_rawheader_t *raw) {
+ INSIST(sizeof(cooked->format) == sizeof(raw->h.format));
+ memset(raw->pad, 0, sizeof(raw->pad));
+ memcpy(raw->h.format, cooked->format, sizeof(raw->h.format));
+ journal_pos_encode(&raw->h.begin, &cooked->begin);
+ journal_pos_encode(&raw->h.end, &cooked->end);
+ encode_uint32(cooked->index_size, raw->h.index_size);
+}
+
+/*
+ * Journal file I/O subroutines, with error checking and reporting.
+ */
+static isc_result_t
+journal_seek(dns_journal_t *j, isc_uint32_t offset) {
+ isc_result_t result;
+ result = isc_stdio_seek(j->fp, (long)offset, SEEK_SET);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "%s: seek: %s", j->filename,
+ isc_result_totext(result));
+ return (ISC_R_UNEXPECTED);
+ }
+ j->offset = offset;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+journal_read(dns_journal_t *j, void *mem, size_t nbytes) {
+ isc_result_t result;
+
+ result = isc_stdio_read(mem, 1, nbytes, j->fp, NULL);
+ if (result != ISC_R_SUCCESS) {
+ if (result == ISC_R_EOF)
+ return (ISC_R_NOMORE);
+ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "%s: read: %s",
+ j->filename, isc_result_totext(result));
+ return (ISC_R_UNEXPECTED);
+ }
+ j->offset += nbytes;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+journal_write(dns_journal_t *j, void *mem, size_t nbytes) {
+ isc_result_t result;
+
+ result = isc_stdio_write(mem, 1, nbytes, j->fp, NULL);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "%s: write: %s",
+ j->filename, isc_result_totext(result));
+ return (ISC_R_UNEXPECTED);
+ }
+ j->offset += nbytes;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+journal_fsync(dns_journal_t *j) {
+ isc_result_t result;
+ result = isc_stdio_flush(j->fp);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "%s: flush: %s",
+ j->filename, isc_result_totext(result));
+ return (ISC_R_UNEXPECTED);
+ }
+ result = isc_stdio_sync(j->fp);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "%s: fsync: %s",
+ j->filename, isc_result_totext(result));
+ return (ISC_R_UNEXPECTED);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Read/write a transaction header at the current file position.
+ */
+
+static isc_result_t
+journal_read_xhdr(dns_journal_t *j, journal_xhdr_t *xhdr) {
+ journal_rawxhdr_t raw;
+ isc_result_t result;
+ result = journal_read(j, &raw, sizeof(raw));
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ xhdr->size = decode_uint32(raw.size);
+ xhdr->serial0 = decode_uint32(raw.serial0);
+ xhdr->serial1 = decode_uint32(raw.serial1);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+journal_write_xhdr(dns_journal_t *j, isc_uint32_t size,
+ isc_uint32_t serial0, isc_uint32_t serial1)
+{
+ journal_rawxhdr_t raw;
+ encode_uint32(size, raw.size);
+ encode_uint32(serial0, raw.serial0);
+ encode_uint32(serial1, raw.serial1);
+ return (journal_write(j, &raw, sizeof(raw)));
+}
+
+
+/*
+ * Read an RR header at the current file position.
+ */
+
+static isc_result_t
+journal_read_rrhdr(dns_journal_t *j, journal_rrhdr_t *rrhdr) {
+ journal_rawrrhdr_t raw;
+ isc_result_t result;
+ result = journal_read(j, &raw, sizeof(raw));
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ rrhdr->size = decode_uint32(raw.size);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+journal_file_create(isc_mem_t *mctx, const char *filename) {
+ FILE *fp = NULL;
+ isc_result_t result;
+ journal_header_t header;
+ journal_rawheader_t rawheader;
+ int index_size = 56; /* XXX configurable */
+ int size;
+ void *mem; /* Memory for temporary index image. */
+
+ INSIST(sizeof(journal_rawheader_t) == JOURNAL_HEADER_SIZE);
+
+ result = isc_stdio_open(filename, "wb", &fp);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "%s: create: %s",
+ filename, isc_result_totext(result));
+ return (ISC_R_UNEXPECTED);
+ }
+
+ header = initial_journal_header;
+ header.index_size = index_size;
+ journal_header_encode(&header, &rawheader);
+
+ size = sizeof(journal_rawheader_t) +
+ index_size * sizeof(journal_rawpos_t);
+
+ mem = isc_mem_get(mctx, size);
+ if (mem == NULL) {
+ (void)isc_stdio_close(fp);
+ (void)isc_file_remove(filename);
+ return (ISC_R_NOMEMORY);
+ }
+ memset(mem, 0, size);
+ memcpy(mem, &rawheader, sizeof(rawheader));
+
+ result = isc_stdio_write(mem, 1, (size_t) size, fp, NULL);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "%s: write: %s",
+ filename, isc_result_totext(result));
+ (void)isc_stdio_close(fp);
+ (void)isc_file_remove(filename);
+ isc_mem_put(mctx, mem, size);
+ return (ISC_R_UNEXPECTED);
+ }
+ isc_mem_put(mctx, mem, size);
+
+ result = isc_stdio_close(fp);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "%s: close: %s",
+ filename, isc_result_totext(result));
+ (void)isc_file_remove(filename);
+ return (ISC_R_UNEXPECTED);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+journal_open(isc_mem_t *mctx, const char *filename, isc_boolean_t write,
+ isc_boolean_t create, dns_journal_t **journalp) {
+ FILE *fp = NULL;
+ isc_result_t result;
+ journal_rawheader_t rawheader;
+ dns_journal_t *j;
+
+ INSIST(journalp != NULL && *journalp == NULL);
+ j = isc_mem_get(mctx, sizeof(*j));
+ if (j == NULL)
+ return (ISC_R_NOMEMORY);
+
+ j->mctx = mctx;
+ j->state = JOURNAL_STATE_INVALID;
+ j->fp = NULL;
+ j->filename = filename;
+ j->index = NULL;
+ j->rawindex = NULL;
+
+ result = isc_stdio_open(j->filename, write ? "rb+" : "rb", &fp);
+
+ if (result == ISC_R_FILENOTFOUND) {
+ if (create) {
+ isc_log_write(JOURNAL_COMMON_LOGARGS,
+ ISC_LOG_INFO,
+ "journal file %s does not exist, "
+ "creating it",
+ j->filename);
+ CHECK(journal_file_create(mctx, filename));
+ /*
+ * Retry.
+ */
+ result = isc_stdio_open(j->filename, "rb+", &fp);
+ } else {
+ FAIL(ISC_R_NOTFOUND);
+ }
+ }
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "%s: open: %s",
+ j->filename, isc_result_totext(result));
+ FAIL(ISC_R_UNEXPECTED);
+ }
+
+ j->fp = fp;
+
+ /*
+ * Set magic early so that seek/read can succeed.
+ */
+ j->magic = DNS_JOURNAL_MAGIC;
+
+ CHECK(journal_seek(j, 0));
+ CHECK(journal_read(j, &rawheader, sizeof(rawheader)));
+
+ if (memcmp(rawheader.h.format, initial_journal_header.format,
+ sizeof(initial_journal_header.format)) != 0) {
+ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "%s: journal format not recognized",
+ j->filename);
+ FAIL(ISC_R_UNEXPECTED);
+ }
+ journal_header_decode(&rawheader, &j->header);
+
+ /*
+ * If there is an index, read the raw index into a dynamically
+ * allocated buffer and then convert it into a cooked index.
+ */
+ if (j->header.index_size != 0) {
+ unsigned int i;
+ unsigned int rawbytes;
+ unsigned char *p;
+
+ rawbytes = j->header.index_size * sizeof(journal_rawpos_t);
+ j->rawindex = isc_mem_get(mctx, rawbytes);
+ if (j->rawindex == NULL)
+ FAIL(ISC_R_NOMEMORY);
+
+ CHECK(journal_read(j, j->rawindex, rawbytes));
+
+ j->index = isc_mem_get(mctx, j->header.index_size *
+ sizeof(journal_pos_t));
+ if (j->index == NULL)
+ FAIL(ISC_R_NOMEMORY);
+
+ p = j->rawindex;
+ for (i = 0; i < j->header.index_size; i++) {
+ j->index[i].serial = decode_uint32(p);
+ p += 4;
+ j->index[i].offset = decode_uint32(p);
+ p += 4;
+ }
+ INSIST(p == j->rawindex + rawbytes);
+ }
+ j->offset = -1; /* Invalid, must seek explicitly. */
+
+ /*
+ * Initialize the iterator.
+ */
+ dns_name_init(&j->it.name, NULL);
+ dns_rdata_init(&j->it.rdata);
+
+ /*
+ * Set up empty initial buffers for uncheched and checked
+ * wire format RR data. They will be reallocated
+ * later.
+ */
+ isc_buffer_init(&j->it.source, NULL, 0);
+ isc_buffer_init(&j->it.target, NULL, 0);
+ dns_decompress_init(&j->it.dctx, -1, DNS_DECOMPRESS_NONE);
+
+ j->state =
+ write ? JOURNAL_STATE_WRITE : JOURNAL_STATE_READ;
+
+ *journalp = j;
+ return (ISC_R_SUCCESS);
+
+ failure:
+ j->magic = 0;
+ if (j->index != NULL) {
+ isc_mem_put(j->mctx, j->index, j->header.index_size *
+ sizeof(journal_rawpos_t));
+ j->index = NULL;
+ }
+ if (j->fp != NULL)
+ (void)isc_stdio_close(j->fp);
+ isc_mem_put(j->mctx, j, sizeof(*j));
+ return (result);
+}
+
+isc_result_t
+dns_journal_open(isc_mem_t *mctx, const char *filename, isc_boolean_t write,
+ dns_journal_t **journalp) {
+ isc_result_t result;
+ int namelen;
+ char backup[1024];
+
+ result = journal_open(mctx, filename, write, write, journalp);
+ if (result == ISC_R_NOTFOUND) {
+ namelen = strlen(filename);
+ if (namelen > 4 && strcmp(filename + namelen - 4, ".jnl") == 0)
+ namelen -= 4;
+
+ result = isc_string_printf(backup, sizeof(backup), "%.*s.jbk",
+ namelen, filename);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = journal_open(mctx, backup, write, write, journalp);
+ }
+ return (result);
+}
+
+/*
+ * A comparison function defining the sorting order for
+ * entries in the IXFR-style journal file.
+ *
+ * The IXFR format requires that deletions are sorted before
+ * additions, and within either one, SOA records are sorted
+ * before others.
+ *
+ * Also sort the non-SOA records by type as a courtesy to the
+ * server receiving the IXFR - it may help reduce the amount of
+ * rdataset merging it has to do.
+ */
+static int
+ixfr_order(const void *av, const void *bv) {
+ dns_difftuple_t const * const *ap = av;
+ dns_difftuple_t const * const *bp = bv;
+ dns_difftuple_t const *a = *ap;
+ dns_difftuple_t const *b = *bp;
+ int r;
+ int bop = 0, aop = 0;
+
+ switch (a->op) {
+ case DNS_DIFFOP_DEL:
+ case DNS_DIFFOP_DELRESIGN:
+ aop = 1;
+ break;
+ case DNS_DIFFOP_ADD:
+ case DNS_DIFFOP_ADDRESIGN:
+ aop = 0;
+ break;
+ default:
+ INSIST(0);
+ }
+
+ switch (b->op) {
+ case DNS_DIFFOP_DEL:
+ case DNS_DIFFOP_DELRESIGN:
+ bop = 1;
+ break;
+ case DNS_DIFFOP_ADD:
+ case DNS_DIFFOP_ADDRESIGN:
+ bop = 0;
+ break;
+ default:
+ INSIST(0);
+ }
+
+ r = bop - aop;
+ if (r != 0)
+ return (r);
+
+ r = (b->rdata.type == dns_rdatatype_soa) -
+ (a->rdata.type == dns_rdatatype_soa);
+ if (r != 0)
+ return (r);
+
+ r = (a->rdata.type - b->rdata.type);
+ return (r);
+}
+
+/*
+ * Advance '*pos' to the next journal transaction.
+ *
+ * Requires:
+ * *pos refers to a valid journal transaction.
+ *
+ * Ensures:
+ * When ISC_R_SUCCESS is returned,
+ * *pos refers to the next journal transaction.
+ *
+ * Returns one of:
+ *
+ * ISC_R_SUCCESS
+ * ISC_R_NOMORE *pos pointed at the last transaction
+ * Other results due to file errors are possible.
+ */
+static isc_result_t
+journal_next(dns_journal_t *j, journal_pos_t *pos) {
+ isc_result_t result;
+ journal_xhdr_t xhdr;
+ REQUIRE(DNS_JOURNAL_VALID(j));
+
+ result = journal_seek(j, pos->offset);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (pos->serial == j->header.end.serial)
+ return (ISC_R_NOMORE);
+ /*
+ * Read the header of the current transaction.
+ * This will return ISC_R_NOMORE if we are at EOF.
+ */
+ result = journal_read_xhdr(j, &xhdr);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * Check serial number consistency.
+ */
+ if (xhdr.serial0 != pos->serial) {
+ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "%s: journal file corrupt: "
+ "expected serial %u, got %u",
+ j->filename, pos->serial, xhdr.serial0);
+ return (ISC_R_UNEXPECTED);
+ }
+
+ /*
+ * Check for offset wraparound.
+ */
+ if ((isc_offset_t)(pos->offset + sizeof(journal_rawxhdr_t) + xhdr.size)
+ < pos->offset) {
+ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "%s: offset too large", j->filename);
+ return (ISC_R_UNEXPECTED);
+ }
+
+ pos->offset += sizeof(journal_rawxhdr_t) + xhdr.size;
+ pos->serial = xhdr.serial1;
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * If the index of the journal 'j' contains an entry "better"
+ * than '*best_guess', replace '*best_guess' with it.
+ *
+ * "Better" means having a serial number closer to 'serial'
+ * but not greater than 'serial'.
+ */
+static void
+index_find(dns_journal_t *j, isc_uint32_t serial, journal_pos_t *best_guess) {
+ unsigned int i;
+ if (j->index == NULL)
+ return;
+ for (i = 0; i < j->header.index_size; i++) {
+ if (POS_VALID(j->index[i]) &&
+ DNS_SERIAL_GE(serial, j->index[i].serial) &&
+ DNS_SERIAL_GT(j->index[i].serial, best_guess->serial))
+ *best_guess = j->index[i];
+ }
+}
+
+/*
+ * Add a new index entry. If there is no room, make room by removing
+ * the odd-numbered entries and compacting the others into the first
+ * half of the index. This decimates old index entries exponentially
+ * over time, so that the index always contains a much larger fraction
+ * of recent serial numbers than of old ones. This is deliberate -
+ * most index searches are for outgoing IXFR, and IXFR tends to request
+ * recent versions more often than old ones.
+ */
+static void
+index_add(dns_journal_t *j, journal_pos_t *pos) {
+ unsigned int i;
+ if (j->index == NULL)
+ return;
+ /*
+ * Search for a vacant position.
+ */
+ for (i = 0; i < j->header.index_size; i++) {
+ if (! POS_VALID(j->index[i]))
+ break;
+ }
+ if (i == j->header.index_size) {
+ unsigned int k = 0;
+ /*
+ * Found no vacant position. Make some room.
+ */
+ for (i = 0; i < j->header.index_size; i += 2) {
+ j->index[k++] = j->index[i];
+ }
+ i = k; /* 'i' identifies the first vacant position. */
+ while (k < j->header.index_size) {
+ POS_INVALIDATE(j->index[k]);
+ k++;
+ }
+ }
+ INSIST(i < j->header.index_size);
+ INSIST(! POS_VALID(j->index[i]));
+
+ /*
+ * Store the new index entry.
+ */
+ j->index[i] = *pos;
+}
+
+/*
+ * Invalidate any existing index entries that could become
+ * ambiguous when a new transaction with number 'serial' is added.
+ */
+static void
+index_invalidate(dns_journal_t *j, isc_uint32_t serial) {
+ unsigned int i;
+ if (j->index == NULL)
+ return;
+ for (i = 0; i < j->header.index_size; i++) {
+ if (! DNS_SERIAL_GT(serial, j->index[i].serial))
+ POS_INVALIDATE(j->index[i]);
+ }
+}
+
+/*
+ * Try to find a transaction with initial serial number 'serial'
+ * in the journal 'j'.
+ *
+ * If found, store its position at '*pos' and return ISC_R_SUCCESS.
+ *
+ * If 'serial' is current (= the ending serial number of the
+ * last transaction in the journal), set '*pos' to
+ * the position immediately following the last transaction and
+ * return ISC_R_SUCCESS.
+ *
+ * If 'serial' is within the range of addressable serial numbers
+ * covered by the journal but that particular serial number is missing
+ * (from the journal, not just from the index), return ISC_R_NOTFOUND.
+ *
+ * If 'serial' is outside the range of addressable serial numbers
+ * covered by the journal, return ISC_R_RANGE.
+ *
+ */
+static isc_result_t
+journal_find(dns_journal_t *j, isc_uint32_t serial, journal_pos_t *pos) {
+ isc_result_t result;
+ journal_pos_t current_pos;
+ REQUIRE(DNS_JOURNAL_VALID(j));
+
+ if (DNS_SERIAL_GT(j->header.begin.serial, serial))
+ return (ISC_R_RANGE);
+ if (DNS_SERIAL_GT(serial, j->header.end.serial))
+ return (ISC_R_RANGE);
+ if (serial == j->header.end.serial) {
+ *pos = j->header.end;
+ return (ISC_R_SUCCESS);
+ }
+
+ current_pos = j->header.begin;
+ index_find(j, serial, &current_pos);
+
+ while (current_pos.serial != serial) {
+ if (DNS_SERIAL_GT(current_pos.serial, serial))
+ return (ISC_R_NOTFOUND);
+ result = journal_next(j, &current_pos);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ *pos = current_pos;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_journal_begin_transaction(dns_journal_t *j) {
+ isc_uint32_t offset;
+ isc_result_t result;
+ journal_rawxhdr_t hdr;
+
+ REQUIRE(DNS_JOURNAL_VALID(j));
+ REQUIRE(j->state == JOURNAL_STATE_WRITE);
+
+ /*
+ * Find the file offset where the new transaction should
+ * be written, and seek there.
+ */
+ if (JOURNAL_EMPTY(&j->header)) {
+ offset = sizeof(journal_rawheader_t) +
+ j->header.index_size * sizeof(journal_rawpos_t);
+ } else {
+ offset = j->header.end.offset;
+ }
+ j->x.pos[0].offset = offset;
+ j->x.pos[1].offset = offset; /* Initial value, will be incremented. */
+ j->x.n_soa = 0;
+
+ CHECK(journal_seek(j, offset));
+
+ /*
+ * Write a dummy transaction header of all zeroes to reserve
+ * space. It will be filled in when the transaction is
+ * finished.
+ */
+ memset(&hdr, 0, sizeof(hdr));
+ CHECK(journal_write(j, &hdr, sizeof(hdr)));
+ j->x.pos[1].offset = j->offset;
+
+ j->state = JOURNAL_STATE_TRANSACTION;
+ result = ISC_R_SUCCESS;
+ failure:
+ return (result);
+}
+
+isc_result_t
+dns_journal_writediff(dns_journal_t *j, dns_diff_t *diff) {
+ dns_difftuple_t *t;
+ isc_buffer_t buffer;
+ void *mem = NULL;
+ unsigned int size;
+ isc_result_t result;
+ isc_region_t used;
+
+ REQUIRE(DNS_DIFF_VALID(diff));
+ REQUIRE(j->state == JOURNAL_STATE_TRANSACTION);
+
+ isc_log_write(JOURNAL_DEBUG_LOGARGS(3), "writing to journal");
+ (void)dns_diff_print(diff, NULL);
+
+ /*
+ * Pass 1: determine the buffer size needed, and
+ * keep track of SOA serial numbers.
+ */
+ size = 0;
+ for (t = ISC_LIST_HEAD(diff->tuples); t != NULL;
+ t = ISC_LIST_NEXT(t, link))
+ {
+ if (t->rdata.type == dns_rdatatype_soa) {
+ if (j->x.n_soa < 2)
+ j->x.pos[j->x.n_soa].serial =
+ dns_soa_getserial(&t->rdata);
+ j->x.n_soa++;
+ }
+ size += sizeof(journal_rawrrhdr_t);
+ size += t->name.length; /* XXX should have access macro? */
+ size += 10;
+ size += t->rdata.length;
+ }
+
+ mem = isc_mem_get(j->mctx, size);
+ if (mem == NULL)
+ return (ISC_R_NOMEMORY);
+
+ isc_buffer_init(&buffer, mem, size);
+
+ /*
+ * Pass 2. Write RRs to buffer.
+ */
+ for (t = ISC_LIST_HEAD(diff->tuples); t != NULL;
+ t = ISC_LIST_NEXT(t, link))
+ {
+ /*
+ * Write the RR header.
+ */
+ isc_buffer_putuint32(&buffer, t->name.length + 10 +
+ t->rdata.length);
+ /*
+ * Write the owner name, RR header, and RR data.
+ */
+ isc_buffer_putmem(&buffer, t->name.ndata, t->name.length);
+ isc_buffer_putuint16(&buffer, t->rdata.type);
+ isc_buffer_putuint16(&buffer, t->rdata.rdclass);
+ isc_buffer_putuint32(&buffer, t->ttl);
+ INSIST(t->rdata.length < 65536);
+ isc_buffer_putuint16(&buffer, (isc_uint16_t)t->rdata.length);
+ INSIST(isc_buffer_availablelength(&buffer) >= t->rdata.length);
+ isc_buffer_putmem(&buffer, t->rdata.data, t->rdata.length);
+ }
+
+ isc_buffer_usedregion(&buffer, &used);
+ INSIST(used.length == size);
+
+ j->x.pos[1].offset += used.length;
+
+ /*
+ * Write the buffer contents to the journal file.
+ */
+ CHECK(journal_write(j, used.base, used.length));
+
+ result = ISC_R_SUCCESS;
+
+ failure:
+ if (mem != NULL)
+ isc_mem_put(j->mctx, mem, size);
+ return (result);
+
+}
+
+isc_result_t
+dns_journal_commit(dns_journal_t *j) {
+ isc_result_t result;
+ journal_rawheader_t rawheader;
+
+ REQUIRE(DNS_JOURNAL_VALID(j));
+ REQUIRE(j->state == JOURNAL_STATE_TRANSACTION);
+
+ /*
+ * Perform some basic consistency checks.
+ */
+ if (j->x.n_soa != 2) {
+ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "%s: malformed transaction: %d SOAs",
+ j->filename, j->x.n_soa);
+ return (ISC_R_UNEXPECTED);
+ }
+ if (! (DNS_SERIAL_GT(j->x.pos[1].serial, j->x.pos[0].serial) ||
+ (bind8_compat &&
+ j->x.pos[1].serial == j->x.pos[0].serial)))
+ {
+ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "%s: malformed transaction: serial number "
+ "would decrease", j->filename);
+ return (ISC_R_UNEXPECTED);
+ }
+ if (! JOURNAL_EMPTY(&j->header)) {
+ if (j->x.pos[0].serial != j->header.end.serial) {
+ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "malformed transaction: "
+ "%s last serial %u != "
+ "transaction first serial %u",
+ j->filename,
+ j->header.end.serial,
+ j->x.pos[0].serial);
+ return (ISC_R_UNEXPECTED);
+ }
+ }
+
+ /*
+ * Some old journal entries may become non-addressable
+ * when we increment the current serial number. Purge them
+ * by stepping header.begin forward to the first addressable
+ * transaction. Also purge them from the index.
+ */
+ if (! JOURNAL_EMPTY(&j->header)) {
+ while (! DNS_SERIAL_GT(j->x.pos[1].serial,
+ j->header.begin.serial)) {
+ CHECK(journal_next(j, &j->header.begin));
+ }
+ index_invalidate(j, j->x.pos[1].serial);
+ }
+#ifdef notyet
+ if (DNS_SERIAL_GT(last_dumped_serial, j->x.pos[1].serial)) {
+ force_dump(...);
+ }
+#endif
+
+ /*
+ * Commit the transaction data to stable storage.
+ */
+ CHECK(journal_fsync(j));
+
+ /*
+ * Update the transaction header.
+ */
+ CHECK(journal_seek(j, j->x.pos[0].offset));
+ CHECK(journal_write_xhdr(j, (j->x.pos[1].offset - j->x.pos[0].offset) -
+ sizeof(journal_rawxhdr_t),
+ j->x.pos[0].serial, j->x.pos[1].serial));
+
+ /*
+ * Update the journal header.
+ */
+ if (JOURNAL_EMPTY(&j->header)) {
+ j->header.begin = j->x.pos[0];
+ }
+ j->header.end = j->x.pos[1];
+ journal_header_encode(&j->header, &rawheader);
+ CHECK(journal_seek(j, 0));
+ CHECK(journal_write(j, &rawheader, sizeof(rawheader)));
+
+ /*
+ * Update the index.
+ */
+ index_add(j, &j->x.pos[0]);
+
+ /*
+ * Convert the index into on-disk format and write
+ * it to disk.
+ */
+ CHECK(index_to_disk(j));
+
+ /*
+ * Commit the header to stable storage.
+ */
+ CHECK(journal_fsync(j));
+
+ /*
+ * We no longer have a transaction open.
+ */
+ j->state = JOURNAL_STATE_WRITE;
+
+ result = ISC_R_SUCCESS;
+
+ failure:
+ return (result);
+}
+
+isc_result_t
+dns_journal_write_transaction(dns_journal_t *j, dns_diff_t *diff) {
+ isc_result_t result;
+ CHECK(dns_diff_sort(diff, ixfr_order));
+ CHECK(dns_journal_begin_transaction(j));
+ CHECK(dns_journal_writediff(j, diff));
+ CHECK(dns_journal_commit(j));
+ result = ISC_R_SUCCESS;
+ failure:
+ return (result);
+}
+
+void
+dns_journal_destroy(dns_journal_t **journalp) {
+ dns_journal_t *j = *journalp;
+ REQUIRE(DNS_JOURNAL_VALID(j));
+
+ j->it.result = ISC_R_FAILURE;
+ dns_name_invalidate(&j->it.name);
+ dns_decompress_invalidate(&j->it.dctx);
+ if (j->rawindex != NULL)
+ isc_mem_put(j->mctx, j->rawindex, j->header.index_size *
+ sizeof(journal_rawpos_t));
+ if (j->index != NULL)
+ isc_mem_put(j->mctx, j->index, j->header.index_size *
+ sizeof(journal_pos_t));
+ if (j->it.target.base != NULL)
+ isc_mem_put(j->mctx, j->it.target.base, j->it.target.length);
+ if (j->it.source.base != NULL)
+ isc_mem_put(j->mctx, j->it.source.base, j->it.source.length);
+
+ if (j->fp != NULL)
+ (void)isc_stdio_close(j->fp);
+ j->magic = 0;
+ isc_mem_put(j->mctx, j, sizeof(*j));
+ *journalp = NULL;
+}
+
+/*
+ * Roll the open journal 'j' into the database 'db'.
+ * A new database version will be created.
+ */
+
+/* XXX Share code with incoming IXFR? */
+
+static isc_result_t
+roll_forward(dns_journal_t *j, dns_db_t *db, unsigned int options) {
+ isc_buffer_t source; /* Transaction data from disk */
+ isc_buffer_t target; /* Ditto after _fromwire check */
+ isc_uint32_t db_serial; /* Database SOA serial */
+ isc_uint32_t end_serial; /* Last journal SOA serial */
+ isc_result_t result;
+ dns_dbversion_t *ver = NULL;
+ journal_pos_t pos;
+ dns_diff_t diff;
+ unsigned int n_soa = 0;
+ unsigned int n_put = 0;
+ dns_diffop_t op;
+
+ REQUIRE(DNS_JOURNAL_VALID(j));
+ REQUIRE(DNS_DB_VALID(db));
+
+ dns_diff_init(j->mctx, &diff);
+
+ /*
+ * Set up empty initial buffers for uncheched and checked
+ * wire format transaction data. They will be reallocated
+ * later.
+ */
+ isc_buffer_init(&source, NULL, 0);
+ isc_buffer_init(&target, NULL, 0);
+
+ /*
+ * Create the new database version.
+ */
+ CHECK(dns_db_newversion(db, &ver));
+
+ /*
+ * Get the current database SOA serial number.
+ */
+ CHECK(dns_db_getsoaserial(db, ver, &db_serial));
+
+ /*
+ * Locate a journal entry for the current database serial.
+ */
+ CHECK(journal_find(j, db_serial, &pos));
+ /*
+ * XXX do more drastic things, like marking zone stale,
+ * if this fails?
+ */
+ /*
+ * XXXRTH The zone code should probably mark the zone as bad and
+ * scream loudly into the log if this is a dynamic update
+ * log reply that failed.
+ */
+
+ end_serial = dns_journal_last_serial(j);
+ if (db_serial == end_serial)
+ CHECK(DNS_R_UPTODATE);
+
+ CHECK(dns_journal_iter_init(j, db_serial, end_serial));
+
+ for (result = dns_journal_first_rr(j);
+ result == ISC_R_SUCCESS;
+ result = dns_journal_next_rr(j))
+ {
+ dns_name_t *name;
+ isc_uint32_t ttl;
+ dns_rdata_t *rdata;
+ dns_difftuple_t *tuple = NULL;
+
+ name = NULL;
+ rdata = NULL;
+ dns_journal_current_rr(j, &name, &ttl, &rdata);
+
+ if (rdata->type == dns_rdatatype_soa) {
+ n_soa++;
+ if (n_soa == 2)
+ db_serial = j->it.current_serial;
+ }
+
+ if (n_soa == 3)
+ n_soa = 1;
+ if (n_soa == 0) {
+ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "%s: journal file corrupt: missing "
+ "initial SOA", j->filename);
+ FAIL(ISC_R_UNEXPECTED);
+ }
+ if ((options & DNS_JOURNALOPT_RESIGN) != 0)
+ op = (n_soa == 1) ? DNS_DIFFOP_DELRESIGN :
+ DNS_DIFFOP_ADDRESIGN;
+ else
+ op = (n_soa == 1) ? DNS_DIFFOP_DEL : DNS_DIFFOP_ADD;
+
+ CHECK(dns_difftuple_create(diff.mctx, op, name, ttl, rdata,
+ &tuple));
+ dns_diff_append(&diff, &tuple);
+
+ if (++n_put > 100) {
+ isc_log_write(JOURNAL_DEBUG_LOGARGS(3),
+ "%s: applying diff to database (%u)",
+ j->filename, db_serial);
+ (void)dns_diff_print(&diff, NULL);
+ CHECK(dns_diff_apply(&diff, db, ver));
+ dns_diff_clear(&diff);
+ n_put = 0;
+ }
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ CHECK(result);
+
+ if (n_put != 0) {
+ isc_log_write(JOURNAL_DEBUG_LOGARGS(3),
+ "%s: applying final diff to database (%u)",
+ j->filename, db_serial);
+ (void)dns_diff_print(&diff, NULL);
+ CHECK(dns_diff_apply(&diff, db, ver));
+ dns_diff_clear(&diff);
+ }
+
+ failure:
+ if (ver != NULL)
+ dns_db_closeversion(db, &ver, result == ISC_R_SUCCESS ?
+ ISC_TRUE : ISC_FALSE);
+
+ if (source.base != NULL)
+ isc_mem_put(j->mctx, source.base, source.length);
+ if (target.base != NULL)
+ isc_mem_put(j->mctx, target.base, target.length);
+
+ dns_diff_clear(&diff);
+
+ return (result);
+}
+
+isc_result_t
+dns_journal_rollforward(isc_mem_t *mctx, dns_db_t *db,
+ unsigned int options, const char *filename)
+{
+ dns_journal_t *j;
+ isc_result_t result;
+
+ REQUIRE(DNS_DB_VALID(db));
+ REQUIRE(filename != NULL);
+
+ j = NULL;
+ result = dns_journal_open(mctx, filename, ISC_FALSE, &j);
+ if (result == ISC_R_NOTFOUND) {
+ isc_log_write(JOURNAL_DEBUG_LOGARGS(3),
+ "no journal file, but that's OK");
+ return (DNS_R_NOJOURNAL);
+ }
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (JOURNAL_EMPTY(&j->header))
+ result = DNS_R_UPTODATE;
+ else
+ result = roll_forward(j, db, options);
+
+ dns_journal_destroy(&j);
+
+ return (result);
+}
+
+isc_result_t
+dns_journal_print(isc_mem_t *mctx, const char *filename, FILE *file) {
+ dns_journal_t *j;
+ isc_buffer_t source; /* Transaction data from disk */
+ isc_buffer_t target; /* Ditto after _fromwire check */
+ isc_uint32_t start_serial; /* Database SOA serial */
+ isc_uint32_t end_serial; /* Last journal SOA serial */
+ isc_result_t result;
+ dns_diff_t diff;
+ unsigned int n_soa = 0;
+ unsigned int n_put = 0;
+
+ REQUIRE(filename != NULL);
+
+ j = NULL;
+ result = dns_journal_open(mctx, filename, ISC_FALSE, &j);
+ if (result == ISC_R_NOTFOUND) {
+ isc_log_write(JOURNAL_DEBUG_LOGARGS(3), "no journal file");
+ return (DNS_R_NOJOURNAL);
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "journal open failure: %s: %s",
+ isc_result_totext(result), filename);
+ return (result);
+ }
+
+ dns_diff_init(j->mctx, &diff);
+
+ /*
+ * Set up empty initial buffers for uncheched and checked
+ * wire format transaction data. They will be reallocated
+ * later.
+ */
+ isc_buffer_init(&source, NULL, 0);
+ isc_buffer_init(&target, NULL, 0);
+
+ start_serial = dns_journal_first_serial(j);
+ end_serial = dns_journal_last_serial(j);
+
+ CHECK(dns_journal_iter_init(j, start_serial, end_serial));
+
+ for (result = dns_journal_first_rr(j);
+ result == ISC_R_SUCCESS;
+ result = dns_journal_next_rr(j))
+ {
+ dns_name_t *name;
+ isc_uint32_t ttl;
+ dns_rdata_t *rdata;
+ dns_difftuple_t *tuple = NULL;
+
+ name = NULL;
+ rdata = NULL;
+ dns_journal_current_rr(j, &name, &ttl, &rdata);
+
+ if (rdata->type == dns_rdatatype_soa)
+ n_soa++;
+
+ if (n_soa == 3)
+ n_soa = 1;
+ if (n_soa == 0) {
+ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "%s: journal file corrupt: missing "
+ "initial SOA", j->filename);
+ FAIL(ISC_R_UNEXPECTED);
+ }
+ CHECK(dns_difftuple_create(diff.mctx, n_soa == 1 ?
+ DNS_DIFFOP_DEL : DNS_DIFFOP_ADD,
+ name, ttl, rdata, &tuple));
+ dns_diff_append(&diff, &tuple);
+
+ if (++n_put > 100) {
+ result = dns_diff_print(&diff, file);
+ dns_diff_clear(&diff);
+ n_put = 0;
+ if (result != ISC_R_SUCCESS)
+ break;
+ }
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ CHECK(result);
+
+ if (n_put != 0) {
+ result = dns_diff_print(&diff, file);
+ dns_diff_clear(&diff);
+ }
+ goto cleanup;
+
+ failure:
+ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "%s: cannot print: journal file corrupt", j->filename);
+
+ cleanup:
+ if (source.base != NULL)
+ isc_mem_put(j->mctx, source.base, source.length);
+ if (target.base != NULL)
+ isc_mem_put(j->mctx, target.base, target.length);
+
+ dns_diff_clear(&diff);
+ dns_journal_destroy(&j);
+
+ return (result);
+}
+
+/**************************************************************************/
+/*
+ * Miscellaneous accessors.
+ */
+isc_uint32_t dns_journal_first_serial(dns_journal_t *j) {
+ return (j->header.begin.serial);
+}
+
+isc_uint32_t dns_journal_last_serial(dns_journal_t *j) {
+ return (j->header.end.serial);
+}
+
+/**************************************************************************/
+/*
+ * Iteration support.
+ *
+ * When serving an outgoing IXFR, we transmit a part the journal starting
+ * at the serial number in the IXFR request and ending at the serial
+ * number that is current when the IXFR request arrives. The ending
+ * serial number is not necessarily at the end of the journal:
+ * the journal may grow while the IXFR is in progress, but we stop
+ * when we reach the serial number that was current when the IXFR started.
+ */
+
+static isc_result_t read_one_rr(dns_journal_t *j);
+
+/*
+ * Make sure the buffer 'b' is has at least 'size' bytes
+ * allocated, and clear it.
+ *
+ * Requires:
+ * Either b->base is NULL, or it points to b->length bytes of memory
+ * previously allocated by isc_mem_get().
+ */
+
+static isc_result_t
+size_buffer(isc_mem_t *mctx, isc_buffer_t *b, unsigned size) {
+ if (b->length < size) {
+ void *mem = isc_mem_get(mctx, size);
+ if (mem == NULL)
+ return (ISC_R_NOMEMORY);
+ if (b->base != NULL)
+ isc_mem_put(mctx, b->base, b->length);
+ b->base = mem;
+ b->length = size;
+ }
+ isc_buffer_clear(b);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_journal_iter_init(dns_journal_t *j,
+ isc_uint32_t begin_serial, isc_uint32_t end_serial)
+{
+ isc_result_t result;
+
+ CHECK(journal_find(j, begin_serial, &j->it.bpos));
+ INSIST(j->it.bpos.serial == begin_serial);
+
+ CHECK(journal_find(j, end_serial, &j->it.epos));
+ INSIST(j->it.epos.serial == end_serial);
+
+ result = ISC_R_SUCCESS;
+ failure:
+ j->it.result = result;
+ return (j->it.result);
+}
+
+
+isc_result_t
+dns_journal_first_rr(dns_journal_t *j) {
+ isc_result_t result;
+
+ /*
+ * Seek to the beginning of the first transaction we are
+ * interested in.
+ */
+ CHECK(journal_seek(j, j->it.bpos.offset));
+ j->it.current_serial = j->it.bpos.serial;
+
+ j->it.xsize = 0; /* We have no transaction data yet... */
+ j->it.xpos = 0; /* ...and haven't used any of it. */
+
+ return (read_one_rr(j));
+
+ failure:
+ return (result);
+}
+
+static isc_result_t
+read_one_rr(dns_journal_t *j) {
+ isc_result_t result;
+
+ dns_rdatatype_t rdtype;
+ dns_rdataclass_t rdclass;
+ unsigned int rdlen;
+ isc_uint32_t ttl;
+ journal_xhdr_t xhdr;
+ journal_rrhdr_t rrhdr;
+
+ INSIST(j->offset <= j->it.epos.offset);
+ if (j->offset == j->it.epos.offset)
+ return (ISC_R_NOMORE);
+ if (j->it.xpos == j->it.xsize) {
+ /*
+ * We are at a transaction boundary.
+ * Read another transaction header.
+ */
+ CHECK(journal_read_xhdr(j, &xhdr));
+ if (xhdr.size == 0) {
+ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "%s: journal corrupt: empty transaction",
+ j->filename);
+ FAIL(ISC_R_UNEXPECTED);
+ }
+ if (xhdr.serial0 != j->it.current_serial) {
+ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "%s: journal file corrupt: "
+ "expected serial %u, got %u",
+ j->filename,
+ j->it.current_serial, xhdr.serial0);
+ FAIL(ISC_R_UNEXPECTED);
+ }
+ j->it.xsize = xhdr.size;
+ j->it.xpos = 0;
+ }
+ /*
+ * Read an RR.
+ */
+ CHECK(journal_read_rrhdr(j, &rrhdr));
+ /*
+ * Perform a sanity check on the journal RR size.
+ * The smallest possible RR has a 1-byte owner name
+ * and a 10-byte header. The largest possible
+ * RR has 65535 bytes of data, a header, and a maximum-
+ * size owner name, well below 70 k total.
+ */
+ if (rrhdr.size < 1+10 || rrhdr.size > 70000) {
+ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "%s: journal corrupt: impossible RR size "
+ "(%d bytes)", j->filename, rrhdr.size);
+ FAIL(ISC_R_UNEXPECTED);
+ }
+
+ CHECK(size_buffer(j->mctx, &j->it.source, rrhdr.size));
+ CHECK(journal_read(j, j->it.source.base, rrhdr.size));
+ isc_buffer_add(&j->it.source, rrhdr.size);
+
+ /*
+ * The target buffer is made the same size
+ * as the source buffer, with the assumption that when
+ * no compression in present, the output of dns_*_fromwire()
+ * is no larger than the input.
+ */
+ CHECK(size_buffer(j->mctx, &j->it.target, rrhdr.size));
+
+ /*
+ * Parse the owner name. We don't know where it
+ * ends yet, so we make the entire "remaining"
+ * part of the buffer "active".
+ */
+ isc_buffer_setactive(&j->it.source,
+ j->it.source.used - j->it.source.current);
+ CHECK(dns_name_fromwire(&j->it.name, &j->it.source,
+ &j->it.dctx, 0, &j->it.target));
+
+ /*
+ * Check that the RR header is there, and parse it.
+ */
+ if (isc_buffer_remaininglength(&j->it.source) < 10)
+ FAIL(DNS_R_FORMERR);
+
+ rdtype = isc_buffer_getuint16(&j->it.source);
+ rdclass = isc_buffer_getuint16(&j->it.source);
+ ttl = isc_buffer_getuint32(&j->it.source);
+ rdlen = isc_buffer_getuint16(&j->it.source);
+
+ /*
+ * Parse the rdata.
+ */
+ if (isc_buffer_remaininglength(&j->it.source) != rdlen)
+ FAIL(DNS_R_FORMERR);
+ isc_buffer_setactive(&j->it.source, rdlen);
+ dns_rdata_reset(&j->it.rdata);
+ CHECK(dns_rdata_fromwire(&j->it.rdata, rdclass,
+ rdtype, &j->it.source, &j->it.dctx,
+ 0, &j->it.target));
+ j->it.ttl = ttl;
+
+ j->it.xpos += sizeof(journal_rawrrhdr_t) + rrhdr.size;
+ if (rdtype == dns_rdatatype_soa) {
+ /* XXX could do additional consistency checks here */
+ j->it.current_serial = dns_soa_getserial(&j->it.rdata);
+ }
+
+ result = ISC_R_SUCCESS;
+
+ failure:
+ j->it.result = result;
+ return (result);
+}
+
+isc_result_t
+dns_journal_next_rr(dns_journal_t *j) {
+ j->it.result = read_one_rr(j);
+ return (j->it.result);
+}
+
+void
+dns_journal_current_rr(dns_journal_t *j, dns_name_t **name, isc_uint32_t *ttl,
+ dns_rdata_t **rdata)
+{
+ REQUIRE(j->it.result == ISC_R_SUCCESS);
+ *name = &j->it.name;
+ *ttl = j->it.ttl;
+ *rdata = &j->it.rdata;
+}
+
+/**************************************************************************/
+/*
+ * Generating diffs from databases
+ */
+
+/*
+ * Construct a diff containing all the RRs at the current name of the
+ * database iterator 'dbit' in database 'db', version 'ver'.
+ * Set '*name' to the current name, and append the diff to 'diff'.
+ * All new tuples will have the operation 'op'.
+ *
+ * Requires: 'name' must have buffer large enough to hold the name.
+ * Typically, a dns_fixedname_t would be used.
+ */
+static isc_result_t
+get_name_diff(dns_db_t *db, dns_dbversion_t *ver, isc_stdtime_t now,
+ dns_dbiterator_t *dbit, dns_name_t *name, dns_diffop_t op,
+ dns_diff_t *diff)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ dns_rdatasetiter_t *rdsiter = NULL;
+ dns_difftuple_t *tuple = NULL;
+
+ result = dns_dbiterator_current(dbit, &node, name);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_db_allrdatasets(db, node, ver, now, &rdsiter);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_node;
+
+ for (result = dns_rdatasetiter_first(rdsiter);
+ result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(rdsiter))
+ {
+ dns_rdataset_t rdataset;
+
+ dns_rdataset_init(&rdataset);
+ dns_rdatasetiter_current(rdsiter, &rdataset);
+
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset))
+ {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_current(&rdataset, &rdata);
+ result = dns_difftuple_create(diff->mctx, op, name,
+ rdataset.ttl, &rdata,
+ &tuple);
+ if (result != ISC_R_SUCCESS) {
+ dns_rdataset_disassociate(&rdataset);
+ goto cleanup_iterator;
+ }
+ dns_diff_append(diff, &tuple);
+ }
+ dns_rdataset_disassociate(&rdataset);
+ if (result != ISC_R_NOMORE)
+ goto cleanup_iterator;
+ }
+ if (result != ISC_R_NOMORE)
+ goto cleanup_iterator;
+
+ result = ISC_R_SUCCESS;
+
+ cleanup_iterator:
+ dns_rdatasetiter_destroy(&rdsiter);
+
+ cleanup_node:
+ dns_db_detachnode(db, &node);
+
+ return (result);
+}
+
+/*
+ * Comparison function for use by dns_diff_subtract when sorting
+ * the diffs to be subtracted. The sort keys are the rdata type
+ * and the rdata itself. The owner name is ignored, because
+ * it is known to be the same for all tuples.
+ */
+static int
+rdata_order(const void *av, const void *bv) {
+ dns_difftuple_t const * const *ap = av;
+ dns_difftuple_t const * const *bp = bv;
+ dns_difftuple_t const *a = *ap;
+ dns_difftuple_t const *b = *bp;
+ int r;
+ r = (b->rdata.type - a->rdata.type);
+ if (r != 0)
+ return (r);
+ r = dns_rdata_compare(&a->rdata, &b->rdata);
+ return (r);
+}
+
+static isc_result_t
+dns_diff_subtract(dns_diff_t diff[2], dns_diff_t *r) {
+ isc_result_t result;
+ dns_difftuple_t *p[2];
+ int i, t;
+ isc_boolean_t append;
+
+ CHECK(dns_diff_sort(&diff[0], rdata_order));
+ CHECK(dns_diff_sort(&diff[1], rdata_order));
+
+ for (;;) {
+ p[0] = ISC_LIST_HEAD(diff[0].tuples);
+ p[1] = ISC_LIST_HEAD(diff[1].tuples);
+ if (p[0] == NULL && p[1] == NULL)
+ break;
+
+ for (i = 0; i < 2; i++)
+ if (p[!i] == NULL) {
+ ISC_LIST_UNLINK(diff[i].tuples, p[i], link);
+ ISC_LIST_APPEND(r->tuples, p[i], link);
+ goto next;
+ }
+ t = rdata_order(&p[0], &p[1]);
+ if (t < 0) {
+ ISC_LIST_UNLINK(diff[0].tuples, p[0], link);
+ ISC_LIST_APPEND(r->tuples, p[0], link);
+ goto next;
+ }
+ if (t > 0) {
+ ISC_LIST_UNLINK(diff[1].tuples, p[1], link);
+ ISC_LIST_APPEND(r->tuples, p[1], link);
+ goto next;
+ }
+ INSIST(t == 0);
+ /*
+ * Identical RRs in both databases; skip them both
+ * if the ttl differs.
+ */
+ append = ISC_TF(p[0]->ttl != p[1]->ttl);
+ for (i = 0; i < 2; i++) {
+ ISC_LIST_UNLINK(diff[i].tuples, p[i], link);
+ if (append) {
+ ISC_LIST_APPEND(r->tuples, p[i], link);
+ } else {
+ dns_difftuple_free(&p[i]);
+ }
+ }
+ next: ;
+ }
+ result = ISC_R_SUCCESS;
+ failure:
+ return (result);
+}
+
+/*
+ * Compare the databases 'dba' and 'dbb' and generate a journal
+ * entry containing the changes to make 'dba' from 'dbb' (note
+ * the order). This journal entry will consist of a single,
+ * possibly very large transaction.
+ */
+
+isc_result_t
+dns_db_diff(isc_mem_t *mctx,
+ dns_db_t *dba, dns_dbversion_t *dbvera,
+ dns_db_t *dbb, dns_dbversion_t *dbverb,
+ const char *journal_filename)
+{
+ dns_db_t *db[2];
+ dns_dbversion_t *ver[2];
+ dns_dbiterator_t *dbit[2] = { NULL, NULL };
+ isc_boolean_t have[2] = { ISC_FALSE, ISC_FALSE };
+ dns_fixedname_t fixname[2];
+ isc_result_t result, itresult[2];
+ dns_diff_t diff[2], resultdiff;
+ int i, t;
+ dns_journal_t *journal = NULL;
+
+ db[0] = dba, db[1] = dbb;
+ ver[0] = dbvera, ver[1] = dbverb;
+
+ dns_diff_init(mctx, &diff[0]);
+ dns_diff_init(mctx, &diff[1]);
+ dns_diff_init(mctx, &resultdiff);
+
+ dns_fixedname_init(&fixname[0]);
+ dns_fixedname_init(&fixname[1]);
+
+ result = dns_journal_open(mctx, journal_filename, ISC_TRUE, &journal);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_db_createiterator(db[0], 0, &dbit[0]);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_journal;
+ result = dns_db_createiterator(db[1], 0, &dbit[1]);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_interator0;
+
+ itresult[0] = dns_dbiterator_first(dbit[0]);
+ itresult[1] = dns_dbiterator_first(dbit[1]);
+
+ for (;;) {
+ for (i = 0; i < 2; i++) {
+ if (! have[i] && itresult[i] == ISC_R_SUCCESS) {
+ CHECK(get_name_diff(db[i], ver[i], 0, dbit[i],
+ dns_fixedname_name(&fixname[i]),
+ i == 0 ?
+ DNS_DIFFOP_ADD :
+ DNS_DIFFOP_DEL,
+ &diff[i]));
+ itresult[i] = dns_dbiterator_next(dbit[i]);
+ have[i] = ISC_TRUE;
+ }
+ }
+
+ if (! have[0] && ! have[1]) {
+ INSIST(ISC_LIST_EMPTY(diff[0].tuples));
+ INSIST(ISC_LIST_EMPTY(diff[1].tuples));
+ break;
+ }
+
+ for (i = 0; i < 2; i++) {
+ if (! have[!i]) {
+ ISC_LIST_APPENDLIST(resultdiff.tuples,
+ diff[i].tuples, link);
+ INSIST(ISC_LIST_EMPTY(diff[i].tuples));
+ have[i] = ISC_FALSE;
+ goto next;
+ }
+ }
+
+ t = dns_name_compare(dns_fixedname_name(&fixname[0]),
+ dns_fixedname_name(&fixname[1]));
+ if (t < 0) {
+ ISC_LIST_APPENDLIST(resultdiff.tuples,
+ diff[0].tuples, link);
+ INSIST(ISC_LIST_EMPTY(diff[0].tuples));
+ have[0] = ISC_FALSE;
+ continue;
+ }
+ if (t > 0) {
+ ISC_LIST_APPENDLIST(resultdiff.tuples,
+ diff[1].tuples, link);
+ INSIST(ISC_LIST_EMPTY(diff[1].tuples));
+ have[1] = ISC_FALSE;
+ continue;
+ }
+ INSIST(t == 0);
+ CHECK(dns_diff_subtract(diff, &resultdiff));
+ INSIST(ISC_LIST_EMPTY(diff[0].tuples));
+ INSIST(ISC_LIST_EMPTY(diff[1].tuples));
+ have[0] = have[1] = ISC_FALSE;
+ next: ;
+ }
+ if (itresult[0] != ISC_R_NOMORE)
+ FAIL(itresult[0]);
+ if (itresult[1] != ISC_R_NOMORE)
+ FAIL(itresult[1]);
+
+ if (ISC_LIST_EMPTY(resultdiff.tuples)) {
+ isc_log_write(JOURNAL_DEBUG_LOGARGS(3), "no changes");
+ } else {
+ CHECK(dns_journal_write_transaction(journal, &resultdiff));
+ }
+ INSIST(ISC_LIST_EMPTY(diff[0].tuples));
+ INSIST(ISC_LIST_EMPTY(diff[1].tuples));
+
+ failure:
+ dns_diff_clear(&resultdiff);
+ dns_dbiterator_destroy(&dbit[1]);
+ cleanup_interator0:
+ dns_dbiterator_destroy(&dbit[0]);
+ cleanup_journal:
+ dns_journal_destroy(&journal);
+ return (result);
+}
+
+isc_result_t
+dns_journal_compact(isc_mem_t *mctx, char *filename, isc_uint32_t serial,
+ isc_uint32_t target_size)
+{
+ unsigned int i;
+ journal_pos_t best_guess;
+ journal_pos_t current_pos;
+ dns_journal_t *j = NULL;
+ dns_journal_t *new = NULL;
+ journal_rawheader_t rawheader;
+ unsigned int copy_length;
+ int namelen;
+ char *buf = NULL;
+ unsigned int size = 0;
+ isc_result_t result;
+ unsigned int indexend;
+ char newname[1024];
+ char backup[1024];
+ isc_boolean_t is_backup = ISC_FALSE;
+
+ namelen = strlen(filename);
+ if (namelen > 4 && strcmp(filename + namelen - 4, ".jnl") == 0)
+ namelen -= 4;
+
+ result = isc_string_printf(newname, sizeof(newname), "%.*s.jnw",
+ namelen, filename);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = isc_string_printf(backup, sizeof(backup), "%.*s.jbk",
+ namelen, filename);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = journal_open(mctx, filename, ISC_FALSE, ISC_FALSE, &j);
+ if (result == ISC_R_NOTFOUND) {
+ is_backup = ISC_TRUE;
+ result = journal_open(mctx, backup, ISC_FALSE, ISC_FALSE, &j);
+ }
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (JOURNAL_EMPTY(&j->header)) {
+ dns_journal_destroy(&j);
+ return (ISC_R_SUCCESS);
+ }
+
+ if (DNS_SERIAL_GT(j->header.begin.serial, serial) ||
+ DNS_SERIAL_GT(serial, j->header.end.serial)) {
+ dns_journal_destroy(&j);
+ return (ISC_R_RANGE);
+ }
+
+ /*
+ * Cope with very small target sizes.
+ */
+ indexend = sizeof(journal_rawheader_t) +
+ j->header.index_size * sizeof(journal_rawpos_t);
+ if (target_size < indexend * 2)
+ target_size = target_size/2 + indexend;
+
+ /*
+ * See if there is any work to do.
+ */
+ if ((isc_uint32_t) j->header.end.offset < target_size) {
+ dns_journal_destroy(&j);
+ return (ISC_R_SUCCESS);
+ }
+
+ CHECK(journal_open(mctx, newname, ISC_TRUE, ISC_TRUE, &new));
+
+ /*
+ * Remove overhead so space test below can succeed.
+ */
+ if (target_size >= indexend)
+ target_size -= indexend;
+
+ /*
+ * Find if we can create enough free space.
+ */
+ best_guess = j->header.begin;
+ for (i = 0; i < j->header.index_size; i++) {
+ if (POS_VALID(j->index[i]) &&
+ DNS_SERIAL_GE(serial, j->index[i].serial) &&
+ ((isc_uint32_t)(j->header.end.offset - j->index[i].offset)
+ >= target_size / 2) &&
+ j->index[i].offset > best_guess.offset)
+ best_guess = j->index[i];
+ }
+
+ current_pos = best_guess;
+ while (current_pos.serial != serial) {
+ CHECK(journal_next(j, &current_pos));
+ if (current_pos.serial == j->header.end.serial)
+ break;
+
+ if (DNS_SERIAL_GE(serial, current_pos.serial) &&
+ ((isc_uint32_t)(j->header.end.offset - current_pos.offset)
+ >= (target_size / 2)) &&
+ current_pos.offset > best_guess.offset)
+ best_guess = current_pos;
+ else
+ break;
+ }
+
+ INSIST(best_guess.serial != j->header.end.serial);
+ if (best_guess.serial != serial)
+ CHECK(journal_next(j, &best_guess));
+
+ /*
+ * We should now be roughly half target_size provided
+ * we did not reach 'serial'. If not we will just copy
+ * all uncommitted deltas regardless of the size.
+ */
+ copy_length = j->header.end.offset - best_guess.offset;
+
+ if (copy_length != 0) {
+ /*
+ * Copy best_guess to end into space just freed.
+ */
+ size = 64*1024;
+ if (copy_length < size)
+ size = copy_length;
+ buf = isc_mem_get(mctx, size);
+ if (buf == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto failure;
+ }
+
+ CHECK(journal_seek(j, best_guess.offset));
+ CHECK(journal_seek(new, indexend));
+ for (i = 0; i < copy_length; i += size) {
+ unsigned int len = (copy_length - i) > size ? size :
+ (copy_length - i);
+ CHECK(journal_read(j, buf, len));
+ CHECK(journal_write(new, buf, len));
+ }
+
+ CHECK(journal_fsync(new));
+
+ /*
+ * Compute new header.
+ */
+ new->header.begin.serial = best_guess.serial;
+ new->header.begin.offset = indexend;
+ new->header.end.serial = j->header.end.serial;
+ new->header.end.offset = indexend + copy_length;
+
+ /*
+ * Update the journal header.
+ */
+ journal_header_encode(&new->header, &rawheader);
+ CHECK(journal_seek(new, 0));
+ CHECK(journal_write(new, &rawheader, sizeof(rawheader)));
+ CHECK(journal_fsync(new));
+
+ /*
+ * Build new index.
+ */
+ current_pos = new->header.begin;
+ while (current_pos.serial != new->header.end.serial) {
+ index_add(new, &current_pos);
+ CHECK(journal_next(new, &current_pos));
+ }
+
+ /*
+ * Write index.
+ */
+ CHECK(index_to_disk(new));
+ CHECK(journal_fsync(new));
+
+ indexend = new->header.end.offset;
+ }
+ dns_journal_destroy(&new);
+
+ /*
+ * With a UFS file system this should just succeed and be atomic.
+ * Any IXFR outs will just continue and the old journal will be
+ * removed on final close.
+ *
+ * With MSDOS / NTFS we need to do a two stage rename triggered
+ * bu EEXISTS. Hopefully all IXFR's that were active at the last
+ * rename are now complete.
+ */
+ if (rename(newname, filename) == -1) {
+ if (errno == EACCES && !is_backup) {
+ result = isc_file_remove(backup);
+ if (result != ISC_R_SUCCESS &&
+ result != ISC_R_FILENOTFOUND)
+ goto failure;
+ if (rename(filename, backup) == -1)
+ goto maperrno;
+ if (rename(newname, filename) == -1)
+ goto maperrno;
+ (void)isc_file_remove(backup);
+ } else {
+ maperrno:
+ result = ISC_R_FAILURE;
+ goto failure;
+ }
+ }
+
+ dns_journal_destroy(&j);
+ result = ISC_R_SUCCESS;
+
+ failure:
+ (void)isc_file_remove(newname);
+ if (buf != NULL)
+ isc_mem_put(mctx, buf, size);
+ if (j != NULL)
+ dns_journal_destroy(&j);
+ if (new != NULL)
+ dns_journal_destroy(&new);
+ return (result);
+}
+
+static isc_result_t
+index_to_disk(dns_journal_t *j) {
+ isc_result_t result = ISC_R_SUCCESS;
+
+ if (j->header.index_size != 0) {
+ unsigned int i;
+ unsigned char *p;
+ unsigned int rawbytes;
+
+ rawbytes = j->header.index_size * sizeof(journal_rawpos_t);
+
+ p = j->rawindex;
+ for (i = 0; i < j->header.index_size; i++) {
+ encode_uint32(j->index[i].serial, p);
+ p += 4;
+ encode_uint32(j->index[i].offset, p);
+ p += 4;
+ }
+ INSIST(p == j->rawindex + rawbytes);
+
+ CHECK(journal_seek(j, sizeof(journal_rawheader_t)));
+ CHECK(journal_write(j, j->rawindex, rawbytes));
+ }
+failure:
+ return (result);
+}
diff --git a/lib/dns/key.c b/lib/dns/key.c
new file mode 100644
index 0000000..5cf4442
--- /dev/null
+++ b/lib/dns/key.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: key.c,v 1.8 2007/06/19 23:47:16 tbox Exp $ */
+
+#include <config.h>
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include <isc/region.h>
+#include <isc/util.h>
+
+#include <dns/keyvalues.h>
+
+#include <dst/dst.h>
+
+#include "dst_internal.h"
+
+isc_uint16_t
+dst_region_computeid(const isc_region_t *source, unsigned int alg) {
+ isc_uint32_t ac;
+ const unsigned char *p;
+ int size;
+
+ REQUIRE(source != NULL);
+ REQUIRE(source->length >= 4);
+
+ p = source->base;
+ size = source->length;
+
+ if (alg == DST_ALG_RSAMD5)
+ return ((p[size - 3] << 8) + p[size - 2]);
+
+ for (ac = 0; size > 1; size -= 2, p += 2)
+ ac += ((*p) << 8) + *(p + 1);
+
+ if (size > 0)
+ ac += ((*p) << 8);
+ ac += (ac >> 16) & 0xffff;
+
+ return ((isc_uint16_t)(ac & 0xffff));
+}
+
+dns_name_t *
+dst_key_name(const dst_key_t *key) {
+ REQUIRE(VALID_KEY(key));
+ return (key->key_name);
+}
+
+unsigned int
+dst_key_size(const dst_key_t *key) {
+ REQUIRE(VALID_KEY(key));
+ return (key->key_size);
+}
+
+unsigned int
+dst_key_proto(const dst_key_t *key) {
+ REQUIRE(VALID_KEY(key));
+ return (key->key_proto);
+}
+
+unsigned int
+dst_key_alg(const dst_key_t *key) {
+ REQUIRE(VALID_KEY(key));
+ return (key->key_alg);
+}
+
+isc_uint32_t
+dst_key_flags(const dst_key_t *key) {
+ REQUIRE(VALID_KEY(key));
+ return (key->key_flags);
+}
+
+dns_keytag_t
+dst_key_id(const dst_key_t *key) {
+ REQUIRE(VALID_KEY(key));
+ return (key->key_id);
+}
+
+dns_rdataclass_t
+dst_key_class(const dst_key_t *key) {
+ REQUIRE(VALID_KEY(key));
+ return (key->key_class);
+}
+
+isc_boolean_t
+dst_key_iszonekey(const dst_key_t *key) {
+ REQUIRE(VALID_KEY(key));
+
+ if ((key->key_flags & DNS_KEYTYPE_NOAUTH) != 0)
+ return (ISC_FALSE);
+ if ((key->key_flags & DNS_KEYFLAG_OWNERMASK) != DNS_KEYOWNER_ZONE)
+ return (ISC_FALSE);
+ if (key->key_proto != DNS_KEYPROTO_DNSSEC &&
+ key->key_proto != DNS_KEYPROTO_ANY)
+ return (ISC_FALSE);
+ return (ISC_TRUE);
+}
+
+isc_boolean_t
+dst_key_isnullkey(const dst_key_t *key) {
+ REQUIRE(VALID_KEY(key));
+
+ if ((key->key_flags & DNS_KEYFLAG_TYPEMASK) != DNS_KEYTYPE_NOKEY)
+ return (ISC_FALSE);
+ if ((key->key_flags & DNS_KEYFLAG_OWNERMASK) != DNS_KEYOWNER_ZONE)
+ return (ISC_FALSE);
+ if (key->key_proto != DNS_KEYPROTO_DNSSEC &&
+ key->key_proto != DNS_KEYPROTO_ANY)
+ return (ISC_FALSE);
+ return (ISC_TRUE);
+}
+
+void
+dst_key_setbits(dst_key_t *key, isc_uint16_t bits) {
+ unsigned int maxbits;
+ REQUIRE(VALID_KEY(key));
+ if (bits != 0) {
+ RUNTIME_CHECK(dst_key_sigsize(key, &maxbits) == ISC_R_SUCCESS);
+ maxbits *= 8;
+ REQUIRE(bits <= maxbits);
+ }
+ key->key_bits = bits;
+}
+
+isc_uint16_t
+dst_key_getbits(const dst_key_t *key) {
+ REQUIRE(VALID_KEY(key));
+ return (key->key_bits);
+}
+
+/*! \file */
diff --git a/lib/dns/keytable.c b/lib/dns/keytable.c
new file mode 100644
index 0000000..bffd2d3
--- /dev/null
+++ b/lib/dns/keytable.c
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: keytable.c,v 1.34 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/mem.h>
+#include <isc/rwlock.h>
+#include <isc/string.h> /* Required for HP/UX (and others?) */
+#include <isc/util.h>
+
+#include <dns/keytable.h>
+#include <dns/fixedname.h>
+#include <dns/rbt.h>
+#include <dns/result.h>
+
+struct dns_keytable {
+ /* Unlocked. */
+ unsigned int magic;
+ isc_mem_t *mctx;
+ isc_mutex_t lock;
+ isc_rwlock_t rwlock;
+ /* Locked by lock. */
+ isc_uint32_t active_nodes;
+ /* Locked by rwlock. */
+ isc_uint32_t references;
+ dns_rbt_t *table;
+};
+
+#define KEYTABLE_MAGIC ISC_MAGIC('K', 'T', 'b', 'l')
+#define VALID_KEYTABLE(kt) ISC_MAGIC_VALID(kt, KEYTABLE_MAGIC)
+
+struct dns_keynode {
+ unsigned int magic;
+ dst_key_t * key;
+ struct dns_keynode * next;
+};
+
+#define KEYNODE_MAGIC ISC_MAGIC('K', 'N', 'o', 'd')
+#define VALID_KEYNODE(kn) ISC_MAGIC_VALID(kn, KEYNODE_MAGIC)
+
+static void
+free_keynode(void *node, void *arg) {
+ dns_keynode_t *keynode = node;
+ isc_mem_t *mctx = arg;
+
+ REQUIRE(VALID_KEYNODE(keynode));
+ dst_key_free(&keynode->key);
+ if (keynode->next != NULL)
+ free_keynode(keynode->next, mctx);
+ isc_mem_put(mctx, keynode, sizeof(dns_keynode_t));
+}
+
+isc_result_t
+dns_keytable_create(isc_mem_t *mctx, dns_keytable_t **keytablep) {
+ dns_keytable_t *keytable;
+ isc_result_t result;
+
+ /*
+ * Create a keytable.
+ */
+
+ REQUIRE(keytablep != NULL && *keytablep == NULL);
+
+ keytable = isc_mem_get(mctx, sizeof(*keytable));
+ if (keytable == NULL)
+ return (ISC_R_NOMEMORY);
+
+ keytable->table = NULL;
+ result = dns_rbt_create(mctx, free_keynode, mctx, &keytable->table);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_keytable;
+
+ result = isc_mutex_init(&keytable->lock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_rbt;
+
+ result = isc_rwlock_init(&keytable->rwlock, 0, 0);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_lock;
+
+ keytable->mctx = mctx;
+ keytable->active_nodes = 0;
+ keytable->references = 1;
+ keytable->magic = KEYTABLE_MAGIC;
+ *keytablep = keytable;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup_lock:
+ DESTROYLOCK(&keytable->lock);
+
+ cleanup_rbt:
+ dns_rbt_destroy(&keytable->table);
+
+ cleanup_keytable:
+ isc_mem_put(mctx, keytable, sizeof(*keytable));
+
+ return (result);
+}
+
+
+void
+dns_keytable_attach(dns_keytable_t *source, dns_keytable_t **targetp) {
+
+ /*
+ * Attach *targetp to source.
+ */
+
+ REQUIRE(VALID_KEYTABLE(source));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ RWLOCK(&source->rwlock, isc_rwlocktype_write);
+
+ INSIST(source->references > 0);
+ source->references++;
+ INSIST(source->references != 0);
+
+ RWUNLOCK(&source->rwlock, isc_rwlocktype_write);
+
+ *targetp = source;
+}
+
+void
+dns_keytable_detach(dns_keytable_t **keytablep) {
+ isc_boolean_t destroy = ISC_FALSE;
+ dns_keytable_t *keytable;
+
+ /*
+ * Detach *keytablep from its keytable.
+ */
+
+ REQUIRE(keytablep != NULL && VALID_KEYTABLE(*keytablep));
+
+ keytable = *keytablep;
+
+ RWLOCK(&keytable->rwlock, isc_rwlocktype_write);
+
+ INSIST(keytable->references > 0);
+ keytable->references--;
+ LOCK(&keytable->lock);
+ if (keytable->references == 0 && keytable->active_nodes == 0)
+ destroy = ISC_TRUE;
+ UNLOCK(&keytable->lock);
+
+ RWUNLOCK(&keytable->rwlock, isc_rwlocktype_write);
+
+ if (destroy) {
+ dns_rbt_destroy(&keytable->table);
+ isc_rwlock_destroy(&keytable->rwlock);
+ DESTROYLOCK(&keytable->lock);
+ keytable->magic = 0;
+ isc_mem_put(keytable->mctx, keytable, sizeof(*keytable));
+ }
+
+ *keytablep = NULL;
+}
+
+isc_result_t
+dns_keytable_add(dns_keytable_t *keytable, dst_key_t **keyp) {
+ isc_result_t result;
+ dns_keynode_t *knode;
+ dns_rbtnode_t *node;
+ dns_name_t *keyname;
+
+ /*
+ * Add '*keyp' to 'keytable'.
+ */
+
+ REQUIRE(VALID_KEYTABLE(keytable));
+ REQUIRE(keyp != NULL);
+
+ keyname = dst_key_name(*keyp);
+
+ knode = isc_mem_get(keytable->mctx, sizeof(*knode));
+ if (knode == NULL)
+ return (ISC_R_NOMEMORY);
+
+ RWLOCK(&keytable->rwlock, isc_rwlocktype_write);
+
+ node = NULL;
+ result = dns_rbt_addnode(keytable->table, keyname, &node);
+
+ if (result == ISC_R_SUCCESS || result == ISC_R_EXISTS) {
+ knode->magic = KEYNODE_MAGIC;
+ knode->key = *keyp;
+ knode->next = node->data;
+ node->data = knode;
+ *keyp = NULL;
+ knode = NULL;
+ result = ISC_R_SUCCESS;
+ }
+
+ RWUNLOCK(&keytable->rwlock, isc_rwlocktype_write);
+
+ if (knode != NULL)
+ isc_mem_put(keytable->mctx, knode, sizeof(*knode));
+
+ return (result);
+}
+
+isc_result_t
+dns_keytable_findkeynode(dns_keytable_t *keytable, dns_name_t *name,
+ dns_secalg_t algorithm, dns_keytag_t tag,
+ dns_keynode_t **keynodep)
+{
+ isc_result_t result;
+ dns_keynode_t *knode;
+ void *data;
+
+ /*
+ * Search for a key named 'name', matching 'algorithm' and 'tag' in
+ * 'keytable'.
+ */
+
+ REQUIRE(VALID_KEYTABLE(keytable));
+ REQUIRE(dns_name_isabsolute(name));
+ REQUIRE(keynodep != NULL && *keynodep == NULL);
+
+ RWLOCK(&keytable->rwlock, isc_rwlocktype_read);
+
+ /*
+ * Note we don't want the DNS_R_PARTIALMATCH from dns_rbt_findname()
+ * as that indicates that 'name' was not found.
+ *
+ * DNS_R_PARTIALMATCH indicates that the name was found but we
+ * didn't get a match on algorithm and key id arguments.
+ */
+ knode = NULL;
+ data = NULL;
+ result = dns_rbt_findname(keytable->table, name, 0, NULL, &data);
+
+ if (result == ISC_R_SUCCESS) {
+ INSIST(data != NULL);
+ for (knode = data; knode != NULL; knode = knode->next) {
+ if (algorithm == dst_key_alg(knode->key)
+ && tag == dst_key_id(knode->key))
+ break;
+ }
+ if (knode != NULL) {
+ LOCK(&keytable->lock);
+ keytable->active_nodes++;
+ UNLOCK(&keytable->lock);
+ *keynodep = knode;
+ } else
+ result = DNS_R_PARTIALMATCH;
+ } else if (result == DNS_R_PARTIALMATCH)
+ result = ISC_R_NOTFOUND;
+
+ RWUNLOCK(&keytable->rwlock, isc_rwlocktype_read);
+
+ return (result);
+}
+
+isc_result_t
+dns_keytable_findnextkeynode(dns_keytable_t *keytable, dns_keynode_t *keynode,
+ dns_keynode_t **nextnodep)
+{
+ isc_result_t result;
+ dns_keynode_t *knode;
+
+ /*
+ * Search for the next key with the same properties as 'keynode' in
+ * 'keytable'.
+ */
+
+ REQUIRE(VALID_KEYTABLE(keytable));
+ REQUIRE(VALID_KEYNODE(keynode));
+ REQUIRE(nextnodep != NULL && *nextnodep == NULL);
+
+ for (knode = keynode->next; knode != NULL; knode = knode->next) {
+ if (dst_key_alg(keynode->key) == dst_key_alg(knode->key) &&
+ dst_key_id(keynode->key) == dst_key_id(knode->key))
+ break;
+ }
+ if (knode != NULL) {
+ LOCK(&keytable->lock);
+ keytable->active_nodes++;
+ UNLOCK(&keytable->lock);
+ result = ISC_R_SUCCESS;
+ *nextnodep = knode;
+ } else
+ result = ISC_R_NOTFOUND;
+
+ return (result);
+}
+
+isc_result_t
+dns_keytable_finddeepestmatch(dns_keytable_t *keytable, dns_name_t *name,
+ dns_name_t *foundname)
+{
+ isc_result_t result;
+ void *data;
+
+ /*
+ * Search for the deepest match in 'keytable'.
+ */
+
+ REQUIRE(VALID_KEYTABLE(keytable));
+ REQUIRE(dns_name_isabsolute(name));
+ REQUIRE(foundname != NULL);
+
+ RWLOCK(&keytable->rwlock, isc_rwlocktype_read);
+
+ data = NULL;
+ result = dns_rbt_findname(keytable->table, name, 0, foundname, &data);
+
+ if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
+ result = ISC_R_SUCCESS;
+
+ RWUNLOCK(&keytable->rwlock, isc_rwlocktype_read);
+
+ return (result);
+}
+
+void
+dns_keytable_detachkeynode(dns_keytable_t *keytable, dns_keynode_t **keynodep)
+{
+ /*
+ * Give back a keynode found via dns_keytable_findkeynode().
+ */
+
+ REQUIRE(VALID_KEYTABLE(keytable));
+ REQUIRE(keynodep != NULL && VALID_KEYNODE(*keynodep));
+
+ LOCK(&keytable->lock);
+ INSIST(keytable->active_nodes > 0);
+ keytable->active_nodes--;
+ UNLOCK(&keytable->lock);
+
+ *keynodep = NULL;
+}
+
+isc_result_t
+dns_keytable_issecuredomain(dns_keytable_t *keytable, dns_name_t *name,
+ isc_boolean_t *wantdnssecp)
+{
+ isc_result_t result;
+ void *data;
+
+ /*
+ * Is 'name' at or beneath a trusted key?
+ */
+
+ REQUIRE(VALID_KEYTABLE(keytable));
+ REQUIRE(dns_name_isabsolute(name));
+ REQUIRE(wantdnssecp != NULL);
+
+ RWLOCK(&keytable->rwlock, isc_rwlocktype_read);
+
+ data = NULL;
+ result = dns_rbt_findname(keytable->table, name, 0, NULL, &data);
+
+ if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
+ INSIST(data != NULL);
+ *wantdnssecp = ISC_TRUE;
+ result = ISC_R_SUCCESS;
+ } else if (result == ISC_R_NOTFOUND) {
+ *wantdnssecp = ISC_FALSE;
+ result = ISC_R_SUCCESS;
+ }
+
+ RWUNLOCK(&keytable->rwlock, isc_rwlocktype_read);
+
+ return (result);
+}
+
+dst_key_t *
+dns_keynode_key(dns_keynode_t *keynode) {
+
+ /*
+ * Get the DST key associated with keynode.
+ */
+
+ REQUIRE(VALID_KEYNODE(keynode));
+
+ return (keynode->key);
+}
diff --git a/lib/dns/lib.c b/lib/dns/lib.c
new file mode 100644
index 0000000..6f98b53
--- /dev/null
+++ b/lib/dns/lib.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: lib.c,v 1.16 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stddef.h>
+
+#include <isc/once.h>
+#include <isc/msgcat.h>
+#include <isc/util.h>
+
+#include <dns/lib.h>
+
+/***
+ *** Globals
+ ***/
+
+LIBDNS_EXTERNAL_DATA unsigned int dns_pps = 0U;
+LIBDNS_EXTERNAL_DATA isc_msgcat_t * dns_msgcat = NULL;
+
+
+/***
+ *** Private
+ ***/
+
+static isc_once_t msgcat_once = ISC_ONCE_INIT;
+
+
+/***
+ *** Functions
+ ***/
+
+static void
+open_msgcat(void) {
+ isc_msgcat_open("libdns.cat", &dns_msgcat);
+}
+
+void
+dns_lib_initmsgcat(void) {
+
+ /*
+ * Initialize the DNS library's message catalog, dns_msgcat, if it
+ * has not already been initialized.
+ */
+
+ RUNTIME_CHECK(isc_once_do(&msgcat_once, open_msgcat) == ISC_R_SUCCESS);
+}
diff --git a/lib/dns/log.c b/lib/dns/log.c
new file mode 100644
index 0000000..7551e15
--- /dev/null
+++ b/lib/dns/log.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001, 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: log.c,v 1.45 2007/06/18 23:47:40 tbox Exp $ */
+
+/*! \file */
+
+/* Principal Authors: DCL */
+
+#include <config.h>
+
+#include <isc/util.h>
+
+#include <dns/log.h>
+
+/*%
+ * When adding a new category, be sure to add the appropriate
+ * \#define to <dns/log.h>.
+ */
+LIBDNS_EXTERNAL_DATA isc_logcategory_t dns_categories[] = {
+ { "notify", 0 },
+ { "database", 0 },
+ { "security", 0 },
+ { "_placeholder", 0 },
+ { "dnssec", 0 },
+ { "resolver", 0 },
+ { "xfer-in", 0 },
+ { "xfer-out", 0 },
+ { "dispatch", 0 },
+ { "lame-servers", 0 },
+ { "delegation-only", 0 },
+ { "edns-disabled", 0 },
+ { NULL, 0 }
+};
+
+/*%
+ * When adding a new module, be sure to add the appropriate
+ * \#define to <dns/log.h>.
+ */
+LIBDNS_EXTERNAL_DATA isc_logmodule_t dns_modules[] = {
+ { "dns/db", 0 },
+ { "dns/rbtdb", 0 },
+ { "dns/rbtdb64", 0 },
+ { "dns/rbt", 0 },
+ { "dns/rdata", 0 },
+ { "dns/master", 0 },
+ { "dns/message", 0 },
+ { "dns/cache", 0 },
+ { "dns/config", 0 },
+ { "dns/resolver", 0 },
+ { "dns/zone", 0 },
+ { "dns/journal", 0 },
+ { "dns/adb", 0 },
+ { "dns/xfrin", 0 },
+ { "dns/xfrout", 0 },
+ { "dns/acl", 0 },
+ { "dns/validator", 0 },
+ { "dns/dispatch", 0 },
+ { "dns/request", 0 },
+ { "dns/masterdump", 0 },
+ { "dns/tsig", 0 },
+ { "dns/tkey", 0 },
+ { "dns/sdb", 0 },
+ { "dns/diff", 0 },
+ { "dns/hints", 0 },
+ { "dns/acache", 0 },
+ { "dns/dlz", 0 },
+ { NULL, 0 }
+};
+
+LIBDNS_EXTERNAL_DATA isc_log_t *dns_lctx = NULL;
+
+void
+dns_log_init(isc_log_t *lctx) {
+ REQUIRE(lctx != NULL);
+
+ isc_log_registercategories(lctx, dns_categories);
+ isc_log_registermodules(lctx, dns_modules);
+}
+
+void
+dns_log_setcontext(isc_log_t *lctx) {
+ dns_lctx = lctx;
+}
diff --git a/lib/dns/lookup.c b/lib/dns/lookup.c
new file mode 100644
index 0000000..d5fc7aa
--- /dev/null
+++ b/lib/dns/lookup.c
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001, 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: lookup.c,v 1.21 2007/06/18 23:47:40 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/mem.h>
+#include <isc/netaddr.h>
+#include <isc/string.h> /* Required for HP/UX (and others?) */
+#include <isc/task.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/events.h>
+#include <dns/lookup.h>
+#include <dns/rdata.h>
+#include <dns/rdataset.h>
+#include <dns/rdatastruct.h>
+#include <dns/resolver.h>
+#include <dns/result.h>
+#include <dns/view.h>
+
+struct dns_lookup {
+ /* Unlocked. */
+ unsigned int magic;
+ isc_mem_t * mctx;
+ isc_mutex_t lock;
+ dns_rdatatype_t type;
+ dns_fixedname_t name;
+ /* Locked by lock. */
+ unsigned int options;
+ isc_task_t * task;
+ dns_view_t * view;
+ dns_lookupevent_t * event;
+ dns_fetch_t * fetch;
+ unsigned int restarts;
+ isc_boolean_t canceled;
+ dns_rdataset_t rdataset;
+ dns_rdataset_t sigrdataset;
+};
+
+#define LOOKUP_MAGIC ISC_MAGIC('l', 'o', 'o', 'k')
+#define VALID_LOOKUP(l) ISC_MAGIC_VALID((l), LOOKUP_MAGIC)
+
+#define MAX_RESTARTS 16
+
+static void lookup_find(dns_lookup_t *lookup, dns_fetchevent_t *event);
+
+static void
+fetch_done(isc_task_t *task, isc_event_t *event) {
+ dns_lookup_t *lookup = event->ev_arg;
+ dns_fetchevent_t *fevent;
+
+ UNUSED(task);
+ REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE);
+ REQUIRE(VALID_LOOKUP(lookup));
+ REQUIRE(lookup->task == task);
+ fevent = (dns_fetchevent_t *)event;
+ REQUIRE(fevent->fetch == lookup->fetch);
+
+ lookup_find(lookup, fevent);
+}
+
+static inline isc_result_t
+start_fetch(dns_lookup_t *lookup) {
+ isc_result_t result;
+
+ /*
+ * The caller must be holding the lookup's lock.
+ */
+
+ REQUIRE(lookup->fetch == NULL);
+
+ result = dns_resolver_createfetch(lookup->view->resolver,
+ dns_fixedname_name(&lookup->name),
+ lookup->type,
+ NULL, NULL, NULL, 0,
+ lookup->task, fetch_done, lookup,
+ &lookup->rdataset,
+ &lookup->sigrdataset,
+ &lookup->fetch);
+
+ return (result);
+}
+
+static isc_result_t
+build_event(dns_lookup_t *lookup) {
+ dns_name_t *name = NULL;
+ dns_rdataset_t *rdataset = NULL;
+ dns_rdataset_t *sigrdataset = NULL;
+ isc_result_t result;
+
+ name = isc_mem_get(lookup->mctx, sizeof(dns_name_t));
+ if (name == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto fail;
+ }
+ dns_name_init(name, NULL);
+ result = dns_name_dup(dns_fixedname_name(&lookup->name),
+ lookup->mctx, name);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+
+ if (dns_rdataset_isassociated(&lookup->rdataset)) {
+ rdataset = isc_mem_get(lookup->mctx, sizeof(dns_rdataset_t));
+ if (rdataset == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto fail;
+ }
+ dns_rdataset_init(rdataset);
+ dns_rdataset_clone(&lookup->rdataset, rdataset);
+ }
+
+ if (dns_rdataset_isassociated(&lookup->sigrdataset)) {
+ sigrdataset = isc_mem_get(lookup->mctx,
+ sizeof(dns_rdataset_t));
+ if (sigrdataset == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto fail;
+ }
+ dns_rdataset_init(sigrdataset);
+ dns_rdataset_clone(&lookup->sigrdataset, sigrdataset);
+ }
+
+ lookup->event->name = name;
+ lookup->event->rdataset = rdataset;
+ lookup->event->sigrdataset = sigrdataset;
+
+ return (ISC_R_SUCCESS);
+
+ fail:
+ if (name != NULL) {
+ if (dns_name_dynamic(name))
+ dns_name_free(name, lookup->mctx);
+ isc_mem_put(lookup->mctx, name, sizeof(dns_name_t));
+ }
+ if (rdataset != NULL) {
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ isc_mem_put(lookup->mctx, rdataset, sizeof(dns_rdataset_t));
+ }
+ return (result);
+}
+
+static isc_result_t
+view_find(dns_lookup_t *lookup, dns_name_t *foundname) {
+ isc_result_t result;
+ dns_name_t *name = dns_fixedname_name(&lookup->name);
+ dns_rdatatype_t type;
+
+ if (lookup->type == dns_rdatatype_rrsig)
+ type = dns_rdatatype_any;
+ else
+ type = lookup->type;
+
+ result = dns_view_find(lookup->view, name, type, 0, 0, ISC_FALSE,
+ &lookup->event->db, &lookup->event->node,
+ foundname, &lookup->rdataset,
+ &lookup->sigrdataset);
+ return (result);
+}
+
+static void
+lookup_find(dns_lookup_t *lookup, dns_fetchevent_t *event) {
+ isc_result_t result;
+ isc_boolean_t want_restart;
+ isc_boolean_t send_event;
+ dns_name_t *name, *fname, *prefix;
+ dns_fixedname_t foundname, fixed;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ unsigned int nlabels;
+ int order;
+ dns_namereln_t namereln;
+ dns_rdata_cname_t cname;
+ dns_rdata_dname_t dname;
+
+ REQUIRE(VALID_LOOKUP(lookup));
+
+ LOCK(&lookup->lock);
+
+ result = ISC_R_SUCCESS;
+ name = dns_fixedname_name(&lookup->name);
+
+ do {
+ lookup->restarts++;
+ want_restart = ISC_FALSE;
+ send_event = ISC_TRUE;
+
+ if (event == NULL && !lookup->canceled) {
+ dns_fixedname_init(&foundname);
+ fname = dns_fixedname_name(&foundname);
+ INSIST(!dns_rdataset_isassociated(&lookup->rdataset));
+ INSIST(!dns_rdataset_isassociated
+ (&lookup->sigrdataset));
+ /*
+ * If we have restarted then clear the old node. */
+ if (lookup->event->node != NULL) {
+ INSIST(lookup->event->db != NULL);
+ dns_db_detachnode(lookup->event->db,
+ &lookup->event->node);
+ }
+ if (lookup->event->db != NULL)
+ dns_db_detach(&lookup->event->db);
+ result = view_find(lookup, fname);
+ if (result == ISC_R_NOTFOUND) {
+ /*
+ * We don't know anything about the name.
+ * Launch a fetch.
+ */
+ if (lookup->event->node != NULL) {
+ INSIST(lookup->event->db != NULL);
+ dns_db_detachnode(lookup->event->db,
+ &lookup->event->node);
+ }
+ if (lookup->event->db != NULL)
+ dns_db_detach(&lookup->event->db);
+ result = start_fetch(lookup);
+ if (result == ISC_R_SUCCESS)
+ send_event = ISC_FALSE;
+ goto done;
+ }
+ } else if (event != NULL) {
+ result = event->result;
+ fname = dns_fixedname_name(&event->foundname);
+ dns_resolver_destroyfetch(&lookup->fetch);
+ INSIST(event->rdataset == &lookup->rdataset);
+ INSIST(event->sigrdataset == &lookup->sigrdataset);
+ } else
+ fname = NULL; /* Silence compiler warning. */
+
+ /*
+ * If we've been canceled, forget about the result.
+ */
+ if (lookup->canceled)
+ result = ISC_R_CANCELED;
+
+ switch (result) {
+ case ISC_R_SUCCESS:
+ result = build_event(lookup);
+ if (event == NULL)
+ break;
+ if (event->db != NULL)
+ dns_db_attach(event->db, &lookup->event->db);
+ if (event->node != NULL)
+ dns_db_attachnode(lookup->event->db,
+ event->node,
+ &lookup->event->node);
+ break;
+ case DNS_R_CNAME:
+ /*
+ * Copy the CNAME's target into the lookup's
+ * query name and start over.
+ */
+ result = dns_rdataset_first(&lookup->rdataset);
+ if (result != ISC_R_SUCCESS)
+ break;
+ dns_rdataset_current(&lookup->rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &cname, NULL);
+ dns_rdata_reset(&rdata);
+ if (result != ISC_R_SUCCESS)
+ break;
+ result = dns_name_copy(&cname.cname, name, NULL);
+ dns_rdata_freestruct(&cname);
+ if (result == ISC_R_SUCCESS) {
+ want_restart = ISC_TRUE;
+ send_event = ISC_FALSE;
+ }
+ break;
+ case DNS_R_DNAME:
+ namereln = dns_name_fullcompare(name, fname, &order,
+ &nlabels);
+ INSIST(namereln == dns_namereln_subdomain);
+ /*
+ * Get the target name of the DNAME.
+ */
+ result = dns_rdataset_first(&lookup->rdataset);
+ if (result != ISC_R_SUCCESS)
+ break;
+ dns_rdataset_current(&lookup->rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &dname, NULL);
+ dns_rdata_reset(&rdata);
+ if (result != ISC_R_SUCCESS)
+ break;
+ /*
+ * Construct the new query name and start over.
+ */
+ dns_fixedname_init(&fixed);
+ prefix = dns_fixedname_name(&fixed);
+ dns_name_split(name, nlabels, prefix, NULL);
+ result = dns_name_concatenate(prefix, &dname.dname,
+ name, NULL);
+ dns_rdata_freestruct(&dname);
+ if (result == ISC_R_SUCCESS) {
+ want_restart = ISC_TRUE;
+ send_event = ISC_FALSE;
+ }
+ break;
+ default:
+ send_event = ISC_TRUE;
+ }
+
+ if (dns_rdataset_isassociated(&lookup->rdataset))
+ dns_rdataset_disassociate(&lookup->rdataset);
+ if (dns_rdataset_isassociated(&lookup->sigrdataset))
+ dns_rdataset_disassociate(&lookup->sigrdataset);
+
+ done:
+ if (event != NULL) {
+ if (event->node != NULL)
+ dns_db_detachnode(event->db, &event->node);
+ if (event->db != NULL)
+ dns_db_detach(&event->db);
+ isc_event_free(ISC_EVENT_PTR(&event));
+ }
+
+ /*
+ * Limit the number of restarts.
+ */
+ if (want_restart && lookup->restarts == MAX_RESTARTS) {
+ want_restart = ISC_FALSE;
+ result = ISC_R_QUOTA;
+ send_event = ISC_TRUE;
+ }
+
+ } while (want_restart);
+
+ if (send_event) {
+ lookup->event->result = result;
+ lookup->event->ev_sender = lookup;
+ isc_task_sendanddetach(&lookup->task,
+ (isc_event_t **)&lookup->event);
+ dns_view_detach(&lookup->view);
+ }
+
+ UNLOCK(&lookup->lock);
+}
+
+static void
+levent_destroy(isc_event_t *event) {
+ dns_lookupevent_t *levent;
+ isc_mem_t *mctx;
+
+ REQUIRE(event->ev_type == DNS_EVENT_LOOKUPDONE);
+ mctx = event->ev_destroy_arg;
+ levent = (dns_lookupevent_t *)event;
+
+ if (levent->name != NULL) {
+ if (dns_name_dynamic(levent->name))
+ dns_name_free(levent->name, mctx);
+ isc_mem_put(mctx, levent->name, sizeof(dns_name_t));
+ }
+ if (levent->rdataset != NULL) {
+ dns_rdataset_disassociate(levent->rdataset);
+ isc_mem_put(mctx, levent->rdataset, sizeof(dns_rdataset_t));
+ }
+ if (levent->sigrdataset != NULL) {
+ dns_rdataset_disassociate(levent->sigrdataset);
+ isc_mem_put(mctx, levent->sigrdataset, sizeof(dns_rdataset_t));
+ }
+ if (levent->node != NULL)
+ dns_db_detachnode(levent->db, &levent->node);
+ if (levent->db != NULL)
+ dns_db_detach(&levent->db);
+ isc_mem_put(mctx, event, event->ev_size);
+}
+
+isc_result_t
+dns_lookup_create(isc_mem_t *mctx, dns_name_t *name, dns_rdatatype_t type,
+ dns_view_t *view, unsigned int options, isc_task_t *task,
+ isc_taskaction_t action, void *arg, dns_lookup_t **lookupp)
+{
+ isc_result_t result;
+ dns_lookup_t *lookup;
+ isc_event_t *ievent;
+
+ lookup = isc_mem_get(mctx, sizeof(*lookup));
+ if (lookup == NULL)
+ return (ISC_R_NOMEMORY);
+ lookup->mctx = mctx;
+ lookup->options = options;
+
+ ievent = isc_event_allocate(mctx, lookup, DNS_EVENT_LOOKUPDONE,
+ action, arg, sizeof(*lookup->event));
+ if (ievent == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_lookup;
+ }
+ lookup->event = (dns_lookupevent_t *)ievent;
+ lookup->event->ev_destroy = levent_destroy;
+ lookup->event->ev_destroy_arg = mctx;
+ lookup->event->result = ISC_R_FAILURE;
+ lookup->event->name = NULL;
+ lookup->event->rdataset = NULL;
+ lookup->event->sigrdataset = NULL;
+ lookup->event->db = NULL;
+ lookup->event->node = NULL;
+
+ lookup->task = NULL;
+ isc_task_attach(task, &lookup->task);
+
+ result = isc_mutex_init(&lookup->lock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_event;
+
+ dns_fixedname_init(&lookup->name);
+
+ result = dns_name_copy(name, dns_fixedname_name(&lookup->name), NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_lock;
+
+ lookup->type = type;
+ lookup->view = NULL;
+ dns_view_attach(view, &lookup->view);
+ lookup->fetch = NULL;
+ lookup->restarts = 0;
+ lookup->canceled = ISC_FALSE;
+ dns_rdataset_init(&lookup->rdataset);
+ dns_rdataset_init(&lookup->sigrdataset);
+ lookup->magic = LOOKUP_MAGIC;
+
+ *lookupp = lookup;
+
+ lookup_find(lookup, NULL);
+
+ return (ISC_R_SUCCESS);
+
+ cleanup_lock:
+ DESTROYLOCK(&lookup->lock);
+
+ cleanup_event:
+ ievent = (isc_event_t *)lookup->event;
+ isc_event_free(&ievent);
+ lookup->event = NULL;
+
+ isc_task_detach(&lookup->task);
+
+ cleanup_lookup:
+ isc_mem_put(mctx, lookup, sizeof(*lookup));
+
+ return (result);
+}
+
+void
+dns_lookup_cancel(dns_lookup_t *lookup) {
+ REQUIRE(VALID_LOOKUP(lookup));
+
+ LOCK(&lookup->lock);
+
+ if (!lookup->canceled) {
+ lookup->canceled = ISC_TRUE;
+ if (lookup->fetch != NULL) {
+ INSIST(lookup->view != NULL);
+ dns_resolver_cancelfetch(lookup->fetch);
+ }
+ }
+
+ UNLOCK(&lookup->lock);
+}
+
+void
+dns_lookup_destroy(dns_lookup_t **lookupp) {
+ dns_lookup_t *lookup;
+
+ REQUIRE(lookupp != NULL);
+ lookup = *lookupp;
+ REQUIRE(VALID_LOOKUP(lookup));
+ REQUIRE(lookup->event == NULL);
+ REQUIRE(lookup->task == NULL);
+ REQUIRE(lookup->view == NULL);
+ if (dns_rdataset_isassociated(&lookup->rdataset))
+ dns_rdataset_disassociate(&lookup->rdataset);
+ if (dns_rdataset_isassociated(&lookup->sigrdataset))
+ dns_rdataset_disassociate(&lookup->sigrdataset);
+
+ DESTROYLOCK(&lookup->lock);
+ lookup->magic = 0;
+ isc_mem_put(lookup->mctx, lookup, sizeof(*lookup));
+
+ *lookupp = NULL;
+}
diff --git a/lib/dns/master.c b/lib/dns/master.c
new file mode 100644
index 0000000..743e87b
--- /dev/null
+++ b/lib/dns/master.c
@@ -0,0 +1,2892 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: master.c,v 1.171 2008/04/02 02:37:42 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/event.h>
+#include <isc/lex.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/serial.h>
+#include <isc/stdio.h>
+#include <isc/stdtime.h>
+#include <isc/string.h>
+#include <isc/task.h>
+#include <isc/util.h>
+
+#include <dns/callbacks.h>
+#include <dns/events.h>
+#include <dns/fixedname.h>
+#include <dns/master.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatastruct.h>
+#include <dns/rdatatype.h>
+#include <dns/result.h>
+#include <dns/soa.h>
+#include <dns/time.h>
+#include <dns/ttl.h>
+
+/*!
+ * Grow the number of dns_rdatalist_t (#RDLSZ) and dns_rdata_t (#RDSZ) structures
+ * by these sizes when we need to.
+ *
+ */
+/*% RDLSZ reflects the number of different types with the same name expected. */
+#define RDLSZ 32
+/*%
+ * RDSZ reflects the number of rdata expected at a give name that can fit into
+ * 64k.
+ */
+#define RDSZ 512
+
+#define NBUFS 4
+#define MAXWIRESZ 255
+
+/*%
+ * Target buffer size and minimum target size.
+ * MINTSIZ must be big enough to hold the largest rdata record.
+ * \brief
+ * TSIZ >= MINTSIZ
+ */
+#define TSIZ (128*1024)
+/*%
+ * max message size - header - root - type - class - ttl - rdlen
+ */
+#define MINTSIZ (65535 - 12 - 1 - 2 - 2 - 4 - 2)
+/*%
+ * Size for tokens in the presentation format,
+ * The largest tokens are the base64 blocks in KEY and CERT records,
+ * Largest key allowed is about 1372 bytes but
+ * there is no fixed upper bound on CERT records.
+ * 2K is too small for some X.509s, 8K is overkill.
+ */
+#define TOKENSIZ (8*1024)
+
+#define DNS_MASTER_BUFSZ 2048
+
+typedef ISC_LIST(dns_rdatalist_t) rdatalist_head_t;
+
+typedef struct dns_incctx dns_incctx_t;
+
+/*%
+ * Master file load state.
+ */
+
+struct dns_loadctx {
+ unsigned int magic;
+ isc_mem_t *mctx;
+ dns_masterformat_t format;
+
+ dns_rdatacallbacks_t *callbacks;
+ isc_task_t *task;
+ dns_loaddonefunc_t done;
+ void *done_arg;
+
+ /* Common methods */
+ isc_result_t (*openfile)(dns_loadctx_t *lctx,
+ const char *filename);
+ isc_result_t (*load)(dns_loadctx_t *lctx);
+
+ /* Members specific to the text format: */
+ isc_lex_t *lex;
+ isc_boolean_t keep_lex;
+ unsigned int options;
+ isc_boolean_t ttl_known;
+ isc_boolean_t default_ttl_known;
+ isc_boolean_t warn_1035;
+ isc_boolean_t warn_tcr;
+ isc_boolean_t warn_sigexpired;
+ isc_boolean_t seen_include;
+ isc_uint32_t ttl;
+ isc_uint32_t default_ttl;
+ dns_rdataclass_t zclass;
+ dns_fixedname_t fixed_top;
+ dns_name_t *top; /*%< top of zone */
+
+ /* Members specific to the raw format: */
+ FILE *f;
+ isc_boolean_t first;
+
+ /* Which fixed buffers we are using? */
+ unsigned int loop_cnt; /*% records per quantum,
+ * 0 => all. */
+ isc_boolean_t canceled;
+ isc_mutex_t lock;
+ isc_result_t result;
+ /* locked by lock */
+ isc_uint32_t references;
+ dns_incctx_t *inc;
+ isc_uint32_t resign;
+};
+
+struct dns_incctx {
+ dns_incctx_t *parent;
+ dns_name_t *origin;
+ dns_name_t *current;
+ dns_name_t *glue;
+ dns_fixedname_t fixed[NBUFS]; /* working buffers */
+ unsigned int in_use[NBUFS]; /* covert to bitmap? */
+ int glue_in_use;
+ int current_in_use;
+ int origin_in_use;
+ isc_boolean_t drop;
+ unsigned int glue_line;
+ unsigned int current_line;
+};
+
+#define DNS_LCTX_MAGIC ISC_MAGIC('L','c','t','x')
+#define DNS_LCTX_VALID(lctx) ISC_MAGIC_VALID(lctx, DNS_LCTX_MAGIC)
+
+#define DNS_AS_STR(t) ((t).value.as_textregion.base)
+
+static isc_result_t
+openfile_text(dns_loadctx_t *lctx, const char *master_file);
+
+static isc_result_t
+openfile_raw(dns_loadctx_t *lctx, const char *master_file);
+
+static isc_result_t
+load_text(dns_loadctx_t *lctx);
+
+static isc_result_t
+load_raw(dns_loadctx_t *lctx);
+
+static isc_result_t
+pushfile(const char *master_file, dns_name_t *origin, dns_loadctx_t *lctx);
+
+static isc_result_t
+commit(dns_rdatacallbacks_t *, dns_loadctx_t *, rdatalist_head_t *,
+ dns_name_t *, const char *, unsigned int);
+
+static isc_boolean_t
+is_glue(rdatalist_head_t *, dns_name_t *);
+
+static dns_rdatalist_t *
+grow_rdatalist(int, dns_rdatalist_t *, int, rdatalist_head_t *,
+ rdatalist_head_t *, isc_mem_t *mctx);
+
+static dns_rdata_t *
+grow_rdata(int, dns_rdata_t *, int, rdatalist_head_t *, rdatalist_head_t *,
+ isc_mem_t *);
+
+static void
+load_quantum(isc_task_t *task, isc_event_t *event);
+
+static isc_result_t
+task_send(dns_loadctx_t *lctx);
+
+static void
+loadctx_destroy(dns_loadctx_t *lctx);
+
+#define GETTOKEN(lexer, options, token, eol) \
+ do { \
+ result = gettoken(lexer, options, token, eol, callbacks); \
+ switch (result) { \
+ case ISC_R_SUCCESS: \
+ break; \
+ case ISC_R_UNEXPECTED: \
+ goto insist_and_cleanup; \
+ default: \
+ if (MANYERRS(lctx, result)) { \
+ SETRESULT(lctx, result); \
+ LOGIT(result); \
+ read_till_eol = ISC_TRUE; \
+ goto next_line; \
+ } else \
+ goto log_and_cleanup; \
+ } \
+ if ((token)->type == isc_tokentype_special) { \
+ result = DNS_R_SYNTAX; \
+ if (MANYERRS(lctx, result)) { \
+ SETRESULT(lctx, result); \
+ LOGIT(result); \
+ read_till_eol = ISC_TRUE; \
+ goto next_line; \
+ } else \
+ goto log_and_cleanup; \
+ } \
+ } while (0)
+
+#define COMMITALL \
+ do { \
+ result = commit(callbacks, lctx, &current_list, \
+ ictx->current, source, ictx->current_line); \
+ if (MANYERRS(lctx, result)) { \
+ SETRESULT(lctx, result); \
+ } else if (result != ISC_R_SUCCESS) \
+ goto insist_and_cleanup; \
+ result = commit(callbacks, lctx, &glue_list, \
+ ictx->glue, source, ictx->glue_line); \
+ if (MANYERRS(lctx, result)) { \
+ SETRESULT(lctx, result); \
+ } else if (result != ISC_R_SUCCESS) \
+ goto insist_and_cleanup; \
+ rdcount = 0; \
+ rdlcount = 0; \
+ isc_buffer_init(&target, target_mem, target_size); \
+ rdcount_save = rdcount; \
+ rdlcount_save = rdlcount; \
+ } while (0)
+
+#define WARNUNEXPECTEDEOF(lexer) \
+ do { \
+ if (isc_lex_isfile(lexer)) \
+ (*callbacks->warn)(callbacks, \
+ "%s: file does not end with newline", \
+ source); \
+ } while (0)
+
+#define EXPECTEOL \
+ do { \
+ GETTOKEN(lctx->lex, 0, &token, ISC_TRUE); \
+ if (token.type != isc_tokentype_eol) { \
+ isc_lex_ungettoken(lctx->lex, &token); \
+ result = DNS_R_EXTRATOKEN; \
+ if (MANYERRS(lctx, result)) { \
+ SETRESULT(lctx, result); \
+ LOGIT(result); \
+ read_till_eol = ISC_TRUE; \
+ continue; \
+ } else if (result != ISC_R_SUCCESS) \
+ goto log_and_cleanup; \
+ } \
+ } while (0)
+
+#define MANYERRS(lctx, result) \
+ ((result != ISC_R_SUCCESS) && \
+ (result != ISC_R_IOERROR) && \
+ ((lctx)->options & DNS_MASTER_MANYERRORS) != 0)
+
+#define SETRESULT(lctx, r) \
+ do { \
+ if ((lctx)->result == ISC_R_SUCCESS) \
+ (lctx)->result = r; \
+ } while (0)
+
+#define LOGITFILE(result, filename) \
+ if (result == ISC_R_INVALIDFILE || result == ISC_R_FILENOTFOUND || \
+ result == ISC_R_IOERROR || result == ISC_R_TOOMANYOPENFILES || \
+ result == ISC_R_NOPERM) \
+ (*callbacks->error)(callbacks, "%s: %s:%lu: %s: %s", \
+ "dns_master_load", source, line, \
+ filename, dns_result_totext(result)); \
+ else LOGIT(result)
+
+#define LOGIT(result) \
+ if (result == ISC_R_NOMEMORY) \
+ (*callbacks->error)(callbacks, "dns_master_load: %s", \
+ dns_result_totext(result)); \
+ else \
+ (*callbacks->error)(callbacks, "%s: %s:%lu: %s", \
+ "dns_master_load", \
+ source, line, dns_result_totext(result))
+
+
+static unsigned char in_addr_arpa_data[] = "\007IN-ADDR\004ARPA";
+static unsigned char in_addr_arpa_offsets[] = { 0, 8, 13 };
+static const dns_name_t in_addr_arpa =
+{
+ DNS_NAME_MAGIC,
+ in_addr_arpa_data, 14, 3,
+ DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
+ in_addr_arpa_offsets, NULL,
+ {(void *)-1, (void *)-1},
+ {NULL, NULL}
+};
+
+static unsigned char ip6_int_data[] = "\003IP6\003INT";
+static unsigned char ip6_int_offsets[] = { 0, 4, 8 };
+static const dns_name_t ip6_int =
+{
+ DNS_NAME_MAGIC,
+ ip6_int_data, 9, 3,
+ DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
+ ip6_int_offsets, NULL,
+ {(void *)-1, (void *)-1},
+ {NULL, NULL}
+};
+
+static unsigned char ip6_arpa_data[] = "\003IP6\004ARPA";
+static unsigned char ip6_arpa_offsets[] = { 0, 4, 9 };
+static const dns_name_t ip6_arpa =
+{
+ DNS_NAME_MAGIC,
+ ip6_arpa_data, 10, 3,
+ DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
+ ip6_arpa_offsets, NULL,
+ {(void *)-1, (void *)-1},
+ {NULL, NULL}
+};
+
+
+static inline isc_result_t
+gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *token,
+ isc_boolean_t eol, dns_rdatacallbacks_t *callbacks)
+{
+ isc_result_t result;
+
+ options |= ISC_LEXOPT_EOL | ISC_LEXOPT_EOF | ISC_LEXOPT_DNSMULTILINE |
+ ISC_LEXOPT_ESCAPE;
+ result = isc_lex_gettoken(lex, options, token);
+ if (result != ISC_R_SUCCESS) {
+ switch (result) {
+ case ISC_R_NOMEMORY:
+ return (ISC_R_NOMEMORY);
+ default:
+ (*callbacks->error)(callbacks,
+ "dns_master_load: %s:%lu:"
+ " isc_lex_gettoken() failed: %s",
+ isc_lex_getsourcename(lex),
+ isc_lex_getsourceline(lex),
+ isc_result_totext(result));
+ return (result);
+ }
+ /*NOTREACHED*/
+ }
+ if (eol != ISC_TRUE)
+ if (token->type == isc_tokentype_eol ||
+ token->type == isc_tokentype_eof) {
+ (*callbacks->error)(callbacks,
+ "dns_master_load: %s:%lu: unexpected end of %s",
+ isc_lex_getsourcename(lex),
+ isc_lex_getsourceline(lex),
+ (token->type ==
+ isc_tokentype_eol) ?
+ "line" : "file");
+ return (ISC_R_UNEXPECTEDEND);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+
+void
+dns_loadctx_attach(dns_loadctx_t *source, dns_loadctx_t **target) {
+
+ REQUIRE(target != NULL && *target == NULL);
+ REQUIRE(DNS_LCTX_VALID(source));
+
+ LOCK(&source->lock);
+ INSIST(source->references > 0);
+ source->references++;
+ INSIST(source->references != 0); /* Overflow? */
+ UNLOCK(&source->lock);
+
+ *target = source;
+}
+
+void
+dns_loadctx_detach(dns_loadctx_t **lctxp) {
+ dns_loadctx_t *lctx;
+ isc_boolean_t need_destroy = ISC_FALSE;
+
+ REQUIRE(lctxp != NULL);
+ lctx = *lctxp;
+ REQUIRE(DNS_LCTX_VALID(lctx));
+
+ LOCK(&lctx->lock);
+ INSIST(lctx->references > 0);
+ lctx->references--;
+ if (lctx->references == 0)
+ need_destroy = ISC_TRUE;
+ UNLOCK(&lctx->lock);
+
+ if (need_destroy)
+ loadctx_destroy(lctx);
+ *lctxp = NULL;
+}
+
+static void
+incctx_destroy(isc_mem_t *mctx, dns_incctx_t *ictx) {
+ dns_incctx_t *parent;
+
+ again:
+ parent = ictx->parent;
+ ictx->parent = NULL;
+
+ isc_mem_put(mctx, ictx, sizeof(*ictx));
+
+ if (parent != NULL) {
+ ictx = parent;
+ goto again;
+ }
+}
+
+static void
+loadctx_destroy(dns_loadctx_t *lctx) {
+ isc_mem_t *mctx;
+ isc_result_t result;
+
+ REQUIRE(DNS_LCTX_VALID(lctx));
+
+ lctx->magic = 0;
+ if (lctx->inc != NULL)
+ incctx_destroy(lctx->mctx, lctx->inc);
+
+ if (lctx->f != NULL) {
+ result = isc_stdio_close(lctx->f);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_stdio_close() failed: %s",
+ isc_result_totext(result));
+ }
+ }
+
+ /* isc_lex_destroy() will close all open streams */
+ if (lctx->lex != NULL && !lctx->keep_lex)
+ isc_lex_destroy(&lctx->lex);
+
+ if (lctx->task != NULL)
+ isc_task_detach(&lctx->task);
+ DESTROYLOCK(&lctx->lock);
+ mctx = NULL;
+ isc_mem_attach(lctx->mctx, &mctx);
+ isc_mem_detach(&lctx->mctx);
+ isc_mem_put(mctx, lctx, sizeof(*lctx));
+ isc_mem_detach(&mctx);
+}
+
+static isc_result_t
+incctx_create(isc_mem_t *mctx, dns_name_t *origin, dns_incctx_t **ictxp) {
+ dns_incctx_t *ictx;
+ isc_region_t r;
+ int i;
+
+ ictx = isc_mem_get(mctx, sizeof(*ictx));
+ if (ictx == NULL)
+ return (ISC_R_NOMEMORY);
+
+ for (i = 0; i < NBUFS; i++) {
+ dns_fixedname_init(&ictx->fixed[i]);
+ ictx->in_use[i] = ISC_FALSE;
+ }
+
+ ictx->origin_in_use = 0;
+ ictx->origin = dns_fixedname_name(&ictx->fixed[ictx->origin_in_use]);
+ ictx->in_use[ictx->origin_in_use] = ISC_TRUE;
+ dns_name_toregion(origin, &r);
+ dns_name_fromregion(ictx->origin, &r);
+
+ ictx->glue = NULL;
+ ictx->current = NULL;
+ ictx->glue_in_use = -1;
+ ictx->current_in_use = -1;
+ ictx->parent = NULL;
+ ictx->drop = ISC_FALSE;
+ ictx->glue_line = 0;
+ ictx->current_line = 0;
+
+ *ictxp = ictx;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+loadctx_create(dns_masterformat_t format, isc_mem_t *mctx,
+ unsigned int options, isc_uint32_t resign, dns_name_t *top,
+ dns_rdataclass_t zclass, dns_name_t *origin,
+ dns_rdatacallbacks_t *callbacks, isc_task_t *task,
+ dns_loaddonefunc_t done, void *done_arg, isc_lex_t *lex,
+ dns_loadctx_t **lctxp)
+{
+ dns_loadctx_t *lctx;
+ isc_result_t result;
+ isc_region_t r;
+ isc_lexspecials_t specials;
+
+ REQUIRE(lctxp != NULL && *lctxp == NULL);
+ REQUIRE(callbacks != NULL);
+ REQUIRE(callbacks->add != NULL);
+ REQUIRE(callbacks->error != NULL);
+ REQUIRE(callbacks->warn != NULL);
+ REQUIRE(mctx != NULL);
+ REQUIRE(dns_name_isabsolute(top));
+ REQUIRE(dns_name_isabsolute(origin));
+ REQUIRE((task == NULL && done == NULL) ||
+ (task != NULL && done != NULL));
+
+ lctx = isc_mem_get(mctx, sizeof(*lctx));
+ if (lctx == NULL)
+ return (ISC_R_NOMEMORY);
+ result = isc_mutex_init(&lctx->lock);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, lctx, sizeof(*lctx));
+ return (result);
+ }
+
+ lctx->inc = NULL;
+ result = incctx_create(mctx, origin, &lctx->inc);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_ctx;
+
+ lctx->format = format;
+ switch (format) {
+ default:
+ INSIST(0);
+ case dns_masterformat_text:
+ lctx->openfile = openfile_text;
+ lctx->load = load_text;
+ break;
+ case dns_masterformat_raw:
+ lctx->openfile = openfile_raw;
+ lctx->load = load_raw;
+ break;
+ }
+
+ if (lex != NULL) {
+ lctx->lex = lex;
+ lctx->keep_lex = ISC_TRUE;
+ } else {
+ lctx->lex = NULL;
+ result = isc_lex_create(mctx, TOKENSIZ, &lctx->lex);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_inc;
+ lctx->keep_lex = ISC_FALSE;
+ memset(specials, 0, sizeof(specials));
+ specials['('] = 1;
+ specials[')'] = 1;
+ specials['"'] = 1;
+ isc_lex_setspecials(lctx->lex, specials);
+ isc_lex_setcomments(lctx->lex, ISC_LEXCOMMENT_DNSMASTERFILE);
+ }
+
+ lctx->ttl_known = ISC_FALSE;
+ lctx->ttl = 0;
+ lctx->default_ttl_known = ISC_FALSE;
+ lctx->default_ttl = 0;
+ lctx->warn_1035 = ISC_TRUE; /* XXX Argument? */
+ lctx->warn_tcr = ISC_TRUE; /* XXX Argument? */
+ lctx->warn_sigexpired = ISC_TRUE; /* XXX Argument? */
+ lctx->options = options;
+ lctx->seen_include = ISC_FALSE;
+ lctx->zclass = zclass;
+ lctx->resign = resign;
+ lctx->result = ISC_R_SUCCESS;
+
+ dns_fixedname_init(&lctx->fixed_top);
+ lctx->top = dns_fixedname_name(&lctx->fixed_top);
+ dns_name_toregion(top, &r);
+ dns_name_fromregion(lctx->top, &r);
+
+ lctx->f = NULL;
+ lctx->first = ISC_TRUE;
+
+ lctx->loop_cnt = (done != NULL) ? 100 : 0;
+ lctx->callbacks = callbacks;
+ lctx->task = NULL;
+ if (task != NULL)
+ isc_task_attach(task, &lctx->task);
+ lctx->done = done;
+ lctx->done_arg = done_arg;
+ lctx->canceled = ISC_FALSE;
+ lctx->mctx = NULL;
+ isc_mem_attach(mctx, &lctx->mctx);
+ lctx->references = 1; /* Implicit attach. */
+ lctx->magic = DNS_LCTX_MAGIC;
+ *lctxp = lctx;
+ return (ISC_R_SUCCESS);
+
+ cleanup_inc:
+ incctx_destroy(mctx, lctx->inc);
+ cleanup_ctx:
+ isc_mem_put(mctx, lctx, sizeof(*lctx));
+ return (result);
+}
+
+static isc_result_t
+genname(char *name, int it, char *buffer, size_t length) {
+ char fmt[sizeof("%04000000000d")];
+ char numbuf[128];
+ char *cp;
+ char mode[2];
+ int delta = 0;
+ isc_textregion_t r;
+ unsigned int n;
+ unsigned int width;
+
+ r.base = buffer;
+ r.length = length;
+
+ while (*name != '\0') {
+ if (*name == '$') {
+ name++;
+ if (*name == '$') {
+ if (r.length == 0)
+ return (ISC_R_NOSPACE);
+ r.base[0] = *name++;
+ isc_textregion_consume(&r, 1);
+ continue;
+ }
+ strcpy(fmt, "%d");
+ /* Get format specifier. */
+ if (*name == '{' ) {
+ n = sscanf(name, "{%d,%u,%1[doxX]}",
+ &delta, &width, mode);
+ switch (n) {
+ case 1:
+ break;
+ case 2:
+ n = snprintf(fmt, sizeof(fmt),
+ "%%0%ud", width);
+ break;
+ case 3:
+ n = snprintf(fmt, sizeof(fmt),
+ "%%0%u%c", width, mode[0]);
+ break;
+ default:
+ return (DNS_R_SYNTAX);
+ }
+ if (n >= sizeof(fmt))
+ return (ISC_R_NOSPACE);
+ /* Skip past closing brace. */
+ while (*name != '\0' && *name++ != '}')
+ continue;
+ }
+ n = snprintf(numbuf, sizeof(numbuf), fmt, it + delta);
+ if (n >= sizeof(numbuf))
+ return (ISC_R_NOSPACE);
+ cp = numbuf;
+ while (*cp != '\0') {
+ if (r.length == 0)
+ return (ISC_R_NOSPACE);
+ r.base[0] = *cp++;
+ isc_textregion_consume(&r, 1);
+ }
+ } else if (*name == '\\') {
+ if (r.length == 0)
+ return (ISC_R_NOSPACE);
+ r.base[0] = *name++;
+ isc_textregion_consume(&r, 1);
+ if (*name == '\0')
+ continue;
+ if (r.length == 0)
+ return (ISC_R_NOSPACE);
+ r.base[0] = *name++;
+ isc_textregion_consume(&r, 1);
+ } else {
+ if (r.length == 0)
+ return (ISC_R_NOSPACE);
+ r.base[0] = *name++;
+ isc_textregion_consume(&r, 1);
+ }
+ }
+ if (r.length == 0)
+ return (ISC_R_NOSPACE);
+ r.base[0] = '\0';
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+openfile_text(dns_loadctx_t *lctx, const char *master_file) {
+ return (isc_lex_openfile(lctx->lex, master_file));
+}
+
+static isc_result_t
+openfile_raw(dns_loadctx_t *lctx, const char *master_file) {
+ isc_result_t result;
+
+ result = isc_stdio_open(master_file, "r", &lctx->f);
+ if (result != ISC_R_SUCCESS && result != ISC_R_FILENOTFOUND) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_stdio_open() failed: %s",
+ isc_result_totext(result));
+ }
+
+ return (result);
+}
+
+static isc_result_t
+generate(dns_loadctx_t *lctx, char *range, char *lhs, char *gtype, char *rhs,
+ const char *source, unsigned int line)
+{
+ char *target_mem = NULL;
+ char *lhsbuf = NULL;
+ char *rhsbuf = NULL;
+ dns_fixedname_t ownerfixed;
+ dns_name_t *owner;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdatacallbacks_t *callbacks;
+ dns_rdatalist_t rdatalist;
+ dns_rdatatype_t type;
+ rdatalist_head_t head;
+ int n;
+ int target_size = MINTSIZ; /* only one rdata at a time */
+ isc_buffer_t buffer;
+ isc_buffer_t target;
+ isc_result_t result;
+ isc_textregion_t r;
+ unsigned int start, stop, step, i;
+ dns_incctx_t *ictx;
+
+ ictx = lctx->inc;
+ callbacks = lctx->callbacks;
+ dns_fixedname_init(&ownerfixed);
+ owner = dns_fixedname_name(&ownerfixed);
+ ISC_LIST_INIT(head);
+
+ target_mem = isc_mem_get(lctx->mctx, target_size);
+ rhsbuf = isc_mem_get(lctx->mctx, DNS_MASTER_BUFSZ);
+ lhsbuf = isc_mem_get(lctx->mctx, DNS_MASTER_BUFSZ);
+ if (target_mem == NULL || rhsbuf == NULL || lhsbuf == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto error_cleanup;
+ }
+ isc_buffer_init(&target, target_mem, target_size);
+
+ n = sscanf(range, "%u-%u/%u", &start, &stop, &step);
+ if (n < 2 || stop < start) {
+ (*callbacks->error)(callbacks,
+ "%s: %s:%lu: invalid range '%s'",
+ "$GENERATE", source, line, range);
+ result = DNS_R_SYNTAX;
+ goto insist_cleanup;
+ }
+ if (n == 2)
+ step = 1;
+
+ /*
+ * Get type.
+ */
+ r.base = gtype;
+ r.length = strlen(gtype);
+ result = dns_rdatatype_fromtext(&type, &r);
+ if (result != ISC_R_SUCCESS) {
+ (*callbacks->error)(callbacks,
+ "%s: %s:%lu: unknown RR type '%s'",
+ "$GENERATE", source, line, gtype);
+ goto insist_cleanup;
+ }
+
+ switch (type) {
+ case dns_rdatatype_ns:
+ case dns_rdatatype_ptr:
+ case dns_rdatatype_cname:
+ case dns_rdatatype_dname:
+ break;
+
+ case dns_rdatatype_a:
+ case dns_rdatatype_aaaa:
+ if (lctx->zclass == dns_rdataclass_in ||
+ lctx->zclass == dns_rdataclass_ch ||
+ lctx->zclass == dns_rdataclass_hs)
+ break;
+ /* FALLTHROUGH */
+ default:
+ (*callbacks->error)(callbacks,
+ "%s: %s:%lu: unsupported type '%s'",
+ "$GENERATE", source, line, gtype);
+ result = ISC_R_NOTIMPLEMENTED;
+ goto error_cleanup;
+ }
+
+ ISC_LIST_INIT(rdatalist.rdata);
+ ISC_LINK_INIT(&rdatalist, link);
+ for (i = start; i <= stop; i += step) {
+ result = genname(lhs, i, lhsbuf, DNS_MASTER_BUFSZ);
+ if (result != ISC_R_SUCCESS)
+ goto error_cleanup;
+ result = genname(rhs, i, rhsbuf, DNS_MASTER_BUFSZ);
+ if (result != ISC_R_SUCCESS)
+ goto error_cleanup;
+
+ isc_buffer_init(&buffer, lhsbuf, strlen(lhsbuf));
+ isc_buffer_add(&buffer, strlen(lhsbuf));
+ isc_buffer_setactive(&buffer, strlen(lhsbuf));
+ result = dns_name_fromtext(owner, &buffer, ictx->origin,
+ 0, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto error_cleanup;
+
+ if ((lctx->options & DNS_MASTER_ZONE) != 0 &&
+ (lctx->options & DNS_MASTER_SLAVE) == 0 &&
+ !dns_name_issubdomain(owner, lctx->top))
+ {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_name_format(owner, namebuf, sizeof(namebuf));
+ /*
+ * Ignore out-of-zone data.
+ */
+ (*callbacks->warn)(callbacks,
+ "%s:%lu: "
+ "ignoring out-of-zone data (%s)",
+ source, line, namebuf);
+ continue;
+ }
+
+ isc_buffer_init(&buffer, rhsbuf, strlen(rhsbuf));
+ isc_buffer_add(&buffer, strlen(rhsbuf));
+ isc_buffer_setactive(&buffer, strlen(rhsbuf));
+
+ result = isc_lex_openbuffer(lctx->lex, &buffer);
+ if (result != ISC_R_SUCCESS)
+ goto error_cleanup;
+
+ isc_buffer_init(&target, target_mem, target_size);
+ result = dns_rdata_fromtext(&rdata, lctx->zclass, type,
+ lctx->lex, ictx->origin, 0,
+ lctx->mctx, &target, callbacks);
+ RUNTIME_CHECK(isc_lex_close(lctx->lex) == ISC_R_SUCCESS);
+ if (result != ISC_R_SUCCESS)
+ goto error_cleanup;
+
+ rdatalist.type = type;
+ rdatalist.covers = 0;
+ rdatalist.rdclass = lctx->zclass;
+ rdatalist.ttl = lctx->ttl;
+ ISC_LIST_PREPEND(head, &rdatalist, link);
+ ISC_LIST_APPEND(rdatalist.rdata, &rdata, link);
+ result = commit(callbacks, lctx, &head, owner, source, line);
+ ISC_LIST_UNLINK(rdatalist.rdata, &rdata, link);
+ if (result != ISC_R_SUCCESS)
+ goto error_cleanup;
+ dns_rdata_reset(&rdata);
+ }
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+
+ error_cleanup:
+ if (result == ISC_R_NOMEMORY)
+ (*callbacks->error)(callbacks, "$GENERATE: %s",
+ dns_result_totext(result));
+ else
+ (*callbacks->error)(callbacks, "$GENERATE: %s:%lu: %s",
+ source, line, dns_result_totext(result));
+
+ insist_cleanup:
+ INSIST(result != ISC_R_SUCCESS);
+
+ cleanup:
+ if (target_mem != NULL)
+ isc_mem_put(lctx->mctx, target_mem, target_size);
+ if (lhsbuf != NULL)
+ isc_mem_put(lctx->mctx, lhsbuf, DNS_MASTER_BUFSZ);
+ if (rhsbuf != NULL)
+ isc_mem_put(lctx->mctx, rhsbuf, DNS_MASTER_BUFSZ);
+ return (result);
+}
+
+static void
+limit_ttl(dns_rdatacallbacks_t *callbacks, const char *source, unsigned int line,
+ isc_uint32_t *ttlp)
+{
+ if (*ttlp > 0x7fffffffUL) {
+ (callbacks->warn)(callbacks,
+ "%s: %s:%lu: "
+ "$TTL %lu > MAXTTL, "
+ "setting $TTL to 0",
+ "dns_master_load",
+ source, line,
+ *ttlp);
+ *ttlp = 0;
+ }
+}
+
+static isc_result_t
+check_ns(dns_loadctx_t *lctx, isc_token_t *token, const char *source,
+ unsigned long line)
+{
+ char *tmp = NULL;
+ isc_result_t result = ISC_R_SUCCESS;
+ void (*callback)(struct dns_rdatacallbacks *, const char *, ...);
+
+ if ((lctx->options & DNS_MASTER_FATALNS) != 0)
+ callback = lctx->callbacks->error;
+ else
+ callback = lctx->callbacks->warn;
+
+ if (token->type == isc_tokentype_string) {
+ struct in_addr addr;
+ struct in6_addr addr6;
+
+ tmp = isc_mem_strdup(lctx->mctx, DNS_AS_STR(*token));
+ if (tmp == NULL)
+ return (ISC_R_NOMEMORY);
+ /*
+ * Catch both "1.2.3.4" and "1.2.3.4."
+ */
+ if (tmp[strlen(tmp) - 1] == '.')
+ tmp[strlen(tmp) - 1] = '\0';
+ if (inet_aton(tmp, &addr) == 1 ||
+ inet_pton(AF_INET6, tmp, &addr6) == 1)
+ result = DNS_R_NSISADDRESS;
+ }
+ if (result != ISC_R_SUCCESS)
+ (*callback)(lctx->callbacks, "%s:%lu: NS record '%s' "
+ "appears to be an address",
+ source, line, DNS_AS_STR(*token));
+ if (tmp != NULL)
+ isc_mem_free(lctx->mctx, tmp);
+ return (result);
+}
+
+static void
+check_wildcard(dns_incctx_t *ictx, const char *source, unsigned long line,
+ dns_rdatacallbacks_t *callbacks)
+{
+ dns_name_t *name;
+
+ name = (ictx->glue != NULL) ? ictx->glue : ictx->current;
+ if (dns_name_internalwildcard(name)) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ (*callbacks->warn)(callbacks, "%s:%lu: warning: ownername "
+ "'%s' contains an non-terminal wildcard",
+ source, line, namebuf);
+ }
+}
+
+static isc_result_t
+load_text(dns_loadctx_t *lctx) {
+ dns_rdataclass_t rdclass;
+ dns_rdatatype_t type, covers;
+ isc_uint32_t ttl_offset = 0;
+ dns_name_t *new_name;
+ isc_boolean_t current_has_delegation = ISC_FALSE;
+ isc_boolean_t done = ISC_FALSE;
+ isc_boolean_t finish_origin = ISC_FALSE;
+ isc_boolean_t finish_include = ISC_FALSE;
+ isc_boolean_t read_till_eol = ISC_FALSE;
+ isc_boolean_t initialws;
+ char *include_file = NULL;
+ isc_token_t token;
+ isc_result_t result = ISC_R_UNEXPECTED;
+ rdatalist_head_t glue_list;
+ rdatalist_head_t current_list;
+ dns_rdatalist_t *this;
+ dns_rdatalist_t *rdatalist = NULL;
+ dns_rdatalist_t *new_rdatalist;
+ int rdlcount = 0;
+ int rdlcount_save = 0;
+ int rdatalist_size = 0;
+ isc_buffer_t buffer;
+ isc_buffer_t target;
+ isc_buffer_t target_ft;
+ isc_buffer_t target_save;
+ dns_rdata_t *rdata = NULL;
+ dns_rdata_t *new_rdata;
+ int rdcount = 0;
+ int rdcount_save = 0;
+ int rdata_size = 0;
+ unsigned char *target_mem = NULL;
+ int target_size = TSIZ;
+ int new_in_use;
+ unsigned int loop_cnt = 0;
+ isc_mem_t *mctx;
+ dns_rdatacallbacks_t *callbacks;
+ dns_incctx_t *ictx;
+ char *range = NULL;
+ char *lhs = NULL;
+ char *gtype = NULL;
+ char *rhs = NULL;
+ const char *source = "";
+ unsigned long line = 0;
+ isc_boolean_t explicit_ttl;
+ isc_stdtime_t now;
+ char classname1[DNS_RDATACLASS_FORMATSIZE];
+ char classname2[DNS_RDATACLASS_FORMATSIZE];
+ unsigned int options = 0;
+
+ REQUIRE(DNS_LCTX_VALID(lctx));
+ callbacks = lctx->callbacks;
+ mctx = lctx->mctx;
+ ictx = lctx->inc;
+
+ ISC_LIST_INIT(glue_list);
+ ISC_LIST_INIT(current_list);
+
+ isc_stdtime_get(&now);
+
+ /*
+ * Allocate target_size of buffer space. This is greater than twice
+ * the maximum individual RR data size.
+ */
+ target_mem = isc_mem_get(mctx, target_size);
+ if (target_mem == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto log_and_cleanup;
+ }
+ isc_buffer_init(&target, target_mem, target_size);
+ target_save = target;
+
+ if ((lctx->options & DNS_MASTER_CHECKNAMES) != 0)
+ options |= DNS_RDATA_CHECKNAMES;
+ if ((lctx->options & DNS_MASTER_CHECKNAMESFAIL) != 0)
+ options |= DNS_RDATA_CHECKNAMESFAIL;
+ if ((lctx->options & DNS_MASTER_CHECKMX) != 0)
+ options |= DNS_RDATA_CHECKMX;
+ if ((lctx->options & DNS_MASTER_CHECKMXFAIL) != 0)
+ options |= DNS_RDATA_CHECKMXFAIL;
+ source = isc_lex_getsourcename(lctx->lex);
+ do {
+ initialws = ISC_FALSE;
+ line = isc_lex_getsourceline(lctx->lex);
+ GETTOKEN(lctx->lex, ISC_LEXOPT_INITIALWS | ISC_LEXOPT_QSTRING,
+ &token, ISC_TRUE);
+ line = isc_lex_getsourceline(lctx->lex);
+
+ if (token.type == isc_tokentype_eof) {
+ if (read_till_eol)
+ WARNUNEXPECTEDEOF(lctx->lex);
+ /* Pop the include stack? */
+ if (ictx->parent != NULL) {
+ COMMITALL;
+ lctx->inc = ictx->parent;
+ ictx->parent = NULL;
+ incctx_destroy(lctx->mctx, ictx);
+ RUNTIME_CHECK(isc_lex_close(lctx->lex) == ISC_R_SUCCESS);
+ line = isc_lex_getsourceline(lctx->lex);
+ source = isc_lex_getsourcename(lctx->lex);
+ ictx = lctx->inc;
+ EXPECTEOL;
+ continue;
+ }
+ done = ISC_TRUE;
+ continue;
+ }
+
+ if (token.type == isc_tokentype_eol) {
+ read_till_eol = ISC_FALSE;
+ continue; /* blank line */
+ }
+
+ if (read_till_eol)
+ continue;
+
+ if (token.type == isc_tokentype_initialws) {
+ /*
+ * Still working on the same name.
+ */
+ initialws = ISC_TRUE;
+ } else if (token.type == isc_tokentype_string ||
+ token.type == isc_tokentype_qstring) {
+
+ /*
+ * "$" Support.
+ *
+ * "$ORIGIN" and "$INCLUDE" can both take domain names.
+ * The processing of "$ORIGIN" and "$INCLUDE" extends
+ * across the normal domain name processing.
+ */
+
+ if (strcasecmp(DNS_AS_STR(token), "$ORIGIN") == 0) {
+ GETTOKEN(lctx->lex, 0, &token, ISC_FALSE);
+ finish_origin = ISC_TRUE;
+ } else if (strcasecmp(DNS_AS_STR(token),
+ "$TTL") == 0) {
+ GETTOKEN(lctx->lex, 0, &token, ISC_FALSE);
+ result =
+ dns_ttl_fromtext(&token.value.as_textregion,
+ &lctx->ttl);
+ if (MANYERRS(lctx, result)) {
+ SETRESULT(lctx, result);
+ lctx->ttl = 0;
+ } else if (result != ISC_R_SUCCESS)
+ goto insist_and_cleanup;
+ limit_ttl(callbacks, source, line, &lctx->ttl);
+ lctx->default_ttl = lctx->ttl;
+ lctx->default_ttl_known = ISC_TRUE;
+ EXPECTEOL;
+ continue;
+ } else if (strcasecmp(DNS_AS_STR(token),
+ "$INCLUDE") == 0) {
+ COMMITALL;
+ if ((lctx->options & DNS_MASTER_NOINCLUDE)
+ != 0)
+ {
+ (callbacks->error)(callbacks,
+ "%s: %s:%lu: $INCLUDE not allowed",
+ "dns_master_load",
+ source, line);
+ result = DNS_R_REFUSED;
+ goto insist_and_cleanup;
+ }
+ if (ttl_offset != 0) {
+ (callbacks->error)(callbacks,
+ "%s: %s:%lu: $INCLUDE "
+ "may not be used with $DATE",
+ "dns_master_load",
+ source, line);
+ result = DNS_R_SYNTAX;
+ goto insist_and_cleanup;
+ }
+ GETTOKEN(lctx->lex, ISC_LEXOPT_QSTRING, &token,
+ ISC_FALSE);
+ if (include_file != NULL)
+ isc_mem_free(mctx, include_file);
+ include_file = isc_mem_strdup(mctx,
+ DNS_AS_STR(token));
+ if (include_file == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto log_and_cleanup;
+ }
+ GETTOKEN(lctx->lex, 0, &token, ISC_TRUE);
+
+ if (token.type == isc_tokentype_eol ||
+ token.type == isc_tokentype_eof) {
+ if (token.type == isc_tokentype_eof)
+ WARNUNEXPECTEDEOF(lctx->lex);
+ isc_lex_ungettoken(lctx->lex, &token);
+ /*
+ * No origin field.
+ */
+ result = pushfile(include_file,
+ ictx->origin, lctx);
+ if (MANYERRS(lctx, result)) {
+ SETRESULT(lctx, result);
+ LOGITFILE(result, include_file);
+ continue;
+ } else if (result != ISC_R_SUCCESS) {
+ LOGITFILE(result, include_file);
+ goto insist_and_cleanup;
+ }
+ ictx = lctx->inc;
+ line = isc_lex_getsourceline(lctx->lex);
+ source =
+ isc_lex_getsourcename(lctx->lex);
+ continue;
+ }
+ /*
+ * There is an origin field. Fall through
+ * to domain name processing code and do
+ * the actual inclusion later.
+ */
+ finish_include = ISC_TRUE;
+ } else if (strcasecmp(DNS_AS_STR(token),
+ "$DATE") == 0) {
+ isc_int64_t dump_time64;
+ isc_stdtime_t dump_time, current_time;
+ GETTOKEN(lctx->lex, 0, &token, ISC_FALSE);
+ isc_stdtime_get(&current_time);
+ result = dns_time64_fromtext(DNS_AS_STR(token),
+ &dump_time64);
+ if (MANYERRS(lctx, result)) {
+ SETRESULT(lctx, result);
+ LOGIT(result);
+ dump_time64 = 0;
+ } else if (result != ISC_R_SUCCESS)
+ goto log_and_cleanup;
+ dump_time = (isc_stdtime_t)dump_time64;
+ if (dump_time != dump_time64) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "%s: %s:%lu: $DATE outside epoch",
+ "dns_master_load", source, line);
+ result = ISC_R_UNEXPECTED;
+ goto insist_and_cleanup;
+ }
+ if (dump_time > current_time) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "%s: %s:%lu: "
+ "$DATE in future, using current date",
+ "dns_master_load", source, line);
+ dump_time = current_time;
+ }
+ ttl_offset = current_time - dump_time;
+ EXPECTEOL;
+ continue;
+ } else if (strcasecmp(DNS_AS_STR(token),
+ "$GENERATE") == 0) {
+ /*
+ * Lazy cleanup.
+ */
+ if (range != NULL)
+ isc_mem_free(mctx, range);
+ if (lhs != NULL)
+ isc_mem_free(mctx, lhs);
+ if (gtype != NULL)
+ isc_mem_free(mctx, gtype);
+ if (rhs != NULL)
+ isc_mem_free(mctx, rhs);
+ range = lhs = gtype = rhs = NULL;
+ /* RANGE */
+ GETTOKEN(lctx->lex, 0, &token, ISC_FALSE);
+ range = isc_mem_strdup(mctx,
+ DNS_AS_STR(token));
+ if (range == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto log_and_cleanup;
+ }
+ /* LHS */
+ GETTOKEN(lctx->lex, 0, &token, ISC_FALSE);
+ lhs = isc_mem_strdup(mctx, DNS_AS_STR(token));
+ if (lhs == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto log_and_cleanup;
+ }
+ rdclass = 0;
+ explicit_ttl = ISC_FALSE;
+ /* CLASS? */
+ GETTOKEN(lctx->lex, 0, &token, ISC_FALSE);
+ if (dns_rdataclass_fromtext(&rdclass,
+ &token.value.as_textregion)
+ == ISC_R_SUCCESS) {
+ GETTOKEN(lctx->lex, 0, &token,
+ ISC_FALSE);
+ }
+ /* TTL? */
+ if (dns_ttl_fromtext(&token.value.as_textregion,
+ &lctx->ttl)
+ == ISC_R_SUCCESS) {
+ limit_ttl(callbacks, source, line,
+ &lctx->ttl);
+ lctx->ttl_known = ISC_TRUE;
+ explicit_ttl = ISC_TRUE;
+ GETTOKEN(lctx->lex, 0, &token,
+ ISC_FALSE);
+ }
+ /* CLASS? */
+ if (rdclass == 0 &&
+ dns_rdataclass_fromtext(&rdclass,
+ &token.value.as_textregion)
+ == ISC_R_SUCCESS)
+ GETTOKEN(lctx->lex, 0, &token,
+ ISC_FALSE);
+ /* TYPE */
+ gtype = isc_mem_strdup(mctx,
+ DNS_AS_STR(token));
+ if (gtype == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto log_and_cleanup;
+ }
+ /* RHS */
+ GETTOKEN(lctx->lex, 0, &token, ISC_FALSE);
+ rhs = isc_mem_strdup(mctx, DNS_AS_STR(token));
+ if (rhs == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto log_and_cleanup;
+ }
+ if (!lctx->ttl_known &&
+ !lctx->default_ttl_known) {
+ (*callbacks->error)(callbacks,
+ "%s: %s:%lu: no TTL specified",
+ "dns_master_load", source, line);
+ result = DNS_R_NOTTL;
+ if (MANYERRS(lctx, result)) {
+ SETRESULT(lctx, result);
+ lctx->ttl = 0;
+ } else if (result != ISC_R_SUCCESS)
+ goto insist_and_cleanup;
+ } else if (!explicit_ttl &&
+ lctx->default_ttl_known) {
+ lctx->ttl = lctx->default_ttl;
+ }
+ /*
+ * If the class specified does not match the
+ * zone's class print out a error message and
+ * exit.
+ */
+ if (rdclass != 0 && rdclass != lctx->zclass) {
+ goto bad_class;
+ }
+ result = generate(lctx, range, lhs, gtype, rhs,
+ source, line);
+ if (MANYERRS(lctx, result)) {
+ SETRESULT(lctx, result);
+ } else if (result != ISC_R_SUCCESS)
+ goto insist_and_cleanup;
+ EXPECTEOL;
+ continue;
+ } else if (strncasecmp(DNS_AS_STR(token),
+ "$", 1) == 0) {
+ (callbacks->error)(callbacks,
+ "%s: %s:%lu: "
+ "unknown $ directive '%s'",
+ "dns_master_load", source, line,
+ DNS_AS_STR(token));
+ result = DNS_R_SYNTAX;
+ if (MANYERRS(lctx, result)) {
+ SETRESULT(lctx, result);
+ } else if (result != ISC_R_SUCCESS)
+ goto insist_and_cleanup;
+ }
+
+ /*
+ * Normal processing resumes.
+ *
+ * Find a free name buffer.
+ */
+ for (new_in_use = 0; new_in_use < NBUFS; new_in_use++)
+ if (!ictx->in_use[new_in_use])
+ break;
+ INSIST(new_in_use < NBUFS);
+ dns_fixedname_init(&ictx->fixed[new_in_use]);
+ new_name = dns_fixedname_name(&ictx->fixed[new_in_use]);
+ isc_buffer_init(&buffer, token.value.as_region.base,
+ token.value.as_region.length);
+ isc_buffer_add(&buffer, token.value.as_region.length);
+ isc_buffer_setactive(&buffer,
+ token.value.as_region.length);
+ result = dns_name_fromtext(new_name, &buffer,
+ ictx->origin, ISC_FALSE, NULL);
+ if (MANYERRS(lctx, result)) {
+ SETRESULT(lctx, result);
+ LOGIT(result);
+ read_till_eol = ISC_TRUE;
+ continue;
+ } else if (result != ISC_R_SUCCESS)
+ goto log_and_cleanup;
+
+ /*
+ * Finish $ORIGIN / $INCLUDE processing if required.
+ */
+ if (finish_origin) {
+ if (ictx->origin_in_use != -1)
+ ictx->in_use[ictx->origin_in_use] =
+ ISC_FALSE;
+ ictx->origin_in_use = new_in_use;
+ ictx->in_use[ictx->origin_in_use] = ISC_TRUE;
+ ictx->origin = new_name;
+ finish_origin = ISC_FALSE;
+ EXPECTEOL;
+ continue;
+ }
+ if (finish_include) {
+ finish_include = ISC_FALSE;
+ result = pushfile(include_file, new_name, lctx);
+ if (MANYERRS(lctx, result)) {
+ SETRESULT(lctx, result);
+ LOGITFILE(result, include_file);
+ continue;
+ } else if (result != ISC_R_SUCCESS) {
+ LOGITFILE(result, include_file);
+ goto insist_and_cleanup;
+ }
+ ictx = lctx->inc;
+ line = isc_lex_getsourceline(lctx->lex);
+ source = isc_lex_getsourcename(lctx->lex);
+ continue;
+ }
+
+ /*
+ * "$" Processing Finished
+ */
+
+ /*
+ * If we are processing glue and the new name does
+ * not match the current glue name, commit the glue
+ * and pop stacks leaving us in 'normal' processing
+ * state. Linked lists are undone by commit().
+ */
+ if (ictx->glue != NULL &&
+ dns_name_compare(ictx->glue, new_name) != 0) {
+ result = commit(callbacks, lctx, &glue_list,
+ ictx->glue, source,
+ ictx->glue_line);
+ if (MANYERRS(lctx, result)) {
+ SETRESULT(lctx, result);
+ } else if (result != ISC_R_SUCCESS)
+ goto insist_and_cleanup;
+ if (ictx->glue_in_use != -1)
+ ictx->in_use[ictx->glue_in_use] =
+ ISC_FALSE;
+ ictx->glue_in_use = -1;
+ ictx->glue = NULL;
+ rdcount = rdcount_save;
+ rdlcount = rdlcount_save;
+ target = target_save;
+ }
+
+ /*
+ * If we are in 'normal' processing state and the new
+ * name does not match the current name, see if the
+ * new name is for glue and treat it as such,
+ * otherwise we have a new name so commit what we
+ * have.
+ */
+ if ((ictx->glue == NULL) && (ictx->current == NULL ||
+ dns_name_compare(ictx->current, new_name) != 0)) {
+ if (current_has_delegation &&
+ is_glue(&current_list, new_name)) {
+ rdcount_save = rdcount;
+ rdlcount_save = rdlcount;
+ target_save = target;
+ ictx->glue = new_name;
+ ictx->glue_in_use = new_in_use;
+ ictx->in_use[ictx->glue_in_use] =
+ ISC_TRUE;
+ } else {
+ result = commit(callbacks, lctx,
+ &current_list,
+ ictx->current,
+ source,
+ ictx->current_line);
+ if (MANYERRS(lctx, result)) {
+ SETRESULT(lctx, result);
+ } else if (result != ISC_R_SUCCESS)
+ goto insist_and_cleanup;
+ rdcount = 0;
+ rdlcount = 0;
+ if (ictx->current_in_use != -1)
+ ictx->in_use[ictx->current_in_use] =
+ ISC_FALSE;
+ ictx->current_in_use = new_in_use;
+ ictx->in_use[ictx->current_in_use] =
+ ISC_TRUE;
+ ictx->current = new_name;
+ current_has_delegation = ISC_FALSE;
+ isc_buffer_init(&target, target_mem,
+ target_size);
+ }
+ /*
+ * Check for internal wildcards.
+ */
+ if ((lctx->options & DNS_MASTER_CHECKWILDCARD)
+ != 0)
+ check_wildcard(ictx, source, line,
+ callbacks);
+
+ }
+ if ((lctx->options & DNS_MASTER_ZONE) != 0 &&
+ (lctx->options & DNS_MASTER_SLAVE) == 0 &&
+ !dns_name_issubdomain(new_name, lctx->top))
+ {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_name_format(new_name, namebuf,
+ sizeof(namebuf));
+ /*
+ * Ignore out-of-zone data.
+ */
+ (*callbacks->warn)(callbacks,
+ "%s:%lu: "
+ "ignoring out-of-zone data (%s)",
+ source, line, namebuf);
+ ictx->drop = ISC_TRUE;
+ } else
+ ictx->drop = ISC_FALSE;
+ } else {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "%s:%lu: isc_lex_gettoken() returned "
+ "unexpected token type (%d)",
+ source, line, token.type);
+ result = ISC_R_UNEXPECTED;
+ if (MANYERRS(lctx, result)) {
+ SETRESULT(lctx, result);
+ LOGIT(result);
+ continue;
+ } else if (result != ISC_R_SUCCESS)
+ goto insist_and_cleanup;
+ }
+
+ /*
+ * Find TTL, class and type. Both TTL and class are optional
+ * and may occur in any order if they exist. TTL and class
+ * come before type which must exist.
+ *
+ * [<TTL>] [<class>] <type> <RDATA>
+ * [<class>] [<TTL>] <type> <RDATA>
+ */
+
+ type = 0;
+ rdclass = 0;
+
+ GETTOKEN(lctx->lex, 0, &token, initialws);
+
+ if (initialws) {
+ if (token.type == isc_tokentype_eol) {
+ read_till_eol = ISC_FALSE;
+ continue; /* blank line */
+ }
+
+ if (token.type == isc_tokentype_eof) {
+ WARNUNEXPECTEDEOF(lctx->lex);
+ read_till_eol = ISC_FALSE;
+ isc_lex_ungettoken(lctx->lex, &token);
+ continue;
+ }
+
+ if (ictx->current == NULL) {
+ (*callbacks->error)(callbacks,
+ "%s:%lu: no current owner name",
+ source, line);
+ result = DNS_R_NOOWNER;
+ if (MANYERRS(lctx, result)) {
+ SETRESULT(lctx, result);
+ read_till_eol = ISC_TRUE;
+ continue;
+ } else if (result != ISC_R_SUCCESS)
+ goto insist_and_cleanup;
+ }
+ }
+
+ if (dns_rdataclass_fromtext(&rdclass,
+ &token.value.as_textregion)
+ == ISC_R_SUCCESS)
+ GETTOKEN(lctx->lex, 0, &token, ISC_FALSE);
+
+ explicit_ttl = ISC_FALSE;
+ if (dns_ttl_fromtext(&token.value.as_textregion, &lctx->ttl)
+ == ISC_R_SUCCESS) {
+ limit_ttl(callbacks, source, line, &lctx->ttl);
+ explicit_ttl = ISC_TRUE;
+ lctx->ttl_known = ISC_TRUE;
+ GETTOKEN(lctx->lex, 0, &token, ISC_FALSE);
+ }
+
+ if (token.type != isc_tokentype_string) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_lex_gettoken() returned unexpected token type");
+ result = ISC_R_UNEXPECTED;
+ if (MANYERRS(lctx, result)) {
+ SETRESULT(lctx, result);
+ read_till_eol = ISC_TRUE;
+ continue;
+ } else if (result != ISC_R_SUCCESS)
+ goto insist_and_cleanup;
+ }
+
+ if (rdclass == 0 &&
+ dns_rdataclass_fromtext(&rdclass,
+ &token.value.as_textregion)
+ == ISC_R_SUCCESS)
+ GETTOKEN(lctx->lex, 0, &token, ISC_FALSE);
+
+ if (token.type != isc_tokentype_string) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_lex_gettoken() returned unexpected token type");
+ result = ISC_R_UNEXPECTED;
+ if (MANYERRS(lctx, result)) {
+ SETRESULT(lctx, result);
+ read_till_eol = ISC_TRUE;
+ continue;
+ } else if (result != ISC_R_SUCCESS)
+ goto insist_and_cleanup;
+ }
+
+ result = dns_rdatatype_fromtext(&type,
+ &token.value.as_textregion);
+ if (result != ISC_R_SUCCESS) {
+ (*callbacks->warn)(callbacks,
+ "%s:%lu: unknown RR type '%.*s'",
+ source, line,
+ token.value.as_textregion.length,
+ token.value.as_textregion.base);
+ if (MANYERRS(lctx, result)) {
+ SETRESULT(lctx, result);
+ read_till_eol = ISC_TRUE;
+ continue;
+ } else if (result != ISC_R_SUCCESS)
+ goto insist_and_cleanup;
+ }
+
+ /*
+ * If the class specified does not match the zone's class
+ * print out a error message and exit.
+ */
+ if (rdclass != 0 && rdclass != lctx->zclass) {
+ bad_class:
+
+ dns_rdataclass_format(rdclass, classname1,
+ sizeof(classname1));
+ dns_rdataclass_format(lctx->zclass, classname2,
+ sizeof(classname2));
+ (*callbacks->error)(callbacks,
+ "%s:%lu: class '%s' != "
+ "zone class '%s'",
+ source, line,
+ classname1, classname2);
+ result = DNS_R_BADCLASS;
+ if (MANYERRS(lctx, result)) {
+ SETRESULT(lctx, result);
+ read_till_eol = ISC_TRUE;
+ continue;
+ } else if (result != ISC_R_SUCCESS)
+ goto insist_and_cleanup;
+ }
+
+ if (type == dns_rdatatype_ns && ictx->glue == NULL)
+ current_has_delegation = ISC_TRUE;
+
+ /*
+ * RFC1123: MD and MF are not allowed to be loaded from
+ * master files.
+ */
+ if ((lctx->options & DNS_MASTER_ZONE) != 0 &&
+ (lctx->options & DNS_MASTER_SLAVE) == 0 &&
+ (type == dns_rdatatype_md || type == dns_rdatatype_mf)) {
+ char typename[DNS_RDATATYPE_FORMATSIZE];
+
+ result = DNS_R_OBSOLETE;
+
+ dns_rdatatype_format(type, typename, sizeof(typename));
+ (*callbacks->error)(callbacks,
+ "%s:%lu: %s '%s': %s",
+ source, line,
+ "type", typename,
+ dns_result_totext(result));
+ if (MANYERRS(lctx, result)) {
+ SETRESULT(lctx, result);
+ } else
+ goto insist_and_cleanup;
+ }
+
+ /*
+ * Find a rdata structure.
+ */
+ if (rdcount == rdata_size) {
+ new_rdata = grow_rdata(rdata_size + RDSZ, rdata,
+ rdata_size, &current_list,
+ &glue_list, mctx);
+ if (new_rdata == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto log_and_cleanup;
+ }
+ rdata_size += RDSZ;
+ rdata = new_rdata;
+ }
+
+ /*
+ * Peek at the NS record.
+ */
+ if (type == dns_rdatatype_ns &&
+ lctx->zclass == dns_rdataclass_in &&
+ (lctx->options & DNS_MASTER_CHECKNS) != 0) {
+
+ GETTOKEN(lctx->lex, 0, &token, ISC_FALSE);
+ result = check_ns(lctx, &token, source, line);
+ isc_lex_ungettoken(lctx->lex, &token);
+ if ((lctx->options & DNS_MASTER_FATALNS) != 0) {
+ if (MANYERRS(lctx, result)) {
+ SETRESULT(lctx, result);
+ } else if (result != ISC_R_SUCCESS)
+ goto insist_and_cleanup;
+ }
+ }
+
+ /*
+ * Check owner name.
+ */
+ options &= ~DNS_RDATA_CHECKREVERSE;
+ if ((lctx->options & DNS_MASTER_CHECKNAMES) != 0) {
+ isc_boolean_t ok;
+ dns_name_t *name;
+
+ name = (ictx->glue != NULL) ? ictx->glue :
+ ictx->current;
+ ok = dns_rdata_checkowner(name, lctx->zclass, type,
+ ISC_TRUE);
+ if (!ok) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ const char *desc;
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ result = DNS_R_BADOWNERNAME;
+ desc = dns_result_totext(result);
+ if ((lctx->options & DNS_MASTER_CHECKNAMESFAIL) != 0) {
+ (*callbacks->error)(callbacks,
+ "%s:%lu: %s: %s",
+ source, line,
+ namebuf, desc);
+ if (MANYERRS(lctx, result)) {
+ SETRESULT(lctx, result);
+ } else if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ } else {
+ (*callbacks->warn)(callbacks,
+ "%s:%lu: %s: %s",
+ source, line,
+ namebuf, desc);
+ }
+ }
+ if (type == dns_rdatatype_ptr &&
+ (dns_name_issubdomain(name, &in_addr_arpa) ||
+ dns_name_issubdomain(name, &ip6_arpa) ||
+ dns_name_issubdomain(name, &ip6_int)))
+ options |= DNS_RDATA_CHECKREVERSE;
+ }
+
+ /*
+ * Read rdata contents.
+ */
+ dns_rdata_init(&rdata[rdcount]);
+ target_ft = target;
+ result = dns_rdata_fromtext(&rdata[rdcount], lctx->zclass,
+ type, lctx->lex, ictx->origin,
+ options, lctx->mctx, &target,
+ callbacks);
+ if (MANYERRS(lctx, result)) {
+ SETRESULT(lctx, result);
+ continue;
+ } else if (result != ISC_R_SUCCESS)
+ goto insist_and_cleanup;
+
+ if (ictx->drop) {
+ target = target_ft;
+ continue;
+ }
+
+ if (type == dns_rdatatype_soa &&
+ (lctx->options & DNS_MASTER_ZONE) != 0 &&
+ dns_name_compare(ictx->current, lctx->top) != 0) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_name_format(ictx->current, namebuf,
+ sizeof(namebuf));
+ (*callbacks->error)(callbacks, "%s:%lu: SOA "
+ "record not at top of zone (%s)",
+ source, line, namebuf);
+ result = DNS_R_NOTZONETOP;
+ if (MANYERRS(lctx, result)) {
+ SETRESULT(lctx, result);
+ read_till_eol = ISC_TRUE;
+ target = target_ft;
+ continue;
+ } else if (result != ISC_R_SUCCESS)
+ goto insist_and_cleanup;
+ }
+
+
+ if (type == dns_rdatatype_rrsig ||
+ type == dns_rdatatype_sig)
+ covers = dns_rdata_covers(&rdata[rdcount]);
+ else
+ covers = 0;
+
+ if (!lctx->ttl_known && !lctx->default_ttl_known) {
+ if (type == dns_rdatatype_soa) {
+ (*callbacks->warn)(callbacks,
+ "%s:%lu: no TTL specified; "
+ "using SOA MINTTL instead",
+ source, line);
+ lctx->ttl = dns_soa_getminimum(&rdata[rdcount]);
+ limit_ttl(callbacks, source, line, &lctx->ttl);
+ lctx->default_ttl = lctx->ttl;
+ lctx->default_ttl_known = ISC_TRUE;
+ } else if ((lctx->options & DNS_MASTER_HINT) != 0) {
+ /*
+ * Zero TTL's are fine for hints.
+ */
+ lctx->ttl = 0;
+ lctx->default_ttl = lctx->ttl;
+ lctx->default_ttl_known = ISC_TRUE;
+ } else {
+ (*callbacks->warn)(callbacks,
+ "%s:%lu: no TTL specified; "
+ "zone rejected",
+ source, line);
+ result = DNS_R_NOTTL;
+ if (MANYERRS(lctx, result)) {
+ SETRESULT(lctx, result);
+ lctx->ttl = 0;
+ } else {
+ goto insist_and_cleanup;
+ }
+ }
+ } else if (!explicit_ttl && lctx->default_ttl_known) {
+ lctx->ttl = lctx->default_ttl;
+ } else if (!explicit_ttl && lctx->warn_1035) {
+ (*callbacks->warn)(callbacks,
+ "%s:%lu: "
+ "using RFC1035 TTL semantics",
+ source, line);
+ lctx->warn_1035 = ISC_FALSE;
+ }
+
+ if (type == dns_rdatatype_rrsig && lctx->warn_sigexpired) {
+ dns_rdata_rrsig_t sig;
+ result = dns_rdata_tostruct(&rdata[rdcount], &sig,
+ NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ if (isc_serial_lt(sig.timeexpire, now)) {
+ (*callbacks->warn)(callbacks,
+ "%s:%lu: "
+ "signature has expired",
+ source, line);
+ lctx->warn_sigexpired = ISC_FALSE;
+ }
+ }
+
+ if ((type == dns_rdatatype_sig || type == dns_rdatatype_nxt) &&
+ lctx->warn_tcr && (lctx->options & DNS_MASTER_ZONE) != 0 &&
+ (lctx->options & DNS_MASTER_SLAVE) == 0) {
+ (*callbacks->warn)(callbacks, "%s:%lu: old style DNSSEC "
+ " zone detected", source, line);
+ lctx->warn_tcr = ISC_FALSE;
+ }
+
+ if ((lctx->options & DNS_MASTER_AGETTL) != 0) {
+ /*
+ * Adjust the TTL for $DATE. If the RR has already
+ * expired, ignore it.
+ */
+ if (lctx->ttl < ttl_offset)
+ continue;
+ lctx->ttl -= ttl_offset;
+ }
+
+ /*
+ * Find type in rdatalist.
+ * If it does not exist create new one and prepend to list
+ * as this will mimimise list traversal.
+ */
+ if (ictx->glue != NULL)
+ this = ISC_LIST_HEAD(glue_list);
+ else
+ this = ISC_LIST_HEAD(current_list);
+
+ while (this != NULL) {
+ if (this->type == type && this->covers == covers)
+ break;
+ this = ISC_LIST_NEXT(this, link);
+ }
+
+ if (this == NULL) {
+ if (rdlcount == rdatalist_size) {
+ new_rdatalist =
+ grow_rdatalist(rdatalist_size + RDLSZ,
+ rdatalist,
+ rdatalist_size,
+ &current_list,
+ &glue_list,
+ mctx);
+ if (new_rdatalist == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto log_and_cleanup;
+ }
+ rdatalist = new_rdatalist;
+ rdatalist_size += RDLSZ;
+ }
+ this = &rdatalist[rdlcount++];
+ this->type = type;
+ this->covers = covers;
+ this->rdclass = lctx->zclass;
+ this->ttl = lctx->ttl;
+ ISC_LIST_INIT(this->rdata);
+ if (ictx->glue != NULL)
+ ISC_LIST_INITANDPREPEND(glue_list, this, link);
+ else
+ ISC_LIST_INITANDPREPEND(current_list, this,
+ link);
+ } else if (this->ttl != lctx->ttl) {
+ (*callbacks->warn)(callbacks,
+ "%s:%lu: "
+ "TTL set to prior TTL (%lu)",
+ source, line, this->ttl);
+ lctx->ttl = this->ttl;
+ }
+
+ ISC_LIST_APPEND(this->rdata, &rdata[rdcount], link);
+ if (ictx->glue != NULL)
+ ictx->glue_line = line;
+ else
+ ictx->current_line = line;
+ rdcount++;
+
+ /*
+ * We must have at least 64k as rdlen is 16 bits.
+ * If we don't commit everything we have so far.
+ */
+ if ((target.length - target.used) < MINTSIZ)
+ COMMITALL;
+ next_line:
+ ;
+ } while (!done && (lctx->loop_cnt == 0 || loop_cnt++ < lctx->loop_cnt));
+
+ /*
+ * Commit what has not yet been committed.
+ */
+ result = commit(callbacks, lctx, &current_list, ictx->current,
+ source, ictx->current_line);
+ if (MANYERRS(lctx, result)) {
+ SETRESULT(lctx, result);
+ } else if (result != ISC_R_SUCCESS)
+ goto insist_and_cleanup;
+ result = commit(callbacks, lctx, &glue_list, ictx->glue,
+ source, ictx->glue_line);
+ if (MANYERRS(lctx, result)) {
+ SETRESULT(lctx, result);
+ } else if (result != ISC_R_SUCCESS)
+ goto insist_and_cleanup;
+
+ if (!done) {
+ INSIST(lctx->done != NULL && lctx->task != NULL);
+ result = DNS_R_CONTINUE;
+ } else if (result == ISC_R_SUCCESS && lctx->result != ISC_R_SUCCESS) {
+ result = lctx->result;
+ } else if (result == ISC_R_SUCCESS && lctx->seen_include)
+ result = DNS_R_SEENINCLUDE;
+ goto cleanup;
+
+ log_and_cleanup:
+ LOGIT(result);
+
+ insist_and_cleanup:
+ INSIST(result != ISC_R_SUCCESS);
+
+ cleanup:
+ while ((this = ISC_LIST_HEAD(current_list)) != NULL)
+ ISC_LIST_UNLINK(current_list, this, link);
+ while ((this = ISC_LIST_HEAD(glue_list)) != NULL)
+ ISC_LIST_UNLINK(glue_list, this, link);
+ if (rdatalist != NULL)
+ isc_mem_put(mctx, rdatalist,
+ rdatalist_size * sizeof(*rdatalist));
+ if (rdata != NULL)
+ isc_mem_put(mctx, rdata, rdata_size * sizeof(*rdata));
+ if (target_mem != NULL)
+ isc_mem_put(mctx, target_mem, target_size);
+ if (include_file != NULL)
+ isc_mem_free(mctx, include_file);
+ if (range != NULL)
+ isc_mem_free(mctx, range);
+ if (lhs != NULL)
+ isc_mem_free(mctx, lhs);
+ if (gtype != NULL)
+ isc_mem_free(mctx, gtype);
+ if (rhs != NULL)
+ isc_mem_free(mctx, rhs);
+ return (result);
+}
+
+static isc_result_t
+pushfile(const char *master_file, dns_name_t *origin, dns_loadctx_t *lctx) {
+ isc_result_t result;
+ dns_incctx_t *ictx;
+ dns_incctx_t *new = NULL;
+ isc_region_t r;
+ int new_in_use;
+
+ REQUIRE(master_file != NULL);
+ REQUIRE(DNS_LCTX_VALID(lctx));
+
+ ictx = lctx->inc;
+ lctx->seen_include = ISC_TRUE;
+
+ result = incctx_create(lctx->mctx, origin, &new);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /* Set current domain. */
+ if (ictx->glue != NULL || ictx->current != NULL) {
+ for (new_in_use = 0; new_in_use < NBUFS; new_in_use++)
+ if (!new->in_use[new_in_use])
+ break;
+ INSIST(new_in_use < NBUFS);
+ new->current_in_use = new_in_use;
+ new->current =
+ dns_fixedname_name(&new->fixed[new->current_in_use]);
+ new->in_use[new->current_in_use] = ISC_TRUE;
+ dns_name_toregion((ictx->glue != NULL) ?
+ ictx->glue : ictx->current, &r);
+ dns_name_fromregion(new->current, &r);
+ new->drop = ictx->drop;
+ }
+
+ result = (lctx->openfile)(lctx, master_file);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ new->parent = ictx;
+ lctx->inc = new;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (new != NULL)
+ incctx_destroy(lctx->mctx, new);
+ return (result);
+}
+
+static inline isc_result_t
+read_and_check(isc_boolean_t do_read, isc_buffer_t *buffer,
+ size_t len, FILE *f)
+{
+ isc_result_t result;
+
+ if (do_read) {
+ INSIST(isc_buffer_availablelength(buffer) >= len);
+ result = isc_stdio_read(isc_buffer_used(buffer), 1, len,
+ f, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ isc_buffer_add(buffer, len);
+ } else if (isc_buffer_remaininglength(buffer) < len)
+ return (ISC_R_RANGE);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+load_raw(dns_loadctx_t *lctx) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_boolean_t done = ISC_FALSE;
+ unsigned int loop_cnt = 0;
+ dns_rdatacallbacks_t *callbacks;
+ unsigned char namebuf[DNS_NAME_MAXWIRE];
+ isc_region_t r;
+ dns_name_t name;
+ rdatalist_head_t head, dummy;
+ dns_rdatalist_t rdatalist;
+ isc_mem_t *mctx = lctx->mctx;
+ dns_rdata_t *rdata = NULL;
+ unsigned int rdata_size = 0;
+ int target_size = TSIZ;
+ isc_buffer_t target;
+ unsigned char *target_mem = NULL;
+
+ REQUIRE(DNS_LCTX_VALID(lctx));
+ callbacks = lctx->callbacks;
+
+ if (lctx->first) {
+ dns_masterrawheader_t header;
+ isc_uint32_t format, version, dumptime;
+ size_t hdrlen = sizeof(format) + sizeof(version) +
+ sizeof(dumptime);
+
+ INSIST(hdrlen <= sizeof(header));
+ isc_buffer_init(&target, &header, sizeof(header));
+
+ result = isc_stdio_read(&header, 1, hdrlen, lctx->f, NULL);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_stdio_read failed: %s",
+ isc_result_totext(result));
+ return (result);
+ }
+ isc_buffer_add(&target, hdrlen);
+ format = isc_buffer_getuint32(&target);
+ if (format != dns_masterformat_raw) {
+ (*callbacks->error)(callbacks,
+ "dns_master_load: "
+ "file format mismatch");
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+
+ version = isc_buffer_getuint32(&target);
+ if (version > DNS_RAWFORMAT_VERSION) {
+ (*callbacks->error)(callbacks,
+ "dns_master_load: "
+ "unsupported file format version");
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+
+ /* Empty read: currently, we do not use dumptime */
+ dumptime = isc_buffer_getuint32(&target);
+
+ lctx->first = ISC_FALSE;
+ }
+
+ ISC_LIST_INIT(head);
+ ISC_LIST_INIT(dummy);
+ dns_rdatalist_init(&rdatalist);
+
+ /*
+ * Allocate target_size of buffer space. This is greater than twice
+ * the maximum individual RR data size.
+ */
+ target_mem = isc_mem_get(mctx, target_size);
+ if (target_mem == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ isc_buffer_init(&target, target_mem, target_size);
+
+ /*
+ * In the following loop, we regard any error fatal regardless of
+ * whether "MANYERRORS" is set in the context option. This is because
+ * normal errors should already have been checked at creation time.
+ * Besides, it is very unlikely that we can recover from an error
+ * in this format, and so trying to continue parsing erroneous data
+ * does not really make sense.
+ */
+ for (loop_cnt = 0;
+ (lctx->loop_cnt == 0 || loop_cnt < lctx->loop_cnt);
+ loop_cnt++) {
+ unsigned int i, rdcount, consumed_name;
+ isc_uint16_t namelen;
+ isc_uint32_t totallen;
+ size_t minlen, readlen;
+ isc_boolean_t sequential_read = ISC_FALSE;
+
+ /* Read the data length */
+ isc_buffer_clear(&target);
+ INSIST(isc_buffer_availablelength(&target) >=
+ sizeof(totallen));
+ result = isc_stdio_read(target.base, 1, sizeof(totallen),
+ lctx->f, NULL);
+ if (result == ISC_R_EOF) {
+ result = ISC_R_SUCCESS;
+ done = ISC_TRUE;
+ break;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ isc_buffer_add(&target, sizeof(totallen));
+ totallen = isc_buffer_getuint32(&target);
+ /*
+ * Validation: the input data must at least contain the common
+ * header.
+ */
+ minlen = sizeof(totallen) + sizeof(isc_uint16_t) +
+ sizeof(isc_uint16_t) + sizeof(isc_uint16_t) +
+ sizeof(isc_uint32_t) + sizeof(isc_uint32_t);
+ if (totallen < minlen) {
+ result = ISC_R_RANGE;
+ goto cleanup;
+ }
+ totallen -= sizeof(totallen);
+
+ isc_buffer_clear(&target);
+ if (totallen > isc_buffer_availablelength(&target)) {
+ /*
+ * The default buffer size should typically be large
+ * enough to store the entire RRset. We could try to
+ * allocate enough space if this is not the case, but
+ * it might cause a hazardous result when "totallen"
+ * is forged. Thus, we'd rather take an inefficient
+ * but robust approach in this atypical case: read
+ * data step by step, and commit partial data when
+ * necessary. Note that the buffer must be large
+ * enough to store the "header part", owner name, and
+ * at least one rdata (however large it is).
+ */
+ sequential_read = ISC_TRUE;
+ readlen = minlen - sizeof(totallen);
+ } else {
+ /*
+ * Typical case. We can read the whole RRset at once
+ * with the default buffer.
+ */
+ readlen = totallen;
+ }
+ result = isc_stdio_read(target.base, 1, readlen,
+ lctx->f, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ isc_buffer_add(&target, readlen);
+
+ /* Construct RRset headers */
+ rdatalist.rdclass = isc_buffer_getuint16(&target);
+ rdatalist.type = isc_buffer_getuint16(&target);
+ rdatalist.covers = isc_buffer_getuint16(&target);
+ rdatalist.ttl = isc_buffer_getuint32(&target);
+ rdcount = isc_buffer_getuint32(&target);
+ if (rdcount == 0) {
+ result = ISC_R_RANGE;
+ goto cleanup;
+ }
+ INSIST(isc_buffer_consumedlength(&target) <= readlen);
+
+ /* Owner name: length followed by name */
+ result = read_and_check(sequential_read, &target,
+ sizeof(namelen), lctx->f);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ namelen = isc_buffer_getuint16(&target);
+ if (namelen > sizeof(namebuf)) {
+ result = ISC_R_RANGE;
+ goto cleanup;
+ }
+
+ result = read_and_check(sequential_read, &target, namelen,
+ lctx->f);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ isc_buffer_setactive(&target, (unsigned int)namelen);
+ isc_buffer_activeregion(&target, &r);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+ isc_buffer_forward(&target, (unsigned int)namelen);
+ consumed_name = isc_buffer_consumedlength(&target);
+
+ /* Rdata contents. */
+ if (rdcount > rdata_size) {
+ dns_rdata_t *new_rdata = NULL;
+
+ new_rdata = grow_rdata(rdata_size + RDSZ, rdata,
+ rdata_size, &head,
+ &dummy, mctx);
+ if (new_rdata == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ rdata_size += RDSZ;
+ rdata = new_rdata;
+ }
+
+ continue_read:
+ for (i = 0; i < rdcount; i++) {
+ isc_uint16_t rdlen;
+
+ dns_rdata_init(&rdata[i]);
+
+ if (sequential_read &&
+ isc_buffer_availablelength(&target) < MINTSIZ) {
+ unsigned int j;
+
+ INSIST(i > 0); /* detect an infinite loop */
+
+ /* Partial Commit. */
+ ISC_LIST_APPEND(head, &rdatalist, link);
+ result = commit(callbacks, lctx, &head, &name,
+ NULL, 0);
+ for (j = 0; j < i; j++) {
+ ISC_LIST_UNLINK(rdatalist.rdata,
+ &rdata[j], link);
+ dns_rdata_reset(&rdata[j]);
+ }
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ /* Rewind the buffer and continue */
+ isc_buffer_clear(&target);
+ isc_buffer_add(&target, consumed_name);
+ isc_buffer_forward(&target, consumed_name);
+
+ rdcount -= i;
+ i = 0;
+
+ goto continue_read;
+ }
+
+ /* rdata length */
+ result = read_and_check(sequential_read, &target,
+ sizeof(rdlen), lctx->f);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ rdlen = isc_buffer_getuint16(&target);
+
+ /* rdata */
+ result = read_and_check(sequential_read, &target,
+ rdlen, lctx->f);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ isc_buffer_setactive(&target, (unsigned int)rdlen);
+ isc_buffer_activeregion(&target, &r);
+ isc_buffer_forward(&target, (unsigned int)rdlen);
+ dns_rdata_fromregion(&rdata[i], rdatalist.rdclass,
+ rdatalist.type, &r);
+
+ ISC_LIST_APPEND(rdatalist.rdata, &rdata[i], link);
+ }
+
+ /*
+ * Sanity check. Still having remaining space is not
+ * necessarily critical, but it very likely indicates broken
+ * or malformed data.
+ */
+ if (isc_buffer_remaininglength(&target) != 0) {
+ result = ISC_R_RANGE;
+ goto cleanup;
+ }
+
+ ISC_LIST_APPEND(head, &rdatalist, link);
+
+ /* Commit this RRset. rdatalist will be unlinked. */
+ result = commit(callbacks, lctx, &head, &name, NULL, 0);
+
+ for (i = 0; i < rdcount; i++) {
+ ISC_LIST_UNLINK(rdatalist.rdata, &rdata[i], link);
+ dns_rdata_reset(&rdata[i]);
+ }
+
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+
+ if (!done) {
+ INSIST(lctx->done != NULL && lctx->task != NULL);
+ result = DNS_R_CONTINUE;
+ } else if (result == ISC_R_SUCCESS && lctx->result != ISC_R_SUCCESS)
+ result = lctx->result;
+
+ cleanup:
+ if (rdata != NULL)
+ isc_mem_put(mctx, rdata, rdata_size * sizeof(*rdata));
+ if (target_mem != NULL)
+ isc_mem_put(mctx, target_mem, target_size);
+ if (result != ISC_R_SUCCESS && result != DNS_R_CONTINUE) {
+ (*callbacks->error)(callbacks, "dns_master_load: %s",
+ dns_result_totext(result));
+ }
+
+ return (result);
+}
+
+isc_result_t
+dns_master_loadfile(const char *master_file, dns_name_t *top,
+ dns_name_t *origin,
+ dns_rdataclass_t zclass, unsigned int options,
+ dns_rdatacallbacks_t *callbacks, isc_mem_t *mctx)
+{
+ return (dns_master_loadfile3(master_file, top, origin, zclass, options,
+ 0, callbacks, mctx, dns_masterformat_text));
+}
+
+isc_result_t
+dns_master_loadfile2(const char *master_file, dns_name_t *top,
+ dns_name_t *origin,
+ dns_rdataclass_t zclass, unsigned int options,
+ dns_rdatacallbacks_t *callbacks, isc_mem_t *mctx,
+ dns_masterformat_t format)
+{
+ return (dns_master_loadfile3(master_file, top, origin, zclass, options,
+ 0, callbacks, mctx, format));
+}
+
+isc_result_t
+dns_master_loadfile3(const char *master_file, dns_name_t *top,
+ dns_name_t *origin, dns_rdataclass_t zclass,
+ unsigned int options, isc_uint32_t resign,
+ dns_rdatacallbacks_t *callbacks, isc_mem_t *mctx,
+ dns_masterformat_t format)
+{
+ dns_loadctx_t *lctx = NULL;
+ isc_result_t result;
+
+ result = loadctx_create(format, mctx, options, resign, top, zclass,
+ origin, callbacks, NULL, NULL, NULL, NULL,
+ &lctx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = (lctx->openfile)(lctx, master_file);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = (lctx->load)(lctx);
+ INSIST(result != DNS_R_CONTINUE);
+
+ cleanup:
+ dns_loadctx_detach(&lctx);
+ return (result);
+}
+
+isc_result_t
+dns_master_loadfileinc(const char *master_file, dns_name_t *top,
+ dns_name_t *origin, dns_rdataclass_t zclass,
+ unsigned int options, dns_rdatacallbacks_t *callbacks,
+ isc_task_t *task, dns_loaddonefunc_t done,
+ void *done_arg, dns_loadctx_t **lctxp, isc_mem_t *mctx)
+{
+ return (dns_master_loadfileinc3(master_file, top, origin, zclass,
+ options, 0, callbacks, task, done,
+ done_arg, lctxp, mctx,
+ dns_masterformat_text));
+}
+
+isc_result_t
+dns_master_loadfileinc2(const char *master_file, dns_name_t *top,
+ dns_name_t *origin, dns_rdataclass_t zclass,
+ unsigned int options, dns_rdatacallbacks_t *callbacks,
+ isc_task_t *task, dns_loaddonefunc_t done,
+ void *done_arg, dns_loadctx_t **lctxp, isc_mem_t *mctx,
+ dns_masterformat_t format)
+{
+ return (dns_master_loadfileinc3(master_file, top, origin, zclass,
+ options, 0, callbacks, task, done,
+ done_arg, lctxp, mctx, format));
+}
+
+isc_result_t
+dns_master_loadfileinc3(const char *master_file, dns_name_t *top,
+ dns_name_t *origin, dns_rdataclass_t zclass,
+ unsigned int options, isc_uint32_t resign,
+ dns_rdatacallbacks_t *callbacks, isc_task_t *task,
+ dns_loaddonefunc_t done, void *done_arg,
+ dns_loadctx_t **lctxp, isc_mem_t *mctx,
+ dns_masterformat_t format)
+{
+ dns_loadctx_t *lctx = NULL;
+ isc_result_t result;
+
+ REQUIRE(task != NULL);
+ REQUIRE(done != NULL);
+
+ result = loadctx_create(format, mctx, options, resign, top, zclass,
+ origin, callbacks, task, done, done_arg, NULL,
+ &lctx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = (lctx->openfile)(lctx, master_file);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = task_send(lctx);
+ if (result == ISC_R_SUCCESS) {
+ dns_loadctx_attach(lctx, lctxp);
+ return (DNS_R_CONTINUE);
+ }
+
+ cleanup:
+ dns_loadctx_detach(&lctx);
+ return (result);
+}
+
+isc_result_t
+dns_master_loadstream(FILE *stream, dns_name_t *top, dns_name_t *origin,
+ dns_rdataclass_t zclass, unsigned int options,
+ dns_rdatacallbacks_t *callbacks, isc_mem_t *mctx)
+{
+ isc_result_t result;
+ dns_loadctx_t *lctx = NULL;
+
+ REQUIRE(stream != NULL);
+
+ result = loadctx_create(dns_masterformat_text, mctx, options, 0, top,
+ zclass, origin, callbacks, NULL, NULL, NULL,
+ NULL, &lctx);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = isc_lex_openstream(lctx->lex, stream);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = (lctx->load)(lctx);
+ INSIST(result != DNS_R_CONTINUE);
+
+ cleanup:
+ if (lctx != NULL)
+ dns_loadctx_detach(&lctx);
+ return (result);
+}
+
+isc_result_t
+dns_master_loadstreaminc(FILE *stream, dns_name_t *top, dns_name_t *origin,
+ dns_rdataclass_t zclass, unsigned int options,
+ dns_rdatacallbacks_t *callbacks, isc_task_t *task,
+ dns_loaddonefunc_t done, void *done_arg,
+ dns_loadctx_t **lctxp, isc_mem_t *mctx)
+{
+ isc_result_t result;
+ dns_loadctx_t *lctx = NULL;
+
+ REQUIRE(stream != NULL);
+ REQUIRE(task != NULL);
+ REQUIRE(done != NULL);
+
+ result = loadctx_create(dns_masterformat_text, mctx, options, 0, top,
+ zclass, origin, callbacks, task, done,
+ done_arg, NULL, &lctx);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = isc_lex_openstream(lctx->lex, stream);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = task_send(lctx);
+ if (result == ISC_R_SUCCESS) {
+ dns_loadctx_attach(lctx, lctxp);
+ return (DNS_R_CONTINUE);
+ }
+
+ cleanup:
+ if (lctx != NULL)
+ dns_loadctx_detach(&lctx);
+ return (result);
+}
+
+isc_result_t
+dns_master_loadbuffer(isc_buffer_t *buffer, dns_name_t *top,
+ dns_name_t *origin, dns_rdataclass_t zclass,
+ unsigned int options,
+ dns_rdatacallbacks_t *callbacks, isc_mem_t *mctx)
+{
+ isc_result_t result;
+ dns_loadctx_t *lctx = NULL;
+
+ REQUIRE(buffer != NULL);
+
+ result = loadctx_create(dns_masterformat_text, mctx, options, 0, top,
+ zclass, origin, callbacks, NULL, NULL, NULL,
+ NULL, &lctx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = isc_lex_openbuffer(lctx->lex, buffer);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = (lctx->load)(lctx);
+ INSIST(result != DNS_R_CONTINUE);
+
+ cleanup:
+ dns_loadctx_detach(&lctx);
+ return (result);
+}
+
+isc_result_t
+dns_master_loadbufferinc(isc_buffer_t *buffer, dns_name_t *top,
+ dns_name_t *origin, dns_rdataclass_t zclass,
+ unsigned int options,
+ dns_rdatacallbacks_t *callbacks, isc_task_t *task,
+ dns_loaddonefunc_t done, void *done_arg,
+ dns_loadctx_t **lctxp, isc_mem_t *mctx)
+{
+ isc_result_t result;
+ dns_loadctx_t *lctx = NULL;
+
+ REQUIRE(buffer != NULL);
+ REQUIRE(task != NULL);
+ REQUIRE(done != NULL);
+
+ result = loadctx_create(dns_masterformat_text, mctx, options, 0, top,
+ zclass, origin, callbacks, task, done,
+ done_arg, NULL, &lctx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = isc_lex_openbuffer(lctx->lex, buffer);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = task_send(lctx);
+ if (result == ISC_R_SUCCESS) {
+ dns_loadctx_attach(lctx, lctxp);
+ return (DNS_R_CONTINUE);
+ }
+
+ cleanup:
+ dns_loadctx_detach(&lctx);
+ return (result);
+}
+
+isc_result_t
+dns_master_loadlexer(isc_lex_t *lex, dns_name_t *top,
+ dns_name_t *origin, dns_rdataclass_t zclass,
+ unsigned int options,
+ dns_rdatacallbacks_t *callbacks, isc_mem_t *mctx)
+{
+ isc_result_t result;
+ dns_loadctx_t *lctx = NULL;
+
+ REQUIRE(lex != NULL);
+
+ result = loadctx_create(dns_masterformat_text, mctx, options, 0, top,
+ zclass, origin, callbacks, NULL, NULL, NULL,
+ lex, &lctx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = (lctx->load)(lctx);
+ INSIST(result != DNS_R_CONTINUE);
+
+ dns_loadctx_detach(&lctx);
+ return (result);
+}
+
+isc_result_t
+dns_master_loadlexerinc(isc_lex_t *lex, dns_name_t *top,
+ dns_name_t *origin, dns_rdataclass_t zclass,
+ unsigned int options,
+ dns_rdatacallbacks_t *callbacks, isc_task_t *task,
+ dns_loaddonefunc_t done, void *done_arg,
+ dns_loadctx_t **lctxp, isc_mem_t *mctx)
+{
+ isc_result_t result;
+ dns_loadctx_t *lctx = NULL;
+
+ REQUIRE(lex != NULL);
+ REQUIRE(task != NULL);
+ REQUIRE(done != NULL);
+
+ result = loadctx_create(dns_masterformat_text, mctx, options, 0, top,
+ zclass, origin, callbacks, task, done,
+ done_arg, lex, &lctx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = task_send(lctx);
+ if (result == ISC_R_SUCCESS) {
+ dns_loadctx_attach(lctx, lctxp);
+ return (DNS_R_CONTINUE);
+ }
+
+ dns_loadctx_detach(&lctx);
+ return (result);
+}
+
+/*
+ * Grow the slab of dns_rdatalist_t structures.
+ * Re-link glue and current list.
+ */
+static dns_rdatalist_t *
+grow_rdatalist(int new_len, dns_rdatalist_t *old, int old_len,
+ rdatalist_head_t *current, rdatalist_head_t *glue,
+ isc_mem_t *mctx)
+{
+ dns_rdatalist_t *new;
+ int rdlcount = 0;
+ ISC_LIST(dns_rdatalist_t) save;
+ dns_rdatalist_t *this;
+
+ new = isc_mem_get(mctx, new_len * sizeof(*new));
+ if (new == NULL)
+ return (NULL);
+
+ ISC_LIST_INIT(save);
+ this = ISC_LIST_HEAD(*current);
+ while ((this = ISC_LIST_HEAD(*current)) != NULL) {
+ ISC_LIST_UNLINK(*current, this, link);
+ ISC_LIST_APPEND(save, this, link);
+ }
+ while ((this = ISC_LIST_HEAD(save)) != NULL) {
+ ISC_LIST_UNLINK(save, this, link);
+ new[rdlcount] = *this;
+ ISC_LIST_APPEND(*current, &new[rdlcount], link);
+ rdlcount++;
+ }
+
+ ISC_LIST_INIT(save);
+ this = ISC_LIST_HEAD(*glue);
+ while ((this = ISC_LIST_HEAD(*glue)) != NULL) {
+ ISC_LIST_UNLINK(*glue, this, link);
+ ISC_LIST_APPEND(save, this, link);
+ }
+ while ((this = ISC_LIST_HEAD(save)) != NULL) {
+ ISC_LIST_UNLINK(save, this, link);
+ new[rdlcount] = *this;
+ ISC_LIST_APPEND(*glue, &new[rdlcount], link);
+ rdlcount++;
+ }
+
+ INSIST(rdlcount == old_len);
+ if (old != NULL)
+ isc_mem_put(mctx, old, old_len * sizeof(*old));
+ return (new);
+}
+
+/*
+ * Grow the slab of rdata structs.
+ * Re-link the current and glue chains.
+ */
+static dns_rdata_t *
+grow_rdata(int new_len, dns_rdata_t *old, int old_len,
+ rdatalist_head_t *current, rdatalist_head_t *glue,
+ isc_mem_t *mctx)
+{
+ dns_rdata_t *new;
+ int rdcount = 0;
+ ISC_LIST(dns_rdata_t) save;
+ dns_rdatalist_t *this;
+ dns_rdata_t *rdata;
+
+ new = isc_mem_get(mctx, new_len * sizeof(*new));
+ if (new == NULL)
+ return (NULL);
+ memset(new, 0, new_len * sizeof(*new));
+
+ /*
+ * Copy current relinking.
+ */
+ this = ISC_LIST_HEAD(*current);
+ while (this != NULL) {
+ ISC_LIST_INIT(save);
+ while ((rdata = ISC_LIST_HEAD(this->rdata)) != NULL) {
+ ISC_LIST_UNLINK(this->rdata, rdata, link);
+ ISC_LIST_APPEND(save, rdata, link);
+ }
+ while ((rdata = ISC_LIST_HEAD(save)) != NULL) {
+ ISC_LIST_UNLINK(save, rdata, link);
+ new[rdcount] = *rdata;
+ ISC_LIST_APPEND(this->rdata, &new[rdcount], link);
+ rdcount++;
+ }
+ this = ISC_LIST_NEXT(this, link);
+ }
+
+ /*
+ * Copy glue relinking.
+ */
+ this = ISC_LIST_HEAD(*glue);
+ while (this != NULL) {
+ ISC_LIST_INIT(save);
+ while ((rdata = ISC_LIST_HEAD(this->rdata)) != NULL) {
+ ISC_LIST_UNLINK(this->rdata, rdata, link);
+ ISC_LIST_APPEND(save, rdata, link);
+ }
+ while ((rdata = ISC_LIST_HEAD(save)) != NULL) {
+ ISC_LIST_UNLINK(save, rdata, link);
+ new[rdcount] = *rdata;
+ ISC_LIST_APPEND(this->rdata, &new[rdcount], link);
+ rdcount++;
+ }
+ this = ISC_LIST_NEXT(this, link);
+ }
+ INSIST(rdcount == old_len);
+ if (old != NULL)
+ isc_mem_put(mctx, old, old_len * sizeof(*old));
+ return (new);
+}
+
+static isc_uint32_t
+resign_fromlist(dns_rdatalist_t *this, isc_uint32_t resign) {
+ dns_rdata_t *rdata;
+ dns_rdata_rrsig_t sig;
+ isc_uint32_t when;
+
+ rdata = ISC_LIST_HEAD(this->rdata);
+ INSIST(rdata != NULL);
+ (void)dns_rdata_tostruct(rdata, &sig, NULL);
+ when = sig.timeexpire - resign;
+
+ rdata = ISC_LIST_NEXT(rdata, link);
+ while (rdata != NULL) {
+ (void)dns_rdata_tostruct(rdata, &sig, NULL);
+ if (sig.timeexpire - resign < when)
+ when = sig.timeexpire - resign;
+ rdata = ISC_LIST_NEXT(rdata, link);
+ }
+ return (when);
+}
+
+/*
+ * Convert each element from a rdatalist_t to rdataset then call commit.
+ * Unlink each element as we go.
+ */
+
+static isc_result_t
+commit(dns_rdatacallbacks_t *callbacks, dns_loadctx_t *lctx,
+ rdatalist_head_t *head, dns_name_t *owner,
+ const char *source, unsigned int line)
+{
+ dns_rdatalist_t *this;
+ dns_rdataset_t dataset;
+ isc_result_t result;
+ char namebuf[DNS_NAME_FORMATSIZE];
+ void (*error)(struct dns_rdatacallbacks *, const char *, ...);
+
+ this = ISC_LIST_HEAD(*head);
+ error = callbacks->error;
+
+ if (this == NULL)
+ return (ISC_R_SUCCESS);
+ do {
+ dns_rdataset_init(&dataset);
+ RUNTIME_CHECK(dns_rdatalist_tordataset(this, &dataset)
+ == ISC_R_SUCCESS);
+ dataset.trust = dns_trust_ultimate;
+ /*
+ * If this is a secure dynamic zone set the re-signing time.
+ */
+ if (dataset.type == dns_rdatatype_rrsig &&
+ (lctx->options & DNS_MASTER_RESIGN) != 0) {
+ dataset.attributes |= DNS_RDATASETATTR_RESIGN;
+ dns_name_format(owner, namebuf, sizeof(namebuf));
+ dataset.resign = resign_fromlist(this, lctx->resign);
+ }
+ result = ((*callbacks->add)(callbacks->add_private, owner,
+ &dataset));
+ if (result == ISC_R_NOMEMORY) {
+ (*error)(callbacks, "dns_master_load: %s",
+ dns_result_totext(result));
+ } else if (result != ISC_R_SUCCESS) {
+ dns_name_format(owner, namebuf, sizeof(namebuf));
+ if (source != NULL) {
+ (*error)(callbacks, "%s: %s:%lu: %s: %s",
+ "dns_master_load", source, line,
+ namebuf, dns_result_totext(result));
+ } else {
+ (*error)(callbacks, "%s: %s: %s",
+ "dns_master_load", namebuf,
+ dns_result_totext(result));
+ }
+ }
+ if (MANYERRS(lctx, result))
+ SETRESULT(lctx, result);
+ else if (result != ISC_R_SUCCESS)
+ return (result);
+ ISC_LIST_UNLINK(*head, this, link);
+ this = ISC_LIST_HEAD(*head);
+ } while (this != NULL);
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Returns ISC_TRUE if one of the NS rdata's contains 'owner'.
+ */
+
+static isc_boolean_t
+is_glue(rdatalist_head_t *head, dns_name_t *owner) {
+ dns_rdatalist_t *this;
+ dns_rdata_t *rdata;
+ isc_region_t region;
+ dns_name_t name;
+
+ /*
+ * Find NS rrset.
+ */
+ this = ISC_LIST_HEAD(*head);
+ while (this != NULL) {
+ if (this->type == dns_rdatatype_ns)
+ break;
+ this = ISC_LIST_NEXT(this, link);
+ }
+ if (this == NULL)
+ return (ISC_FALSE);
+
+ rdata = ISC_LIST_HEAD(this->rdata);
+ while (rdata != NULL) {
+ dns_name_init(&name, NULL);
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+ if (dns_name_compare(&name, owner) == 0)
+ return (ISC_TRUE);
+ rdata = ISC_LIST_NEXT(rdata, link);
+ }
+ return (ISC_FALSE);
+}
+
+static void
+load_quantum(isc_task_t *task, isc_event_t *event) {
+ isc_result_t result;
+ dns_loadctx_t *lctx;
+
+ REQUIRE(event != NULL);
+ lctx = event->ev_arg;
+ REQUIRE(DNS_LCTX_VALID(lctx));
+
+ if (lctx->canceled)
+ result = ISC_R_CANCELED;
+ else
+ result = (lctx->load)(lctx);
+ if (result == DNS_R_CONTINUE) {
+ event->ev_arg = lctx;
+ isc_task_send(task, &event);
+ } else {
+ (lctx->done)(lctx->done_arg, result);
+ isc_event_free(&event);
+ dns_loadctx_detach(&lctx);
+ }
+}
+
+static isc_result_t
+task_send(dns_loadctx_t *lctx) {
+ isc_event_t *event;
+
+ event = isc_event_allocate(lctx->mctx, NULL,
+ DNS_EVENT_MASTERQUANTUM,
+ load_quantum, lctx, sizeof(*event));
+ if (event == NULL)
+ return (ISC_R_NOMEMORY);
+ isc_task_send(lctx->task, &event);
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_loadctx_cancel(dns_loadctx_t *lctx) {
+ REQUIRE(DNS_LCTX_VALID(lctx));
+
+ LOCK(&lctx->lock);
+ lctx->canceled = ISC_TRUE;
+ UNLOCK(&lctx->lock);
+}
diff --git a/lib/dns/masterdump.c b/lib/dns/masterdump.c
new file mode 100644
index 0000000..3d46458
--- /dev/null
+++ b/lib/dns/masterdump.c
@@ -0,0 +1,1754 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: masterdump.c,v 1.94 2008/09/24 02:46:22 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdlib.h>
+
+#include <isc/event.h>
+#include <isc/file.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/stdio.h>
+#include <isc/string.h>
+#include <isc/task.h>
+#include <isc/time.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/events.h>
+#include <dns/fixedname.h>
+#include <dns/lib.h>
+#include <dns/log.h>
+#include <dns/master.h>
+#include <dns/masterdump.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/rdatatype.h>
+#include <dns/result.h>
+#include <dns/time.h>
+#include <dns/ttl.h>
+
+#define DNS_DCTX_MAGIC ISC_MAGIC('D', 'c', 't', 'x')
+#define DNS_DCTX_VALID(d) ISC_MAGIC_VALID(d, DNS_DCTX_MAGIC)
+
+#define RETERR(x) do { \
+ isc_result_t _r = (x); \
+ if (_r != ISC_R_SUCCESS) \
+ return (_r); \
+ } while (0)
+
+struct dns_master_style {
+ unsigned int flags; /* DNS_STYLEFLAG_* */
+ unsigned int ttl_column;
+ unsigned int class_column;
+ unsigned int type_column;
+ unsigned int rdata_column;
+ unsigned int line_length;
+ unsigned int tab_width;
+};
+
+/*%
+ * The maximum length of the newline+indentation that is output
+ * when inserting a line break in an RR. This effectively puts an
+ * upper limits on the value of "rdata_column", because if it is
+ * very large, the tabs and spaces needed to reach it will not fit.
+ */
+#define DNS_TOTEXT_LINEBREAK_MAXLEN 100
+
+/*%
+ * Context structure for a masterfile dump in progress.
+ */
+typedef struct dns_totext_ctx {
+ dns_master_style_t style;
+ isc_boolean_t class_printed;
+ char * linebreak;
+ char linebreak_buf[DNS_TOTEXT_LINEBREAK_MAXLEN];
+ dns_name_t * origin;
+ dns_name_t * neworigin;
+ dns_fixedname_t origin_fixname;
+ isc_uint32_t current_ttl;
+ isc_boolean_t current_ttl_valid;
+} dns_totext_ctx_t;
+
+LIBDNS_EXTERNAL_DATA const dns_master_style_t
+dns_master_style_default = {
+ DNS_STYLEFLAG_OMIT_OWNER |
+ DNS_STYLEFLAG_OMIT_CLASS |
+ DNS_STYLEFLAG_REL_OWNER |
+ DNS_STYLEFLAG_REL_DATA |
+ DNS_STYLEFLAG_OMIT_TTL |
+ DNS_STYLEFLAG_TTL |
+ DNS_STYLEFLAG_COMMENT |
+ DNS_STYLEFLAG_MULTILINE,
+ 24, 24, 24, 32, 80, 8
+};
+
+LIBDNS_EXTERNAL_DATA const dns_master_style_t
+dns_master_style_full = {
+ DNS_STYLEFLAG_COMMENT |
+ DNS_STYLEFLAG_RESIGN,
+ 46, 46, 46, 64, 120, 8
+};
+
+LIBDNS_EXTERNAL_DATA const dns_master_style_t
+dns_master_style_explicitttl = {
+ DNS_STYLEFLAG_OMIT_OWNER |
+ DNS_STYLEFLAG_OMIT_CLASS |
+ DNS_STYLEFLAG_REL_OWNER |
+ DNS_STYLEFLAG_REL_DATA |
+ DNS_STYLEFLAG_COMMENT |
+ DNS_STYLEFLAG_MULTILINE,
+ 24, 32, 32, 40, 80, 8
+};
+
+LIBDNS_EXTERNAL_DATA const dns_master_style_t
+dns_master_style_cache = {
+ DNS_STYLEFLAG_OMIT_OWNER |
+ DNS_STYLEFLAG_OMIT_CLASS |
+ DNS_STYLEFLAG_MULTILINE |
+ DNS_STYLEFLAG_TRUST |
+ DNS_STYLEFLAG_NCACHE,
+ 24, 32, 32, 40, 80, 8
+};
+
+LIBDNS_EXTERNAL_DATA const dns_master_style_t
+dns_master_style_simple = {
+ 0,
+ 24, 32, 32, 40, 80, 8
+};
+
+/*%
+ * A style suitable for dns_rdataset_totext().
+ */
+LIBDNS_EXTERNAL_DATA const dns_master_style_t
+dns_master_style_debug = {
+ DNS_STYLEFLAG_REL_OWNER,
+ 24, 32, 40, 48, 80, 8
+};
+
+
+#define N_SPACES 10
+static char spaces[N_SPACES+1] = " ";
+
+#define N_TABS 10
+static char tabs[N_TABS+1] = "\t\t\t\t\t\t\t\t\t\t";
+
+struct dns_dumpctx {
+ unsigned int magic;
+ isc_mem_t *mctx;
+ isc_mutex_t lock;
+ unsigned int references;
+ isc_boolean_t canceled;
+ isc_boolean_t first;
+ isc_boolean_t do_date;
+ isc_stdtime_t now;
+ FILE *f;
+ dns_db_t *db;
+ dns_dbversion_t *version;
+ dns_dbiterator_t *dbiter;
+ dns_totext_ctx_t tctx;
+ isc_task_t *task;
+ dns_dumpdonefunc_t done;
+ void *done_arg;
+ unsigned int nodes;
+ /* dns_master_dumpinc() */
+ char *file;
+ char *tmpfile;
+ dns_masterformat_t format;
+ isc_result_t (*dumpsets)(isc_mem_t *mctx, dns_name_t *name,
+ dns_rdatasetiter_t *rdsiter,
+ dns_totext_ctx_t *ctx,
+ isc_buffer_t *buffer, FILE *f);
+};
+
+#define NXDOMAIN(x) (((x)->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0)
+
+/*%
+ * Output tabs and spaces to go from column '*current' to
+ * column 'to', and update '*current' to reflect the new
+ * current column.
+ */
+static isc_result_t
+indent(unsigned int *current, unsigned int to, int tabwidth,
+ isc_buffer_t *target)
+{
+ isc_region_t r;
+ unsigned char *p;
+ unsigned int from;
+ int ntabs, nspaces, t;
+
+ from = *current;
+
+ if (to < from + 1)
+ to = from + 1;
+
+ ntabs = to / tabwidth - from / tabwidth;
+ if (ntabs < 0)
+ ntabs = 0;
+
+ if (ntabs > 0) {
+ isc_buffer_availableregion(target, &r);
+ if (r.length < (unsigned) ntabs)
+ return (ISC_R_NOSPACE);
+ p = r.base;
+
+ t = ntabs;
+ while (t) {
+ int n = t;
+ if (n > N_TABS)
+ n = N_TABS;
+ memcpy(p, tabs, n);
+ p += n;
+ t -= n;
+ }
+ isc_buffer_add(target, ntabs);
+ from = (to / tabwidth) * tabwidth;
+ }
+
+ nspaces = to - from;
+ INSIST(nspaces >= 0);
+
+ isc_buffer_availableregion(target, &r);
+ if (r.length < (unsigned) nspaces)
+ return (ISC_R_NOSPACE);
+ p = r.base;
+
+ t = nspaces;
+ while (t) {
+ int n = t;
+ if (n > N_SPACES)
+ n = N_SPACES;
+ memcpy(p, spaces, n);
+ p += n;
+ t -= n;
+ }
+ isc_buffer_add(target, nspaces);
+
+ *current = to;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+totext_ctx_init(const dns_master_style_t *style, dns_totext_ctx_t *ctx) {
+ isc_result_t result;
+
+ REQUIRE(style->tab_width != 0);
+
+ ctx->style = *style;
+ ctx->class_printed = ISC_FALSE;
+
+ dns_fixedname_init(&ctx->origin_fixname);
+
+ /*
+ * Set up the line break string if needed.
+ */
+ if ((ctx->style.flags & DNS_STYLEFLAG_MULTILINE) != 0) {
+ isc_buffer_t buf;
+ isc_region_t r;
+ unsigned int col = 0;
+
+ isc_buffer_init(&buf, ctx->linebreak_buf,
+ sizeof(ctx->linebreak_buf));
+
+ isc_buffer_availableregion(&buf, &r);
+ if (r.length < 1)
+ return (DNS_R_TEXTTOOLONG);
+ r.base[0] = '\n';
+ isc_buffer_add(&buf, 1);
+
+ result = indent(&col, ctx->style.rdata_column,
+ ctx->style.tab_width, &buf);
+ /*
+ * Do not return ISC_R_NOSPACE if the line break string
+ * buffer is too small, because that would just make
+ * dump_rdataset() retry indenfinitely with ever
+ * bigger target buffers. That's a different buffer,
+ * so it won't help. Use DNS_R_TEXTTOOLONG as a substitute.
+ */
+ if (result == ISC_R_NOSPACE)
+ return (DNS_R_TEXTTOOLONG);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ isc_buffer_availableregion(&buf, &r);
+ if (r.length < 1)
+ return (DNS_R_TEXTTOOLONG);
+ r.base[0] = '\0';
+ isc_buffer_add(&buf, 1);
+ ctx->linebreak = ctx->linebreak_buf;
+ } else {
+ ctx->linebreak = NULL;
+ }
+
+ ctx->origin = NULL;
+ ctx->neworigin = NULL;
+ ctx->current_ttl = 0;
+ ctx->current_ttl_valid = ISC_FALSE;
+
+ return (ISC_R_SUCCESS);
+}
+
+#define INDENT_TO(col) \
+ do { \
+ if ((result = indent(&column, ctx->style.col, \
+ ctx->style.tab_width, target)) \
+ != ISC_R_SUCCESS) \
+ return (result); \
+ } while (0)
+
+
+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);
+
+ memcpy(region.base, source, l);
+ isc_buffer_add(target, l);
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Convert 'rdataset' to master file text format according to 'ctx',
+ * storing the result in 'target'. If 'owner_name' is NULL, it
+ * is omitted; otherwise 'owner_name' must be valid and have at least
+ * one label.
+ */
+
+static isc_result_t
+rdataset_totext(dns_rdataset_t *rdataset,
+ dns_name_t *owner_name,
+ dns_totext_ctx_t *ctx,
+ isc_boolean_t omit_final_dot,
+ isc_buffer_t *target)
+{
+ isc_result_t result;
+ unsigned int column;
+ isc_boolean_t first = ISC_TRUE;
+ isc_uint32_t current_ttl;
+ isc_boolean_t current_ttl_valid;
+ dns_rdatatype_t type;
+
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+
+ rdataset->attributes |= DNS_RDATASETATTR_LOADORDER;
+ result = dns_rdataset_first(rdataset);
+ REQUIRE(result == ISC_R_SUCCESS);
+
+ current_ttl = ctx->current_ttl;
+ current_ttl_valid = ctx->current_ttl_valid;
+
+ do {
+ column = 0;
+
+ /*
+ * Owner name.
+ */
+ if (owner_name != NULL &&
+ ! ((ctx->style.flags & DNS_STYLEFLAG_OMIT_OWNER) != 0 &&
+ !first))
+ {
+ unsigned int name_start = target->used;
+ RETERR(dns_name_totext(owner_name,
+ omit_final_dot,
+ target));
+ column += target->used - name_start;
+ }
+
+ /*
+ * TTL.
+ */
+ if ((ctx->style.flags & DNS_STYLEFLAG_NO_TTL) == 0 &&
+ !((ctx->style.flags & DNS_STYLEFLAG_OMIT_TTL) != 0 &&
+ current_ttl_valid &&
+ rdataset->ttl == current_ttl))
+ {
+ char ttlbuf[64];
+ isc_region_t r;
+ unsigned int length;
+
+ INDENT_TO(ttl_column);
+ length = snprintf(ttlbuf, sizeof(ttlbuf), "%u",
+ rdataset->ttl);
+ INSIST(length <= sizeof(ttlbuf));
+ isc_buffer_availableregion(target, &r);
+ if (r.length < length)
+ return (ISC_R_NOSPACE);
+ memcpy(r.base, ttlbuf, length);
+ isc_buffer_add(target, length);
+ column += length;
+
+ /*
+ * If the $TTL directive is not in use, the TTL we
+ * just printed becomes the default for subsequent RRs.
+ */
+ if ((ctx->style.flags & DNS_STYLEFLAG_TTL) == 0) {
+ current_ttl = rdataset->ttl;
+ current_ttl_valid = ISC_TRUE;
+ }
+ }
+
+ /*
+ * Class.
+ */
+ if ((ctx->style.flags & DNS_STYLEFLAG_NO_CLASS) == 0 &&
+ ((ctx->style.flags & DNS_STYLEFLAG_OMIT_CLASS) == 0 ||
+ ctx->class_printed == ISC_FALSE))
+ {
+ unsigned int class_start;
+ INDENT_TO(class_column);
+ class_start = target->used;
+ result = dns_rdataclass_totext(rdataset->rdclass,
+ target);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ column += (target->used - class_start);
+ }
+
+ /*
+ * Type.
+ */
+
+ if (rdataset->type == 0) {
+ type = rdataset->covers;
+ } else {
+ type = rdataset->type;
+ }
+
+ {
+ unsigned int type_start;
+ INDENT_TO(type_column);
+ type_start = target->used;
+ if (rdataset->type == 0)
+ RETERR(str_totext("\\-", target));
+ result = dns_rdatatype_totext(type, target);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ column += (target->used - type_start);
+ }
+
+ /*
+ * Rdata.
+ */
+ INDENT_TO(rdata_column);
+ if (rdataset->type == 0) {
+ if (NXDOMAIN(rdataset))
+ RETERR(str_totext(";-$NXDOMAIN\n", target));
+ else
+ RETERR(str_totext(";-$NXRRSET\n", target));
+ } else {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_region_t r;
+
+ dns_rdataset_current(rdataset, &rdata);
+
+ RETERR(dns_rdata_tofmttext(&rdata,
+ ctx->origin,
+ ctx->style.flags,
+ ctx->style.line_length -
+ ctx->style.rdata_column,
+ ctx->linebreak,
+ target));
+
+ isc_buffer_availableregion(target, &r);
+ if (r.length < 1)
+ return (ISC_R_NOSPACE);
+ r.base[0] = '\n';
+ isc_buffer_add(target, 1);
+ }
+
+ first = ISC_FALSE;
+ result = dns_rdataset_next(rdataset);
+ } while (result == ISC_R_SUCCESS);
+
+ if (result != ISC_R_NOMORE)
+ return (result);
+
+ /*
+ * Update the ctx state to reflect what we just printed.
+ * This is done last, only when we are sure we will return
+ * success, because this function may be called multiple
+ * times with increasing buffer sizes until it succeeds,
+ * and failed attempts must not update the state prematurely.
+ */
+ ctx->class_printed = ISC_TRUE;
+ ctx->current_ttl= current_ttl;
+ ctx->current_ttl_valid = current_ttl_valid;
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Print the name, type, and class of an empty rdataset,
+ * such as those used to represent the question section
+ * of a DNS message.
+ */
+static isc_result_t
+question_totext(dns_rdataset_t *rdataset,
+ dns_name_t *owner_name,
+ dns_totext_ctx_t *ctx,
+ isc_boolean_t omit_final_dot,
+ isc_buffer_t *target)
+{
+ unsigned int column;
+ isc_result_t result;
+ isc_region_t r;
+
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ result = dns_rdataset_first(rdataset);
+ REQUIRE(result == ISC_R_NOMORE);
+
+ column = 0;
+
+ /* Owner name */
+ {
+ unsigned int name_start = target->used;
+ RETERR(dns_name_totext(owner_name,
+ omit_final_dot,
+ target));
+ column += target->used - name_start;
+ }
+
+ /* Class */
+ {
+ unsigned int class_start;
+ INDENT_TO(class_column);
+ class_start = target->used;
+ result = dns_rdataclass_totext(rdataset->rdclass, target);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ column += (target->used - class_start);
+ }
+
+ /* Type */
+ {
+ unsigned int type_start;
+ INDENT_TO(type_column);
+ type_start = target->used;
+ result = dns_rdatatype_totext(rdataset->type, target);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ column += (target->used - type_start);
+ }
+
+ isc_buffer_availableregion(target, &r);
+ if (r.length < 1)
+ return (ISC_R_NOSPACE);
+ r.base[0] = '\n';
+ isc_buffer_add(target, 1);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_rdataset_totext(dns_rdataset_t *rdataset,
+ dns_name_t *owner_name,
+ isc_boolean_t omit_final_dot,
+ isc_boolean_t question,
+ isc_buffer_t *target)
+{
+ dns_totext_ctx_t ctx;
+ isc_result_t result;
+ result = totext_ctx_init(&dns_master_style_debug, &ctx);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "could not set master file style");
+ return (ISC_R_UNEXPECTED);
+ }
+
+ /*
+ * The caller might want to give us an empty owner
+ * name (e.g. if they are outputting into a master
+ * file and this rdataset has the same name as the
+ * previous one.)
+ */
+ if (dns_name_countlabels(owner_name) == 0)
+ owner_name = NULL;
+
+ if (question)
+ return (question_totext(rdataset, owner_name, &ctx,
+ omit_final_dot, target));
+ else
+ return (rdataset_totext(rdataset, owner_name, &ctx,
+ omit_final_dot, target));
+}
+
+isc_result_t
+dns_master_rdatasettotext(dns_name_t *owner_name,
+ dns_rdataset_t *rdataset,
+ const dns_master_style_t *style,
+ isc_buffer_t *target)
+{
+ dns_totext_ctx_t ctx;
+ isc_result_t result;
+ result = totext_ctx_init(style, &ctx);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "could not set master file style");
+ return (ISC_R_UNEXPECTED);
+ }
+
+ return (rdataset_totext(rdataset, owner_name, &ctx,
+ ISC_FALSE, target));
+}
+
+isc_result_t
+dns_master_questiontotext(dns_name_t *owner_name,
+ dns_rdataset_t *rdataset,
+ const dns_master_style_t *style,
+ isc_buffer_t *target)
+{
+ dns_totext_ctx_t ctx;
+ isc_result_t result;
+ result = totext_ctx_init(style, &ctx);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "could not set master file style");
+ return (ISC_R_UNEXPECTED);
+ }
+
+ return (question_totext(rdataset, owner_name, &ctx,
+ ISC_FALSE, target));
+}
+
+/*
+ * Print an rdataset. 'buffer' is a scratch buffer, which must have been
+ * dynamically allocated by the caller. It must be large enough to
+ * hold the result from dns_ttl_totext(). If more than that is needed,
+ * the buffer will be grown automatically.
+ */
+
+static isc_result_t
+dump_rdataset(isc_mem_t *mctx, dns_name_t *name, dns_rdataset_t *rdataset,
+ dns_totext_ctx_t *ctx,
+ isc_buffer_t *buffer, FILE *f)
+{
+ isc_region_t r;
+ isc_result_t result;
+
+ REQUIRE(buffer->length > 0);
+
+ /*
+ * Output a $TTL directive if needed.
+ */
+
+ if ((ctx->style.flags & DNS_STYLEFLAG_TTL) != 0) {
+ if (ctx->current_ttl_valid == ISC_FALSE ||
+ ctx->current_ttl != rdataset->ttl)
+ {
+ if ((ctx->style.flags & DNS_STYLEFLAG_COMMENT) != 0)
+ {
+ isc_buffer_clear(buffer);
+ result = dns_ttl_totext(rdataset->ttl,
+ ISC_TRUE, buffer);
+ INSIST(result == ISC_R_SUCCESS);
+ isc_buffer_usedregion(buffer, &r);
+ fprintf(f, "$TTL %u\t; %.*s\n", rdataset->ttl,
+ (int) r.length, (char *) r.base);
+ } else {
+ fprintf(f, "$TTL %u\n", rdataset->ttl);
+ }
+ ctx->current_ttl = rdataset->ttl;
+ ctx->current_ttl_valid = ISC_TRUE;
+ }
+ }
+
+ isc_buffer_clear(buffer);
+
+ /*
+ * Generate the text representation of the rdataset into
+ * the buffer. If the buffer is too small, grow it.
+ */
+ for (;;) {
+ int newlength;
+ void *newmem;
+ result = rdataset_totext(rdataset, name, ctx,
+ ISC_FALSE, buffer);
+ if (result != ISC_R_NOSPACE)
+ break;
+
+ newlength = buffer->length * 2;
+ newmem = isc_mem_get(mctx, newlength);
+ if (newmem == NULL)
+ return (ISC_R_NOMEMORY);
+ isc_mem_put(mctx, buffer->base, buffer->length);
+ isc_buffer_init(buffer, newmem, newlength);
+ }
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * Write the buffer contents to the master file.
+ */
+ isc_buffer_usedregion(buffer, &r);
+ result = isc_stdio_write(r.base, 1, (size_t)r.length, f, NULL);
+
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "master file write failed: %s",
+ isc_result_totext(result));
+ return (result);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Define the order in which rdatasets should be printed in zone
+ * files. We will print SOA and NS records before others, SIGs
+ * immediately following the things they sign, and order everything
+ * else by RR number. This is all just for aesthetics and
+ * compatibility with buggy software that expects the SOA to be first;
+ * the DNS specifications allow any order.
+ */
+
+static int
+dump_order(const dns_rdataset_t *rds) {
+ int t;
+ int sig;
+ if (rds->type == dns_rdatatype_rrsig) {
+ t = rds->covers;
+ sig = 1;
+ } else {
+ t = rds->type;
+ sig = 0;
+ }
+ switch (t) {
+ case dns_rdatatype_soa:
+ t = 0;
+ break;
+ case dns_rdatatype_ns:
+ t = 1;
+ break;
+ default:
+ t += 2;
+ break;
+ }
+ return (t << 1) + sig;
+}
+
+static int
+dump_order_compare(const void *a, const void *b) {
+ return (dump_order(*((const dns_rdataset_t * const *) a)) -
+ dump_order(*((const dns_rdataset_t * const *) b)));
+}
+
+/*
+ * Dump all the rdatasets of a domain name to a master file. We make
+ * a "best effort" attempt to sort the RRsets in a nice order, but if
+ * there are more than MAXSORT RRsets, we punt and only sort them in
+ * groups of MAXSORT. This is not expected to ever happen in practice
+ * since much less than 64 RR types have been registered with the
+ * IANA, so far, and the output will be correct (though not
+ * aesthetically pleasing) even if it does happen.
+ */
+
+#define MAXSORT 64
+
+static const char *trustnames[] = {
+ "none",
+ "pending",
+ "additional",
+ "glue",
+ "answer",
+ "authauthority",
+ "authanswer",
+ "secure",
+ "local" /* aka ultimate */
+};
+
+const char *
+dns_trust_totext(dns_trust_t trust) {
+ if (trust >= sizeof(trustnames)/sizeof(*trustnames))
+ return ("bad");
+ return (trustnames[trust]);
+}
+
+static isc_result_t
+dump_rdatasets_text(isc_mem_t *mctx, dns_name_t *name,
+ dns_rdatasetiter_t *rdsiter, dns_totext_ctx_t *ctx,
+ isc_buffer_t *buffer, FILE *f)
+{
+ isc_result_t itresult, dumpresult;
+ isc_region_t r;
+ dns_rdataset_t rdatasets[MAXSORT];
+ dns_rdataset_t *sorted[MAXSORT];
+ int i, n;
+
+ itresult = dns_rdatasetiter_first(rdsiter);
+ dumpresult = ISC_R_SUCCESS;
+
+ if (itresult == ISC_R_SUCCESS && ctx->neworigin != NULL) {
+ isc_buffer_clear(buffer);
+ itresult = dns_name_totext(ctx->neworigin, ISC_FALSE, buffer);
+ RUNTIME_CHECK(itresult == ISC_R_SUCCESS);
+ isc_buffer_usedregion(buffer, &r);
+ fprintf(f, "$ORIGIN %.*s\n", (int) r.length, (char *) r.base);
+ ctx->neworigin = NULL;
+ }
+
+ again:
+ for (i = 0;
+ itresult == ISC_R_SUCCESS && i < MAXSORT;
+ itresult = dns_rdatasetiter_next(rdsiter), i++) {
+ dns_rdataset_init(&rdatasets[i]);
+ dns_rdatasetiter_current(rdsiter, &rdatasets[i]);
+ sorted[i] = &rdatasets[i];
+ }
+ n = i;
+ INSIST(n <= MAXSORT);
+
+ qsort(sorted, n, sizeof(sorted[0]), dump_order_compare);
+
+ for (i = 0; i < n; i++) {
+ dns_rdataset_t *rds = sorted[i];
+ if (ctx->style.flags & DNS_STYLEFLAG_TRUST) {
+ unsigned int trust = rds->trust;
+ INSIST(trust < (sizeof(trustnames) /
+ sizeof(trustnames[0])));
+ fprintf(f, "; %s\n", trustnames[trust]);
+ }
+ if (rds->type == 0 &&
+ (ctx->style.flags & DNS_STYLEFLAG_NCACHE) == 0) {
+ /* Omit negative cache entries */
+ } else {
+ isc_result_t result =
+ dump_rdataset(mctx, name, rds, ctx,
+ buffer, f);
+ if (result != ISC_R_SUCCESS)
+ dumpresult = result;
+ if ((ctx->style.flags & DNS_STYLEFLAG_OMIT_OWNER) != 0)
+ name = NULL;
+ }
+ if (ctx->style.flags & DNS_STYLEFLAG_RESIGN &&
+ rds->attributes & DNS_RDATASETATTR_RESIGN) {
+ isc_buffer_t b;
+ char buf[sizeof("YYYYMMDDHHMMSS")];
+ memset(buf, 0, sizeof(buf));
+ isc_buffer_init(&b, buf, sizeof(buf) - 1);
+ dns_time64_totext((isc_uint64_t)rds->resign, &b);
+ fprintf(f, "; resign=%s\n", buf);
+ }
+ dns_rdataset_disassociate(rds);
+ }
+
+ if (dumpresult != ISC_R_SUCCESS)
+ return (dumpresult);
+
+ /*
+ * If we got more data than could be sorted at once,
+ * go handle the rest.
+ */
+ if (itresult == ISC_R_SUCCESS)
+ goto again;
+
+ if (itresult == ISC_R_NOMORE)
+ itresult = ISC_R_SUCCESS;
+
+ return (itresult);
+}
+
+/*
+ * Dump given RRsets in the "raw" format.
+ */
+static isc_result_t
+dump_rdataset_raw(isc_mem_t *mctx, dns_name_t *name, dns_rdataset_t *rdataset,
+ isc_buffer_t *buffer, FILE *f)
+{
+ isc_result_t result;
+ isc_uint32_t totallen;
+ isc_uint16_t dlen;
+ isc_region_t r, r_hdr;
+
+ REQUIRE(buffer->length > 0);
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+
+ restart:
+ totallen = 0;
+ result = dns_rdataset_first(rdataset);
+ REQUIRE(result == ISC_R_SUCCESS);
+
+ isc_buffer_clear(buffer);
+
+ /*
+ * Common header and owner name (length followed by name)
+ * These fields should be in a moderate length, so we assume we
+ * can store all of them in the initial buffer.
+ */
+ isc_buffer_availableregion(buffer, &r_hdr);
+ INSIST(r_hdr.length >= sizeof(dns_masterrawrdataset_t));
+ isc_buffer_putuint32(buffer, totallen); /* XXX: leave space */
+ isc_buffer_putuint16(buffer, rdataset->rdclass); /* 16-bit class */
+ isc_buffer_putuint16(buffer, rdataset->type); /* 16-bit type */
+ isc_buffer_putuint16(buffer, rdataset->covers); /* same as type */
+ isc_buffer_putuint32(buffer, rdataset->ttl); /* 32-bit TTL */
+ isc_buffer_putuint32(buffer, dns_rdataset_count(rdataset));
+ totallen = isc_buffer_usedlength(buffer);
+ INSIST(totallen <= sizeof(dns_masterrawrdataset_t));
+
+ dns_name_toregion(name, &r);
+ INSIST(isc_buffer_availablelength(buffer) >=
+ (sizeof(dlen) + r.length));
+ dlen = (isc_uint16_t)r.length;
+ isc_buffer_putuint16(buffer, dlen);
+ isc_buffer_copyregion(buffer, &r);
+ totallen += sizeof(dlen) + r.length;
+
+ do {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_region_t r;
+
+ dns_rdataset_current(rdataset, &rdata);
+ dns_rdata_toregion(&rdata, &r);
+ INSIST(r.length <= 0xffffU);
+ dlen = (isc_uint16_t)r.length;
+
+ /*
+ * Copy the rdata into the buffer. If the buffer is too small,
+ * grow it. This should be rare, so we'll simply restart the
+ * entire procedure (or should we copy the old data and
+ * continue?).
+ */
+ if (isc_buffer_availablelength(buffer) <
+ sizeof(dlen) + r.length) {
+ int newlength;
+ void *newmem;
+
+ newlength = buffer->length * 2;
+ newmem = isc_mem_get(mctx, newlength);
+ if (newmem == NULL)
+ return (ISC_R_NOMEMORY);
+ isc_mem_put(mctx, buffer->base, buffer->length);
+ isc_buffer_init(buffer, newmem, newlength);
+ goto restart;
+ }
+ isc_buffer_putuint16(buffer, dlen);
+ isc_buffer_copyregion(buffer, &r);
+ totallen += sizeof(dlen) + r.length;
+
+ result = dns_rdataset_next(rdataset);
+ } while (result == ISC_R_SUCCESS);
+
+ if (result != ISC_R_NOMORE)
+ return (result);
+
+ /*
+ * Fill in the total length field.
+ * XXX: this is a bit tricky. Since we have already "used" the space
+ * for the total length in the buffer, we first remember the entire
+ * buffer length in the region, "rewind", and then write the value.
+ */
+ isc_buffer_usedregion(buffer, &r);
+ isc_buffer_clear(buffer);
+ isc_buffer_putuint32(buffer, totallen);
+ INSIST(isc_buffer_usedlength(buffer) < totallen);
+
+ /*
+ * Write the buffer contents to the raw master file.
+ */
+ result = isc_stdio_write(r.base, 1, (size_t)r.length, f, NULL);
+
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "raw master file write failed: %s",
+ isc_result_totext(result));
+ return (result);
+ }
+
+ return (result);
+}
+
+static isc_result_t
+dump_rdatasets_raw(isc_mem_t *mctx, dns_name_t *name,
+ dns_rdatasetiter_t *rdsiter, dns_totext_ctx_t *ctx,
+ isc_buffer_t *buffer, FILE *f)
+{
+ isc_result_t result;
+ dns_rdataset_t rdataset;
+
+ for (result = dns_rdatasetiter_first(rdsiter);
+ result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(rdsiter)) {
+
+ dns_rdataset_init(&rdataset);
+ dns_rdatasetiter_current(rdsiter, &rdataset);
+
+ if (rdataset.type == 0 &&
+ (ctx->style.flags & DNS_STYLEFLAG_NCACHE) == 0) {
+ /* Omit negative cache entries */
+ } else {
+ result = dump_rdataset_raw(mctx, name, &rdataset,
+ buffer, f);
+ }
+ dns_rdataset_disassociate(&rdataset);
+ }
+
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+
+ return (result);
+}
+
+/*
+ * Initial size of text conversion buffer. The buffer is used
+ * for several purposes: converting origin names, rdatasets,
+ * $DATE timestamps, and comment strings for $TTL directives.
+ *
+ * When converting rdatasets, it is dynamically resized, but
+ * when converting origins, timestamps, etc it is not. Therefore,
+ * the initial size must large enough to hold the longest possible
+ * text representation of any domain name (for $ORIGIN).
+ */
+static const int initial_buffer_length = 1200;
+
+static isc_result_t
+dumptostreaminc(dns_dumpctx_t *dctx);
+
+static void
+dumpctx_destroy(dns_dumpctx_t *dctx) {
+
+ dctx->magic = 0;
+ DESTROYLOCK(&dctx->lock);
+ dns_dbiterator_destroy(&dctx->dbiter);
+ if (dctx->version != NULL)
+ dns_db_closeversion(dctx->db, &dctx->version, ISC_FALSE);
+ dns_db_detach(&dctx->db);
+ if (dctx->task != NULL)
+ isc_task_detach(&dctx->task);
+ if (dctx->file != NULL)
+ isc_mem_free(dctx->mctx, dctx->file);
+ if (dctx->tmpfile != NULL)
+ isc_mem_free(dctx->mctx, dctx->tmpfile);
+ isc_mem_putanddetach(&dctx->mctx, dctx, sizeof(*dctx));
+}
+
+void
+dns_dumpctx_attach(dns_dumpctx_t *source, dns_dumpctx_t **target) {
+
+ REQUIRE(DNS_DCTX_VALID(source));
+ REQUIRE(target != NULL && *target == NULL);
+
+ LOCK(&source->lock);
+ INSIST(source->references > 0);
+ source->references++;
+ INSIST(source->references != 0); /* Overflow? */
+ UNLOCK(&source->lock);
+
+ *target = source;
+}
+
+void
+dns_dumpctx_detach(dns_dumpctx_t **dctxp) {
+ dns_dumpctx_t *dctx;
+ isc_boolean_t need_destroy = ISC_FALSE;
+
+ REQUIRE(dctxp != NULL);
+ dctx = *dctxp;
+ REQUIRE(DNS_DCTX_VALID(dctx));
+
+ *dctxp = NULL;
+
+ LOCK(&dctx->lock);
+ INSIST(dctx->references != 0);
+ dctx->references--;
+ if (dctx->references == 0)
+ need_destroy = ISC_TRUE;
+ UNLOCK(&dctx->lock);
+ if (need_destroy)
+ dumpctx_destroy(dctx);
+}
+
+dns_dbversion_t *
+dns_dumpctx_version(dns_dumpctx_t *dctx) {
+ REQUIRE(DNS_DCTX_VALID(dctx));
+ return (dctx->version);
+}
+
+dns_db_t *
+dns_dumpctx_db(dns_dumpctx_t *dctx) {
+ REQUIRE(DNS_DCTX_VALID(dctx));
+ return (dctx->db);
+}
+
+void
+dns_dumpctx_cancel(dns_dumpctx_t *dctx) {
+ REQUIRE(DNS_DCTX_VALID(dctx));
+
+ LOCK(&dctx->lock);
+ dctx->canceled = ISC_TRUE;
+ UNLOCK(&dctx->lock);
+}
+
+static isc_result_t
+closeandrename(FILE *f, isc_result_t result, const char *temp, const char *file)
+{
+ isc_result_t tresult;
+ isc_boolean_t logit = ISC_TF(result == ISC_R_SUCCESS);
+
+ if (result == ISC_R_SUCCESS)
+ result = isc_stdio_sync(f);
+ if (result != ISC_R_SUCCESS && logit) {
+ isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
+ "dumping master file: %s: fsync: %s",
+ temp, isc_result_totext(result));
+ logit = ISC_FALSE;
+ }
+ tresult = isc_stdio_close(f);
+ if (result == ISC_R_SUCCESS)
+ result = tresult;
+ if (result != ISC_R_SUCCESS && logit) {
+ isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
+ "dumping master file: %s: fclose: %s",
+ temp, isc_result_totext(result));
+ logit = ISC_FALSE;
+ }
+ if (result == ISC_R_SUCCESS)
+ result = isc_file_rename(temp, file);
+ else
+ (void)isc_file_remove(temp);
+ if (result != ISC_R_SUCCESS && logit) {
+ isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
+ "dumping master file: rename: %s: %s",
+ file, isc_result_totext(result));
+ }
+ return (result);
+}
+
+static void
+dump_quantum(isc_task_t *task, isc_event_t *event) {
+ isc_result_t result;
+ isc_result_t tresult;
+ dns_dumpctx_t *dctx;
+
+ REQUIRE(event != NULL);
+ dctx = event->ev_arg;
+ REQUIRE(DNS_DCTX_VALID(dctx));
+ if (dctx->canceled)
+ result = ISC_R_CANCELED;
+ else
+ result = dumptostreaminc(dctx);
+ if (result == DNS_R_CONTINUE) {
+ event->ev_arg = dctx;
+ isc_task_send(task, &event);
+ return;
+ }
+
+ if (dctx->file != NULL) {
+ tresult = closeandrename(dctx->f, result,
+ dctx->tmpfile, dctx->file);
+ if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS)
+ result = tresult;
+ }
+ (dctx->done)(dctx->done_arg, result);
+ isc_event_free(&event);
+ dns_dumpctx_detach(&dctx);
+}
+
+static isc_result_t
+task_send(dns_dumpctx_t *dctx) {
+ isc_event_t *event;
+
+ event = isc_event_allocate(dctx->mctx, NULL, DNS_EVENT_DUMPQUANTUM,
+ dump_quantum, dctx, sizeof(*event));
+ if (event == NULL)
+ return (ISC_R_NOMEMORY);
+ isc_task_send(dctx->task, &event);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+dumpctx_create(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
+ const dns_master_style_t *style, FILE *f, dns_dumpctx_t **dctxp,
+ dns_masterformat_t format)
+{
+ dns_dumpctx_t *dctx;
+ isc_result_t result;
+ unsigned int options;
+
+ dctx = isc_mem_get(mctx, sizeof(*dctx));
+ if (dctx == NULL)
+ return (ISC_R_NOMEMORY);
+
+ dctx->mctx = NULL;
+ dctx->f = f;
+ dctx->dbiter = NULL;
+ dctx->db = NULL;
+ dctx->version = NULL;
+ dctx->done = NULL;
+ dctx->done_arg = NULL;
+ dctx->task = NULL;
+ dctx->nodes = 0;
+ dctx->first = ISC_TRUE;
+ dctx->canceled = ISC_FALSE;
+ dctx->file = NULL;
+ dctx->tmpfile = NULL;
+ dctx->format = format;
+
+ switch (format) {
+ case dns_masterformat_text:
+ dctx->dumpsets = dump_rdatasets_text;
+ break;
+ case dns_masterformat_raw:
+ dctx->dumpsets = dump_rdatasets_raw;
+ break;
+ default:
+ INSIST(0);
+ break;
+ }
+
+ result = totext_ctx_init(style, &dctx->tctx);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "could not set master file style");
+ goto cleanup;
+ }
+
+ isc_stdtime_get(&dctx->now);
+ dns_db_attach(db, &dctx->db);
+
+ dctx->do_date = dns_db_iscache(dctx->db);
+
+ if (dctx->format == dns_masterformat_text &&
+ (dctx->tctx.style.flags & DNS_STYLEFLAG_REL_OWNER) != 0) {
+ options = DNS_DB_RELATIVENAMES;
+ } else
+ options = 0;
+ result = dns_db_createiterator(dctx->db, options, &dctx->dbiter);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = isc_mutex_init(&dctx->lock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ if (version != NULL)
+ dns_db_attachversion(dctx->db, version, &dctx->version);
+ else if (!dns_db_iscache(db))
+ dns_db_currentversion(dctx->db, &dctx->version);
+ isc_mem_attach(mctx, &dctx->mctx);
+ dctx->references = 1;
+ dctx->magic = DNS_DCTX_MAGIC;
+ *dctxp = dctx;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (dctx->dbiter != NULL)
+ dns_dbiterator_destroy(&dctx->dbiter);
+ if (dctx->db != NULL)
+ dns_db_detach(&dctx->db);
+ if (dctx != NULL)
+ isc_mem_put(mctx, dctx, sizeof(*dctx));
+ return (result);
+}
+
+static isc_result_t
+dumptostreaminc(dns_dumpctx_t *dctx) {
+ isc_result_t result;
+ isc_buffer_t buffer;
+ char *bufmem;
+ isc_region_t r;
+ dns_name_t *name;
+ dns_fixedname_t fixname;
+ unsigned int nodes;
+ dns_masterrawheader_t rawheader;
+ isc_uint32_t now32;
+ isc_time_t start;
+
+ bufmem = isc_mem_get(dctx->mctx, initial_buffer_length);
+ if (bufmem == NULL)
+ return (ISC_R_NOMEMORY);
+
+ isc_buffer_init(&buffer, bufmem, initial_buffer_length);
+
+ dns_fixedname_init(&fixname);
+ name = dns_fixedname_name(&fixname);
+
+ if (dctx->first) {
+ switch (dctx->format) {
+ case dns_masterformat_text:
+ /*
+ * If the database has cache semantics, output an
+ * RFC2540 $DATE directive so that the TTLs can be
+ * adjusted when it is reloaded. For zones it is not
+ * really needed, and it would make the file
+ * incompatible with pre-RFC2540 software, so we omit
+ * it in the zone case.
+ */
+ if (dctx->do_date) {
+ result = dns_time32_totext(dctx->now, &buffer);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ isc_buffer_usedregion(&buffer, &r);
+ fprintf(dctx->f, "$DATE %.*s\n",
+ (int) r.length, (char *) r.base);
+ }
+ break;
+ case dns_masterformat_raw:
+ r.base = (unsigned char *)&rawheader;
+ r.length = sizeof(rawheader);
+ isc_buffer_region(&buffer, &r);
+ isc_buffer_putuint32(&buffer, dns_masterformat_raw);
+ isc_buffer_putuint32(&buffer, DNS_RAWFORMAT_VERSION);
+ if (sizeof(now32) != sizeof(dctx->now)) {
+ /*
+ * We assume isc_stdtime_t is a 32-bit integer,
+ * which should be the case on most cases.
+ * If it turns out to be uncommon, we'll need
+ * to bump the version number and revise the
+ * header format.
+ */
+ isc_log_write(dns_lctx,
+ ISC_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTERDUMP,
+ ISC_LOG_INFO,
+ "dumping master file in raw "
+ "format: stdtime is not 32bits");
+ now32 = 0;
+ } else
+ now32 = dctx->now;
+ isc_buffer_putuint32(&buffer, now32);
+ INSIST(isc_buffer_usedlength(&buffer) <=
+ sizeof(rawheader));
+ result = isc_stdio_write(buffer.base, 1,
+ isc_buffer_usedlength(&buffer),
+ dctx->f, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ isc_buffer_clear(&buffer);
+ break;
+ default:
+ INSIST(0);
+ }
+
+ result = dns_dbiterator_first(dctx->dbiter);
+ dctx->first = ISC_FALSE;
+ } else
+ result = ISC_R_SUCCESS;
+
+ nodes = dctx->nodes;
+ isc_time_now(&start);
+ while (result == ISC_R_SUCCESS && (dctx->nodes == 0 || nodes--)) {
+ dns_rdatasetiter_t *rdsiter = NULL;
+ dns_dbnode_t *node = NULL;
+
+ result = dns_dbiterator_current(dctx->dbiter, &node, name);
+ if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN)
+ break;
+ if (result == DNS_R_NEWORIGIN) {
+ dns_name_t *origin =
+ dns_fixedname_name(&dctx->tctx.origin_fixname);
+ result = dns_dbiterator_origin(dctx->dbiter, origin);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ if ((dctx->tctx.style.flags & DNS_STYLEFLAG_REL_DATA) != 0)
+ dctx->tctx.origin = origin;
+ dctx->tctx.neworigin = origin;
+ }
+ result = dns_db_allrdatasets(dctx->db, node, dctx->version,
+ dctx->now, &rdsiter);
+ if (result != ISC_R_SUCCESS) {
+ dns_db_detachnode(dctx->db, &node);
+ goto fail;
+ }
+ result = (dctx->dumpsets)(dctx->mctx, name, rdsiter,
+ &dctx->tctx, &buffer, dctx->f);
+ dns_rdatasetiter_destroy(&rdsiter);
+ if (result != ISC_R_SUCCESS) {
+ dns_db_detachnode(dctx->db, &node);
+ goto fail;
+ }
+ dns_db_detachnode(dctx->db, &node);
+ result = dns_dbiterator_next(dctx->dbiter);
+ }
+
+ /*
+ * Work out how many nodes can be written in the time between
+ * two requests to the nameserver. Smooth the resulting number and
+ * use it as a estimate for the number of nodes to be written in the
+ * next iteration.
+ */
+ if (dctx->nodes != 0 && result == ISC_R_SUCCESS) {
+ unsigned int pps = dns_pps; /* packets per second */
+ unsigned int interval;
+ isc_uint64_t usecs;
+ isc_time_t end;
+
+ isc_time_now(&end);
+ if (pps < 100)
+ pps = 100;
+ interval = 1000000 / pps; /* interval in usecs */
+ if (interval == 0)
+ interval = 1;
+ usecs = isc_time_microdiff(&end, &start);
+ if (usecs == 0) {
+ dctx->nodes = dctx->nodes * 2;
+ if (dctx->nodes > 1000)
+ dctx->nodes = 1000;
+ } else {
+ nodes = dctx->nodes * interval;
+ nodes /= (unsigned int)usecs;
+ if (nodes == 0)
+ nodes = 1;
+ else if (nodes > 1000)
+ nodes = 1000;
+
+ /* Smooth and assign. */
+ dctx->nodes = (nodes + dctx->nodes * 7) / 8;
+
+ isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTERDUMP,
+ ISC_LOG_DEBUG(1),
+ "dumptostreaminc(%p) new nodes -> %d\n",
+ dctx, dctx->nodes);
+ }
+ result = DNS_R_CONTINUE;
+ } else if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ fail:
+ RUNTIME_CHECK(dns_dbiterator_pause(dctx->dbiter) == ISC_R_SUCCESS);
+ isc_mem_put(dctx->mctx, buffer.base, buffer.length);
+ return (result);
+}
+
+isc_result_t
+dns_master_dumptostreaminc(isc_mem_t *mctx, dns_db_t *db,
+ dns_dbversion_t *version,
+ const dns_master_style_t *style,
+ FILE *f, isc_task_t *task,
+ dns_dumpdonefunc_t done, void *done_arg,
+ dns_dumpctx_t **dctxp)
+{
+ dns_dumpctx_t *dctx = NULL;
+ isc_result_t result;
+
+ REQUIRE(task != NULL);
+ REQUIRE(f != NULL);
+ REQUIRE(done != NULL);
+
+ result = dumpctx_create(mctx, db, version, style, f, &dctx,
+ dns_masterformat_text);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ isc_task_attach(task, &dctx->task);
+ dctx->done = done;
+ dctx->done_arg = done_arg;
+ dctx->nodes = 100;
+
+ result = task_send(dctx);
+ if (result == ISC_R_SUCCESS) {
+ dns_dumpctx_attach(dctx, dctxp);
+ return (DNS_R_CONTINUE);
+ }
+
+ dns_dumpctx_detach(&dctx);
+ return (result);
+}
+
+/*
+ * Dump an entire database into a master file.
+ */
+isc_result_t
+dns_master_dumptostream(isc_mem_t *mctx, dns_db_t *db,
+ dns_dbversion_t *version,
+ const dns_master_style_t *style,
+ FILE *f)
+{
+ return (dns_master_dumptostream2(mctx, db, version, style,
+ dns_masterformat_text, f));
+}
+
+isc_result_t
+dns_master_dumptostream2(isc_mem_t *mctx, dns_db_t *db,
+ dns_dbversion_t *version,
+ const dns_master_style_t *style,
+ dns_masterformat_t format, FILE *f)
+{
+ dns_dumpctx_t *dctx = NULL;
+ isc_result_t result;
+
+ result = dumpctx_create(mctx, db, version, style, f, &dctx, format);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dumptostreaminc(dctx);
+ INSIST(result != DNS_R_CONTINUE);
+ dns_dumpctx_detach(&dctx);
+ return (result);
+}
+
+static isc_result_t
+opentmp(isc_mem_t *mctx, const char *file, char **tempp, FILE **fp) {
+ FILE *f = NULL;
+ isc_result_t result;
+ char *tempname = NULL;
+ int tempnamelen;
+
+ tempnamelen = strlen(file) + 20;
+ tempname = isc_mem_allocate(mctx, tempnamelen);
+ if (tempname == NULL)
+ return (ISC_R_NOMEMORY);
+
+ result = isc_file_mktemplate(file, tempname, tempnamelen);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = isc_file_openunique(tempname, &f);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
+ "dumping master file: %s: open: %s",
+ tempname, isc_result_totext(result));
+ goto cleanup;
+ }
+ *tempp = tempname;
+ *fp = f;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ isc_mem_free(mctx, tempname);
+ return (result);
+}
+
+isc_result_t
+dns_master_dumpinc(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
+ const dns_master_style_t *style, const char *filename,
+ isc_task_t *task, dns_dumpdonefunc_t done, void *done_arg,
+ dns_dumpctx_t **dctxp)
+{
+ return (dns_master_dumpinc2(mctx, db, version, style, filename, task,
+ done, done_arg, dctxp,
+ dns_masterformat_text));
+}
+
+isc_result_t
+dns_master_dumpinc2(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
+ const dns_master_style_t *style, const char *filename,
+ isc_task_t *task, dns_dumpdonefunc_t done, void *done_arg,
+ dns_dumpctx_t **dctxp, dns_masterformat_t format)
+{
+ FILE *f = NULL;
+ isc_result_t result;
+ char *tempname = NULL;
+ char *file = NULL;
+ dns_dumpctx_t *dctx = NULL;
+
+ file = isc_mem_strdup(mctx, filename);
+ if (file == NULL)
+ return (ISC_R_NOMEMORY);
+
+ result = opentmp(mctx, filename, &tempname, &f);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = dumpctx_create(mctx, db, version, style, f, &dctx, format);
+ if (result != ISC_R_SUCCESS) {
+ (void)isc_stdio_close(f);
+ (void)isc_file_remove(tempname);
+ goto cleanup;
+ }
+
+ isc_task_attach(task, &dctx->task);
+ dctx->done = done;
+ dctx->done_arg = done_arg;
+ dctx->nodes = 100;
+ dctx->file = file;
+ file = NULL;
+ dctx->tmpfile = tempname;
+ tempname = NULL;
+
+ result = task_send(dctx);
+ if (result == ISC_R_SUCCESS) {
+ dns_dumpctx_attach(dctx, dctxp);
+ return (DNS_R_CONTINUE);
+ }
+
+ cleanup:
+ if (dctx != NULL)
+ dns_dumpctx_detach(&dctx);
+ if (file != NULL)
+ isc_mem_free(mctx, file);
+ if (tempname != NULL)
+ isc_mem_free(mctx, tempname);
+ return (result);
+}
+
+isc_result_t
+dns_master_dump(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
+ const dns_master_style_t *style, const char *filename)
+{
+ return (dns_master_dump2(mctx, db, version, style, filename,
+ dns_masterformat_text));
+}
+
+isc_result_t
+dns_master_dump2(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
+ const dns_master_style_t *style, const char *filename,
+ dns_masterformat_t format)
+{
+ FILE *f = NULL;
+ isc_result_t result;
+ char *tempname;
+ dns_dumpctx_t *dctx = NULL;
+
+ result = opentmp(mctx, filename, &tempname, &f);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dumpctx_create(mctx, db, version, style, f, &dctx, format);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = dumptostreaminc(dctx);
+ INSIST(result != DNS_R_CONTINUE);
+ dns_dumpctx_detach(&dctx);
+
+ result = closeandrename(f, result, tempname, filename);
+
+ cleanup:
+ isc_mem_free(mctx, tempname);
+ return (result);
+}
+
+/*
+ * Dump a database node into a master file.
+ * XXX: this function assumes the text format.
+ */
+isc_result_t
+dns_master_dumpnodetostream(isc_mem_t *mctx, dns_db_t *db,
+ dns_dbversion_t *version,
+ dns_dbnode_t *node, dns_name_t *name,
+ const dns_master_style_t *style,
+ FILE *f)
+{
+ isc_result_t result;
+ isc_buffer_t buffer;
+ char *bufmem;
+ isc_stdtime_t now;
+ dns_totext_ctx_t ctx;
+ dns_rdatasetiter_t *rdsiter = NULL;
+
+ result = totext_ctx_init(style, &ctx);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "could not set master file style");
+ return (ISC_R_UNEXPECTED);
+ }
+
+ isc_stdtime_get(&now);
+
+ bufmem = isc_mem_get(mctx, initial_buffer_length);
+ if (bufmem == NULL)
+ return (ISC_R_NOMEMORY);
+
+ isc_buffer_init(&buffer, bufmem, initial_buffer_length);
+
+ result = dns_db_allrdatasets(db, node, version, now, &rdsiter);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ result = dump_rdatasets_text(mctx, name, rdsiter, &ctx, &buffer, f);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ dns_rdatasetiter_destroy(&rdsiter);
+
+ result = ISC_R_SUCCESS;
+
+ failure:
+ isc_mem_put(mctx, buffer.base, buffer.length);
+ return (result);
+}
+
+isc_result_t
+dns_master_dumpnode(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
+ dns_dbnode_t *node, dns_name_t *name,
+ const dns_master_style_t *style, const char *filename)
+{
+ FILE *f = NULL;
+ isc_result_t result;
+
+ result = isc_stdio_open(filename, "w", &f);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
+ "dumping node to file: %s: open: %s", filename,
+ isc_result_totext(result));
+ return (ISC_R_UNEXPECTED);
+ }
+
+ result = dns_master_dumpnodetostream(mctx, db, version, node, name,
+ style, f);
+
+ result = isc_stdio_close(f);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
+ "dumping master file: %s: close: %s", filename,
+ isc_result_totext(result));
+ return (ISC_R_UNEXPECTED);
+ }
+
+ return (result);
+}
+
+isc_result_t
+dns_master_stylecreate(dns_master_style_t **stylep, unsigned int flags,
+ unsigned int ttl_column, unsigned int class_column,
+ unsigned int type_column, unsigned int rdata_column,
+ unsigned int line_length, unsigned int tab_width,
+ isc_mem_t *mctx)
+{
+ dns_master_style_t *style;
+
+ REQUIRE(stylep != NULL && *stylep == NULL);
+ style = isc_mem_get(mctx, sizeof(*style));
+ if (style == NULL)
+ return (ISC_R_NOMEMORY);
+
+ style->flags = flags;
+ style->ttl_column = ttl_column;
+ style->class_column = class_column;
+ style->type_column = type_column;
+ style->rdata_column = rdata_column;
+ style->line_length = line_length;
+ style->tab_width = tab_width;
+
+ *stylep = style;
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_master_styledestroy(dns_master_style_t **stylep, isc_mem_t *mctx) {
+ dns_master_style_t *style;
+
+ REQUIRE(stylep != NULL && *stylep != NULL);
+ style = *stylep;
+ *stylep = NULL;
+ isc_mem_put(mctx, style, sizeof(*style));
+}
diff --git a/lib/dns/message.c b/lib/dns/message.c
new file mode 100644
index 0000000..3420e55
--- /dev/null
+++ b/lib/dns/message.c
@@ -0,0 +1,3378 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: message.c,v 1.245 2008/09/24 02:46:22 marka Exp $ */
+
+/*! \file */
+
+/***
+ *** Imports
+ ***/
+
+#include <config.h>
+#include <ctype.h>
+
+#include <isc/buffer.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/dnssec.h>
+#include <dns/keyvalues.h>
+#include <dns/log.h>
+#include <dns/masterdump.h>
+#include <dns/message.h>
+#include <dns/opcode.h>
+#include <dns/rdata.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatastruct.h>
+#include <dns/result.h>
+#include <dns/tsig.h>
+#include <dns/view.h>
+
+#ifdef SKAN_MSG_DEBUG
+static void
+hexdump(const char *msg, const char *msg2, void *base, size_t len) {
+ unsigned char *p;
+ unsigned int cnt;
+
+ p = base;
+ cnt = 0;
+
+ printf("*** %s [%s] (%u bytes @ %p)\n", msg, msg2, len, base);
+
+ while (cnt < len) {
+ if (cnt % 16 == 0)
+ printf("%p: ", p);
+ else if (cnt % 8 == 0)
+ printf(" |");
+ printf(" %02x %c", *p, (isprint(*p) ? *p : ' '));
+ p++;
+ cnt++;
+
+ if (cnt % 16 == 0)
+ printf("\n");
+ }
+
+ if (cnt % 16 != 0)
+ printf("\n");
+}
+#endif
+
+#define DNS_MESSAGE_OPCODE_MASK 0x7800U
+#define DNS_MESSAGE_OPCODE_SHIFT 11
+#define DNS_MESSAGE_RCODE_MASK 0x000fU
+#define DNS_MESSAGE_FLAG_MASK 0x8ff0U
+#define DNS_MESSAGE_EDNSRCODE_MASK 0xff000000U
+#define DNS_MESSAGE_EDNSRCODE_SHIFT 24
+#define DNS_MESSAGE_EDNSVERSION_MASK 0x00ff0000U
+#define DNS_MESSAGE_EDNSVERSION_SHIFT 16
+
+#define VALID_NAMED_SECTION(s) (((s) > DNS_SECTION_ANY) \
+ && ((s) < DNS_SECTION_MAX))
+#define VALID_SECTION(s) (((s) >= DNS_SECTION_ANY) \
+ && ((s) < DNS_SECTION_MAX))
+#define ADD_STRING(b, s) {if (strlen(s) >= \
+ isc_buffer_availablelength(b)) \
+ return(ISC_R_NOSPACE); else \
+ isc_buffer_putstr(b, s);}
+#define VALID_PSEUDOSECTION(s) (((s) >= DNS_PSEUDOSECTION_ANY) \
+ && ((s) < DNS_PSEUDOSECTION_MAX))
+
+#define OPTOUT(x) (((x)->attributes & DNS_RDATASETATTR_OPTOUT) != 0)
+
+/*%
+ * This is the size of each individual scratchpad buffer, and the numbers
+ * of various block allocations used within the server.
+ * XXXMLG These should come from a config setting.
+ */
+#define SCRATCHPAD_SIZE 512
+#define NAME_COUNT 8
+#define OFFSET_COUNT 4
+#define RDATA_COUNT 8
+#define RDATALIST_COUNT 8
+#define RDATASET_COUNT RDATALIST_COUNT
+
+/*%
+ * Text representation of the different items, for message_totext
+ * functions.
+ */
+static const char *sectiontext[] = {
+ "QUESTION",
+ "ANSWER",
+ "AUTHORITY",
+ "ADDITIONAL"
+};
+
+static const char *updsectiontext[] = {
+ "ZONE",
+ "PREREQUISITE",
+ "UPDATE",
+ "ADDITIONAL"
+};
+
+static const char *opcodetext[] = {
+ "QUERY",
+ "IQUERY",
+ "STATUS",
+ "RESERVED3",
+ "NOTIFY",
+ "UPDATE",
+ "RESERVED6",
+ "RESERVED7",
+ "RESERVED8",
+ "RESERVED9",
+ "RESERVED10",
+ "RESERVED11",
+ "RESERVED12",
+ "RESERVED13",
+ "RESERVED14",
+ "RESERVED15"
+};
+
+static const char *rcodetext[] = {
+ "NOERROR",
+ "FORMERR",
+ "SERVFAIL",
+ "NXDOMAIN",
+ "NOTIMP",
+ "REFUSED",
+ "YXDOMAIN",
+ "YXRRSET",
+ "NXRRSET",
+ "NOTAUTH",
+ "NOTZONE",
+ "RESERVED11",
+ "RESERVED12",
+ "RESERVED13",
+ "RESERVED14",
+ "RESERVED15",
+ "BADVERS"
+};
+
+
+/*%
+ * "helper" type, which consists of a block of some type, and is linkable.
+ * For it to work, sizeof(dns_msgblock_t) must be a multiple of the pointer
+ * size, or the allocated elements will not be alligned correctly.
+ */
+struct dns_msgblock {
+ unsigned int count;
+ unsigned int remaining;
+ ISC_LINK(dns_msgblock_t) link;
+}; /* dynamically sized */
+
+static inline dns_msgblock_t *
+msgblock_allocate(isc_mem_t *, unsigned int, unsigned int);
+
+#define msgblock_get(block, type) \
+ ((type *)msgblock_internalget(block, sizeof(type)))
+
+static inline void *
+msgblock_internalget(dns_msgblock_t *, unsigned int);
+
+static inline void
+msgblock_reset(dns_msgblock_t *);
+
+static inline void
+msgblock_free(isc_mem_t *, dns_msgblock_t *, unsigned int);
+
+/*
+ * Allocate a new dns_msgblock_t, and return a pointer to it. If no memory
+ * is free, return NULL.
+ */
+static inline dns_msgblock_t *
+msgblock_allocate(isc_mem_t *mctx, unsigned int sizeof_type,
+ unsigned int count)
+{
+ dns_msgblock_t *block;
+ unsigned int length;
+
+ length = sizeof(dns_msgblock_t) + (sizeof_type * count);
+
+ block = isc_mem_get(mctx, length);
+ if (block == NULL)
+ return (NULL);
+
+ block->count = count;
+ block->remaining = count;
+
+ ISC_LINK_INIT(block, link);
+
+ return (block);
+}
+
+/*
+ * Return an element from the msgblock. If no more are available, return
+ * NULL.
+ */
+static inline void *
+msgblock_internalget(dns_msgblock_t *block, unsigned int sizeof_type) {
+ void *ptr;
+
+ if (block == NULL || block->remaining == 0)
+ return (NULL);
+
+ block->remaining--;
+
+ ptr = (((unsigned char *)block)
+ + sizeof(dns_msgblock_t)
+ + (sizeof_type * block->remaining));
+
+ return (ptr);
+}
+
+static inline void
+msgblock_reset(dns_msgblock_t *block) {
+ block->remaining = block->count;
+}
+
+/*
+ * Release memory associated with a message block.
+ */
+static inline void
+msgblock_free(isc_mem_t *mctx, dns_msgblock_t *block, unsigned int sizeof_type)
+{
+ unsigned int length;
+
+ length = sizeof(dns_msgblock_t) + (sizeof_type * block->count);
+
+ isc_mem_put(mctx, block, length);
+}
+
+/*
+ * Allocate a new dynamic buffer, and attach it to this message as the
+ * "current" buffer. (which is always the last on the list, for our
+ * uses)
+ */
+static inline isc_result_t
+newbuffer(dns_message_t *msg, unsigned int size) {
+ isc_result_t result;
+ isc_buffer_t *dynbuf;
+
+ dynbuf = NULL;
+ result = isc_buffer_allocate(msg->mctx, &dynbuf, size);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_NOMEMORY);
+
+ ISC_LIST_APPEND(msg->scratchpad, dynbuf, link);
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_buffer_t *
+currentbuffer(dns_message_t *msg) {
+ isc_buffer_t *dynbuf;
+
+ dynbuf = ISC_LIST_TAIL(msg->scratchpad);
+ INSIST(dynbuf != NULL);
+
+ return (dynbuf);
+}
+
+static inline void
+releaserdata(dns_message_t *msg, dns_rdata_t *rdata) {
+ ISC_LIST_PREPEND(msg->freerdata, rdata, link);
+}
+
+static inline dns_rdata_t *
+newrdata(dns_message_t *msg) {
+ dns_msgblock_t *msgblock;
+ dns_rdata_t *rdata;
+
+ rdata = ISC_LIST_HEAD(msg->freerdata);
+ if (rdata != NULL) {
+ ISC_LIST_UNLINK(msg->freerdata, rdata, link);
+ return (rdata);
+ }
+
+ msgblock = ISC_LIST_TAIL(msg->rdatas);
+ rdata = msgblock_get(msgblock, dns_rdata_t);
+ if (rdata == NULL) {
+ msgblock = msgblock_allocate(msg->mctx, sizeof(dns_rdata_t),
+ RDATA_COUNT);
+ if (msgblock == NULL)
+ return (NULL);
+
+ ISC_LIST_APPEND(msg->rdatas, msgblock, link);
+
+ rdata = msgblock_get(msgblock, dns_rdata_t);
+ }
+
+ dns_rdata_init(rdata);
+ return (rdata);
+}
+
+static inline void
+releaserdatalist(dns_message_t *msg, dns_rdatalist_t *rdatalist) {
+ ISC_LIST_PREPEND(msg->freerdatalist, rdatalist, link);
+}
+
+static inline dns_rdatalist_t *
+newrdatalist(dns_message_t *msg) {
+ dns_msgblock_t *msgblock;
+ dns_rdatalist_t *rdatalist;
+
+ rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
+ if (rdatalist != NULL) {
+ ISC_LIST_UNLINK(msg->freerdatalist, rdatalist, link);
+ return (rdatalist);
+ }
+
+ msgblock = ISC_LIST_TAIL(msg->rdatalists);
+ rdatalist = msgblock_get(msgblock, dns_rdatalist_t);
+ if (rdatalist == NULL) {
+ msgblock = msgblock_allocate(msg->mctx,
+ sizeof(dns_rdatalist_t),
+ RDATALIST_COUNT);
+ if (msgblock == NULL)
+ return (NULL);
+
+ ISC_LIST_APPEND(msg->rdatalists, msgblock, link);
+
+ rdatalist = msgblock_get(msgblock, dns_rdatalist_t);
+ }
+
+ return (rdatalist);
+}
+
+static inline dns_offsets_t *
+newoffsets(dns_message_t *msg) {
+ dns_msgblock_t *msgblock;
+ dns_offsets_t *offsets;
+
+ msgblock = ISC_LIST_TAIL(msg->offsets);
+ offsets = msgblock_get(msgblock, dns_offsets_t);
+ if (offsets == NULL) {
+ msgblock = msgblock_allocate(msg->mctx,
+ sizeof(dns_offsets_t),
+ OFFSET_COUNT);
+ if (msgblock == NULL)
+ return (NULL);
+
+ ISC_LIST_APPEND(msg->offsets, msgblock, link);
+
+ offsets = msgblock_get(msgblock, dns_offsets_t);
+ }
+
+ return (offsets);
+}
+
+static inline void
+msginitheader(dns_message_t *m) {
+ m->id = 0;
+ m->flags = 0;
+ m->rcode = 0;
+ m->opcode = 0;
+ m->rdclass = 0;
+}
+
+static inline void
+msginitprivate(dns_message_t *m) {
+ unsigned int i;
+
+ for (i = 0; i < DNS_SECTION_MAX; i++) {
+ m->cursors[i] = NULL;
+ m->counts[i] = 0;
+ }
+ m->opt = NULL;
+ m->sig0 = NULL;
+ m->sig0name = NULL;
+ m->tsig = NULL;
+ m->tsigname = NULL;
+ m->state = DNS_SECTION_ANY; /* indicate nothing parsed or rendered */
+ m->opt_reserved = 0;
+ m->sig_reserved = 0;
+ m->reserved = 0;
+ m->buffer = NULL;
+}
+
+static inline void
+msginittsig(dns_message_t *m) {
+ m->tsigstatus = dns_rcode_noerror;
+ m->querytsigstatus = dns_rcode_noerror;
+ m->tsigkey = NULL;
+ m->tsigctx = NULL;
+ m->sigstart = -1;
+ m->sig0key = NULL;
+ m->sig0status = dns_rcode_noerror;
+ m->timeadjust = 0;
+}
+
+/*
+ * Init elements to default state. Used both when allocating a new element
+ * and when resetting one.
+ */
+static inline void
+msginit(dns_message_t *m) {
+ msginitheader(m);
+ msginitprivate(m);
+ msginittsig(m);
+ m->header_ok = 0;
+ m->question_ok = 0;
+ m->tcp_continuation = 0;
+ m->verified_sig = 0;
+ m->verify_attempted = 0;
+ m->order = NULL;
+ m->order_arg = NULL;
+ m->query.base = NULL;
+ m->query.length = 0;
+ m->free_query = 0;
+ m->saved.base = NULL;
+ m->saved.length = 0;
+ m->free_saved = 0;
+ m->querytsig = NULL;
+}
+
+static inline void
+msgresetnames(dns_message_t *msg, unsigned int first_section) {
+ unsigned int i;
+ dns_name_t *name, *next_name;
+ dns_rdataset_t *rds, *next_rds;
+
+ /*
+ * Clean up name lists by calling the rdataset disassociate function.
+ */
+ for (i = first_section; i < DNS_SECTION_MAX; i++) {
+ name = ISC_LIST_HEAD(msg->sections[i]);
+ while (name != NULL) {
+ next_name = ISC_LIST_NEXT(name, link);
+ ISC_LIST_UNLINK(msg->sections[i], name, link);
+
+ rds = ISC_LIST_HEAD(name->list);
+ while (rds != NULL) {
+ next_rds = ISC_LIST_NEXT(rds, link);
+ ISC_LIST_UNLINK(name->list, rds, link);
+
+ INSIST(dns_rdataset_isassociated(rds));
+ dns_rdataset_disassociate(rds);
+ isc_mempool_put(msg->rdspool, rds);
+ rds = next_rds;
+ }
+ if (dns_name_dynamic(name))
+ dns_name_free(name, msg->mctx);
+ isc_mempool_put(msg->namepool, name);
+ name = next_name;
+ }
+ }
+}
+
+static void
+msgresetopt(dns_message_t *msg)
+{
+ if (msg->opt != NULL) {
+ if (msg->opt_reserved > 0) {
+ dns_message_renderrelease(msg, msg->opt_reserved);
+ msg->opt_reserved = 0;
+ }
+ INSIST(dns_rdataset_isassociated(msg->opt));
+ dns_rdataset_disassociate(msg->opt);
+ isc_mempool_put(msg->rdspool, msg->opt);
+ msg->opt = NULL;
+ }
+}
+
+static void
+msgresetsigs(dns_message_t *msg, isc_boolean_t replying) {
+ if (msg->sig_reserved > 0) {
+ dns_message_renderrelease(msg, msg->sig_reserved);
+ msg->sig_reserved = 0;
+ }
+ if (msg->tsig != NULL) {
+ INSIST(dns_rdataset_isassociated(msg->tsig));
+ INSIST(msg->namepool != NULL);
+ if (replying) {
+ INSIST(msg->querytsig == NULL);
+ msg->querytsig = msg->tsig;
+ } else {
+ dns_rdataset_disassociate(msg->tsig);
+ isc_mempool_put(msg->rdspool, msg->tsig);
+ if (msg->querytsig != NULL) {
+ dns_rdataset_disassociate(msg->querytsig);
+ isc_mempool_put(msg->rdspool, msg->querytsig);
+ }
+ }
+ if (dns_name_dynamic(msg->tsigname))
+ dns_name_free(msg->tsigname, msg->mctx);
+ isc_mempool_put(msg->namepool, msg->tsigname);
+ msg->tsig = NULL;
+ msg->tsigname = NULL;
+ } else if (msg->querytsig != NULL && !replying) {
+ dns_rdataset_disassociate(msg->querytsig);
+ isc_mempool_put(msg->rdspool, msg->querytsig);
+ msg->querytsig = NULL;
+ }
+ if (msg->sig0 != NULL) {
+ INSIST(dns_rdataset_isassociated(msg->sig0));
+ dns_rdataset_disassociate(msg->sig0);
+ isc_mempool_put(msg->rdspool, msg->sig0);
+ if (msg->sig0name != NULL) {
+ if (dns_name_dynamic(msg->sig0name))
+ dns_name_free(msg->sig0name, msg->mctx);
+ isc_mempool_put(msg->namepool, msg->sig0name);
+ }
+ msg->sig0 = NULL;
+ msg->sig0name = NULL;
+ }
+}
+
+/*
+ * Free all but one (or everything) for this message. This is used by
+ * both dns_message_reset() and dns_message_destroy().
+ */
+static void
+msgreset(dns_message_t *msg, isc_boolean_t everything) {
+ dns_msgblock_t *msgblock, *next_msgblock;
+ isc_buffer_t *dynbuf, *next_dynbuf;
+ dns_rdata_t *rdata;
+ dns_rdatalist_t *rdatalist;
+
+ msgresetnames(msg, 0);
+ msgresetopt(msg);
+ msgresetsigs(msg, ISC_FALSE);
+
+ /*
+ * Clean up linked lists.
+ */
+
+ /*
+ * Run through the free lists, and just unlink anything found there.
+ * The memory isn't lost since these are part of message blocks we
+ * have allocated.
+ */
+ rdata = ISC_LIST_HEAD(msg->freerdata);
+ while (rdata != NULL) {
+ ISC_LIST_UNLINK(msg->freerdata, rdata, link);
+ rdata = ISC_LIST_HEAD(msg->freerdata);
+ }
+ rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
+ while (rdatalist != NULL) {
+ ISC_LIST_UNLINK(msg->freerdatalist, rdatalist, link);
+ rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
+ }
+
+ dynbuf = ISC_LIST_HEAD(msg->scratchpad);
+ INSIST(dynbuf != NULL);
+ if (!everything) {
+ isc_buffer_clear(dynbuf);
+ dynbuf = ISC_LIST_NEXT(dynbuf, link);
+ }
+ while (dynbuf != NULL) {
+ next_dynbuf = ISC_LIST_NEXT(dynbuf, link);
+ ISC_LIST_UNLINK(msg->scratchpad, dynbuf, link);
+ isc_buffer_free(&dynbuf);
+ dynbuf = next_dynbuf;
+ }
+
+ msgblock = ISC_LIST_HEAD(msg->rdatas);
+ if (!everything && msgblock != NULL) {
+ msgblock_reset(msgblock);
+ msgblock = ISC_LIST_NEXT(msgblock, link);
+ }
+ while (msgblock != NULL) {
+ next_msgblock = ISC_LIST_NEXT(msgblock, link);
+ ISC_LIST_UNLINK(msg->rdatas, msgblock, link);
+ msgblock_free(msg->mctx, msgblock, sizeof(dns_rdata_t));
+ msgblock = next_msgblock;
+ }
+
+ /*
+ * rdatalists could be empty.
+ */
+
+ msgblock = ISC_LIST_HEAD(msg->rdatalists);
+ if (!everything && msgblock != NULL) {
+ msgblock_reset(msgblock);
+ msgblock = ISC_LIST_NEXT(msgblock, link);
+ }
+ while (msgblock != NULL) {
+ next_msgblock = ISC_LIST_NEXT(msgblock, link);
+ ISC_LIST_UNLINK(msg->rdatalists, msgblock, link);
+ msgblock_free(msg->mctx, msgblock, sizeof(dns_rdatalist_t));
+ msgblock = next_msgblock;
+ }
+
+ msgblock = ISC_LIST_HEAD(msg->offsets);
+ if (!everything && msgblock != NULL) {
+ msgblock_reset(msgblock);
+ msgblock = ISC_LIST_NEXT(msgblock, link);
+ }
+ while (msgblock != NULL) {
+ next_msgblock = ISC_LIST_NEXT(msgblock, link);
+ ISC_LIST_UNLINK(msg->offsets, msgblock, link);
+ msgblock_free(msg->mctx, msgblock, sizeof(dns_offsets_t));
+ msgblock = next_msgblock;
+ }
+
+ if (msg->tsigkey != NULL) {
+ dns_tsigkey_detach(&msg->tsigkey);
+ msg->tsigkey = NULL;
+ }
+
+ if (msg->tsigctx != NULL)
+ dst_context_destroy(&msg->tsigctx);
+
+ if (msg->query.base != NULL) {
+ if (msg->free_query != 0)
+ isc_mem_put(msg->mctx, msg->query.base,
+ msg->query.length);
+ msg->query.base = NULL;
+ msg->query.length = 0;
+ }
+
+ if (msg->saved.base != NULL) {
+ if (msg->free_saved != 0)
+ isc_mem_put(msg->mctx, msg->saved.base,
+ msg->saved.length);
+ msg->saved.base = NULL;
+ msg->saved.length = 0;
+ }
+
+ /*
+ * cleanup the buffer cleanup list
+ */
+ dynbuf = ISC_LIST_HEAD(msg->cleanup);
+ while (dynbuf != NULL) {
+ next_dynbuf = ISC_LIST_NEXT(dynbuf, link);
+ ISC_LIST_UNLINK(msg->cleanup, dynbuf, link);
+ isc_buffer_free(&dynbuf);
+ dynbuf = next_dynbuf;
+ }
+
+ /*
+ * Set other bits to normal default values.
+ */
+ if (!everything)
+ msginit(msg);
+
+ ENSURE(isc_mempool_getallocated(msg->namepool) == 0);
+ ENSURE(isc_mempool_getallocated(msg->rdspool) == 0);
+}
+
+static unsigned int
+spacefortsig(dns_tsigkey_t *key, int otherlen) {
+ isc_region_t r1, r2;
+ unsigned int x;
+ isc_result_t result;
+
+ /*
+ * The space required for an TSIG record is:
+ *
+ * n1 bytes for the name
+ * 2 bytes for the type
+ * 2 bytes for the class
+ * 4 bytes for the ttl
+ * 2 bytes for the rdlength
+ * n2 bytes for the algorithm name
+ * 6 bytes for the time signed
+ * 2 bytes for the fudge
+ * 2 bytes for the MAC size
+ * x bytes for the MAC
+ * 2 bytes for the original id
+ * 2 bytes for the error
+ * 2 bytes for the other data length
+ * y bytes for the other data (at most)
+ * ---------------------------------
+ * 26 + n1 + n2 + x + y bytes
+ */
+
+ dns_name_toregion(&key->name, &r1);
+ dns_name_toregion(key->algorithm, &r2);
+ if (key->key == NULL)
+ x = 0;
+ else {
+ result = dst_key_sigsize(key->key, &x);
+ if (result != ISC_R_SUCCESS)
+ x = 0;
+ }
+ return (26 + r1.length + r2.length + x + otherlen);
+}
+
+isc_result_t
+dns_message_create(isc_mem_t *mctx, unsigned int intent, dns_message_t **msgp)
+{
+ dns_message_t *m;
+ isc_result_t result;
+ isc_buffer_t *dynbuf;
+ unsigned int i;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(msgp != NULL);
+ REQUIRE(*msgp == NULL);
+ REQUIRE(intent == DNS_MESSAGE_INTENTPARSE
+ || intent == DNS_MESSAGE_INTENTRENDER);
+
+ m = isc_mem_get(mctx, sizeof(dns_message_t));
+ if (m == NULL)
+ return (ISC_R_NOMEMORY);
+
+ /*
+ * No allocations until further notice. Just initialize all lists
+ * and other members that are freed in the cleanup phase here.
+ */
+
+ m->magic = DNS_MESSAGE_MAGIC;
+ m->from_to_wire = intent;
+ msginit(m);
+
+ for (i = 0; i < DNS_SECTION_MAX; i++)
+ ISC_LIST_INIT(m->sections[i]);
+ m->mctx = mctx;
+
+ ISC_LIST_INIT(m->scratchpad);
+ ISC_LIST_INIT(m->cleanup);
+ m->namepool = NULL;
+ m->rdspool = NULL;
+ ISC_LIST_INIT(m->rdatas);
+ ISC_LIST_INIT(m->rdatalists);
+ ISC_LIST_INIT(m->offsets);
+ ISC_LIST_INIT(m->freerdata);
+ ISC_LIST_INIT(m->freerdatalist);
+
+ /*
+ * Ok, it is safe to allocate (and then "goto cleanup" if failure)
+ */
+
+ result = isc_mempool_create(m->mctx, sizeof(dns_name_t), &m->namepool);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ isc_mempool_setfreemax(m->namepool, NAME_COUNT);
+ isc_mempool_setname(m->namepool, "msg:names");
+
+ result = isc_mempool_create(m->mctx, sizeof(dns_rdataset_t),
+ &m->rdspool);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ isc_mempool_setfreemax(m->rdspool, NAME_COUNT);
+ isc_mempool_setname(m->rdspool, "msg:rdataset");
+
+ dynbuf = NULL;
+ result = isc_buffer_allocate(mctx, &dynbuf, SCRATCHPAD_SIZE);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ ISC_LIST_APPEND(m->scratchpad, dynbuf, link);
+
+ m->cctx = NULL;
+
+ *msgp = m;
+ return (ISC_R_SUCCESS);
+
+ /*
+ * Cleanup for error returns.
+ */
+ cleanup:
+ dynbuf = ISC_LIST_HEAD(m->scratchpad);
+ if (dynbuf != NULL) {
+ ISC_LIST_UNLINK(m->scratchpad, dynbuf, link);
+ isc_buffer_free(&dynbuf);
+ }
+ if (m->namepool != NULL)
+ isc_mempool_destroy(&m->namepool);
+ if (m->rdspool != NULL)
+ isc_mempool_destroy(&m->rdspool);
+ m->magic = 0;
+ isc_mem_put(mctx, m, sizeof(dns_message_t));
+
+ return (ISC_R_NOMEMORY);
+}
+
+void
+dns_message_reset(dns_message_t *msg, unsigned int intent) {
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(intent == DNS_MESSAGE_INTENTPARSE
+ || intent == DNS_MESSAGE_INTENTRENDER);
+
+ msgreset(msg, ISC_FALSE);
+ msg->from_to_wire = intent;
+}
+
+void
+dns_message_destroy(dns_message_t **msgp) {
+ dns_message_t *msg;
+
+ REQUIRE(msgp != NULL);
+ REQUIRE(DNS_MESSAGE_VALID(*msgp));
+
+ msg = *msgp;
+ *msgp = NULL;
+
+ msgreset(msg, ISC_TRUE);
+ isc_mempool_destroy(&msg->namepool);
+ isc_mempool_destroy(&msg->rdspool);
+ msg->magic = 0;
+ isc_mem_put(msg->mctx, msg, sizeof(dns_message_t));
+}
+
+static isc_result_t
+findname(dns_name_t **foundname, dns_name_t *target,
+ dns_namelist_t *section)
+{
+ dns_name_t *curr;
+
+ for (curr = ISC_LIST_TAIL(*section);
+ curr != NULL;
+ curr = ISC_LIST_PREV(curr, link)) {
+ if (dns_name_equal(curr, target)) {
+ if (foundname != NULL)
+ *foundname = curr;
+ return (ISC_R_SUCCESS);
+ }
+ }
+
+ return (ISC_R_NOTFOUND);
+}
+
+isc_result_t
+dns_message_find(dns_name_t *name, dns_rdataclass_t rdclass,
+ dns_rdatatype_t type, dns_rdatatype_t covers,
+ dns_rdataset_t **rdataset)
+{
+ dns_rdataset_t *curr;
+
+ if (rdataset != NULL) {
+ REQUIRE(*rdataset == NULL);
+ }
+
+ for (curr = ISC_LIST_TAIL(name->list);
+ curr != NULL;
+ curr = ISC_LIST_PREV(curr, link)) {
+ if (curr->rdclass == rdclass &&
+ curr->type == type && curr->covers == covers) {
+ if (rdataset != NULL)
+ *rdataset = curr;
+ return (ISC_R_SUCCESS);
+ }
+ }
+
+ return (ISC_R_NOTFOUND);
+}
+
+isc_result_t
+dns_message_findtype(dns_name_t *name, dns_rdatatype_t type,
+ dns_rdatatype_t covers, dns_rdataset_t **rdataset)
+{
+ dns_rdataset_t *curr;
+
+ REQUIRE(name != NULL);
+ if (rdataset != NULL) {
+ REQUIRE(*rdataset == NULL);
+ }
+
+ for (curr = ISC_LIST_TAIL(name->list);
+ curr != NULL;
+ curr = ISC_LIST_PREV(curr, link)) {
+ if (curr->type == type && curr->covers == covers) {
+ if (rdataset != NULL)
+ *rdataset = curr;
+ return (ISC_R_SUCCESS);
+ }
+ }
+
+ return (ISC_R_NOTFOUND);
+}
+
+/*
+ * Read a name from buffer "source".
+ */
+static isc_result_t
+getname(dns_name_t *name, isc_buffer_t *source, dns_message_t *msg,
+ dns_decompress_t *dctx)
+{
+ isc_buffer_t *scratch;
+ isc_result_t result;
+ unsigned int tries;
+
+ scratch = currentbuffer(msg);
+
+ /*
+ * First try: use current buffer.
+ * Second try: allocate a new buffer and use that.
+ */
+ tries = 0;
+ while (tries < 2) {
+ result = dns_name_fromwire(name, source, dctx, ISC_FALSE,
+ scratch);
+
+ if (result == ISC_R_NOSPACE) {
+ tries++;
+
+ result = newbuffer(msg, SCRATCHPAD_SIZE);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ scratch = currentbuffer(msg);
+ dns_name_reset(name);
+ } else {
+ return (result);
+ }
+ }
+
+ INSIST(0); /* Cannot get here... */
+ return (ISC_R_UNEXPECTED);
+}
+
+static isc_result_t
+getrdata(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
+ dns_rdataclass_t rdclass, dns_rdatatype_t rdtype,
+ unsigned int rdatalen, dns_rdata_t *rdata)
+{
+ isc_buffer_t *scratch;
+ isc_result_t result;
+ unsigned int tries;
+ unsigned int trysize;
+
+ scratch = currentbuffer(msg);
+
+ isc_buffer_setactive(source, rdatalen);
+
+ /*
+ * First try: use current buffer.
+ * Second try: allocate a new buffer of size
+ * max(SCRATCHPAD_SIZE, 2 * compressed_rdatalen)
+ * (the data will fit if it was not more than 50% compressed)
+ * Subsequent tries: double buffer size on each try.
+ */
+ tries = 0;
+ trysize = 0;
+ /* XXX possibly change this to a while (tries < 2) loop */
+ for (;;) {
+ result = dns_rdata_fromwire(rdata, rdclass, rdtype,
+ source, dctx, 0,
+ scratch);
+
+ if (result == ISC_R_NOSPACE) {
+ if (tries == 0) {
+ trysize = 2 * rdatalen;
+ if (trysize < SCRATCHPAD_SIZE)
+ trysize = SCRATCHPAD_SIZE;
+ } else {
+ INSIST(trysize != 0);
+ if (trysize >= 65535)
+ return (ISC_R_NOSPACE);
+ /* XXX DNS_R_RRTOOLONG? */
+ trysize *= 2;
+ }
+ tries++;
+ result = newbuffer(msg, trysize);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ scratch = currentbuffer(msg);
+ } else {
+ return (result);
+ }
+ }
+}
+
+#define DO_FORMERR \
+ do { \
+ if (best_effort) \
+ seen_problem = ISC_TRUE; \
+ else { \
+ result = DNS_R_FORMERR; \
+ goto cleanup; \
+ } \
+ } while (0)
+
+static isc_result_t
+getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
+ unsigned int options)
+{
+ isc_region_t r;
+ unsigned int count;
+ dns_name_t *name;
+ dns_name_t *name2;
+ dns_offsets_t *offsets;
+ dns_rdataset_t *rdataset;
+ dns_rdatalist_t *rdatalist;
+ isc_result_t result;
+ dns_rdatatype_t rdtype;
+ dns_rdataclass_t rdclass;
+ dns_namelist_t *section;
+ isc_boolean_t free_name;
+ isc_boolean_t best_effort;
+ isc_boolean_t seen_problem;
+
+ section = &msg->sections[DNS_SECTION_QUESTION];
+
+ best_effort = ISC_TF(options & DNS_MESSAGEPARSE_BESTEFFORT);
+ seen_problem = ISC_FALSE;
+
+ name = NULL;
+ rdataset = NULL;
+ rdatalist = NULL;
+
+ for (count = 0; count < msg->counts[DNS_SECTION_QUESTION]; count++) {
+ name = isc_mempool_get(msg->namepool);
+ if (name == NULL)
+ return (ISC_R_NOMEMORY);
+ free_name = ISC_TRUE;
+
+ offsets = newoffsets(msg);
+ if (offsets == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ dns_name_init(name, *offsets);
+
+ /*
+ * Parse the name out of this packet.
+ */
+ isc_buffer_remainingregion(source, &r);
+ isc_buffer_setactive(source, r.length);
+ result = getname(name, source, msg, dctx);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ /*
+ * Run through the section, looking to see if this name
+ * is already there. If it is found, put back the allocated
+ * name since we no longer need it, and set our name pointer
+ * to point to the name we found.
+ */
+ result = findname(&name2, name, section);
+
+ /*
+ * If it is the first name in the section, accept it.
+ *
+ * If it is not, but is not the same as the name already
+ * in the question section, append to the section. Note that
+ * here in the question section this is illegal, so return
+ * FORMERR. In the future, check the opcode to see if
+ * this should be legal or not. In either case we no longer
+ * need this name pointer.
+ */
+ if (result != ISC_R_SUCCESS) {
+ if (!ISC_LIST_EMPTY(*section))
+ DO_FORMERR;
+ ISC_LIST_APPEND(*section, name, link);
+ free_name = ISC_FALSE;
+ } else {
+ isc_mempool_put(msg->namepool, name);
+ name = name2;
+ name2 = NULL;
+ free_name = ISC_FALSE;
+ }
+
+ /*
+ * Get type and class.
+ */
+ isc_buffer_remainingregion(source, &r);
+ if (r.length < 4) {
+ result = ISC_R_UNEXPECTEDEND;
+ goto cleanup;
+ }
+ rdtype = isc_buffer_getuint16(source);
+ rdclass = isc_buffer_getuint16(source);
+
+ /*
+ * If this class is different than the one we already read,
+ * this is an error.
+ */
+ if (msg->state == DNS_SECTION_ANY) {
+ msg->state = DNS_SECTION_QUESTION;
+ msg->rdclass = rdclass;
+ } else if (msg->rdclass != rdclass)
+ DO_FORMERR;
+
+ /*
+ * Can't ask the same question twice.
+ */
+ result = dns_message_find(name, rdclass, rdtype, 0, NULL);
+ if (result == ISC_R_SUCCESS)
+ DO_FORMERR;
+
+ /*
+ * Allocate a new rdatalist.
+ */
+ rdatalist = newrdatalist(msg);
+ if (rdatalist == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ rdataset = isc_mempool_get(msg->rdspool);
+ if (rdataset == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ /*
+ * Convert rdatalist to rdataset, and attach the latter to
+ * the name.
+ */
+ rdatalist->type = rdtype;
+ rdatalist->covers = 0;
+ rdatalist->rdclass = rdclass;
+ rdatalist->ttl = 0;
+ ISC_LIST_INIT(rdatalist->rdata);
+
+ dns_rdataset_init(rdataset);
+ result = dns_rdatalist_tordataset(rdatalist, rdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ rdataset->attributes |= DNS_RDATASETATTR_QUESTION;
+
+ ISC_LIST_APPEND(name->list, rdataset, link);
+ rdataset = NULL;
+ }
+
+ if (seen_problem)
+ return (DNS_R_RECOVERABLE);
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (rdataset != NULL) {
+ INSIST(!dns_rdataset_isassociated(rdataset));
+ isc_mempool_put(msg->rdspool, rdataset);
+ }
+#if 0
+ if (rdatalist != NULL)
+ isc_mempool_put(msg->rdlpool, rdatalist);
+#endif
+ if (free_name)
+ isc_mempool_put(msg->namepool, name);
+
+ return (result);
+}
+
+static isc_boolean_t
+update(dns_section_t section, dns_rdataclass_t rdclass) {
+ if (section == DNS_SECTION_PREREQUISITE)
+ return (ISC_TF(rdclass == dns_rdataclass_any ||
+ rdclass == dns_rdataclass_none));
+ if (section == DNS_SECTION_UPDATE)
+ return (ISC_TF(rdclass == dns_rdataclass_any));
+ return (ISC_FALSE);
+}
+
+static isc_result_t
+getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
+ dns_section_t sectionid, unsigned int options)
+{
+ isc_region_t r;
+ unsigned int count, rdatalen;
+ dns_name_t *name;
+ dns_name_t *name2;
+ dns_offsets_t *offsets;
+ dns_rdataset_t *rdataset;
+ dns_rdatalist_t *rdatalist;
+ isc_result_t result;
+ dns_rdatatype_t rdtype, covers;
+ dns_rdataclass_t rdclass;
+ dns_rdata_t *rdata;
+ dns_ttl_t ttl;
+ dns_namelist_t *section;
+ isc_boolean_t free_name, free_rdataset;
+ isc_boolean_t preserve_order, best_effort, seen_problem;
+ isc_boolean_t issigzero;
+
+ preserve_order = ISC_TF(options & DNS_MESSAGEPARSE_PRESERVEORDER);
+ best_effort = ISC_TF(options & DNS_MESSAGEPARSE_BESTEFFORT);
+ seen_problem = ISC_FALSE;
+
+ for (count = 0; count < msg->counts[sectionid]; count++) {
+ int recstart = source->current;
+ isc_boolean_t skip_name_search, skip_type_search;
+
+ section = &msg->sections[sectionid];
+
+ skip_name_search = ISC_FALSE;
+ skip_type_search = ISC_FALSE;
+ free_name = ISC_FALSE;
+ free_rdataset = ISC_FALSE;
+
+ name = isc_mempool_get(msg->namepool);
+ if (name == NULL)
+ return (ISC_R_NOMEMORY);
+ free_name = ISC_TRUE;
+
+ offsets = newoffsets(msg);
+ if (offsets == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ dns_name_init(name, *offsets);
+
+ /*
+ * Parse the name out of this packet.
+ */
+ isc_buffer_remainingregion(source, &r);
+ isc_buffer_setactive(source, r.length);
+ result = getname(name, source, msg, dctx);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ /*
+ * Get type, class, ttl, and rdatalen. Verify that at least
+ * rdatalen bytes remain. (Some of this is deferred to
+ * later.)
+ */
+ isc_buffer_remainingregion(source, &r);
+ if (r.length < 2 + 2 + 4 + 2) {
+ result = ISC_R_UNEXPECTEDEND;
+ goto cleanup;
+ }
+ rdtype = isc_buffer_getuint16(source);
+ rdclass = isc_buffer_getuint16(source);
+
+ /*
+ * If there was no question section, we may not yet have
+ * established a class. Do so now.
+ */
+ if (msg->state == DNS_SECTION_ANY &&
+ rdtype != dns_rdatatype_opt && /* class is UDP SIZE */
+ rdtype != dns_rdatatype_tsig && /* class is ANY */
+ rdtype != dns_rdatatype_tkey) { /* class is undefined */
+ msg->rdclass = rdclass;
+ msg->state = DNS_SECTION_QUESTION;
+ }
+
+ /*
+ * If this class is different than the one in the question
+ * section, bail.
+ */
+ if (msg->opcode != dns_opcode_update
+ && rdtype != dns_rdatatype_tsig
+ && rdtype != dns_rdatatype_opt
+ && rdtype != dns_rdatatype_dnskey /* in a TKEY query */
+ && rdtype != dns_rdatatype_sig /* SIG(0) */
+ && rdtype != dns_rdatatype_tkey /* Win2000 TKEY */
+ && msg->rdclass != dns_rdataclass_any
+ && msg->rdclass != rdclass)
+ DO_FORMERR;
+
+ /*
+ * Special type handling for TSIG, OPT, and TKEY.
+ */
+ if (rdtype == dns_rdatatype_tsig) {
+ /*
+ * If it is a tsig, verify that it is in the
+ * additional data section.
+ */
+ if (sectionid != DNS_SECTION_ADDITIONAL ||
+ rdclass != dns_rdataclass_any ||
+ count != msg->counts[sectionid] - 1)
+ DO_FORMERR;
+ msg->sigstart = recstart;
+ skip_name_search = ISC_TRUE;
+ skip_type_search = ISC_TRUE;
+ } else if (rdtype == dns_rdatatype_opt) {
+ /*
+ * The name of an OPT record must be ".", it
+ * must be in the additional data section, and
+ * it must be the first OPT we've seen.
+ */
+ if (!dns_name_equal(dns_rootname, name) ||
+ msg->opt != NULL)
+ DO_FORMERR;
+ skip_name_search = ISC_TRUE;
+ skip_type_search = ISC_TRUE;
+ } else if (rdtype == dns_rdatatype_tkey) {
+ /*
+ * A TKEY must be in the additional section if this
+ * is a query, and the answer section if this is a
+ * response. Unless it's a Win2000 client.
+ *
+ * Its class is ignored.
+ */
+ dns_section_t tkeysection;
+
+ if ((msg->flags & DNS_MESSAGEFLAG_QR) == 0)
+ tkeysection = DNS_SECTION_ADDITIONAL;
+ else
+ tkeysection = DNS_SECTION_ANSWER;
+ if (sectionid != tkeysection &&
+ sectionid != DNS_SECTION_ANSWER)
+ DO_FORMERR;
+ }
+
+ /*
+ * ... now get ttl and rdatalen, and check buffer.
+ */
+ ttl = isc_buffer_getuint32(source);
+ rdatalen = isc_buffer_getuint16(source);
+ r.length -= (2 + 2 + 4 + 2);
+ if (r.length < rdatalen) {
+ result = ISC_R_UNEXPECTEDEND;
+ goto cleanup;
+ }
+
+ /*
+ * Read the rdata from the wire format. Interpret the
+ * rdata according to its actual class, even if it had a
+ * DynDNS meta-class in the packet (unless this is a TSIG).
+ * Then put the meta-class back into the finished rdata.
+ */
+ rdata = newrdata(msg);
+ if (rdata == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ if (msg->opcode == dns_opcode_update &&
+ update(sectionid, rdclass)) {
+ if (rdatalen != 0) {
+ result = DNS_R_FORMERR;
+ goto cleanup;
+ }
+ /*
+ * When the rdata is empty, the data pointer is
+ * never dereferenced, but it must still be non-NULL.
+ * Casting 1 rather than "" avoids warnings about
+ * discarding the const attribute of a string,
+ * for compilers that would warn about such things.
+ */
+ rdata->data = (unsigned char *)1;
+ rdata->length = 0;
+ rdata->rdclass = rdclass;
+ rdata->type = rdtype;
+ rdata->flags = DNS_RDATA_UPDATE;
+ result = ISC_R_SUCCESS;
+ } else if (rdclass == dns_rdataclass_none &&
+ msg->opcode == dns_opcode_update &&
+ sectionid == DNS_SECTION_UPDATE) {
+ result = getrdata(source, msg, dctx, msg->rdclass,
+ rdtype, rdatalen, rdata);
+ } else
+ result = getrdata(source, msg, dctx, rdclass,
+ rdtype, rdatalen, rdata);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ rdata->rdclass = rdclass;
+ issigzero = ISC_FALSE;
+ if (rdtype == dns_rdatatype_rrsig &&
+ rdata->flags == 0) {
+ covers = dns_rdata_covers(rdata);
+ if (covers == 0)
+ DO_FORMERR;
+ } else if (rdtype == dns_rdatatype_sig /* SIG(0) */ &&
+ rdata->flags == 0) {
+ covers = dns_rdata_covers(rdata);
+ if (covers == 0) {
+ if (sectionid != DNS_SECTION_ADDITIONAL ||
+ count != msg->counts[sectionid] - 1)
+ DO_FORMERR;
+ msg->sigstart = recstart;
+ skip_name_search = ISC_TRUE;
+ skip_type_search = ISC_TRUE;
+ issigzero = ISC_TRUE;
+ }
+ } else
+ covers = 0;
+
+ /*
+ * If we are doing a dynamic update or this is a meta-type,
+ * don't bother searching for a name, just append this one
+ * to the end of the message.
+ */
+ if (preserve_order || msg->opcode == dns_opcode_update ||
+ skip_name_search) {
+ if (rdtype != dns_rdatatype_opt &&
+ rdtype != dns_rdatatype_tsig &&
+ !issigzero)
+ {
+ ISC_LIST_APPEND(*section, name, link);
+ free_name = ISC_FALSE;
+ }
+ } else {
+ /*
+ * Run through the section, looking to see if this name
+ * is already there. If it is found, put back the
+ * allocated name since we no longer need it, and set
+ * our name pointer to point to the name we found.
+ */
+ result = findname(&name2, name, section);
+
+ /*
+ * If it is a new name, append to the section.
+ */
+ if (result == ISC_R_SUCCESS) {
+ isc_mempool_put(msg->namepool, name);
+ name = name2;
+ } else {
+ ISC_LIST_APPEND(*section, name, link);
+ }
+ free_name = ISC_FALSE;
+ }
+
+ /*
+ * Search name for the particular type and class.
+ * Skip this stage if in update mode or this is a meta-type.
+ */
+ if (preserve_order || msg->opcode == dns_opcode_update ||
+ skip_type_search)
+ result = ISC_R_NOTFOUND;
+ else {
+ /*
+ * If this is a type that can only occur in
+ * the question section, fail.
+ */
+ if (dns_rdatatype_questiononly(rdtype))
+ DO_FORMERR;
+
+ rdataset = NULL;
+ result = dns_message_find(name, rdclass, rdtype,
+ covers, &rdataset);
+ }
+
+ /*
+ * If we found an rdataset that matches, we need to
+ * append this rdata to that set. If we did not, we need
+ * to create a new rdatalist, store the important bits there,
+ * convert it to an rdataset, and link the latter to the name.
+ * Yuck. When appending, make certain that the type isn't
+ * a singleton type, such as SOA or CNAME.
+ *
+ * Note that this check will be bypassed when preserving order,
+ * the opcode is an update, or the type search is skipped.
+ */
+ if (result == ISC_R_SUCCESS) {
+ if (dns_rdatatype_issingleton(rdtype))
+ DO_FORMERR;
+ }
+
+ if (result == ISC_R_NOTFOUND) {
+ rdataset = isc_mempool_get(msg->rdspool);
+ if (rdataset == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ free_rdataset = ISC_TRUE;
+
+ rdatalist = newrdatalist(msg);
+ if (rdatalist == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ rdatalist->type = rdtype;
+ rdatalist->covers = covers;
+ rdatalist->rdclass = rdclass;
+ rdatalist->ttl = ttl;
+ ISC_LIST_INIT(rdatalist->rdata);
+
+ dns_rdataset_init(rdataset);
+ RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist,
+ rdataset)
+ == ISC_R_SUCCESS);
+
+ if (rdtype != dns_rdatatype_opt &&
+ rdtype != dns_rdatatype_tsig &&
+ !issigzero)
+ {
+ ISC_LIST_APPEND(name->list, rdataset, link);
+ free_rdataset = ISC_FALSE;
+ }
+ }
+
+ /*
+ * Minimize TTLs.
+ *
+ * Section 5.2 of RFC2181 says we should drop
+ * nonauthoritative rrsets where the TTLs differ, but we
+ * currently treat them the as if they were authoritative and
+ * minimize them.
+ */
+ if (ttl != rdataset->ttl) {
+ rdataset->attributes |= DNS_RDATASETATTR_TTLADJUSTED;
+ if (ttl < rdataset->ttl)
+ rdataset->ttl = ttl;
+ }
+
+ /* Append this rdata to the rdataset. */
+ dns_rdatalist_fromrdataset(rdataset, &rdatalist);
+ ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
+
+ /*
+ * If this is an OPT record, remember it. Also, set
+ * the extended rcode. Note that msg->opt will only be set
+ * if best-effort parsing is enabled.
+ */
+ if (rdtype == dns_rdatatype_opt && msg->opt == NULL) {
+ dns_rcode_t ercode;
+
+ msg->opt = rdataset;
+ rdataset = NULL;
+ free_rdataset = ISC_FALSE;
+ ercode = (dns_rcode_t)
+ ((msg->opt->ttl & DNS_MESSAGE_EDNSRCODE_MASK)
+ >> 20);
+ msg->rcode |= ercode;
+ isc_mempool_put(msg->namepool, name);
+ free_name = ISC_FALSE;
+ }
+
+ /*
+ * If this is an SIG(0) or TSIG record, remember it. Note
+ * that msg->sig0 or msg->tsig will only be set if best-effort
+ * parsing is enabled.
+ */
+ if (issigzero && msg->sig0 == NULL) {
+ msg->sig0 = rdataset;
+ msg->sig0name = name;
+ rdataset = NULL;
+ free_rdataset = ISC_FALSE;
+ free_name = ISC_FALSE;
+ } else if (rdtype == dns_rdatatype_tsig && msg->tsig == NULL) {
+ msg->tsig = rdataset;
+ msg->tsigname = name;
+ rdataset = NULL;
+ free_rdataset = ISC_FALSE;
+ free_name = ISC_FALSE;
+ }
+
+ if (seen_problem) {
+ if (free_name)
+ isc_mempool_put(msg->namepool, name);
+ if (free_rdataset)
+ isc_mempool_put(msg->rdspool, rdataset);
+ free_name = free_rdataset = ISC_FALSE;
+ }
+ INSIST(free_name == ISC_FALSE);
+ INSIST(free_rdataset == ISC_FALSE);
+ }
+
+ if (seen_problem)
+ return (DNS_R_RECOVERABLE);
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (free_name)
+ isc_mempool_put(msg->namepool, name);
+ if (free_rdataset)
+ isc_mempool_put(msg->rdspool, rdataset);
+
+ return (result);
+}
+
+isc_result_t
+dns_message_parse(dns_message_t *msg, isc_buffer_t *source,
+ unsigned int options)
+{
+ isc_region_t r;
+ dns_decompress_t dctx;
+ isc_result_t ret;
+ isc_uint16_t tmpflags;
+ isc_buffer_t origsource;
+ isc_boolean_t seen_problem;
+ isc_boolean_t ignore_tc;
+
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(source != NULL);
+ REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE);
+
+ seen_problem = ISC_FALSE;
+ ignore_tc = ISC_TF(options & DNS_MESSAGEPARSE_IGNORETRUNCATION);
+
+ origsource = *source;
+
+ msg->header_ok = 0;
+ msg->question_ok = 0;
+
+ isc_buffer_remainingregion(source, &r);
+ if (r.length < DNS_MESSAGE_HEADERLEN)
+ return (ISC_R_UNEXPECTEDEND);
+
+ msg->id = isc_buffer_getuint16(source);
+ tmpflags = isc_buffer_getuint16(source);
+ msg->opcode = ((tmpflags & DNS_MESSAGE_OPCODE_MASK)
+ >> DNS_MESSAGE_OPCODE_SHIFT);
+ msg->rcode = (dns_rcode_t)(tmpflags & DNS_MESSAGE_RCODE_MASK);
+ msg->flags = (tmpflags & DNS_MESSAGE_FLAG_MASK);
+ msg->counts[DNS_SECTION_QUESTION] = isc_buffer_getuint16(source);
+ msg->counts[DNS_SECTION_ANSWER] = isc_buffer_getuint16(source);
+ msg->counts[DNS_SECTION_AUTHORITY] = isc_buffer_getuint16(source);
+ msg->counts[DNS_SECTION_ADDITIONAL] = isc_buffer_getuint16(source);
+
+ msg->header_ok = 1;
+
+ /*
+ * -1 means no EDNS.
+ */
+ dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_ANY);
+
+ dns_decompress_setmethods(&dctx, DNS_COMPRESS_GLOBAL14);
+
+ ret = getquestions(source, msg, &dctx, options);
+ if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
+ goto truncated;
+ if (ret == DNS_R_RECOVERABLE) {
+ seen_problem = ISC_TRUE;
+ ret = ISC_R_SUCCESS;
+ }
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+ msg->question_ok = 1;
+
+ ret = getsection(source, msg, &dctx, DNS_SECTION_ANSWER, options);
+ if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
+ goto truncated;
+ if (ret == DNS_R_RECOVERABLE) {
+ seen_problem = ISC_TRUE;
+ ret = ISC_R_SUCCESS;
+ }
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ ret = getsection(source, msg, &dctx, DNS_SECTION_AUTHORITY, options);
+ if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
+ goto truncated;
+ if (ret == DNS_R_RECOVERABLE) {
+ seen_problem = ISC_TRUE;
+ ret = ISC_R_SUCCESS;
+ }
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ ret = getsection(source, msg, &dctx, DNS_SECTION_ADDITIONAL, options);
+ if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
+ goto truncated;
+ if (ret == DNS_R_RECOVERABLE) {
+ seen_problem = ISC_TRUE;
+ ret = ISC_R_SUCCESS;
+ }
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ isc_buffer_remainingregion(source, &r);
+ if (r.length != 0) {
+ isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_MESSAGE, ISC_LOG_DEBUG(3),
+ "message has %u byte(s) of trailing garbage",
+ r.length);
+ }
+
+ truncated:
+ if ((options & DNS_MESSAGEPARSE_CLONEBUFFER) == 0)
+ isc_buffer_usedregion(&origsource, &msg->saved);
+ else {
+ msg->saved.length = isc_buffer_usedlength(&origsource);
+ msg->saved.base = isc_mem_get(msg->mctx, msg->saved.length);
+ if (msg->saved.base == NULL)
+ return (ISC_R_NOMEMORY);
+ memcpy(msg->saved.base, isc_buffer_base(&origsource),
+ msg->saved.length);
+ msg->free_saved = 1;
+ }
+
+ if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
+ return (DNS_R_RECOVERABLE);
+ if (seen_problem == ISC_TRUE)
+ return (DNS_R_RECOVERABLE);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_message_renderbegin(dns_message_t *msg, dns_compress_t *cctx,
+ isc_buffer_t *buffer)
+{
+ isc_region_t r;
+
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(buffer != NULL);
+ REQUIRE(msg->buffer == NULL);
+ REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
+
+ msg->cctx = cctx;
+
+ /*
+ * Erase the contents of this buffer.
+ */
+ isc_buffer_clear(buffer);
+
+ /*
+ * Make certain there is enough for at least the header in this
+ * buffer.
+ */
+ isc_buffer_availableregion(buffer, &r);
+ if (r.length < DNS_MESSAGE_HEADERLEN)
+ return (ISC_R_NOSPACE);
+
+ if (r.length < msg->reserved)
+ return (ISC_R_NOSPACE);
+
+ /*
+ * Reserve enough space for the header in this buffer.
+ */
+ isc_buffer_add(buffer, DNS_MESSAGE_HEADERLEN);
+
+ msg->buffer = buffer;
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_message_renderchangebuffer(dns_message_t *msg, isc_buffer_t *buffer) {
+ isc_region_t r, rn;
+
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(buffer != NULL);
+ REQUIRE(msg->buffer != NULL);
+
+ /*
+ * Ensure that the new buffer is empty, and has enough space to
+ * hold the current contents.
+ */
+ isc_buffer_clear(buffer);
+
+ isc_buffer_availableregion(buffer, &rn);
+ isc_buffer_usedregion(msg->buffer, &r);
+ REQUIRE(rn.length > r.length);
+
+ /*
+ * Copy the contents from the old to the new buffer.
+ */
+ isc_buffer_add(buffer, r.length);
+ memcpy(rn.base, r.base, r.length);
+
+ msg->buffer = buffer;
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_message_renderrelease(dns_message_t *msg, unsigned int space) {
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(space <= msg->reserved);
+
+ msg->reserved -= space;
+}
+
+isc_result_t
+dns_message_renderreserve(dns_message_t *msg, unsigned int space) {
+ isc_region_t r;
+
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+
+ if (msg->buffer != NULL) {
+ isc_buffer_availableregion(msg->buffer, &r);
+ if (r.length < (space + msg->reserved))
+ return (ISC_R_NOSPACE);
+ }
+
+ msg->reserved += space;
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_boolean_t
+wrong_priority(dns_rdataset_t *rds, int pass, dns_rdatatype_t preferred_glue) {
+ int pass_needed;
+
+ /*
+ * If we are not rendering class IN, this ordering is bogus.
+ */
+ if (rds->rdclass != dns_rdataclass_in)
+ return (ISC_FALSE);
+
+ switch (rds->type) {
+ case dns_rdatatype_a:
+ case dns_rdatatype_aaaa:
+ if (preferred_glue == rds->type)
+ pass_needed = 4;
+ else
+ pass_needed = 3;
+ break;
+ case dns_rdatatype_rrsig:
+ case dns_rdatatype_dnskey:
+ pass_needed = 2;
+ break;
+ default:
+ pass_needed = 1;
+ }
+
+ if (pass_needed >= pass)
+ return (ISC_FALSE);
+
+ return (ISC_TRUE);
+}
+
+isc_result_t
+dns_message_rendersection(dns_message_t *msg, dns_section_t sectionid,
+ unsigned int options)
+{
+ dns_namelist_t *section;
+ dns_name_t *name, *next_name;
+ dns_rdataset_t *rdataset, *next_rdataset;
+ unsigned int count, total;
+ isc_result_t result;
+ isc_buffer_t st; /* for rollbacks */
+ int pass;
+ isc_boolean_t partial = ISC_FALSE;
+ unsigned int rd_options;
+ dns_rdatatype_t preferred_glue = 0;
+
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(msg->buffer != NULL);
+ REQUIRE(VALID_NAMED_SECTION(sectionid));
+
+ section = &msg->sections[sectionid];
+
+ if ((sectionid == DNS_SECTION_ADDITIONAL)
+ && (options & DNS_MESSAGERENDER_ORDERED) == 0) {
+ if ((options & DNS_MESSAGERENDER_PREFER_A) != 0) {
+ preferred_glue = dns_rdatatype_a;
+ pass = 4;
+ } else if ((options & DNS_MESSAGERENDER_PREFER_AAAA) != 0) {
+ preferred_glue = dns_rdatatype_aaaa;
+ pass = 4;
+ } else
+ pass = 3;
+ } else
+ pass = 1;
+
+ if ((options & DNS_MESSAGERENDER_OMITDNSSEC) == 0)
+ rd_options = 0;
+ else
+ rd_options = DNS_RDATASETTOWIRE_OMITDNSSEC;
+
+ /*
+ * Shrink the space in the buffer by the reserved amount.
+ */
+ msg->buffer->length -= msg->reserved;
+
+ total = 0;
+ if (msg->reserved == 0 && (options & DNS_MESSAGERENDER_PARTIAL) != 0)
+ partial = ISC_TRUE;
+
+ /*
+ * Render required glue first. Set TC if it won't fit.
+ */
+ name = ISC_LIST_HEAD(*section);
+ if (name != NULL) {
+ rdataset = ISC_LIST_HEAD(name->list);
+ if (rdataset != NULL &&
+ (rdataset->attributes & DNS_RDATASETATTR_REQUIREDGLUE) != 0 &&
+ (rdataset->attributes & DNS_RDATASETATTR_RENDERED) == 0) {
+ const void *order_arg = msg->order_arg;
+ st = *(msg->buffer);
+ count = 0;
+ if (partial)
+ result = dns_rdataset_towirepartial(rdataset,
+ name,
+ msg->cctx,
+ msg->buffer,
+ msg->order,
+ order_arg,
+ rd_options,
+ &count,
+ NULL);
+ else
+ result = dns_rdataset_towiresorted(rdataset,
+ name,
+ msg->cctx,
+ msg->buffer,
+ msg->order,
+ order_arg,
+ rd_options,
+ &count);
+ total += count;
+ if (partial && result == ISC_R_NOSPACE) {
+ msg->flags |= DNS_MESSAGEFLAG_TC;
+ msg->buffer->length += msg->reserved;
+ msg->counts[sectionid] += total;
+ return (result);
+ }
+ if (result != ISC_R_SUCCESS) {
+ INSIST(st.used < 65536);
+ dns_compress_rollback(msg->cctx,
+ (isc_uint16_t)st.used);
+ *(msg->buffer) = st; /* rollback */
+ msg->buffer->length += msg->reserved;
+ msg->counts[sectionid] += total;
+ return (result);
+ }
+ rdataset->attributes |= DNS_RDATASETATTR_RENDERED;
+ }
+ }
+
+ do {
+ name = ISC_LIST_HEAD(*section);
+ if (name == NULL) {
+ msg->buffer->length += msg->reserved;
+ msg->counts[sectionid] += total;
+ return (ISC_R_SUCCESS);
+ }
+
+ while (name != NULL) {
+ next_name = ISC_LIST_NEXT(name, link);
+
+ rdataset = ISC_LIST_HEAD(name->list);
+ while (rdataset != NULL) {
+ next_rdataset = ISC_LIST_NEXT(rdataset, link);
+
+ if ((rdataset->attributes &
+ DNS_RDATASETATTR_RENDERED) != 0)
+ goto next;
+
+ if (((options & DNS_MESSAGERENDER_ORDERED)
+ == 0)
+ && (sectionid == DNS_SECTION_ADDITIONAL)
+ && wrong_priority(rdataset, pass,
+ preferred_glue))
+ goto next;
+
+ st = *(msg->buffer);
+
+ count = 0;
+ if (partial)
+ result = dns_rdataset_towirepartial(
+ rdataset,
+ name,
+ msg->cctx,
+ msg->buffer,
+ msg->order,
+ msg->order_arg,
+ rd_options,
+ &count,
+ NULL);
+ else
+ result = dns_rdataset_towiresorted(
+ rdataset,
+ name,
+ msg->cctx,
+ msg->buffer,
+ msg->order,
+ msg->order_arg,
+ rd_options,
+ &count);
+
+ total += count;
+
+ /*
+ * If out of space, record stats on what we
+ * rendered so far, and return that status.
+ *
+ * XXXMLG Need to change this when
+ * dns_rdataset_towire() can render partial
+ * sets starting at some arbitary point in the
+ * set. This will include setting a bit in the
+ * rdataset to indicate that a partial
+ * rendering was done, and some state saved
+ * somewhere (probably in the message struct)
+ * to indicate where to continue from.
+ */
+ if (partial && result == ISC_R_NOSPACE) {
+ msg->buffer->length += msg->reserved;
+ msg->counts[sectionid] += total;
+ return (result);
+ }
+ if (result != ISC_R_SUCCESS) {
+ INSIST(st.used < 65536);
+ dns_compress_rollback(msg->cctx,
+ (isc_uint16_t)st.used);
+ *(msg->buffer) = st; /* rollback */
+ msg->buffer->length += msg->reserved;
+ msg->counts[sectionid] += total;
+ return (result);
+ }
+
+ /*
+ * If we have rendered non-validated data,
+ * ensure that the AD bit is not set.
+ */
+ if (rdataset->trust != dns_trust_secure &&
+ (sectionid == DNS_SECTION_ANSWER ||
+ sectionid == DNS_SECTION_AUTHORITY))
+ msg->flags &= ~DNS_MESSAGEFLAG_AD;
+ if (OPTOUT(rdataset))
+ msg->flags &= ~DNS_MESSAGEFLAG_AD;
+
+ rdataset->attributes |=
+ DNS_RDATASETATTR_RENDERED;
+
+ next:
+ rdataset = next_rdataset;
+ }
+
+ name = next_name;
+ }
+ } while (--pass != 0);
+
+ msg->buffer->length += msg->reserved;
+ msg->counts[sectionid] += total;
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_message_renderheader(dns_message_t *msg, isc_buffer_t *target) {
+ isc_uint16_t tmp;
+ isc_region_t r;
+
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(target != NULL);
+
+ isc_buffer_availableregion(target, &r);
+ REQUIRE(r.length >= DNS_MESSAGE_HEADERLEN);
+
+ isc_buffer_putuint16(target, msg->id);
+
+ tmp = ((msg->opcode << DNS_MESSAGE_OPCODE_SHIFT)
+ & DNS_MESSAGE_OPCODE_MASK);
+ tmp |= (msg->rcode & DNS_MESSAGE_RCODE_MASK);
+ tmp |= (msg->flags & DNS_MESSAGE_FLAG_MASK);
+
+ INSIST(msg->counts[DNS_SECTION_QUESTION] < 65536 &&
+ msg->counts[DNS_SECTION_ANSWER] < 65536 &&
+ msg->counts[DNS_SECTION_AUTHORITY] < 65536 &&
+ msg->counts[DNS_SECTION_ADDITIONAL] < 65536);
+
+ isc_buffer_putuint16(target, tmp);
+ isc_buffer_putuint16(target,
+ (isc_uint16_t)msg->counts[DNS_SECTION_QUESTION]);
+ isc_buffer_putuint16(target,
+ (isc_uint16_t)msg->counts[DNS_SECTION_ANSWER]);
+ isc_buffer_putuint16(target,
+ (isc_uint16_t)msg->counts[DNS_SECTION_AUTHORITY]);
+ isc_buffer_putuint16(target,
+ (isc_uint16_t)msg->counts[DNS_SECTION_ADDITIONAL]);
+}
+
+isc_result_t
+dns_message_renderend(dns_message_t *msg) {
+ isc_buffer_t tmpbuf;
+ isc_region_t r;
+ int result;
+ unsigned int count;
+
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(msg->buffer != NULL);
+
+ if ((msg->rcode & ~DNS_MESSAGE_RCODE_MASK) != 0 && msg->opt == NULL) {
+ /*
+ * We have an extended rcode but are not using EDNS.
+ */
+ return (DNS_R_FORMERR);
+ }
+
+ /*
+ * If we've got an OPT record, render it.
+ */
+ if (msg->opt != NULL) {
+ dns_message_renderrelease(msg, msg->opt_reserved);
+ msg->opt_reserved = 0;
+ /*
+ * Set the extended rcode.
+ */
+ msg->opt->ttl &= ~DNS_MESSAGE_EDNSRCODE_MASK;
+ msg->opt->ttl |= ((msg->rcode << 20) &
+ DNS_MESSAGE_EDNSRCODE_MASK);
+ /*
+ * Render.
+ */
+ count = 0;
+ result = dns_rdataset_towire(msg->opt, dns_rootname,
+ msg->cctx, msg->buffer, 0,
+ &count);
+ msg->counts[DNS_SECTION_ADDITIONAL] += count;
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ /*
+ * If we're adding a TSIG or SIG(0) to a truncated message,
+ * clear all rdatasets from the message except for the question
+ * before adding the TSIG or SIG(0). If the question doesn't fit,
+ * don't include it.
+ */
+ if ((msg->tsigkey != NULL || msg->sig0key != NULL) &&
+ (msg->flags & DNS_MESSAGEFLAG_TC) != 0)
+ {
+ isc_buffer_t *buf;
+
+ msgresetnames(msg, DNS_SECTION_ANSWER);
+ buf = msg->buffer;
+ dns_message_renderreset(msg);
+ msg->buffer = buf;
+ isc_buffer_clear(msg->buffer);
+ isc_buffer_add(msg->buffer, DNS_MESSAGE_HEADERLEN);
+ dns_compress_rollback(msg->cctx, 0);
+ result = dns_message_rendersection(msg, DNS_SECTION_QUESTION,
+ 0);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOSPACE)
+ return (result);
+ }
+
+ /*
+ * If we're adding a TSIG record, generate and render it.
+ */
+ if (msg->tsigkey != NULL) {
+ dns_message_renderrelease(msg, msg->sig_reserved);
+ msg->sig_reserved = 0;
+ result = dns_tsig_sign(msg);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ count = 0;
+ result = dns_rdataset_towire(msg->tsig, msg->tsigname,
+ msg->cctx, msg->buffer, 0,
+ &count);
+ msg->counts[DNS_SECTION_ADDITIONAL] += count;
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ /*
+ * If we're adding a SIG(0) record, generate and render it.
+ */
+ if (msg->sig0key != NULL) {
+ dns_message_renderrelease(msg, msg->sig_reserved);
+ msg->sig_reserved = 0;
+ result = dns_dnssec_signmessage(msg, msg->sig0key);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ count = 0;
+ /*
+ * Note: dns_rootname is used here, not msg->sig0name, since
+ * the owner name of a SIG(0) is irrelevant, and will not
+ * be set in a message being rendered.
+ */
+ result = dns_rdataset_towire(msg->sig0, dns_rootname,
+ msg->cctx, msg->buffer, 0,
+ &count);
+ msg->counts[DNS_SECTION_ADDITIONAL] += count;
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ isc_buffer_usedregion(msg->buffer, &r);
+ isc_buffer_init(&tmpbuf, r.base, r.length);
+
+ dns_message_renderheader(msg, &tmpbuf);
+
+ msg->buffer = NULL; /* forget about this buffer only on success XXX */
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_message_renderreset(dns_message_t *msg) {
+ unsigned int i;
+ dns_name_t *name;
+ dns_rdataset_t *rds;
+
+ /*
+ * Reset the message so that it may be rendered again.
+ */
+
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
+
+ msg->buffer = NULL;
+
+ for (i = 0; i < DNS_SECTION_MAX; i++) {
+ msg->cursors[i] = NULL;
+ msg->counts[i] = 0;
+ for (name = ISC_LIST_HEAD(msg->sections[i]);
+ name != NULL;
+ name = ISC_LIST_NEXT(name, link)) {
+ for (rds = ISC_LIST_HEAD(name->list);
+ rds != NULL;
+ rds = ISC_LIST_NEXT(rds, link)) {
+ rds->attributes &= ~DNS_RDATASETATTR_RENDERED;
+ }
+ }
+ }
+ if (msg->tsigname != NULL)
+ dns_message_puttempname(msg, &msg->tsigname);
+ if (msg->tsig != NULL) {
+ dns_rdataset_disassociate(msg->tsig);
+ dns_message_puttemprdataset(msg, &msg->tsig);
+ }
+ if (msg->sig0 != NULL) {
+ dns_rdataset_disassociate(msg->sig0);
+ dns_message_puttemprdataset(msg, &msg->sig0);
+ }
+}
+
+isc_result_t
+dns_message_firstname(dns_message_t *msg, dns_section_t section) {
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(VALID_NAMED_SECTION(section));
+
+ msg->cursors[section] = ISC_LIST_HEAD(msg->sections[section]);
+
+ if (msg->cursors[section] == NULL)
+ return (ISC_R_NOMORE);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_message_nextname(dns_message_t *msg, dns_section_t section) {
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(VALID_NAMED_SECTION(section));
+ REQUIRE(msg->cursors[section] != NULL);
+
+ msg->cursors[section] = ISC_LIST_NEXT(msg->cursors[section], link);
+
+ if (msg->cursors[section] == NULL)
+ return (ISC_R_NOMORE);
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_message_currentname(dns_message_t *msg, dns_section_t section,
+ dns_name_t **name)
+{
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(VALID_NAMED_SECTION(section));
+ REQUIRE(name != NULL && *name == NULL);
+ REQUIRE(msg->cursors[section] != NULL);
+
+ *name = msg->cursors[section];
+}
+
+isc_result_t
+dns_message_findname(dns_message_t *msg, dns_section_t section,
+ dns_name_t *target, dns_rdatatype_t type,
+ dns_rdatatype_t covers, dns_name_t **name,
+ dns_rdataset_t **rdataset)
+{
+ dns_name_t *foundname;
+ isc_result_t result;
+
+ /*
+ * XXX These requirements are probably too intensive, especially
+ * where things can be NULL, but as they are they ensure that if
+ * something is NON-NULL, indicating that the caller expects it
+ * to be filled in, that we can in fact fill it in.
+ */
+ REQUIRE(msg != NULL);
+ REQUIRE(VALID_SECTION(section));
+ REQUIRE(target != NULL);
+ if (name != NULL)
+ REQUIRE(*name == NULL);
+ if (type == dns_rdatatype_any) {
+ REQUIRE(rdataset == NULL);
+ } else {
+ if (rdataset != NULL)
+ REQUIRE(*rdataset == NULL);
+ }
+
+ result = findname(&foundname, target,
+ &msg->sections[section]);
+
+ if (result == ISC_R_NOTFOUND)
+ return (DNS_R_NXDOMAIN);
+ else if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (name != NULL)
+ *name = foundname;
+
+ /*
+ * And now look for the type.
+ */
+ if (type == dns_rdatatype_any)
+ return (ISC_R_SUCCESS);
+
+ result = dns_message_findtype(foundname, type, covers, rdataset);
+ if (result == ISC_R_NOTFOUND)
+ return (DNS_R_NXRRSET);
+
+ return (result);
+}
+
+void
+dns_message_movename(dns_message_t *msg, dns_name_t *name,
+ dns_section_t fromsection,
+ dns_section_t tosection)
+{
+ REQUIRE(msg != NULL);
+ REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
+ REQUIRE(name != NULL);
+ REQUIRE(VALID_NAMED_SECTION(fromsection));
+ REQUIRE(VALID_NAMED_SECTION(tosection));
+
+ /*
+ * Unlink the name from the old section
+ */
+ ISC_LIST_UNLINK(msg->sections[fromsection], name, link);
+ ISC_LIST_APPEND(msg->sections[tosection], name, link);
+}
+
+void
+dns_message_addname(dns_message_t *msg, dns_name_t *name,
+ dns_section_t section)
+{
+ REQUIRE(msg != NULL);
+ REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
+ REQUIRE(name != NULL);
+ REQUIRE(VALID_NAMED_SECTION(section));
+
+ ISC_LIST_APPEND(msg->sections[section], name, link);
+}
+
+void
+dns_message_removename(dns_message_t *msg, dns_name_t *name,
+ dns_section_t section)
+{
+ REQUIRE(msg != NULL);
+ REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
+ REQUIRE(name != NULL);
+ REQUIRE(VALID_NAMED_SECTION(section));
+
+ ISC_LIST_UNLINK(msg->sections[section], name, link);
+}
+
+isc_result_t
+dns_message_gettempname(dns_message_t *msg, dns_name_t **item) {
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(item != NULL && *item == NULL);
+
+ *item = isc_mempool_get(msg->namepool);
+ if (*item == NULL)
+ return (ISC_R_NOMEMORY);
+ dns_name_init(*item, NULL);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_message_gettempoffsets(dns_message_t *msg, dns_offsets_t **item) {
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(item != NULL && *item == NULL);
+
+ *item = newoffsets(msg);
+ if (*item == NULL)
+ return (ISC_R_NOMEMORY);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_message_gettemprdata(dns_message_t *msg, dns_rdata_t **item) {
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(item != NULL && *item == NULL);
+
+ *item = newrdata(msg);
+ if (*item == NULL)
+ return (ISC_R_NOMEMORY);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_message_gettemprdataset(dns_message_t *msg, dns_rdataset_t **item) {
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(item != NULL && *item == NULL);
+
+ *item = isc_mempool_get(msg->rdspool);
+ if (*item == NULL)
+ return (ISC_R_NOMEMORY);
+
+ dns_rdataset_init(*item);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_message_gettemprdatalist(dns_message_t *msg, dns_rdatalist_t **item) {
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(item != NULL && *item == NULL);
+
+ *item = newrdatalist(msg);
+ if (*item == NULL)
+ return (ISC_R_NOMEMORY);
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_message_puttempname(dns_message_t *msg, dns_name_t **item) {
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(item != NULL && *item != NULL);
+
+ if (dns_name_dynamic(*item))
+ dns_name_free(*item, msg->mctx);
+ isc_mempool_put(msg->namepool, *item);
+ *item = NULL;
+}
+
+void
+dns_message_puttemprdata(dns_message_t *msg, dns_rdata_t **item) {
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(item != NULL && *item != NULL);
+
+ releaserdata(msg, *item);
+ *item = NULL;
+}
+
+void
+dns_message_puttemprdataset(dns_message_t *msg, dns_rdataset_t **item) {
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(item != NULL && *item != NULL);
+
+ REQUIRE(!dns_rdataset_isassociated(*item));
+ isc_mempool_put(msg->rdspool, *item);
+ *item = NULL;
+}
+
+void
+dns_message_puttemprdatalist(dns_message_t *msg, dns_rdatalist_t **item) {
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(item != NULL && *item != NULL);
+
+ releaserdatalist(msg, *item);
+ *item = NULL;
+}
+
+isc_result_t
+dns_message_peekheader(isc_buffer_t *source, dns_messageid_t *idp,
+ unsigned int *flagsp)
+{
+ isc_region_t r;
+ isc_buffer_t buffer;
+ dns_messageid_t id;
+ unsigned int flags;
+
+ REQUIRE(source != NULL);
+
+ buffer = *source;
+
+ isc_buffer_remainingregion(&buffer, &r);
+ if (r.length < DNS_MESSAGE_HEADERLEN)
+ return (ISC_R_UNEXPECTEDEND);
+
+ id = isc_buffer_getuint16(&buffer);
+ flags = isc_buffer_getuint16(&buffer);
+ flags &= DNS_MESSAGE_FLAG_MASK;
+
+ if (flagsp != NULL)
+ *flagsp = flags;
+ if (idp != NULL)
+ *idp = id;
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_message_reply(dns_message_t *msg, isc_boolean_t want_question_section) {
+ unsigned int first_section;
+ isc_result_t result;
+
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE((msg->flags & DNS_MESSAGEFLAG_QR) == 0);
+
+ if (!msg->header_ok)
+ return (DNS_R_FORMERR);
+ if (msg->opcode != dns_opcode_query &&
+ msg->opcode != dns_opcode_notify)
+ want_question_section = ISC_FALSE;
+ if (want_question_section) {
+ if (!msg->question_ok)
+ return (DNS_R_FORMERR);
+ first_section = DNS_SECTION_ANSWER;
+ } else
+ first_section = DNS_SECTION_QUESTION;
+ msg->from_to_wire = DNS_MESSAGE_INTENTRENDER;
+ msgresetnames(msg, first_section);
+ msgresetopt(msg);
+ msgresetsigs(msg, ISC_TRUE);
+ msginitprivate(msg);
+ /*
+ * We now clear most flags and then set QR, ensuring that the
+ * reply's flags will be in a reasonable state.
+ */
+ msg->flags &= DNS_MESSAGE_REPLYPRESERVE;
+ msg->flags |= DNS_MESSAGEFLAG_QR;
+
+ /*
+ * This saves the query TSIG status, if the query was signed, and
+ * reserves space in the reply for the TSIG.
+ */
+ if (msg->tsigkey != NULL) {
+ unsigned int otherlen = 0;
+ msg->querytsigstatus = msg->tsigstatus;
+ msg->tsigstatus = dns_rcode_noerror;
+ if (msg->querytsigstatus == dns_tsigerror_badtime)
+ otherlen = 6;
+ msg->sig_reserved = spacefortsig(msg->tsigkey, otherlen);
+ result = dns_message_renderreserve(msg, msg->sig_reserved);
+ if (result != ISC_R_SUCCESS) {
+ msg->sig_reserved = 0;
+ return (result);
+ }
+ }
+ if (msg->saved.base != NULL) {
+ msg->query.base = msg->saved.base;
+ msg->query.length = msg->saved.length;
+ msg->free_query = msg->free_saved;
+ msg->saved.base = NULL;
+ msg->saved.length = 0;
+ msg->free_saved = 0;
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+dns_rdataset_t *
+dns_message_getopt(dns_message_t *msg) {
+
+ /*
+ * Get the OPT record for 'msg'.
+ */
+
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+
+ return (msg->opt);
+}
+
+isc_result_t
+dns_message_setopt(dns_message_t *msg, dns_rdataset_t *opt) {
+ isc_result_t result;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ /*
+ * Set the OPT record for 'msg'.
+ */
+
+ /*
+ * The space required for an OPT record is:
+ *
+ * 1 byte for the name
+ * 2 bytes for the type
+ * 2 bytes for the class
+ * 4 bytes for the ttl
+ * 2 bytes for the rdata length
+ * ---------------------------------
+ * 11 bytes
+ *
+ * plus the length of the rdata.
+ */
+
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(opt->type == dns_rdatatype_opt);
+ REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
+ REQUIRE(msg->state == DNS_SECTION_ANY);
+
+ msgresetopt(msg);
+
+ result = dns_rdataset_first(opt);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ dns_rdataset_current(opt, &rdata);
+ msg->opt_reserved = 11 + rdata.length;
+ result = dns_message_renderreserve(msg, msg->opt_reserved);
+ if (result != ISC_R_SUCCESS) {
+ msg->opt_reserved = 0;
+ goto cleanup;
+ }
+
+ msg->opt = opt;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ dns_message_puttemprdataset(msg, &opt);
+ return (result);
+
+}
+
+dns_rdataset_t *
+dns_message_gettsig(dns_message_t *msg, dns_name_t **owner) {
+
+ /*
+ * Get the TSIG record and owner for 'msg'.
+ */
+
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(owner == NULL || *owner == NULL);
+
+ if (owner != NULL)
+ *owner = msg->tsigname;
+ return (msg->tsig);
+}
+
+isc_result_t
+dns_message_settsigkey(dns_message_t *msg, dns_tsigkey_t *key) {
+ isc_result_t result;
+
+ /*
+ * Set the TSIG key for 'msg'
+ */
+
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(msg->state == DNS_SECTION_ANY);
+
+ if (key == NULL && msg->tsigkey != NULL) {
+ if (msg->sig_reserved != 0) {
+ dns_message_renderrelease(msg, msg->sig_reserved);
+ msg->sig_reserved = 0;
+ }
+ dns_tsigkey_detach(&msg->tsigkey);
+ }
+ if (key != NULL) {
+ REQUIRE(msg->tsigkey == NULL && msg->sig0key == NULL);
+ dns_tsigkey_attach(key, &msg->tsigkey);
+ if (msg->from_to_wire == DNS_MESSAGE_INTENTRENDER) {
+ msg->sig_reserved = spacefortsig(msg->tsigkey, 0);
+ result = dns_message_renderreserve(msg,
+ msg->sig_reserved);
+ if (result != ISC_R_SUCCESS) {
+ dns_tsigkey_detach(&msg->tsigkey);
+ msg->sig_reserved = 0;
+ return (result);
+ }
+ }
+ }
+ return (ISC_R_SUCCESS);
+}
+
+dns_tsigkey_t *
+dns_message_gettsigkey(dns_message_t *msg) {
+
+ /*
+ * Get the TSIG key for 'msg'
+ */
+
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+
+ return (msg->tsigkey);
+}
+
+isc_result_t
+dns_message_setquerytsig(dns_message_t *msg, isc_buffer_t *querytsig) {
+ dns_rdata_t *rdata = NULL;
+ dns_rdatalist_t *list = NULL;
+ dns_rdataset_t *set = NULL;
+ isc_buffer_t *buf = NULL;
+ isc_region_t r;
+ isc_result_t result;
+
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(msg->querytsig == NULL);
+
+ if (querytsig == NULL)
+ return (ISC_R_SUCCESS);
+
+ result = dns_message_gettemprdata(msg, &rdata);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = dns_message_gettemprdatalist(msg, &list);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_message_gettemprdataset(msg, &set);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ isc_buffer_usedregion(querytsig, &r);
+ result = isc_buffer_allocate(msg->mctx, &buf, r.length);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ isc_buffer_putmem(buf, r.base, r.length);
+ isc_buffer_usedregion(buf, &r);
+ dns_rdata_init(rdata);
+ dns_rdata_fromregion(rdata, dns_rdataclass_any, dns_rdatatype_tsig, &r);
+ dns_message_takebuffer(msg, &buf);
+ ISC_LIST_INIT(list->rdata);
+ ISC_LIST_APPEND(list->rdata, rdata, link);
+ result = dns_rdatalist_tordataset(list, set);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ msg->querytsig = set;
+
+ return (result);
+
+ cleanup:
+ if (rdata != NULL)
+ dns_message_puttemprdata(msg, &rdata);
+ if (list != NULL)
+ dns_message_puttemprdatalist(msg, &list);
+ if (set != NULL)
+ dns_message_puttemprdataset(msg, &set);
+ return (ISC_R_NOMEMORY);
+}
+
+isc_result_t
+dns_message_getquerytsig(dns_message_t *msg, isc_mem_t *mctx,
+ isc_buffer_t **querytsig) {
+ isc_result_t result;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_region_t r;
+
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(mctx != NULL);
+ REQUIRE(querytsig != NULL && *querytsig == NULL);
+
+ if (msg->tsig == NULL)
+ return (ISC_R_SUCCESS);
+
+ result = dns_rdataset_first(msg->tsig);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ dns_rdataset_current(msg->tsig, &rdata);
+ dns_rdata_toregion(&rdata, &r);
+
+ result = isc_buffer_allocate(mctx, querytsig, r.length);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ isc_buffer_putmem(*querytsig, r.base, r.length);
+ return (ISC_R_SUCCESS);
+}
+
+dns_rdataset_t *
+dns_message_getsig0(dns_message_t *msg, dns_name_t **owner) {
+
+ /*
+ * Get the SIG(0) record for 'msg'.
+ */
+
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(owner == NULL || *owner == NULL);
+
+ if (msg->sig0 != NULL && owner != NULL) {
+ /* If dns_message_getsig0 is called on a rendered message
+ * after the SIG(0) has been applied, we need to return the
+ * root name, not NULL.
+ */
+ if (msg->sig0name == NULL)
+ *owner = dns_rootname;
+ else
+ *owner = msg->sig0name;
+ }
+ return (msg->sig0);
+}
+
+isc_result_t
+dns_message_setsig0key(dns_message_t *msg, dst_key_t *key) {
+ isc_region_t r;
+ unsigned int x;
+ isc_result_t result;
+
+ /*
+ * Set the SIG(0) key for 'msg'
+ */
+
+ /*
+ * The space required for an SIG(0) record is:
+ *
+ * 1 byte for the name
+ * 2 bytes for the type
+ * 2 bytes for the class
+ * 4 bytes for the ttl
+ * 2 bytes for the type covered
+ * 1 byte for the algorithm
+ * 1 bytes for the labels
+ * 4 bytes for the original ttl
+ * 4 bytes for the signature expiration
+ * 4 bytes for the signature inception
+ * 2 bytes for the key tag
+ * n bytes for the signer's name
+ * x bytes for the signature
+ * ---------------------------------
+ * 27 + n + x bytes
+ */
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
+ REQUIRE(msg->state == DNS_SECTION_ANY);
+
+ if (key != NULL) {
+ REQUIRE(msg->sig0key == NULL && msg->tsigkey == NULL);
+ dns_name_toregion(dst_key_name(key), &r);
+ result = dst_key_sigsize(key, &x);
+ if (result != ISC_R_SUCCESS) {
+ msg->sig_reserved = 0;
+ return (result);
+ }
+ msg->sig_reserved = 27 + r.length + x;
+ result = dns_message_renderreserve(msg, msg->sig_reserved);
+ if (result != ISC_R_SUCCESS) {
+ msg->sig_reserved = 0;
+ return (result);
+ }
+ msg->sig0key = key;
+ }
+ return (ISC_R_SUCCESS);
+}
+
+dst_key_t *
+dns_message_getsig0key(dns_message_t *msg) {
+
+ /*
+ * Get the SIG(0) key for 'msg'
+ */
+
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+
+ return (msg->sig0key);
+}
+
+void
+dns_message_takebuffer(dns_message_t *msg, isc_buffer_t **buffer) {
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(buffer != NULL);
+ REQUIRE(ISC_BUFFER_VALID(*buffer));
+
+ ISC_LIST_APPEND(msg->cleanup, *buffer, link);
+ *buffer = NULL;
+}
+
+isc_result_t
+dns_message_signer(dns_message_t *msg, dns_name_t *signer) {
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(signer != NULL);
+ REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE);
+
+ if (msg->tsig == NULL && msg->sig0 == NULL)
+ return (ISC_R_NOTFOUND);
+
+ if (msg->verify_attempted == 0)
+ return (DNS_R_NOTVERIFIEDYET);
+
+ if (!dns_name_hasbuffer(signer)) {
+ isc_buffer_t *dynbuf = NULL;
+ result = isc_buffer_allocate(msg->mctx, &dynbuf, 512);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ dns_name_setbuffer(signer, dynbuf);
+ dns_message_takebuffer(msg, &dynbuf);
+ }
+
+ if (msg->sig0 != NULL) {
+ dns_rdata_sig_t sig;
+
+ result = dns_rdataset_first(msg->sig0);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_rdataset_current(msg->sig0, &rdata);
+
+ result = dns_rdata_tostruct(&rdata, &sig, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (msg->verified_sig && msg->sig0status == dns_rcode_noerror)
+ result = ISC_R_SUCCESS;
+ else
+ result = DNS_R_SIGINVALID;
+ dns_name_clone(&sig.signer, signer);
+ dns_rdata_freestruct(&sig);
+ } else {
+ dns_name_t *identity;
+ dns_rdata_any_tsig_t tsig;
+
+ result = dns_rdataset_first(msg->tsig);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_rdataset_current(msg->tsig, &rdata);
+
+ result = dns_rdata_tostruct(&rdata, &tsig, NULL);
+ if (msg->tsigstatus != dns_rcode_noerror)
+ result = DNS_R_TSIGVERIFYFAILURE;
+ else if (tsig.error != dns_rcode_noerror)
+ result = DNS_R_TSIGERRORSET;
+ else
+ result = ISC_R_SUCCESS;
+ dns_rdata_freestruct(&tsig);
+
+ if (msg->tsigkey == NULL) {
+ /*
+ * If msg->tsigstatus & tsig.error are both
+ * dns_rcode_noerror, the message must have been
+ * verified, which means msg->tsigkey will be
+ * non-NULL.
+ */
+ INSIST(result != ISC_R_SUCCESS);
+ } else {
+ identity = dns_tsigkey_identity(msg->tsigkey);
+ if (identity == NULL) {
+ if (result == ISC_R_SUCCESS)
+ result = DNS_R_NOIDENTITY;
+ identity = &msg->tsigkey->name;
+ }
+ dns_name_clone(identity, signer);
+ }
+ }
+
+ return (result);
+}
+
+void
+dns_message_resetsig(dns_message_t *msg) {
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ msg->verified_sig = 0;
+ msg->verify_attempted = 0;
+ msg->tsigstatus = dns_rcode_noerror;
+ msg->sig0status = dns_rcode_noerror;
+ msg->timeadjust = 0;
+ if (msg->tsigkey != NULL) {
+ dns_tsigkey_detach(&msg->tsigkey);
+ msg->tsigkey = NULL;
+ }
+}
+
+isc_result_t
+dns_message_rechecksig(dns_message_t *msg, dns_view_t *view) {
+ dns_message_resetsig(msg);
+ return (dns_message_checksig(msg, view));
+}
+
+#ifdef SKAN_MSG_DEBUG
+void
+dns_message_dumpsig(dns_message_t *msg, char *txt1) {
+ dns_rdata_t querytsigrdata = DNS_RDATA_INIT;
+ dns_rdata_any_tsig_t querytsig;
+ isc_result_t result;
+
+ if (msg->tsig != NULL) {
+ result = dns_rdataset_first(msg->tsig);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_rdataset_current(msg->tsig, &querytsigrdata);
+ result = dns_rdata_tostruct(&querytsigrdata, &querytsig, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ hexdump(txt1, "TSIG", querytsig.signature,
+ querytsig.siglen);
+ }
+
+ if (msg->querytsig != NULL) {
+ result = dns_rdataset_first(msg->querytsig);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_rdataset_current(msg->querytsig, &querytsigrdata);
+ result = dns_rdata_tostruct(&querytsigrdata, &querytsig, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ hexdump(txt1, "QUERYTSIG", querytsig.signature,
+ querytsig.siglen);
+ }
+}
+#endif
+
+isc_result_t
+dns_message_checksig(dns_message_t *msg, dns_view_t *view) {
+ isc_buffer_t b, msgb;
+
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+
+ if (msg->tsigkey == NULL && msg->tsig == NULL && msg->sig0 == NULL)
+ return (ISC_R_SUCCESS);
+
+ INSIST(msg->saved.base != NULL);
+ isc_buffer_init(&msgb, msg->saved.base, msg->saved.length);
+ isc_buffer_add(&msgb, msg->saved.length);
+ if (msg->tsigkey != NULL || msg->tsig != NULL) {
+#ifdef SKAN_MSG_DEBUG
+ dns_message_dumpsig(msg, "dns_message_checksig#1");
+#endif
+ if (view != NULL)
+ return (dns_view_checksig(view, &msgb, msg));
+ else
+ return (dns_tsig_verify(&msgb, msg, NULL, NULL));
+ } else {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_sig_t sig;
+ dns_rdataset_t keyset;
+ isc_result_t result;
+
+ result = dns_rdataset_first(msg->sig0);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_rdataset_current(msg->sig0, &rdata);
+
+ /*
+ * This can occur when the message is a dynamic update, since
+ * the rdata length checking is relaxed. This should not
+ * happen in a well-formed message, since the SIG(0) is only
+ * looked for in the additional section, and the dynamic update
+ * meta-records are in the prerequisite and update sections.
+ */
+ if (rdata.length == 0)
+ return (ISC_R_UNEXPECTEDEND);
+
+ result = dns_rdata_tostruct(&rdata, &sig, msg->mctx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ dns_rdataset_init(&keyset);
+ if (view == NULL)
+ return (DNS_R_KEYUNAUTHORIZED);
+ result = dns_view_simplefind(view, &sig.signer,
+ dns_rdatatype_key /* SIG(0) */,
+ 0, 0, ISC_FALSE, &keyset, NULL);
+
+ if (result != ISC_R_SUCCESS) {
+ /* XXXBEW Should possibly create a fetch here */
+ result = DNS_R_KEYUNAUTHORIZED;
+ goto freesig;
+ } else if (keyset.trust < dns_trust_secure) {
+ /* XXXBEW Should call a validator here */
+ result = DNS_R_KEYUNAUTHORIZED;
+ goto freesig;
+ }
+ result = dns_rdataset_first(&keyset);
+ INSIST(result == ISC_R_SUCCESS);
+ for (;
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&keyset))
+ {
+ dst_key_t *key = NULL;
+
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(&keyset, &rdata);
+ isc_buffer_init(&b, rdata.data, rdata.length);
+ isc_buffer_add(&b, rdata.length);
+
+ result = dst_key_fromdns(&sig.signer, rdata.rdclass,
+ &b, view->mctx, &key);
+ if (result != ISC_R_SUCCESS)
+ continue;
+ if (dst_key_alg(key) != sig.algorithm ||
+ dst_key_id(key) != sig.keyid ||
+ !(dst_key_proto(key) == DNS_KEYPROTO_DNSSEC ||
+ dst_key_proto(key) == DNS_KEYPROTO_ANY))
+ {
+ dst_key_free(&key);
+ continue;
+ }
+ result = dns_dnssec_verifymessage(&msgb, msg, key);
+ dst_key_free(&key);
+ if (result == ISC_R_SUCCESS)
+ break;
+ }
+ if (result == ISC_R_NOMORE)
+ result = DNS_R_KEYUNAUTHORIZED;
+
+ freesig:
+ if (dns_rdataset_isassociated(&keyset))
+ dns_rdataset_disassociate(&keyset);
+ dns_rdata_freestruct(&sig);
+ return (result);
+ }
+}
+
+isc_result_t
+dns_message_sectiontotext(dns_message_t *msg, dns_section_t section,
+ const dns_master_style_t *style,
+ dns_messagetextflag_t flags,
+ isc_buffer_t *target) {
+ dns_name_t *name, empty_name;
+ dns_rdataset_t *rdataset;
+ isc_result_t result;
+
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(target != NULL);
+ REQUIRE(VALID_SECTION(section));
+
+ if (ISC_LIST_EMPTY(msg->sections[section]))
+ return (ISC_R_SUCCESS);
+
+ if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) {
+ ADD_STRING(target, ";; ");
+ if (msg->opcode != dns_opcode_update) {
+ ADD_STRING(target, sectiontext[section]);
+ } else {
+ ADD_STRING(target, updsectiontext[section]);
+ }
+ ADD_STRING(target, " SECTION:\n");
+ }
+
+ dns_name_init(&empty_name, NULL);
+ result = dns_message_firstname(msg, section);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ do {
+ name = NULL;
+ dns_message_currentname(msg, section, &name);
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link)) {
+ if (section == DNS_SECTION_QUESTION) {
+ ADD_STRING(target, ";");
+ result = dns_master_questiontotext(name,
+ rdataset,
+ style,
+ target);
+ } else {
+ result = dns_master_rdatasettotext(name,
+ rdataset,
+ style,
+ target);
+ }
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ result = dns_message_nextname(msg, section);
+ } while (result == ISC_R_SUCCESS);
+ if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
+ (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
+ ADD_STRING(target, "\n");
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ return (result);
+}
+
+isc_result_t
+dns_message_pseudosectiontotext(dns_message_t *msg,
+ dns_pseudosection_t section,
+ const dns_master_style_t *style,
+ dns_messagetextflag_t flags,
+ isc_buffer_t *target) {
+ dns_rdataset_t *ps = NULL;
+ dns_name_t *name = NULL;
+ isc_result_t result;
+ char buf[sizeof("1234567890")];
+ isc_uint32_t mbz;
+ dns_rdata_t rdata;
+ isc_buffer_t optbuf;
+ isc_uint16_t optcode, optlen;
+ unsigned char *optdata;
+
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(target != NULL);
+ REQUIRE(VALID_PSEUDOSECTION(section));
+
+ switch (section) {
+ case DNS_PSEUDOSECTION_OPT:
+ ps = dns_message_getopt(msg);
+ if (ps == NULL)
+ return (ISC_R_SUCCESS);
+ if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
+ ADD_STRING(target, ";; OPT PSEUDOSECTION:\n");
+ ADD_STRING(target, "; EDNS: version: ");
+ snprintf(buf, sizeof(buf), "%u",
+ (unsigned int)((ps->ttl & 0x00ff0000) >> 16));
+ ADD_STRING(target, buf);
+ ADD_STRING(target, ", flags:");
+ if ((ps->ttl & DNS_MESSAGEEXTFLAG_DO) != 0)
+ ADD_STRING(target, " do");
+ mbz = ps->ttl & ~DNS_MESSAGEEXTFLAG_DO & 0xffff;
+ if (mbz != 0) {
+ ADD_STRING(target, "; MBZ: ");
+ snprintf(buf, sizeof(buf), "%.4x ", mbz);
+ ADD_STRING(target, buf);
+ ADD_STRING(target, ", udp: ");
+ } else
+ ADD_STRING(target, "; udp: ");
+ snprintf(buf, sizeof(buf), "%u\n", (unsigned int)ps->rdclass);
+ ADD_STRING(target, buf);
+
+ result = dns_rdataset_first(ps);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_SUCCESS);
+
+ /* Print EDNS info, if any */
+ dns_rdata_init(&rdata);
+ dns_rdataset_current(ps, &rdata);
+ if (rdata.length < 4)
+ return (ISC_R_SUCCESS);
+
+ isc_buffer_init(&optbuf, rdata.data, rdata.length);
+ isc_buffer_add(&optbuf, rdata.length);
+ optcode = isc_buffer_getuint16(&optbuf);
+ optlen = isc_buffer_getuint16(&optbuf);
+
+ if (optcode == DNS_OPT_NSID) {
+ ADD_STRING(target, "; NSID");
+ } else {
+ ADD_STRING(target, "; OPT=");
+ sprintf(buf, "%u", optcode);
+ ADD_STRING(target, buf);
+ }
+
+ if (optlen != 0) {
+ int i;
+ ADD_STRING(target, ": ");
+
+ optdata = rdata.data + 4;
+ for (i = 0; i < optlen; i++) {
+ sprintf(buf, "%02x ", optdata[i]);
+ ADD_STRING(target, buf);
+ }
+ for (i = 0; i < optlen; i++) {
+ ADD_STRING(target, " (");
+ if (isprint(optdata[i]))
+ isc_buffer_putmem(target, &optdata[i],
+ 1);
+ else
+ isc_buffer_putstr(target, ".");
+ ADD_STRING(target, ")");
+ }
+ }
+ ADD_STRING(target, "\n");
+ return (ISC_R_SUCCESS);
+ case DNS_PSEUDOSECTION_TSIG:
+ ps = dns_message_gettsig(msg, &name);
+ if (ps == NULL)
+ return (ISC_R_SUCCESS);
+ if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
+ ADD_STRING(target, ";; TSIG PSEUDOSECTION:\n");
+ result = dns_master_rdatasettotext(name, ps, style, target);
+ if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
+ (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
+ ADD_STRING(target, "\n");
+ return (result);
+ case DNS_PSEUDOSECTION_SIG0:
+ ps = dns_message_getsig0(msg, &name);
+ if (ps == NULL)
+ return (ISC_R_SUCCESS);
+ if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
+ ADD_STRING(target, ";; SIG0 PSEUDOSECTION:\n");
+ result = dns_master_rdatasettotext(name, ps, style, target);
+ if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
+ (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
+ ADD_STRING(target, "\n");
+ return (result);
+ }
+ return (ISC_R_UNEXPECTED);
+}
+
+isc_result_t
+dns_message_totext(dns_message_t *msg, const dns_master_style_t *style,
+ dns_messagetextflag_t flags, isc_buffer_t *target) {
+ char buf[sizeof("1234567890")];
+ isc_result_t result;
+
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ REQUIRE(target != NULL);
+
+ if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0) {
+ ADD_STRING(target, ";; ->>HEADER<<- opcode: ");
+ ADD_STRING(target, opcodetext[msg->opcode]);
+ ADD_STRING(target, ", status: ");
+ if (msg->rcode < (sizeof(rcodetext)/sizeof(rcodetext[0]))) {
+ ADD_STRING(target, rcodetext[msg->rcode]);
+ } else {
+ snprintf(buf, sizeof(buf), "%4u", msg->rcode);
+ ADD_STRING(target, buf);
+ }
+ ADD_STRING(target, ", id: ");
+ snprintf(buf, sizeof(buf), "%6u", msg->id);
+ ADD_STRING(target, buf);
+ ADD_STRING(target, "\n;; flags: ");
+ if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0)
+ ADD_STRING(target, "qr ");
+ if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0)
+ ADD_STRING(target, "aa ");
+ if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0)
+ ADD_STRING(target, "tc ");
+ if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0)
+ ADD_STRING(target, "rd ");
+ if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0)
+ ADD_STRING(target, "ra ");
+ if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0)
+ ADD_STRING(target, "ad ");
+ if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0)
+ ADD_STRING(target, "cd ");
+ if (msg->opcode != dns_opcode_update) {
+ ADD_STRING(target, "; QUESTION: ");
+ } else {
+ ADD_STRING(target, "; ZONE: ");
+ }
+ snprintf(buf, sizeof(buf), "%1u",
+ msg->counts[DNS_SECTION_QUESTION]);
+ ADD_STRING(target, buf);
+ if (msg->opcode != dns_opcode_update) {
+ ADD_STRING(target, ", ANSWER: ");
+ } else {
+ ADD_STRING(target, ", PREREQ: ");
+ }
+ snprintf(buf, sizeof(buf), "%1u",
+ msg->counts[DNS_SECTION_ANSWER]);
+ ADD_STRING(target, buf);
+ if (msg->opcode != dns_opcode_update) {
+ ADD_STRING(target, ", AUTHORITY: ");
+ } else {
+ ADD_STRING(target, ", UPDATE: ");
+ }
+ snprintf(buf, sizeof(buf), "%1u",
+ msg->counts[DNS_SECTION_AUTHORITY]);
+ ADD_STRING(target, buf);
+ ADD_STRING(target, ", ADDITIONAL: ");
+ snprintf(buf, sizeof(buf), "%1u",
+ msg->counts[DNS_SECTION_ADDITIONAL]);
+ ADD_STRING(target, buf);
+ ADD_STRING(target, "\n");
+ }
+ result = dns_message_pseudosectiontotext(msg,
+ DNS_PSEUDOSECTION_OPT,
+ style, flags, target);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_message_sectiontotext(msg, DNS_SECTION_QUESTION,
+ style, flags, target);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = dns_message_sectiontotext(msg, DNS_SECTION_ANSWER,
+ style, flags, target);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = dns_message_sectiontotext(msg, DNS_SECTION_AUTHORITY,
+ style, flags, target);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = dns_message_sectiontotext(msg, DNS_SECTION_ADDITIONAL,
+ style, flags, target);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_message_pseudosectiontotext(msg,
+ DNS_PSEUDOSECTION_TSIG,
+ style, flags, target);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_message_pseudosectiontotext(msg,
+ DNS_PSEUDOSECTION_SIG0,
+ style, flags, target);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_region_t *
+dns_message_getrawmessage(dns_message_t *msg) {
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ return (&msg->saved);
+}
+
+void
+dns_message_setsortorder(dns_message_t *msg, dns_rdatasetorderfunc_t order,
+ const void *order_arg)
+{
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ msg->order = order;
+ msg->order_arg = order_arg;
+}
+
+void
+dns_message_settimeadjust(dns_message_t *msg, int timeadjust) {
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ msg->timeadjust = timeadjust;
+}
+
+int
+dns_message_gettimeadjust(dns_message_t *msg) {
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ return (msg->timeadjust);
+}
+
+isc_result_t
+dns_opcode_totext(dns_opcode_t opcode, isc_buffer_t *target) {
+
+ REQUIRE(opcode < 16);
+
+ if (isc_buffer_availablelength(target) < strlen(opcodetext[opcode]))
+ return (ISC_R_NOSPACE);
+ isc_buffer_putstr(target, opcodetext[opcode]);
+ return (ISC_R_SUCCESS);
+}
diff --git a/lib/dns/name.c b/lib/dns/name.c
new file mode 100644
index 0000000..f4ea3e9
--- /dev/null
+++ b/lib/dns/name.c
@@ -0,0 +1,2408 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: name.c,v 1.165 2008/04/01 23:47:10 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+
+#include <isc/buffer.h>
+#include <isc/hash.h>
+#include <isc/mem.h>
+#include <isc/once.h>
+#include <isc/print.h>
+#include <isc/string.h>
+#include <isc/thread.h>
+#include <isc/util.h>
+
+#include <dns/compress.h>
+#include <dns/name.h>
+#include <dns/result.h>
+
+#define VALID_NAME(n) ISC_MAGIC_VALID(n, DNS_NAME_MAGIC)
+
+typedef enum {
+ ft_init = 0,
+ ft_start,
+ ft_ordinary,
+ ft_initialescape,
+ ft_escape,
+ ft_escdecimal,
+ ft_at
+} ft_state;
+
+typedef enum {
+ fw_start = 0,
+ fw_ordinary,
+ fw_copy,
+ fw_newcurrent
+} fw_state;
+
+static char digitvalue[256] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*16*/
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*32*/
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*48*/
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /*64*/
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*80*/
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*96*/
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*112*/
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*128*/
+ -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, -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, -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, -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, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*256*/
+};
+
+static unsigned char 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
+};
+
+#define CONVERTTOASCII(c)
+#define CONVERTFROMASCII(c)
+
+#define INIT_OFFSETS(name, var, default) \
+ if (name->offsets != NULL) \
+ var = name->offsets; \
+ else \
+ var = default;
+
+#define SETUP_OFFSETS(name, var, default) \
+ if (name->offsets != NULL) \
+ var = name->offsets; \
+ else { \
+ var = default; \
+ set_offsets(name, var, NULL); \
+ }
+
+/*%
+ * Note: If additional attributes are added that should not be set for
+ * empty names, MAKE_EMPTY() must be changed so it clears them.
+ */
+#define MAKE_EMPTY(name) \
+do { \
+ name->ndata = NULL; \
+ name->length = 0; \
+ name->labels = 0; \
+ name->attributes &= ~DNS_NAMEATTR_ABSOLUTE; \
+} while (0);
+
+/*%
+ * A name is "bindable" if it can be set to point to a new value, i.e.
+ * name->ndata and name->length may be changed.
+ */
+#define BINDABLE(name) \
+ ((name->attributes & (DNS_NAMEATTR_READONLY|DNS_NAMEATTR_DYNAMIC)) \
+ == 0)
+
+/*%
+ * Note that the name data must be a char array, not a string
+ * literal, to avoid compiler warnings about discarding
+ * the const attribute of a string.
+ */
+static unsigned char root_ndata[] = { '\0' };
+static unsigned char root_offsets[] = { 0 };
+
+static dns_name_t root =
+{
+ DNS_NAME_MAGIC,
+ root_ndata, 1, 1,
+ DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
+ root_offsets, NULL,
+ {(void *)-1, (void *)-1},
+ {NULL, NULL}
+};
+
+/* XXXDCL make const? */
+LIBDNS_EXTERNAL_DATA dns_name_t *dns_rootname = &root;
+
+static unsigned char wild_ndata[] = { '\001', '*' };
+static unsigned char wild_offsets[] = { 0 };
+
+static dns_name_t wild =
+{
+ DNS_NAME_MAGIC,
+ wild_ndata, 2, 1,
+ DNS_NAMEATTR_READONLY,
+ wild_offsets, NULL,
+ {(void *)-1, (void *)-1},
+ {NULL, NULL}
+};
+
+/* XXXDCL make const? */
+LIBDNS_EXTERNAL_DATA dns_name_t *dns_wildcardname = &wild;
+
+unsigned int
+dns_fullname_hash(dns_name_t *name, isc_boolean_t case_sensitive);
+
+/*
+ * dns_name_t to text post-conversion procedure.
+ */
+#ifdef ISC_PLATFORM_USETHREADS
+static int thread_key_initialized = 0;
+static isc_mutex_t thread_key_mutex;
+static isc_mem_t *thread_key_mctx = NULL;
+static isc_thread_key_t totext_filter_proc_key;
+static isc_once_t once = ISC_ONCE_INIT;
+#else
+static dns_name_totextfilter_t totext_filter_proc = NULL;
+#endif
+
+static void
+set_offsets(const dns_name_t *name, unsigned char *offsets,
+ dns_name_t *set_name);
+
+void
+dns_name_init(dns_name_t *name, unsigned char *offsets) {
+ /*
+ * Initialize 'name'.
+ */
+ DNS_NAME_INIT(name, offsets);
+}
+
+void
+dns_name_reset(dns_name_t *name) {
+ REQUIRE(VALID_NAME(name));
+ REQUIRE(BINDABLE(name));
+
+ DNS_NAME_RESET(name);
+}
+
+void
+dns_name_invalidate(dns_name_t *name) {
+ /*
+ * Make 'name' invalid.
+ */
+
+ REQUIRE(VALID_NAME(name));
+
+ name->magic = 0;
+ name->ndata = NULL;
+ name->length = 0;
+ name->labels = 0;
+ name->attributes = 0;
+ name->offsets = NULL;
+ name->buffer = NULL;
+ ISC_LINK_INIT(name, link);
+}
+
+void
+dns_name_setbuffer(dns_name_t *name, isc_buffer_t *buffer) {
+ /*
+ * Dedicate a buffer for use with 'name'.
+ */
+
+ REQUIRE(VALID_NAME(name));
+ REQUIRE((buffer != NULL && name->buffer == NULL) ||
+ (buffer == NULL));
+
+ name->buffer = buffer;
+}
+
+isc_boolean_t
+dns_name_hasbuffer(const dns_name_t *name) {
+ /*
+ * Does 'name' have a dedicated buffer?
+ */
+
+ REQUIRE(VALID_NAME(name));
+
+ if (name->buffer != NULL)
+ return (ISC_TRUE);
+
+ return (ISC_FALSE);
+}
+
+isc_boolean_t
+dns_name_isabsolute(const dns_name_t *name) {
+
+ /*
+ * Does 'name' end in the root label?
+ */
+
+ REQUIRE(VALID_NAME(name));
+
+ if ((name->attributes & DNS_NAMEATTR_ABSOLUTE) != 0)
+ return (ISC_TRUE);
+ return (ISC_FALSE);
+}
+
+#define hyphenchar(c) ((c) == 0x2d)
+#define asterchar(c) ((c) == 0x2a)
+#define alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) \
+ || ((c) >= 0x61 && (c) <= 0x7a))
+#define digitchar(c) ((c) >= 0x30 && (c) <= 0x39)
+#define borderchar(c) (alphachar(c) || digitchar(c))
+#define middlechar(c) (borderchar(c) || hyphenchar(c))
+#define domainchar(c) ((c) > 0x20 && (c) < 0x7f)
+
+isc_boolean_t
+dns_name_ismailbox(const dns_name_t *name) {
+ unsigned char *ndata, ch;
+ unsigned int n;
+ isc_boolean_t first;
+
+ REQUIRE(VALID_NAME(name));
+ REQUIRE(name->labels > 0);
+ REQUIRE(name->attributes & DNS_NAMEATTR_ABSOLUTE);
+
+ /*
+ * Root label.
+ */
+ if (name->length == 1)
+ return (ISC_TRUE);
+
+ ndata = name->ndata;
+ n = *ndata++;
+ INSIST(n <= 63);
+ while (n--) {
+ ch = *ndata++;
+ if (!domainchar(ch))
+ return (ISC_FALSE);
+ }
+
+ if (ndata == name->ndata + name->length)
+ return (ISC_FALSE);
+
+ /*
+ * RFC292/RFC1123 hostname.
+ */
+ while (ndata < (name->ndata + name->length)) {
+ n = *ndata++;
+ INSIST(n <= 63);
+ first = ISC_TRUE;
+ while (n--) {
+ ch = *ndata++;
+ if (first || n == 0) {
+ if (!borderchar(ch))
+ return (ISC_FALSE);
+ } else {
+ if (!middlechar(ch))
+ return (ISC_FALSE);
+ }
+ first = ISC_FALSE;
+ }
+ }
+ return (ISC_TRUE);
+}
+
+isc_boolean_t
+dns_name_ishostname(const dns_name_t *name, isc_boolean_t wildcard) {
+ unsigned char *ndata, ch;
+ unsigned int n;
+ isc_boolean_t first;
+
+ REQUIRE(VALID_NAME(name));
+ REQUIRE(name->labels > 0);
+ REQUIRE(name->attributes & DNS_NAMEATTR_ABSOLUTE);
+
+ /*
+ * Root label.
+ */
+ if (name->length == 1)
+ return (ISC_TRUE);
+
+ /*
+ * Skip wildcard if this is a ownername.
+ */
+ ndata = name->ndata;
+ if (wildcard && ndata[0] == 1 && ndata[1] == '*')
+ ndata += 2;
+
+ /*
+ * RFC292/RFC1123 hostname.
+ */
+ while (ndata < (name->ndata + name->length)) {
+ n = *ndata++;
+ INSIST(n <= 63);
+ first = ISC_TRUE;
+ while (n--) {
+ ch = *ndata++;
+ if (first || n == 0) {
+ if (!borderchar(ch))
+ return (ISC_FALSE);
+ } else {
+ if (!middlechar(ch))
+ return (ISC_FALSE);
+ }
+ first = ISC_FALSE;
+ }
+ }
+ return (ISC_TRUE);
+}
+
+isc_boolean_t
+dns_name_iswildcard(const dns_name_t *name) {
+ unsigned char *ndata;
+
+ /*
+ * Is 'name' a wildcard name?
+ */
+
+ REQUIRE(VALID_NAME(name));
+ REQUIRE(name->labels > 0);
+
+ if (name->length >= 2) {
+ ndata = name->ndata;
+ if (ndata[0] == 1 && ndata[1] == '*')
+ return (ISC_TRUE);
+ }
+
+ return (ISC_FALSE);
+}
+
+isc_boolean_t
+dns_name_internalwildcard(const dns_name_t *name) {
+ unsigned char *ndata;
+ unsigned int count;
+ unsigned int label;
+
+ /*
+ * Does 'name' contain a internal wildcard?
+ */
+
+ REQUIRE(VALID_NAME(name));
+ REQUIRE(name->labels > 0);
+
+ /*
+ * Skip first label.
+ */
+ ndata = name->ndata;
+ count = *ndata++;
+ INSIST(count <= 63);
+ ndata += count;
+ label = 1;
+ /*
+ * Check all but the last of the remaining labels.
+ */
+ while (label + 1 < name->labels) {
+ count = *ndata++;
+ INSIST(count <= 63);
+ if (count == 1 && *ndata == '*')
+ return (ISC_TRUE);
+ ndata += count;
+ label++;
+ }
+ return (ISC_FALSE);
+}
+
+static inline unsigned int
+name_hash(dns_name_t *name, isc_boolean_t case_sensitive) {
+ unsigned int length;
+ const unsigned char *s;
+ unsigned int h = 0;
+ unsigned char c;
+
+ length = name->length;
+ if (length > 16)
+ length = 16;
+
+ /*
+ * This hash function is similar to the one Ousterhout
+ * uses in Tcl.
+ */
+ s = name->ndata;
+ if (case_sensitive) {
+ while (length > 0) {
+ h += ( h << 3 ) + *s;
+ s++;
+ length--;
+ }
+ } else {
+ while (length > 0) {
+ c = maptolower[*s];
+ h += ( h << 3 ) + c;
+ s++;
+ length--;
+ }
+ }
+
+ return (h);
+}
+
+unsigned int
+dns_name_hash(dns_name_t *name, isc_boolean_t case_sensitive) {
+ /*
+ * Provide a hash value for 'name'.
+ */
+ REQUIRE(VALID_NAME(name));
+
+ if (name->labels == 0)
+ return (0);
+
+ return (name_hash(name, case_sensitive));
+}
+
+unsigned int
+dns_name_fullhash(dns_name_t *name, isc_boolean_t case_sensitive) {
+ /*
+ * Provide a hash value for 'name'.
+ */
+ REQUIRE(VALID_NAME(name));
+
+ if (name->labels == 0)
+ return (0);
+
+ return (isc_hash_calc((const unsigned char *)name->ndata,
+ name->length, case_sensitive));
+}
+
+unsigned int
+dns_fullname_hash(dns_name_t *name, isc_boolean_t case_sensitive) {
+ /*
+ * This function was deprecated due to the breakage of the name space
+ * convention. We only keep this internally to provide binary backward
+ * compatibility.
+ */
+ REQUIRE(VALID_NAME(name));
+
+ return (dns_name_fullhash(name, case_sensitive));
+}
+
+unsigned int
+dns_name_hashbylabel(dns_name_t *name, isc_boolean_t case_sensitive) {
+ unsigned char *offsets;
+ dns_offsets_t odata;
+ dns_name_t tname;
+ unsigned int h = 0;
+ unsigned int i;
+
+ /*
+ * Provide a hash value for 'name'.
+ */
+ REQUIRE(VALID_NAME(name));
+
+ if (name->labels == 0)
+ return (0);
+ else if (name->labels == 1)
+ return (name_hash(name, case_sensitive));
+
+ SETUP_OFFSETS(name, offsets, odata);
+ DNS_NAME_INIT(&tname, NULL);
+ tname.labels = 1;
+ h = 0;
+ for (i = 0; i < name->labels; i++) {
+ tname.ndata = name->ndata + offsets[i];
+ if (i == name->labels - 1)
+ tname.length = name->length - offsets[i];
+ else
+ tname.length = offsets[i + 1] - offsets[i];
+ h += name_hash(&tname, case_sensitive);
+ }
+
+ return (h);
+}
+
+dns_namereln_t
+dns_name_fullcompare(const dns_name_t *name1, const dns_name_t *name2,
+ int *orderp, unsigned int *nlabelsp)
+{
+ unsigned int l1, l2, l, count1, count2, count, nlabels;
+ int cdiff, ldiff, chdiff;
+ unsigned char *label1, *label2;
+ unsigned char *offsets1, *offsets2;
+ dns_offsets_t odata1, odata2;
+ dns_namereln_t namereln = dns_namereln_none;
+
+ /*
+ * Determine the relative ordering under the DNSSEC order relation of
+ * 'name1' and 'name2', and also determine the hierarchical
+ * relationship of the names.
+ *
+ * Note: It makes no sense for one of the names to be relative and the
+ * other absolute. If both names are relative, then to be meaningfully
+ * compared the caller must ensure that they are both relative to the
+ * same domain.
+ */
+
+ REQUIRE(VALID_NAME(name1));
+ REQUIRE(VALID_NAME(name2));
+ REQUIRE(orderp != NULL);
+ REQUIRE(nlabelsp != NULL);
+ /*
+ * Either name1 is absolute and name2 is absolute, or neither is.
+ */
+ REQUIRE((name1->attributes & DNS_NAMEATTR_ABSOLUTE) ==
+ (name2->attributes & DNS_NAMEATTR_ABSOLUTE));
+
+ SETUP_OFFSETS(name1, offsets1, odata1);
+ SETUP_OFFSETS(name2, offsets2, odata2);
+
+ nlabels = 0;
+ l1 = name1->labels;
+ l2 = name2->labels;
+ ldiff = (int)l1 - (int)l2;
+ if (ldiff < 0)
+ l = l1;
+ else
+ l = l2;
+
+ while (l > 0) {
+ l--;
+ l1--;
+ l2--;
+ label1 = &name1->ndata[offsets1[l1]];
+ label2 = &name2->ndata[offsets2[l2]];
+ count1 = *label1++;
+ count2 = *label2++;
+
+ /*
+ * We dropped bitstring labels, and we don't support any
+ * other extended label types.
+ */
+ INSIST(count1 <= 63 && count2 <= 63);
+
+ cdiff = (int)count1 - (int)count2;
+ if (cdiff < 0)
+ count = count1;
+ else
+ count = count2;
+
+ while (count > 0) {
+ chdiff = (int)maptolower[*label1] -
+ (int)maptolower[*label2];
+ if (chdiff != 0) {
+ *orderp = chdiff;
+ goto done;
+ }
+ count--;
+ label1++;
+ label2++;
+ }
+ if (cdiff != 0) {
+ *orderp = cdiff;
+ goto done;
+ }
+ nlabels++;
+ }
+
+ *orderp = ldiff;
+ if (ldiff < 0)
+ namereln = dns_namereln_contains;
+ else if (ldiff > 0)
+ namereln = dns_namereln_subdomain;
+ else
+ namereln = dns_namereln_equal;
+
+ done:
+ *nlabelsp = nlabels;
+
+ if (nlabels > 0 && namereln == dns_namereln_none)
+ namereln = dns_namereln_commonancestor;
+
+ return (namereln);
+}
+
+int
+dns_name_compare(const dns_name_t *name1, const dns_name_t *name2) {
+ int order;
+ unsigned int nlabels;
+
+ /*
+ * Determine the relative ordering under the DNSSEC order relation of
+ * 'name1' and 'name2'.
+ *
+ * Note: It makes no sense for one of the names to be relative and the
+ * other absolute. If both names are relative, then to be meaningfully
+ * compared the caller must ensure that they are both relative to the
+ * same domain.
+ */
+
+ (void)dns_name_fullcompare(name1, name2, &order, &nlabels);
+
+ return (order);
+}
+
+isc_boolean_t
+dns_name_equal(const dns_name_t *name1, const dns_name_t *name2) {
+ unsigned int l, count;
+ unsigned char c;
+ unsigned char *label1, *label2;
+
+ /*
+ * Are 'name1' and 'name2' equal?
+ *
+ * Note: It makes no sense for one of the names to be relative and the
+ * other absolute. If both names are relative, then to be meaningfully
+ * compared the caller must ensure that they are both relative to the
+ * same domain.
+ */
+
+ REQUIRE(VALID_NAME(name1));
+ REQUIRE(VALID_NAME(name2));
+ /*
+ * Either name1 is absolute and name2 is absolute, or neither is.
+ */
+ REQUIRE((name1->attributes & DNS_NAMEATTR_ABSOLUTE) ==
+ (name2->attributes & DNS_NAMEATTR_ABSOLUTE));
+
+ if (name1->length != name2->length)
+ return (ISC_FALSE);
+
+ l = name1->labels;
+
+ if (l != name2->labels)
+ return (ISC_FALSE);
+
+ label1 = name1->ndata;
+ label2 = name2->ndata;
+ while (l > 0) {
+ l--;
+ count = *label1++;
+ if (count != *label2++)
+ return (ISC_FALSE);
+
+ INSIST(count <= 63); /* no bitstring support */
+
+ while (count > 0) {
+ count--;
+ c = maptolower[*label1++];
+ if (c != maptolower[*label2++])
+ return (ISC_FALSE);
+ }
+ }
+
+ return (ISC_TRUE);
+}
+
+isc_boolean_t
+dns_name_caseequal(const dns_name_t *name1, const dns_name_t *name2) {
+
+ /*
+ * Are 'name1' and 'name2' equal?
+ *
+ * Note: It makes no sense for one of the names to be relative and the
+ * other absolute. If both names are relative, then to be meaningfully
+ * compared the caller must ensure that they are both relative to the
+ * same domain.
+ */
+
+ REQUIRE(VALID_NAME(name1));
+ REQUIRE(VALID_NAME(name2));
+ /*
+ * Either name1 is absolute and name2 is absolute, or neither is.
+ */
+ REQUIRE((name1->attributes & DNS_NAMEATTR_ABSOLUTE) ==
+ (name2->attributes & DNS_NAMEATTR_ABSOLUTE));
+
+ if (name1->length != name2->length)
+ return (ISC_FALSE);
+
+ if (memcmp(name1->ndata, name2->ndata, name1->length) != 0)
+ return (ISC_FALSE);
+
+ return (ISC_TRUE);
+}
+
+int
+dns_name_rdatacompare(const dns_name_t *name1, const dns_name_t *name2) {
+ unsigned int l1, l2, l, count1, count2, count;
+ unsigned char c1, c2;
+ unsigned char *label1, *label2;
+
+ /*
+ * Compare two absolute names as rdata.
+ */
+
+ REQUIRE(VALID_NAME(name1));
+ REQUIRE(name1->labels > 0);
+ REQUIRE((name1->attributes & DNS_NAMEATTR_ABSOLUTE) != 0);
+ REQUIRE(VALID_NAME(name2));
+ REQUIRE(name2->labels > 0);
+ REQUIRE((name2->attributes & DNS_NAMEATTR_ABSOLUTE) != 0);
+
+ l1 = name1->labels;
+ l2 = name2->labels;
+
+ l = (l1 < l2) ? l1 : l2;
+
+ label1 = name1->ndata;
+ label2 = name2->ndata;
+ while (l > 0) {
+ l--;
+ count1 = *label1++;
+ count2 = *label2++;
+
+ /* no bitstring support */
+ INSIST(count1 <= 63 && count2 <= 63);
+
+ if (count1 != count2)
+ return ((count1 < count2) ? -1 : 1);
+ count = count1;
+ while (count > 0) {
+ count--;
+ c1 = maptolower[*label1++];
+ c2 = maptolower[*label2++];
+ if (c1 < c2)
+ return (-1);
+ else if (c1 > c2)
+ return (1);
+ }
+ }
+
+ /*
+ * If one name had more labels than the other, their common
+ * prefix must have been different because the shorter name
+ * ended with the root label and the longer one can't have
+ * a root label in the middle of it. Therefore, if we get
+ * to this point, the lengths must be equal.
+ */
+ INSIST(l1 == l2);
+
+ return (0);
+}
+
+isc_boolean_t
+dns_name_issubdomain(const dns_name_t *name1, const dns_name_t *name2) {
+ int order;
+ unsigned int nlabels;
+ dns_namereln_t namereln;
+
+ /*
+ * Is 'name1' a subdomain of 'name2'?
+ *
+ * Note: It makes no sense for one of the names to be relative and the
+ * other absolute. If both names are relative, then to be meaningfully
+ * compared the caller must ensure that they are both relative to the
+ * same domain.
+ */
+
+ namereln = dns_name_fullcompare(name1, name2, &order, &nlabels);
+ if (namereln == dns_namereln_subdomain ||
+ namereln == dns_namereln_equal)
+ return (ISC_TRUE);
+
+ return (ISC_FALSE);
+}
+
+isc_boolean_t
+dns_name_matcheswildcard(const dns_name_t *name, const dns_name_t *wname) {
+ int order;
+ unsigned int nlabels, labels;
+ dns_name_t tname;
+
+ REQUIRE(VALID_NAME(name));
+ REQUIRE(name->labels > 0);
+ REQUIRE(VALID_NAME(wname));
+ labels = wname->labels;
+ REQUIRE(labels > 0);
+ REQUIRE(dns_name_iswildcard(wname));
+
+ DNS_NAME_INIT(&tname, NULL);
+ dns_name_getlabelsequence(wname, 1, labels - 1, &tname);
+ if (dns_name_fullcompare(name, &tname, &order, &nlabels) ==
+ dns_namereln_subdomain)
+ return (ISC_TRUE);
+ return (ISC_FALSE);
+}
+
+unsigned int
+dns_name_countlabels(const dns_name_t *name) {
+ /*
+ * How many labels does 'name' have?
+ */
+
+ REQUIRE(VALID_NAME(name));
+
+ ENSURE(name->labels <= 128);
+
+ return (name->labels);
+}
+
+void
+dns_name_getlabel(const dns_name_t *name, unsigned int n, dns_label_t *label) {
+ unsigned char *offsets;
+ dns_offsets_t odata;
+
+ /*
+ * Make 'label' refer to the 'n'th least significant label of 'name'.
+ */
+
+ REQUIRE(VALID_NAME(name));
+ REQUIRE(name->labels > 0);
+ REQUIRE(n < name->labels);
+ REQUIRE(label != NULL);
+
+ SETUP_OFFSETS(name, offsets, odata);
+
+ label->base = &name->ndata[offsets[n]];
+ if (n == name->labels - 1)
+ label->length = name->length - offsets[n];
+ else
+ label->length = offsets[n + 1] - offsets[n];
+}
+
+void
+dns_name_getlabelsequence(const dns_name_t *source,
+ unsigned int first, unsigned int n,
+ dns_name_t *target)
+{
+ unsigned char *offsets;
+ dns_offsets_t odata;
+ unsigned int firstoffset, endoffset;
+
+ /*
+ * Make 'target' refer to the 'n' labels including and following
+ * 'first' in 'source'.
+ */
+
+ REQUIRE(VALID_NAME(source));
+ REQUIRE(VALID_NAME(target));
+ REQUIRE(first <= source->labels);
+ REQUIRE(first + n <= source->labels);
+ REQUIRE(BINDABLE(target));
+
+ SETUP_OFFSETS(source, offsets, odata);
+
+ if (first == source->labels)
+ firstoffset = source->length;
+ else
+ firstoffset = offsets[first];
+
+ if (first + n == source->labels)
+ endoffset = source->length;
+ else
+ endoffset = offsets[first + n];
+
+ target->ndata = &source->ndata[firstoffset];
+ target->length = endoffset - firstoffset;
+
+ if (first + n == source->labels && n > 0 &&
+ (source->attributes & DNS_NAMEATTR_ABSOLUTE) != 0)
+ target->attributes |= DNS_NAMEATTR_ABSOLUTE;
+ else
+ target->attributes &= ~DNS_NAMEATTR_ABSOLUTE;
+
+ target->labels = n;
+
+ /*
+ * If source and target are the same, and we're making target
+ * a prefix of source, the offsets table is correct already
+ * so we don't need to call set_offsets().
+ */
+ if (target->offsets != NULL &&
+ (target != source || first != 0))
+ set_offsets(target, target->offsets, NULL);
+}
+
+void
+dns_name_clone(const dns_name_t *source, dns_name_t *target) {
+
+ /*
+ * Make 'target' refer to the same name as 'source'.
+ */
+
+ REQUIRE(VALID_NAME(source));
+ REQUIRE(VALID_NAME(target));
+ REQUIRE(BINDABLE(target));
+
+ target->ndata = source->ndata;
+ target->length = source->length;
+ target->labels = source->labels;
+ target->attributes = source->attributes &
+ (unsigned int)~(DNS_NAMEATTR_READONLY | DNS_NAMEATTR_DYNAMIC |
+ DNS_NAMEATTR_DYNOFFSETS);
+ if (target->offsets != NULL && source->labels > 0) {
+ if (source->offsets != NULL)
+ memcpy(target->offsets, source->offsets,
+ source->labels);
+ else
+ set_offsets(target, target->offsets, NULL);
+ }
+}
+
+void
+dns_name_fromregion(dns_name_t *name, const isc_region_t *r) {
+ unsigned char *offsets;
+ dns_offsets_t odata;
+ unsigned int len;
+ isc_region_t r2;
+
+ /*
+ * Make 'name' refer to region 'r'.
+ */
+
+ REQUIRE(VALID_NAME(name));
+ REQUIRE(r != NULL);
+ REQUIRE(BINDABLE(name));
+
+ INIT_OFFSETS(name, offsets, odata);
+
+ if (name->buffer != NULL) {
+ isc_buffer_clear(name->buffer);
+ isc_buffer_availableregion(name->buffer, &r2);
+ len = (r->length < r2.length) ? r->length : r2.length;
+ if (len > DNS_NAME_MAXWIRE)
+ len = DNS_NAME_MAXWIRE;
+ memcpy(r2.base, r->base, len);
+ name->ndata = r2.base;
+ name->length = len;
+ } else {
+ name->ndata = r->base;
+ name->length = (r->length <= DNS_NAME_MAXWIRE) ?
+ r->length : DNS_NAME_MAXWIRE;
+ }
+
+ if (r->length > 0)
+ set_offsets(name, offsets, name);
+ else {
+ name->labels = 0;
+ name->attributes &= ~DNS_NAMEATTR_ABSOLUTE;
+ }
+
+ if (name->buffer != NULL)
+ isc_buffer_add(name->buffer, name->length);
+}
+
+void
+dns_name_toregion(dns_name_t *name, isc_region_t *r) {
+ /*
+ * Make 'r' refer to 'name'.
+ */
+
+ REQUIRE(VALID_NAME(name));
+ REQUIRE(r != NULL);
+
+ DNS_NAME_TOREGION(name, r);
+}
+
+
+isc_result_t
+dns_name_fromtext(dns_name_t *name, isc_buffer_t *source,
+ dns_name_t *origin, unsigned int options,
+ isc_buffer_t *target)
+{
+ unsigned char *ndata, *label;
+ char *tdata;
+ char c;
+ ft_state state;
+ unsigned int value, count;
+ unsigned int n1, n2, tlen, nrem, nused, digits, labels, tused;
+ isc_boolean_t done;
+ unsigned char *offsets;
+ dns_offsets_t odata;
+ isc_boolean_t downcase;
+
+ /*
+ * Convert the textual representation of a DNS name at source
+ * into uncompressed wire form stored in target.
+ *
+ * Notes:
+ * Relative domain names will have 'origin' appended to them
+ * unless 'origin' is NULL, in which case relative domain names
+ * will remain relative.
+ */
+
+ REQUIRE(VALID_NAME(name));
+ REQUIRE(ISC_BUFFER_VALID(source));
+ REQUIRE((target != NULL && ISC_BUFFER_VALID(target)) ||
+ (target == NULL && ISC_BUFFER_VALID(name->buffer)));
+
+ downcase = ISC_TF((options & DNS_NAME_DOWNCASE) != 0);
+
+ if (target == NULL && name->buffer != NULL) {
+ target = name->buffer;
+ isc_buffer_clear(target);
+ }
+
+ REQUIRE(BINDABLE(name));
+
+ INIT_OFFSETS(name, offsets, odata);
+ offsets[0] = 0;
+
+ /*
+ * Initialize things to make the compiler happy; they're not required.
+ */
+ n1 = 0;
+ n2 = 0;
+ label = NULL;
+ digits = 0;
+ value = 0;
+ count = 0;
+
+ /*
+ * Make 'name' empty in case of failure.
+ */
+ MAKE_EMPTY(name);
+
+ /*
+ * Set up the state machine.
+ */
+ tdata = (char *)source->base + source->current;
+ tlen = isc_buffer_remaininglength(source);
+ tused = 0;
+ ndata = isc_buffer_used(target);
+ nrem = isc_buffer_availablelength(target);
+ if (nrem > 255)
+ nrem = 255;
+ nused = 0;
+ labels = 0;
+ done = ISC_FALSE;
+ state = ft_init;
+
+ while (nrem > 0 && tlen > 0 && !done) {
+ c = *tdata++;
+ tlen--;
+ tused++;
+
+ switch (state) {
+ case ft_init:
+ /*
+ * Is this the root name?
+ */
+ if (c == '.') {
+ if (tlen != 0)
+ return (DNS_R_EMPTYLABEL);
+ labels++;
+ *ndata++ = 0;
+ nrem--;
+ nused++;
+ done = ISC_TRUE;
+ break;
+ }
+ if (c == '@' && tlen == 0) {
+ state = ft_at;
+ break;
+ }
+
+ /* FALLTHROUGH */
+ case ft_start:
+ label = ndata;
+ ndata++;
+ nrem--;
+ nused++;
+ count = 0;
+ if (c == '\\') {
+ state = ft_initialescape;
+ break;
+ }
+ state = ft_ordinary;
+ if (nrem == 0)
+ return (ISC_R_NOSPACE);
+ /* FALLTHROUGH */
+ case ft_ordinary:
+ if (c == '.') {
+ if (count == 0)
+ return (DNS_R_EMPTYLABEL);
+ *label = count;
+ labels++;
+ INSIST(labels <= 127);
+ offsets[labels] = nused;
+ if (tlen == 0) {
+ labels++;
+ *ndata++ = 0;
+ nrem--;
+ nused++;
+ done = ISC_TRUE;
+ }
+ state = ft_start;
+ } else if (c == '\\') {
+ state = ft_escape;
+ } else {
+ if (count >= 63)
+ return (DNS_R_LABELTOOLONG);
+ count++;
+ CONVERTTOASCII(c);
+ if (downcase)
+ c = maptolower[(int)c];
+ *ndata++ = c;
+ nrem--;
+ nused++;
+ }
+ break;
+ case ft_initialescape:
+ if (c == '[') {
+ /*
+ * This looks like a bitstring label, which
+ * was deprecated. Intentionally drop it.
+ */
+ return (DNS_R_BADLABELTYPE);
+ }
+ state = ft_escape;
+ /* FALLTHROUGH */
+ case ft_escape:
+ if (!isdigit(c & 0xff)) {
+ if (count >= 63)
+ return (DNS_R_LABELTOOLONG);
+ count++;
+ CONVERTTOASCII(c);
+ if (downcase)
+ c = maptolower[(int)c];
+ *ndata++ = c;
+ nrem--;
+ nused++;
+ state = ft_ordinary;
+ break;
+ }
+ digits = 0;
+ value = 0;
+ state = ft_escdecimal;
+ /* FALLTHROUGH */
+ case ft_escdecimal:
+ if (!isdigit(c & 0xff))
+ return (DNS_R_BADESCAPE);
+ value *= 10;
+ value += digitvalue[(int)c];
+ digits++;
+ if (digits == 3) {
+ if (value > 255)
+ return (DNS_R_BADESCAPE);
+ if (count >= 63)
+ return (DNS_R_LABELTOOLONG);
+ count++;
+ if (downcase)
+ value = maptolower[value];
+ *ndata++ = value;
+ nrem--;
+ nused++;
+ state = ft_ordinary;
+ }
+ break;
+ default:
+ FATAL_ERROR(__FILE__, __LINE__,
+ "Unexpected state %d", state);
+ /* Does not return. */
+ }
+ }
+
+ if (!done) {
+ if (nrem == 0)
+ return (ISC_R_NOSPACE);
+ INSIST(tlen == 0);
+ if (state != ft_ordinary && state != ft_at)
+ return (ISC_R_UNEXPECTEDEND);
+ if (state == ft_ordinary) {
+ INSIST(count != 0);
+ *label = count;
+ labels++;
+ INSIST(labels <= 127);
+ offsets[labels] = nused;
+ }
+ if (origin != NULL) {
+ if (nrem < origin->length)
+ return (ISC_R_NOSPACE);
+ label = origin->ndata;
+ n1 = origin->length;
+ nrem -= n1;
+ while (n1 > 0) {
+ n2 = *label++;
+ INSIST(n2 <= 63); /* no bitstring support */
+ *ndata++ = n2;
+ n1 -= n2 + 1;
+ nused += n2 + 1;
+ while (n2 > 0) {
+ c = *label++;
+ if (downcase)
+ c = maptolower[(int)c];
+ *ndata++ = c;
+ n2--;
+ }
+ labels++;
+ if (n1 > 0) {
+ INSIST(labels <= 127);
+ offsets[labels] = nused;
+ }
+ }
+ if ((origin->attributes & DNS_NAMEATTR_ABSOLUTE) != 0)
+ name->attributes |= DNS_NAMEATTR_ABSOLUTE;
+ }
+ } else
+ name->attributes |= DNS_NAMEATTR_ABSOLUTE;
+
+ name->ndata = (unsigned char *)target->base + target->used;
+ name->labels = labels;
+ name->length = nused;
+
+ isc_buffer_forward(source, tused);
+ isc_buffer_add(target, name->length);
+
+ return (ISC_R_SUCCESS);
+}
+
+#ifdef ISC_PLATFORM_USETHREADS
+static void
+free_specific(void *arg) {
+ dns_name_totextfilter_t *mem = arg;
+ isc_mem_put(thread_key_mctx, mem, sizeof(*mem));
+ /* Stop use being called again. */
+ (void)isc_thread_key_setspecific(totext_filter_proc_key, NULL);
+}
+
+static void
+thread_key_mutex_init(void) {
+ RUNTIME_CHECK(isc_mutex_init(&thread_key_mutex) == ISC_R_SUCCESS);
+}
+
+static isc_result_t
+totext_filter_proc_key_init(void) {
+ isc_result_t result;
+
+ /*
+ * We need the call to isc_once_do() to support profiled mutex
+ * otherwise thread_key_mutex could be initialized at compile time.
+ */
+ result = isc_once_do(&once, thread_key_mutex_init);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (!thread_key_initialized) {
+ LOCK(&thread_key_mutex);
+ if (thread_key_mctx == NULL)
+ result = isc_mem_create2(0, 0, &thread_key_mctx, 0);
+ if (result != ISC_R_SUCCESS)
+ goto unlock;
+ isc_mem_setname(thread_key_mctx, "threadkey", NULL);
+ isc_mem_setdestroycheck(thread_key_mctx, ISC_FALSE);
+
+ if (!thread_key_initialized &&
+ isc_thread_key_create(&totext_filter_proc_key,
+ free_specific) != 0) {
+ result = ISC_R_FAILURE;
+ isc_mem_detach(&thread_key_mctx);
+ } else
+ thread_key_initialized = 1;
+ unlock:
+ UNLOCK(&thread_key_mutex);
+ }
+ return (result);
+}
+#endif
+
+isc_result_t
+dns_name_totext(dns_name_t *name, isc_boolean_t omit_final_dot,
+ isc_buffer_t *target)
+{
+ unsigned char *ndata;
+ char *tdata;
+ unsigned int nlen, tlen;
+ unsigned char c;
+ unsigned int trem, count;
+ unsigned int labels;
+ isc_boolean_t saw_root = ISC_FALSE;
+ unsigned int oused = target->used;
+#ifdef ISC_PLATFORM_USETHREADS
+ dns_name_totextfilter_t *mem;
+ dns_name_totextfilter_t totext_filter_proc = NULL;
+ isc_result_t result;
+#endif
+
+ /*
+ * This function assumes the name is in proper uncompressed
+ * wire format.
+ */
+ REQUIRE(VALID_NAME(name));
+ REQUIRE(ISC_BUFFER_VALID(target));
+
+#ifdef ISC_PLATFORM_USETHREADS
+ result = totext_filter_proc_key_init();
+ if (result != ISC_R_SUCCESS)
+ return (result);
+#endif
+ ndata = name->ndata;
+ nlen = name->length;
+ labels = name->labels;
+ tdata = isc_buffer_used(target);
+ tlen = isc_buffer_availablelength(target);
+
+ trem = tlen;
+
+ if (labels == 0 && nlen == 0) {
+ /*
+ * Special handling for an empty name.
+ */
+ if (trem == 0)
+ return (ISC_R_NOSPACE);
+
+ /*
+ * The names of these booleans are misleading in this case.
+ * This empty name is not necessarily from the root node of
+ * the DNS root zone, nor is a final dot going to be included.
+ * They need to be set this way, though, to keep the "@"
+ * from being trounced.
+ */
+ saw_root = ISC_TRUE;
+ omit_final_dot = ISC_FALSE;
+ *tdata++ = '@';
+ trem--;
+
+ /*
+ * Skip the while() loop.
+ */
+ nlen = 0;
+ } else if (nlen == 1 && labels == 1 && *ndata == '\0') {
+ /*
+ * Special handling for the root label.
+ */
+ if (trem == 0)
+ return (ISC_R_NOSPACE);
+
+ saw_root = ISC_TRUE;
+ omit_final_dot = ISC_FALSE;
+ *tdata++ = '.';
+ trem--;
+
+ /*
+ * Skip the while() loop.
+ */
+ nlen = 0;
+ }
+
+ while (labels > 0 && nlen > 0 && trem > 0) {
+ labels--;
+ count = *ndata++;
+ nlen--;
+ if (count == 0) {
+ saw_root = ISC_TRUE;
+ break;
+ }
+ if (count < 64) {
+ INSIST(nlen >= count);
+ while (count > 0) {
+ c = *ndata;
+ switch (c) {
+ case 0x22: /* '"' */
+ case 0x28: /* '(' */
+ case 0x29: /* ')' */
+ case 0x2E: /* '.' */
+ case 0x3B: /* ';' */
+ case 0x5C: /* '\\' */
+ /* Special modifiers in zone files. */
+ case 0x40: /* '@' */
+ case 0x24: /* '$' */
+ if (trem < 2)
+ return (ISC_R_NOSPACE);
+ *tdata++ = '\\';
+ CONVERTFROMASCII(c);
+ *tdata++ = c;
+ ndata++;
+ trem -= 2;
+ nlen--;
+ break;
+ default:
+ if (c > 0x20 && c < 0x7f) {
+ if (trem == 0)
+ return (ISC_R_NOSPACE);
+ CONVERTFROMASCII(c);
+ *tdata++ = c;
+ ndata++;
+ trem--;
+ nlen--;
+ } else {
+ if (trem < 4)
+ return (ISC_R_NOSPACE);
+ *tdata++ = 0x5c;
+ *tdata++ = 0x30 +
+ ((c / 100) % 10);
+ *tdata++ = 0x30 +
+ ((c / 10) % 10);
+ *tdata++ = 0x30 + (c % 10);
+ trem -= 4;
+ ndata++;
+ nlen--;
+ }
+ }
+ count--;
+ }
+ } else {
+ FATAL_ERROR(__FILE__, __LINE__,
+ "Unexpected label type %02x", count);
+ /* NOTREACHED */
+ }
+
+ /*
+ * The following assumes names are absolute. If not, we
+ * fix things up later. Note that this means that in some
+ * cases one more byte of text buffer is required than is
+ * needed in the final output.
+ */
+ if (trem == 0)
+ return (ISC_R_NOSPACE);
+ *tdata++ = '.';
+ trem--;
+ }
+
+ if (nlen != 0 && trem == 0)
+ return (ISC_R_NOSPACE);
+
+ if (!saw_root || omit_final_dot)
+ trem++;
+
+ isc_buffer_add(target, tlen - trem);
+
+#ifdef ISC_PLATFORM_USETHREADS
+ mem = isc_thread_key_getspecific(totext_filter_proc_key);
+ if (mem != NULL)
+ totext_filter_proc = *mem;
+#endif
+ if (totext_filter_proc != NULL)
+ return ((*totext_filter_proc)(target, oused, saw_root));
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_name_tofilenametext(dns_name_t *name, isc_boolean_t omit_final_dot,
+ isc_buffer_t *target)
+{
+ unsigned char *ndata;
+ char *tdata;
+ unsigned int nlen, tlen;
+ unsigned char c;
+ unsigned int trem, count;
+ unsigned int labels;
+
+ /*
+ * This function assumes the name is in proper uncompressed
+ * wire format.
+ */
+ REQUIRE(VALID_NAME(name));
+ REQUIRE((name->attributes & DNS_NAMEATTR_ABSOLUTE) != 0);
+ REQUIRE(ISC_BUFFER_VALID(target));
+
+ ndata = name->ndata;
+ nlen = name->length;
+ labels = name->labels;
+ tdata = isc_buffer_used(target);
+ tlen = isc_buffer_availablelength(target);
+
+ trem = tlen;
+
+ if (nlen == 1 && labels == 1 && *ndata == '\0') {
+ /*
+ * Special handling for the root label.
+ */
+ if (trem == 0)
+ return (ISC_R_NOSPACE);
+
+ omit_final_dot = ISC_FALSE;
+ *tdata++ = '.';
+ trem--;
+
+ /*
+ * Skip the while() loop.
+ */
+ nlen = 0;
+ }
+
+ while (labels > 0 && nlen > 0 && trem > 0) {
+ labels--;
+ count = *ndata++;
+ nlen--;
+ if (count == 0)
+ break;
+ if (count < 64) {
+ INSIST(nlen >= count);
+ while (count > 0) {
+ c = *ndata;
+ if ((c >= 0x30 && c <= 0x39) || /* digit */
+ (c >= 0x41 && c <= 0x5A) || /* uppercase */
+ (c >= 0x61 && c <= 0x7A) || /* lowercase */
+ c == 0x2D || /* hyphen */
+ c == 0x5F) /* underscore */
+ {
+ if (trem == 0)
+ return (ISC_R_NOSPACE);
+ /* downcase */
+ if (c >= 0x41 && c <= 0x5A)
+ c += 0x20;
+ CONVERTFROMASCII(c);
+ *tdata++ = c;
+ ndata++;
+ trem--;
+ nlen--;
+ } else {
+ if (trem < 3)
+ return (ISC_R_NOSPACE);
+ sprintf(tdata, "%%%02X", c);
+ tdata += 3;
+ trem -= 3;
+ ndata++;
+ nlen--;
+ }
+ count--;
+ }
+ } else {
+ FATAL_ERROR(__FILE__, __LINE__,
+ "Unexpected label type %02x", count);
+ /* NOTREACHED */
+ }
+
+ /*
+ * The following assumes names are absolute. If not, we
+ * fix things up later. Note that this means that in some
+ * cases one more byte of text buffer is required than is
+ * needed in the final output.
+ */
+ if (trem == 0)
+ return (ISC_R_NOSPACE);
+ *tdata++ = '.';
+ trem--;
+ }
+
+ if (nlen != 0 && trem == 0)
+ return (ISC_R_NOSPACE);
+
+ if (omit_final_dot)
+ trem++;
+
+ isc_buffer_add(target, tlen - trem);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_name_downcase(dns_name_t *source, dns_name_t *name, isc_buffer_t *target) {
+ unsigned char *sndata, *ndata;
+ unsigned int nlen, count, labels;
+ isc_buffer_t buffer;
+
+ /*
+ * Downcase 'source'.
+ */
+
+ REQUIRE(VALID_NAME(source));
+ REQUIRE(VALID_NAME(name));
+ if (source == name) {
+ REQUIRE((name->attributes & DNS_NAMEATTR_READONLY) == 0);
+ isc_buffer_init(&buffer, source->ndata, source->length);
+ target = &buffer;
+ ndata = source->ndata;
+ } else {
+ REQUIRE(BINDABLE(name));
+ REQUIRE((target != NULL && ISC_BUFFER_VALID(target)) ||
+ (target == NULL && ISC_BUFFER_VALID(name->buffer)));
+ if (target == NULL) {
+ target = name->buffer;
+ isc_buffer_clear(name->buffer);
+ }
+ ndata = (unsigned char *)target->base + target->used;
+ name->ndata = ndata;
+ }
+
+ sndata = source->ndata;
+ nlen = source->length;
+ labels = source->labels;
+
+ if (nlen > (target->length - target->used)) {
+ MAKE_EMPTY(name);
+ return (ISC_R_NOSPACE);
+ }
+
+ while (labels > 0 && nlen > 0) {
+ labels--;
+ count = *sndata++;
+ *ndata++ = count;
+ nlen--;
+ if (count < 64) {
+ INSIST(nlen >= count);
+ while (count > 0) {
+ *ndata++ = maptolower[(*sndata++)];
+ nlen--;
+ count--;
+ }
+ } else {
+ FATAL_ERROR(__FILE__, __LINE__,
+ "Unexpected label type %02x", count);
+ /* Does not return. */
+ }
+ }
+
+ if (source != name) {
+ name->labels = source->labels;
+ name->length = source->length;
+ if ((source->attributes & DNS_NAMEATTR_ABSOLUTE) != 0)
+ name->attributes = DNS_NAMEATTR_ABSOLUTE;
+ else
+ name->attributes = 0;
+ if (name->labels > 0 && name->offsets != NULL)
+ set_offsets(name, name->offsets, NULL);
+ }
+
+ isc_buffer_add(target, name->length);
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+set_offsets(const dns_name_t *name, unsigned char *offsets,
+ dns_name_t *set_name)
+{
+ unsigned int offset, count, length, nlabels;
+ unsigned char *ndata;
+ isc_boolean_t absolute;
+
+ ndata = name->ndata;
+ length = name->length;
+ offset = 0;
+ nlabels = 0;
+ absolute = ISC_FALSE;
+ while (offset != length) {
+ INSIST(nlabels < 128);
+ offsets[nlabels++] = offset;
+ count = *ndata++;
+ offset++;
+ INSIST(count <= 63);
+ offset += count;
+ ndata += count;
+ INSIST(offset <= length);
+ if (count == 0) {
+ absolute = ISC_TRUE;
+ break;
+ }
+ }
+ if (set_name != NULL) {
+ INSIST(set_name == name);
+
+ set_name->labels = nlabels;
+ set_name->length = offset;
+ if (absolute)
+ set_name->attributes |= DNS_NAMEATTR_ABSOLUTE;
+ else
+ set_name->attributes &= ~DNS_NAMEATTR_ABSOLUTE;
+ }
+ INSIST(nlabels == name->labels);
+ INSIST(offset == name->length);
+}
+
+isc_result_t
+dns_name_fromwire(dns_name_t *name, isc_buffer_t *source,
+ dns_decompress_t *dctx, unsigned int options,
+ isc_buffer_t *target)
+{
+ unsigned char *cdata, *ndata;
+ unsigned int cused; /* Bytes of compressed name data used */
+ unsigned int nused, labels, n, nmax;
+ unsigned int current, new_current, biggest_pointer;
+ isc_boolean_t done;
+ fw_state state = fw_start;
+ unsigned int c;
+ unsigned char *offsets;
+ dns_offsets_t odata;
+ isc_boolean_t downcase;
+ isc_boolean_t seen_pointer;
+
+ /*
+ * Copy the possibly-compressed name at source into target,
+ * decompressing it. Loop prevention is performed by checking
+ * the new pointer against biggest_pointer.
+ */
+
+ REQUIRE(VALID_NAME(name));
+ REQUIRE((target != NULL && ISC_BUFFER_VALID(target)) ||
+ (target == NULL && ISC_BUFFER_VALID(name->buffer)));
+
+ downcase = ISC_TF((options & DNS_NAME_DOWNCASE) != 0);
+
+ if (target == NULL && name->buffer != NULL) {
+ target = name->buffer;
+ isc_buffer_clear(target);
+ }
+
+ REQUIRE(dctx != NULL);
+ REQUIRE(BINDABLE(name));
+
+ INIT_OFFSETS(name, offsets, odata);
+
+ /*
+ * Make 'name' empty in case of failure.
+ */
+ MAKE_EMPTY(name);
+
+ /*
+ * Initialize things to make the compiler happy; they're not required.
+ */
+ n = 0;
+ new_current = 0;
+
+ /*
+ * Set up.
+ */
+ labels = 0;
+ done = ISC_FALSE;
+
+ ndata = isc_buffer_used(target);
+ nused = 0;
+ seen_pointer = ISC_FALSE;
+
+ /*
+ * Find the maximum number of uncompressed target name
+ * bytes we are willing to generate. This is the smaller
+ * of the available target buffer length and the
+ * maximum legal domain name length (255).
+ */
+ nmax = isc_buffer_availablelength(target);
+ if (nmax > DNS_NAME_MAXWIRE)
+ nmax = DNS_NAME_MAXWIRE;
+
+ cdata = isc_buffer_current(source);
+ cused = 0;
+
+ current = source->current;
+ biggest_pointer = current;
+
+ /*
+ * Note: The following code is not optimized for speed, but
+ * rather for correctness. Speed will be addressed in the future.
+ */
+
+ while (current < source->active && !done) {
+ c = *cdata++;
+ current++;
+ if (!seen_pointer)
+ cused++;
+
+ switch (state) {
+ case fw_start:
+ if (c < 64) {
+ offsets[labels] = nused;
+ labels++;
+ if (nused + c + 1 > nmax)
+ goto full;
+ nused += c + 1;
+ *ndata++ = c;
+ if (c == 0)
+ done = ISC_TRUE;
+ n = c;
+ state = fw_ordinary;
+ } else if (c >= 128 && c < 192) {
+ /*
+ * 14 bit local compression pointer.
+ * Local compression is no longer an
+ * IETF draft.
+ */
+ return (DNS_R_BADLABELTYPE);
+ } else if (c >= 192) {
+ /*
+ * Ordinary 14-bit pointer.
+ */
+ if ((dctx->allowed & DNS_COMPRESS_GLOBAL14) ==
+ 0)
+ return (DNS_R_DISALLOWED);
+ new_current = c & 0x3F;
+ n = 1;
+ state = fw_newcurrent;
+ } else
+ return (DNS_R_BADLABELTYPE);
+ break;
+ case fw_ordinary:
+ if (downcase)
+ c = maptolower[c];
+ /* FALLTHROUGH */
+ case fw_copy:
+ *ndata++ = c;
+ n--;
+ if (n == 0)
+ state = fw_start;
+ break;
+ case fw_newcurrent:
+ new_current *= 256;
+ new_current += c;
+ n--;
+ if (n != 0)
+ break;
+ if (new_current >= biggest_pointer)
+ return (DNS_R_BADPOINTER);
+ biggest_pointer = new_current;
+ current = new_current;
+ cdata = (unsigned char *)source->base + current;
+ seen_pointer = ISC_TRUE;
+ state = fw_start;
+ break;
+ default:
+ FATAL_ERROR(__FILE__, __LINE__,
+ "Unknown state %d", state);
+ /* Does not return. */
+ }
+ }
+
+ if (!done)
+ return (ISC_R_UNEXPECTEDEND);
+
+ name->ndata = (unsigned char *)target->base + target->used;
+ name->labels = labels;
+ name->length = nused;
+ name->attributes |= DNS_NAMEATTR_ABSOLUTE;
+
+ isc_buffer_forward(source, cused);
+ isc_buffer_add(target, name->length);
+
+ return (ISC_R_SUCCESS);
+
+ full:
+ if (nmax == DNS_NAME_MAXWIRE)
+ /*
+ * The name did not fit even though we had a buffer
+ * big enough to fit a maximum-length name.
+ */
+ return (DNS_R_NAMETOOLONG);
+ else
+ /*
+ * The name might fit if only the caller could give us a
+ * big enough buffer.
+ */
+ return (ISC_R_NOSPACE);
+}
+
+isc_result_t
+dns_name_towire(const dns_name_t *name, dns_compress_t *cctx,
+ isc_buffer_t *target)
+{
+ unsigned int methods;
+ isc_uint16_t offset;
+ dns_name_t gp; /* Global compression prefix */
+ isc_boolean_t gf; /* Global compression target found */
+ isc_uint16_t go; /* Global compression offset */
+ dns_offsets_t clo;
+ dns_name_t clname;
+
+ /*
+ * Convert 'name' into wire format, compressing it as specified by the
+ * compression context 'cctx', and storing the result in 'target'.
+ */
+
+ REQUIRE(VALID_NAME(name));
+ REQUIRE(cctx != NULL);
+ REQUIRE(ISC_BUFFER_VALID(target));
+
+ /*
+ * If 'name' doesn't have an offsets table, make a clone which
+ * has one.
+ */
+ if (name->offsets == NULL) {
+ DNS_NAME_INIT(&clname, clo);
+ dns_name_clone(name, &clname);
+ name = &clname;
+ }
+ DNS_NAME_INIT(&gp, NULL);
+
+ offset = target->used; /*XXX*/
+
+ methods = dns_compress_getmethods(cctx);
+
+ if ((name->attributes & DNS_NAMEATTR_NOCOMPRESS) == 0 &&
+ (methods & DNS_COMPRESS_GLOBAL14) != 0)
+ gf = dns_compress_findglobal(cctx, name, &gp, &go);
+ else
+ gf = ISC_FALSE;
+
+ /*
+ * If the offset is too high for 14 bit global compression, we're
+ * out of luck.
+ */
+ if (gf && go >= 0x4000)
+ gf = ISC_FALSE;
+
+ /*
+ * Will the compression pointer reduce the message size?
+ */
+ if (gf && (gp.length + 2) >= name->length)
+ gf = ISC_FALSE;
+
+ if (gf) {
+ if (target->length - target->used < gp.length)
+ return (ISC_R_NOSPACE);
+ (void)memcpy((unsigned char *)target->base + target->used,
+ gp.ndata, (size_t)gp.length);
+ isc_buffer_add(target, gp.length);
+ go |= 0xc000;
+ if (target->length - target->used < 2)
+ return (ISC_R_NOSPACE);
+ isc_buffer_putuint16(target, go);
+ if (gp.length != 0)
+ dns_compress_add(cctx, name, &gp, offset);
+ } else {
+ if (target->length - target->used < name->length)
+ return (ISC_R_NOSPACE);
+ (void)memcpy((unsigned char *)target->base + target->used,
+ name->ndata, (size_t)name->length);
+ isc_buffer_add(target, name->length);
+ dns_compress_add(cctx, name, name, offset);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_name_concatenate(dns_name_t *prefix, dns_name_t *suffix, dns_name_t *name,
+ isc_buffer_t *target)
+{
+ unsigned char *ndata, *offsets;
+ unsigned int nrem, labels, prefix_length, length;
+ isc_boolean_t copy_prefix = ISC_TRUE;
+ isc_boolean_t copy_suffix = ISC_TRUE;
+ isc_boolean_t absolute = ISC_FALSE;
+ dns_name_t tmp_name;
+ dns_offsets_t odata;
+
+ /*
+ * Concatenate 'prefix' and 'suffix'.
+ */
+
+ REQUIRE(prefix == NULL || VALID_NAME(prefix));
+ REQUIRE(suffix == NULL || VALID_NAME(suffix));
+ REQUIRE(name == NULL || VALID_NAME(name));
+ REQUIRE((target != NULL && ISC_BUFFER_VALID(target)) ||
+ (target == NULL && name != NULL && ISC_BUFFER_VALID(name->buffer)));
+ if (prefix == NULL || prefix->labels == 0)
+ copy_prefix = ISC_FALSE;
+ if (suffix == NULL || suffix->labels == 0)
+ copy_suffix = ISC_FALSE;
+ if (copy_prefix &&
+ (prefix->attributes & DNS_NAMEATTR_ABSOLUTE) != 0) {
+ absolute = ISC_TRUE;
+ REQUIRE(!copy_suffix);
+ }
+ if (name == NULL) {
+ DNS_NAME_INIT(&tmp_name, odata);
+ name = &tmp_name;
+ }
+ if (target == NULL) {
+ INSIST(name->buffer != NULL);
+ target = name->buffer;
+ isc_buffer_clear(name->buffer);
+ }
+
+ REQUIRE(BINDABLE(name));
+
+ /*
+ * Set up.
+ */
+ nrem = target->length - target->used;
+ ndata = (unsigned char *)target->base + target->used;
+ if (nrem > DNS_NAME_MAXWIRE)
+ nrem = DNS_NAME_MAXWIRE;
+ length = 0;
+ prefix_length = 0;
+ labels = 0;
+ if (copy_prefix) {
+ prefix_length = prefix->length;
+ length += prefix_length;
+ labels += prefix->labels;
+ }
+ if (copy_suffix) {
+ length += suffix->length;
+ labels += suffix->labels;
+ }
+ if (length > DNS_NAME_MAXWIRE) {
+ MAKE_EMPTY(name);
+ return (DNS_R_NAMETOOLONG);
+ }
+ if (length > nrem) {
+ MAKE_EMPTY(name);
+ return (ISC_R_NOSPACE);
+ }
+
+ if (copy_suffix) {
+ if ((suffix->attributes & DNS_NAMEATTR_ABSOLUTE) != 0)
+ absolute = ISC_TRUE;
+ if (suffix == name && suffix->buffer == target)
+ memmove(ndata + prefix_length, suffix->ndata,
+ suffix->length);
+ else
+ memcpy(ndata + prefix_length, suffix->ndata,
+ suffix->length);
+ }
+
+ /*
+ * If 'prefix' and 'name' are the same object, and the object has
+ * a dedicated buffer, and we're using it, then we don't have to
+ * copy anything.
+ */
+ if (copy_prefix && (prefix != name || prefix->buffer != target))
+ memcpy(ndata, prefix->ndata, prefix_length);
+
+ name->ndata = ndata;
+ name->labels = labels;
+ name->length = length;
+ if (absolute)
+ name->attributes = DNS_NAMEATTR_ABSOLUTE;
+ else
+ name->attributes = 0;
+
+ if (name->labels > 0 && name->offsets != NULL) {
+ INIT_OFFSETS(name, offsets, odata);
+ set_offsets(name, offsets, NULL);
+ }
+
+ isc_buffer_add(target, name->length);
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_name_split(dns_name_t *name, unsigned int suffixlabels,
+ dns_name_t *prefix, dns_name_t *suffix)
+
+{
+ unsigned int splitlabel;
+
+ REQUIRE(VALID_NAME(name));
+ REQUIRE(suffixlabels > 0);
+ REQUIRE(suffixlabels < name->labels);
+ REQUIRE(prefix != NULL || suffix != NULL);
+ REQUIRE(prefix == NULL ||
+ (VALID_NAME(prefix) &&
+ prefix->buffer != NULL &&
+ BINDABLE(prefix)));
+ REQUIRE(suffix == NULL ||
+ (VALID_NAME(suffix) &&
+ suffix->buffer != NULL &&
+ BINDABLE(suffix)));
+
+ splitlabel = name->labels - suffixlabels;
+
+ if (prefix != NULL)
+ dns_name_getlabelsequence(name, 0, splitlabel, prefix);
+
+ if (suffix != NULL)
+ dns_name_getlabelsequence(name, splitlabel,
+ suffixlabels, suffix);
+
+ return;
+}
+
+isc_result_t
+dns_name_dup(const dns_name_t *source, isc_mem_t *mctx,
+ dns_name_t *target)
+{
+ /*
+ * Make 'target' a dynamically allocated copy of 'source'.
+ */
+
+ REQUIRE(VALID_NAME(source));
+ REQUIRE(source->length > 0);
+ REQUIRE(VALID_NAME(target));
+ REQUIRE(BINDABLE(target));
+
+ /*
+ * Make 'target' empty in case of failure.
+ */
+ MAKE_EMPTY(target);
+
+ target->ndata = isc_mem_get(mctx, source->length);
+ if (target->ndata == NULL)
+ return (ISC_R_NOMEMORY);
+
+ memcpy(target->ndata, source->ndata, source->length);
+
+ target->length = source->length;
+ target->labels = source->labels;
+ target->attributes = DNS_NAMEATTR_DYNAMIC;
+ if ((source->attributes & DNS_NAMEATTR_ABSOLUTE) != 0)
+ target->attributes |= DNS_NAMEATTR_ABSOLUTE;
+ if (target->offsets != NULL) {
+ if (source->offsets != NULL)
+ memcpy(target->offsets, source->offsets,
+ source->labels);
+ else
+ set_offsets(target, target->offsets, NULL);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_name_dupwithoffsets(dns_name_t *source, isc_mem_t *mctx,
+ dns_name_t *target)
+{
+ /*
+ * Make 'target' a read-only dynamically allocated copy of 'source'.
+ * 'target' will also have a dynamically allocated offsets table.
+ */
+
+ REQUIRE(VALID_NAME(source));
+ REQUIRE(source->length > 0);
+ REQUIRE(VALID_NAME(target));
+ REQUIRE(BINDABLE(target));
+ REQUIRE(target->offsets == NULL);
+
+ /*
+ * Make 'target' empty in case of failure.
+ */
+ MAKE_EMPTY(target);
+
+ target->ndata = isc_mem_get(mctx, source->length + source->labels);
+ if (target->ndata == NULL)
+ return (ISC_R_NOMEMORY);
+
+ memcpy(target->ndata, source->ndata, source->length);
+
+ target->length = source->length;
+ target->labels = source->labels;
+ target->attributes = DNS_NAMEATTR_DYNAMIC | DNS_NAMEATTR_DYNOFFSETS |
+ DNS_NAMEATTR_READONLY;
+ if ((source->attributes & DNS_NAMEATTR_ABSOLUTE) != 0)
+ target->attributes |= DNS_NAMEATTR_ABSOLUTE;
+ target->offsets = target->ndata + source->length;
+ if (source->offsets != NULL)
+ memcpy(target->offsets, source->offsets, source->labels);
+ else
+ set_offsets(target, target->offsets, NULL);
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_name_free(dns_name_t *name, isc_mem_t *mctx) {
+ size_t size;
+
+ /*
+ * Free 'name'.
+ */
+
+ REQUIRE(VALID_NAME(name));
+ REQUIRE((name->attributes & DNS_NAMEATTR_DYNAMIC) != 0);
+
+ size = name->length;
+ if ((name->attributes & DNS_NAMEATTR_DYNOFFSETS) != 0)
+ size += name->labels;
+ isc_mem_put(mctx, name->ndata, size);
+ dns_name_invalidate(name);
+}
+
+isc_result_t
+dns_name_digest(dns_name_t *name, dns_digestfunc_t digest, void *arg) {
+ dns_name_t downname;
+ unsigned char data[256];
+ isc_buffer_t buffer;
+ isc_result_t result;
+ isc_region_t r;
+
+ /*
+ * Send 'name' in DNSSEC canonical form to 'digest'.
+ */
+
+ REQUIRE(VALID_NAME(name));
+ REQUIRE(digest != NULL);
+
+ DNS_NAME_INIT(&downname, NULL);
+ isc_buffer_init(&buffer, data, sizeof(data));
+
+ result = dns_name_downcase(name, &downname, &buffer);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ isc_buffer_usedregion(&buffer, &r);
+
+ return ((digest)(arg, &r));
+}
+
+isc_boolean_t
+dns_name_dynamic(dns_name_t *name) {
+ REQUIRE(VALID_NAME(name));
+
+ /*
+ * Returns whether there is dynamic memory associated with this name.
+ */
+
+ return ((name->attributes & DNS_NAMEATTR_DYNAMIC) != 0 ?
+ ISC_TRUE : ISC_FALSE);
+}
+
+isc_result_t
+dns_name_print(dns_name_t *name, FILE *stream) {
+ isc_result_t result;
+ isc_buffer_t b;
+ isc_region_t r;
+ char t[1024];
+
+ /*
+ * Print 'name' on 'stream'.
+ */
+
+ REQUIRE(VALID_NAME(name));
+
+ isc_buffer_init(&b, t, sizeof(t));
+ result = dns_name_totext(name, ISC_FALSE, &b);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ isc_buffer_usedregion(&b, &r);
+ fprintf(stream, "%.*s", (int)r.length, (char *)r.base);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_name_settotextfilter(dns_name_totextfilter_t proc) {
+#ifdef ISC_PLATFORM_USETHREADS
+ isc_result_t result;
+ dns_name_totextfilter_t *mem;
+ int res;
+
+ result = totext_filter_proc_key_init();
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * If we already have been here set / clear as appropriate.
+ * Otherwise allocate memory.
+ */
+ mem = isc_thread_key_getspecific(totext_filter_proc_key);
+ if (mem != NULL && proc != NULL) {
+ *mem = proc;
+ return (ISC_R_SUCCESS);
+ }
+ if (proc == NULL) {
+ isc_mem_put(thread_key_mctx, mem, sizeof(*mem));
+ res = isc_thread_key_setspecific(totext_filter_proc_key, NULL);
+ if (res != 0)
+ result = ISC_R_UNEXPECTED;
+ return (result);
+ }
+
+ mem = isc_mem_get(thread_key_mctx, sizeof(*mem));
+ if (mem == NULL)
+ return (ISC_R_NOMEMORY);
+ *mem = proc;
+ if (isc_thread_key_setspecific(totext_filter_proc_key, mem) != 0) {
+ isc_mem_put(thread_key_mctx, mem, sizeof(*mem));
+ result = ISC_R_UNEXPECTED;
+ }
+ return (result);
+#else
+ totext_filter_proc = proc;
+ return (ISC_R_SUCCESS);
+#endif
+}
+
+void
+dns_name_format(dns_name_t *name, char *cp, unsigned int size) {
+ isc_result_t result;
+ isc_buffer_t buf;
+
+ REQUIRE(size > 0);
+
+ /*
+ * Leave room for null termination after buffer.
+ */
+ isc_buffer_init(&buf, cp, size - 1);
+ result = dns_name_totext(name, ISC_TRUE, &buf);
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * Null terminate.
+ */
+ isc_region_t r;
+ isc_buffer_usedregion(&buf, &r);
+ ((char *) r.base)[r.length] = '\0';
+
+ } else
+ snprintf(cp, size, "<unknown>");
+}
+
+isc_result_t
+dns_name_copy(dns_name_t *source, dns_name_t *dest, isc_buffer_t *target) {
+ unsigned char *ndata;
+
+ /*
+ * Make dest a copy of source.
+ */
+
+ REQUIRE(VALID_NAME(source));
+ REQUIRE(VALID_NAME(dest));
+ REQUIRE(target != NULL || dest->buffer != NULL);
+
+ if (target == NULL) {
+ target = dest->buffer;
+ isc_buffer_clear(dest->buffer);
+ }
+
+ REQUIRE(BINDABLE(dest));
+
+ /*
+ * Set up.
+ */
+ if (target->length - target->used < source->length)
+ return (ISC_R_NOSPACE);
+
+ ndata = (unsigned char *)target->base + target->used;
+ dest->ndata = target->base;
+
+ memcpy(ndata, source->ndata, source->length);
+
+ dest->ndata = ndata;
+ dest->labels = source->labels;
+ dest->length = source->length;
+ if ((source->attributes & DNS_NAMEATTR_ABSOLUTE) != 0)
+ dest->attributes = DNS_NAMEATTR_ABSOLUTE;
+ else
+ dest->attributes = 0;
+
+ if (dest->labels > 0 && dest->offsets != NULL) {
+ if (source->offsets != NULL)
+ memcpy(dest->offsets, source->offsets, source->labels);
+ else
+ set_offsets(dest, dest->offsets, NULL);
+ }
+
+ isc_buffer_add(target, dest->length);
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_name_destroy(void) {
+#ifdef ISC_PLATFORM_USETHREADS
+ RUNTIME_CHECK(isc_once_do(&once, thread_key_mutex_init)
+ == ISC_R_SUCCESS);
+
+ LOCK(&thread_key_mutex);
+ if (thread_key_initialized) {
+ isc_mem_detach(&thread_key_mctx);
+ isc_thread_key_delete(totext_filter_proc_key);
+ thread_key_initialized = 0;
+ }
+ UNLOCK(&thread_key_mutex);
+
+#endif
+}
diff --git a/lib/dns/ncache.c b/lib/dns/ncache.c
new file mode 100644
index 0000000..af0450b
--- /dev/null
+++ b/lib/dns/ncache.c
@@ -0,0 +1,658 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: ncache.c,v 1.43 2008/09/25 04:02:38 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/buffer.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/message.h>
+#include <dns/ncache.h>
+#include <dns/rdata.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatastruct.h>
+
+#define DNS_NCACHE_RDATA 20U
+
+/*
+ * The format of an ncache rdata is a sequence of one or more records of
+ * the following format:
+ *
+ * owner name
+ * type
+ * rdata count
+ * rdata length These two occur 'rdata count'
+ * rdata times.
+ *
+ */
+
+static inline isc_result_t
+copy_rdataset(dns_rdataset_t *rdataset, isc_buffer_t *buffer) {
+ isc_result_t result;
+ unsigned int count;
+ isc_region_t ar, r;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ /*
+ * Copy the rdataset count to the buffer.
+ */
+ isc_buffer_availableregion(buffer, &ar);
+ if (ar.length < 2)
+ return (ISC_R_NOSPACE);
+ count = dns_rdataset_count(rdataset);
+ INSIST(count <= 65535);
+ isc_buffer_putuint16(buffer, (isc_uint16_t)count);
+
+ result = dns_rdataset_first(rdataset);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdataset_current(rdataset, &rdata);
+ dns_rdata_toregion(&rdata, &r);
+ INSIST(r.length <= 65535);
+ isc_buffer_availableregion(buffer, &ar);
+ if (ar.length < 2)
+ return (ISC_R_NOSPACE);
+ /*
+ * Copy the rdata length to the buffer.
+ */
+ isc_buffer_putuint16(buffer, (isc_uint16_t)r.length);
+ /*
+ * Copy the rdata to the buffer.
+ */
+ result = isc_buffer_copyregion(buffer, &r);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ dns_rdata_reset(&rdata);
+ result = dns_rdataset_next(rdataset);
+ }
+ if (result != ISC_R_NOMORE)
+ return (result);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_ncache_add(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
+ dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t maxttl,
+ dns_rdataset_t *addedrdataset)
+{
+ return (dns_ncache_addoptout(message, cache, node, covers, now, maxttl,
+ ISC_FALSE, addedrdataset));
+}
+
+isc_result_t
+dns_ncache_addoptout(dns_message_t *message, dns_db_t *cache,
+ dns_dbnode_t *node, dns_rdatatype_t covers,
+ isc_stdtime_t now, dns_ttl_t maxttl,
+ isc_boolean_t optout, dns_rdataset_t *addedrdataset)
+{
+ isc_result_t result;
+ isc_buffer_t buffer;
+ isc_region_t r;
+ dns_rdataset_t *rdataset;
+ dns_rdatatype_t type;
+ dns_name_t *name;
+ dns_ttl_t ttl;
+ dns_trust_t trust;
+ dns_rdata_t rdata[DNS_NCACHE_RDATA];
+ dns_rdataset_t ncrdataset;
+ dns_rdatalist_t ncrdatalist;
+ unsigned char data[4096];
+ unsigned int next = 0;
+
+ /*
+ * Convert the authority data from 'message' into a negative cache
+ * rdataset, and store it in 'cache' at 'node'.
+ */
+
+ REQUIRE(message != NULL);
+
+ /*
+ * We assume that all data in the authority section has been
+ * validated by the caller.
+ */
+
+ /*
+ * Initialize the list.
+ */
+ ncrdatalist.rdclass = dns_db_class(cache);
+ ncrdatalist.type = 0;
+ ncrdatalist.covers = covers;
+ ncrdatalist.ttl = maxttl;
+ ISC_LIST_INIT(ncrdatalist.rdata);
+ ISC_LINK_INIT(&ncrdatalist, link);
+
+ /*
+ * Build an ncache rdatas into buffer.
+ */
+ ttl = maxttl;
+ trust = 0xffff;
+ isc_buffer_init(&buffer, data, sizeof(data));
+ if (message->counts[DNS_SECTION_AUTHORITY])
+ result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
+ else
+ result = ISC_R_NOMORE;
+ while (result == ISC_R_SUCCESS) {
+ name = NULL;
+ dns_message_currentname(message, DNS_SECTION_AUTHORITY,
+ &name);
+ if ((name->attributes & DNS_NAMEATTR_NCACHE) != 0) {
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link)) {
+ if ((rdataset->attributes &
+ DNS_RDATASETATTR_NCACHE) == 0)
+ continue;
+ type = rdataset->type;
+ if (type == dns_rdatatype_rrsig)
+ type = rdataset->covers;
+ if (type == dns_rdatatype_soa ||
+ type == dns_rdatatype_nsec ||
+ type == dns_rdatatype_nsec3) {
+ if (ttl > rdataset->ttl)
+ ttl = rdataset->ttl;
+ if (trust > rdataset->trust)
+ trust = rdataset->trust;
+ /*
+ * Copy the owner name to the buffer.
+ */
+ dns_name_toregion(name, &r);
+ result = isc_buffer_copyregion(&buffer,
+ &r);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ /*
+ * Copy the type to the buffer.
+ */
+ isc_buffer_availableregion(&buffer,
+ &r);
+ if (r.length < 2)
+ return (ISC_R_NOSPACE);
+ isc_buffer_putuint16(&buffer,
+ rdataset->type);
+ /*
+ * Copy the rdataset into the buffer.
+ */
+ result = copy_rdataset(rdataset,
+ &buffer);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (next >= DNS_NCACHE_RDATA)
+ return (ISC_R_NOSPACE);
+ dns_rdata_init(&rdata[next]);
+ isc_buffer_remainingregion(&buffer, &r);
+ rdata[next].data = r.base;
+ rdata[next].length = r.length;
+ rdata[next].rdclass =
+ ncrdatalist.rdclass;
+ rdata[next].type = 0;
+ rdata[next].flags = 0;
+ ISC_LIST_APPEND(ncrdatalist.rdata,
+ &rdata[next], link);
+ isc_buffer_forward(&buffer, r.length);
+ next++;
+ }
+ }
+ }
+ result = dns_message_nextname(message, DNS_SECTION_AUTHORITY);
+ }
+ if (result != ISC_R_NOMORE)
+ return (result);
+
+ if (trust == 0xffff) {
+ /*
+ * We didn't find any authority data from which to create a
+ * negative cache rdataset. In particular, we have no SOA.
+ *
+ * We trust that the caller wants negative caching, so this
+ * means we have a "type 3 nxdomain" or "type 3 nodata"
+ * response (see RFC2308 for details).
+ *
+ * We will now build a suitable negative cache rdataset that
+ * will cause zero bytes to be emitted when converted to
+ * wire format.
+ */
+
+ /*
+ * The ownername must exist, but it doesn't matter what value
+ * it has. We use the root name.
+ */
+ dns_name_toregion(dns_rootname, &r);
+ result = isc_buffer_copyregion(&buffer, &r);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ /*
+ * Copy the type and a zero rdata count to the buffer.
+ */
+ isc_buffer_availableregion(&buffer, &r);
+ if (r.length < 4)
+ return (ISC_R_NOSPACE);
+ isc_buffer_putuint16(&buffer, 0);
+ isc_buffer_putuint16(&buffer, 0);
+ /*
+ * RFC2308, section 5, says that negative answers without
+ * SOAs should not be cached.
+ */
+ ttl = 0;
+ /*
+ * Set trust.
+ */
+ if ((message->flags & DNS_MESSAGEFLAG_AA) != 0 &&
+ message->counts[DNS_SECTION_ANSWER] == 0) {
+ /*
+ * The response has aa set and we haven't followed
+ * any CNAME or DNAME chains.
+ */
+ trust = dns_trust_authauthority;
+ } else
+ trust = dns_trust_additional;
+ /*
+ * Now add it to the cache.
+ */
+ if (next >= DNS_NCACHE_RDATA)
+ return (ISC_R_NOSPACE);
+ dns_rdata_init(&rdata[next]);
+ isc_buffer_remainingregion(&buffer, &r);
+ rdata[next].data = r.base;
+ rdata[next].length = r.length;
+ rdata[next].rdclass = ncrdatalist.rdclass;
+ rdata[next].type = 0;
+ rdata[next].flags = 0;
+ ISC_LIST_APPEND(ncrdatalist.rdata, &rdata[next], link);
+ }
+
+ INSIST(trust != 0xffff);
+
+ ncrdatalist.ttl = ttl;
+
+ dns_rdataset_init(&ncrdataset);
+ RUNTIME_CHECK(dns_rdatalist_tordataset(&ncrdatalist, &ncrdataset)
+ == ISC_R_SUCCESS);
+ ncrdataset.trust = trust;
+ if (message->rcode == dns_rcode_nxdomain)
+ ncrdataset.attributes |= DNS_RDATASETATTR_NXDOMAIN;
+ if (optout)
+ ncrdataset.attributes |= DNS_RDATASETATTR_OPTOUT;
+
+ return (dns_db_addrdataset(cache, node, NULL, now, &ncrdataset,
+ 0, addedrdataset));
+}
+
+isc_result_t
+dns_ncache_towire(dns_rdataset_t *rdataset, dns_compress_t *cctx,
+ isc_buffer_t *target, unsigned int options,
+ unsigned int *countp)
+{
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_result_t result;
+ isc_region_t remaining, tavailable;
+ isc_buffer_t source, savedbuffer, rdlen;
+ dns_name_t name;
+ dns_rdatatype_t type;
+ unsigned int i, rcount, count;
+
+ /*
+ * Convert the negative caching rdataset 'rdataset' to wire format,
+ * compressing names as specified in 'cctx', and storing the result in
+ * 'target'.
+ */
+
+ REQUIRE(rdataset != NULL);
+ REQUIRE(rdataset->type == 0);
+
+ savedbuffer = *target;
+ count = 0;
+
+ result = dns_rdataset_first(rdataset);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdataset_current(rdataset, &rdata);
+ isc_buffer_init(&source, rdata.data, rdata.length);
+ isc_buffer_add(&source, rdata.length);
+ dns_name_init(&name, NULL);
+ isc_buffer_remainingregion(&source, &remaining);
+ dns_name_fromregion(&name, &remaining);
+ INSIST(remaining.length >= name.length);
+ isc_buffer_forward(&source, name.length);
+ remaining.length -= name.length;
+
+ INSIST(remaining.length >= 4);
+ type = isc_buffer_getuint16(&source);
+ rcount = isc_buffer_getuint16(&source);
+
+ for (i = 0; i < rcount; i++) {
+ /*
+ * Get the length of this rdata and set up an
+ * rdata structure for it.
+ */
+ isc_buffer_remainingregion(&source, &remaining);
+ INSIST(remaining.length >= 2);
+ dns_rdata_reset(&rdata);
+ rdata.length = isc_buffer_getuint16(&source);
+ isc_buffer_remainingregion(&source, &remaining);
+ rdata.data = remaining.base;
+ rdata.type = type;
+ rdata.rdclass = rdataset->rdclass;
+ INSIST(remaining.length >= rdata.length);
+ isc_buffer_forward(&source, rdata.length);
+
+ if ((options & DNS_NCACHETOWIRE_OMITDNSSEC) != 0 &&
+ dns_rdatatype_isdnssec(type))
+ continue;
+
+ /*
+ * Write the name.
+ */
+ dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14);
+ result = dns_name_towire(&name, cctx, target);
+ if (result != ISC_R_SUCCESS)
+ goto rollback;
+
+ /*
+ * See if we have space for type, class, ttl, and
+ * rdata length. Write the type, class, and ttl.
+ */
+ isc_buffer_availableregion(target, &tavailable);
+ if (tavailable.length < 10) {
+ result = ISC_R_NOSPACE;
+ goto rollback;
+ }
+ isc_buffer_putuint16(target, type);
+ isc_buffer_putuint16(target, rdataset->rdclass);
+ isc_buffer_putuint32(target, rdataset->ttl);
+
+ /*
+ * Save space for rdata length.
+ */
+ rdlen = *target;
+ isc_buffer_add(target, 2);
+
+ /*
+ * Write the rdata.
+ */
+ result = dns_rdata_towire(&rdata, cctx, target);
+ if (result != ISC_R_SUCCESS)
+ goto rollback;
+
+ /*
+ * Set the rdata length field to the compressed
+ * length.
+ */
+ INSIST((target->used >= rdlen.used + 2) &&
+ (target->used - rdlen.used - 2 < 65536));
+ isc_buffer_putuint16(&rdlen,
+ (isc_uint16_t)(target->used -
+ rdlen.used - 2));
+
+ count++;
+ }
+ INSIST(isc_buffer_remaininglength(&source) == 0);
+ result = dns_rdataset_next(rdataset);
+ dns_rdata_reset(&rdata);
+ }
+ if (result != ISC_R_NOMORE)
+ goto rollback;
+
+ *countp = count;
+
+ return (ISC_R_SUCCESS);
+
+ rollback:
+ INSIST(savedbuffer.used < 65536);
+ dns_compress_rollback(cctx, (isc_uint16_t)savedbuffer.used);
+ *countp = 0;
+ *target = savedbuffer;
+
+ return (result);
+}
+
+static void
+rdataset_disassociate(dns_rdataset_t *rdataset) {
+ UNUSED(rdataset);
+}
+
+static isc_result_t
+rdataset_first(dns_rdataset_t *rdataset) {
+ unsigned char *raw = rdataset->private3;
+ unsigned int count;
+
+ count = raw[0] * 256 + raw[1];
+ if (count == 0) {
+ rdataset->private5 = NULL;
+ return (ISC_R_NOMORE);
+ }
+ raw += 2;
+ /*
+ * The privateuint4 field is the number of rdata beyond the cursor
+ * position, so we decrement the total count by one before storing
+ * it.
+ */
+ count--;
+ rdataset->privateuint4 = count;
+ rdataset->private5 = raw;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+rdataset_next(dns_rdataset_t *rdataset) {
+ unsigned int count;
+ unsigned int length;
+ unsigned char *raw;
+
+ count = rdataset->privateuint4;
+ if (count == 0)
+ return (ISC_R_NOMORE);
+ count--;
+ rdataset->privateuint4 = count;
+ raw = rdataset->private5;
+ length = raw[0] * 256 + raw[1];
+ raw += length + 2;
+ rdataset->private5 = raw;
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) {
+ unsigned char *raw = rdataset->private5;
+ isc_region_t r;
+
+ REQUIRE(raw != NULL);
+
+ r.length = raw[0] * 256 + raw[1];
+ raw += 2;
+ r.base = raw;
+ dns_rdata_fromregion(rdata, rdataset->rdclass, rdataset->type, &r);
+}
+
+static void
+rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target) {
+ *target = *source;
+
+ /*
+ * Reset iterator state.
+ */
+ target->privateuint4 = 0;
+ target->private5 = NULL;
+}
+
+static unsigned int
+rdataset_count(dns_rdataset_t *rdataset) {
+ unsigned char *raw = rdataset->private3;
+ unsigned int count;
+
+ count = raw[0] * 256 + raw[1];
+
+ return (count);
+}
+
+static dns_rdatasetmethods_t rdataset_methods = {
+ rdataset_disassociate,
+ rdataset_first,
+ rdataset_next,
+ rdataset_current,
+ rdataset_clone,
+ rdataset_count,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+isc_result_t
+dns_ncache_getrdataset(dns_rdataset_t *ncacherdataset, dns_name_t *name,
+ dns_rdatatype_t type, dns_rdataset_t *rdataset)
+{
+ isc_result_t result;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_region_t remaining;
+ isc_buffer_t source;
+ dns_name_t tname;
+ dns_rdatatype_t ttype;
+
+ REQUIRE(ncacherdataset != NULL);
+ REQUIRE(ncacherdataset->type == 0);
+ REQUIRE(name != NULL);
+ REQUIRE(!dns_rdataset_isassociated(rdataset));
+ REQUIRE(type != dns_rdatatype_rrsig);
+
+ result = dns_rdataset_first(ncacherdataset);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdataset_current(ncacherdataset, &rdata);
+ isc_buffer_init(&source, rdata.data, rdata.length);
+ isc_buffer_add(&source, rdata.length);
+ dns_name_init(&tname, NULL);
+ isc_buffer_remainingregion(&source, &remaining);
+ dns_name_fromregion(&tname, &remaining);
+ INSIST(remaining.length >= tname.length);
+ isc_buffer_forward(&source, tname.length);
+ remaining.length -= tname.length;
+
+ INSIST(remaining.length >= 4);
+ ttype = isc_buffer_getuint16(&source);
+
+ if (ttype == type && dns_name_equal(&tname, name)) {
+ isc_buffer_remainingregion(&source, &remaining);
+ break;
+ }
+ result = dns_rdataset_next(ncacherdataset);
+ dns_rdata_reset(&rdata);
+ }
+ if (result == ISC_R_NOMORE)
+ return (ISC_R_NOTFOUND);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ INSIST(remaining.length != 0);
+
+ rdataset->methods = &rdataset_methods;
+ rdataset->rdclass = ncacherdataset->rdclass;
+ rdataset->type = type;
+ rdataset->covers = 0;
+ rdataset->ttl = ncacherdataset->ttl;
+ rdataset->trust = ncacherdataset->trust;
+ rdataset->private1 = NULL;
+ rdataset->private2 = NULL;
+
+ rdataset->private3 = remaining.base;
+
+ /*
+ * Reset iterator state.
+ */
+ rdataset->privateuint4 = 0;
+ rdataset->private5 = NULL;
+ rdataset->private6 = NULL;
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_ncache_current(dns_rdataset_t *ncacherdataset, dns_name_t *found,
+ dns_rdataset_t *rdataset)
+{
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_region_t remaining, sigregion;
+ isc_buffer_t source;
+ dns_name_t tname;
+ dns_rdatatype_t type;
+ unsigned int count;
+ dns_rdata_rrsig_t rrsig;
+ unsigned char *raw;
+
+ REQUIRE(ncacherdataset != NULL);
+ REQUIRE(ncacherdataset->type == 0);
+ REQUIRE(found != NULL);
+ REQUIRE(!dns_rdataset_isassociated(rdataset));
+
+ dns_rdataset_current(ncacherdataset, &rdata);
+ isc_buffer_init(&source, rdata.data, rdata.length);
+ isc_buffer_add(&source, rdata.length);
+
+ dns_name_init(&tname, NULL);
+ isc_buffer_remainingregion(&source, &remaining);
+ dns_name_fromregion(found, &remaining);
+ INSIST(remaining.length >= found->length);
+ isc_buffer_forward(&source, found->length);
+ remaining.length -= found->length;
+
+ INSIST(remaining.length >= 4);
+ type = isc_buffer_getuint16(&source);
+ isc_buffer_remainingregion(&source, &remaining);
+
+ rdataset->methods = &rdataset_methods;
+ rdataset->rdclass = ncacherdataset->rdclass;
+ rdataset->type = type;
+ if (type == dns_rdatatype_rrsig) {
+ /*
+ * Extract covers from RRSIG.
+ */
+ raw = remaining.base;
+ count = raw[0] * 256 + raw[1];
+ INSIST(count > 0);
+ raw += 2;
+ sigregion.length = raw[0] * 256 + raw[1];
+ raw += 2;
+ sigregion.base = raw;
+ dns_rdata_reset(&rdata);
+ dns_rdata_fromregion(&rdata, rdataset->rdclass,
+ rdataset->type, &sigregion);
+ (void)dns_rdata_tostruct(&rdata, &rrsig, NULL);
+ rdataset->covers = rrsig.covered;
+ } else
+ rdataset->covers = 0;
+ rdataset->ttl = ncacherdataset->ttl;
+ rdataset->trust = ncacherdataset->trust;
+ rdataset->private1 = NULL;
+ rdataset->private2 = NULL;
+
+ rdataset->private3 = remaining.base;
+
+ /*
+ * Reset iterator state.
+ */
+ rdataset->privateuint4 = 0;
+ rdataset->private5 = NULL;
+ rdataset->private6 = NULL;
+}
diff --git a/lib/dns/nsec.c b/lib/dns/nsec.c
new file mode 100644
index 0000000..53a3b5e
--- /dev/null
+++ b/lib/dns/nsec.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001, 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: nsec.c,v 1.11 2008/09/25 04:02:38 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/nsec.h>
+#include <dns/rdata.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/rdatastruct.h>
+#include <dns/result.h>
+
+#include <dst/dst.h>
+
+#define RETERR(x) do { \
+ result = (x); \
+ if (result != ISC_R_SUCCESS) \
+ goto failure; \
+ } while (0)
+
+static void
+set_bit(unsigned char *array, unsigned int index, unsigned int bit) {
+ unsigned int shift, mask;
+
+ shift = 7 - (index % 8);
+ mask = 1 << shift;
+
+ if (bit != 0)
+ array[index / 8] |= mask;
+ else
+ array[index / 8] &= (~mask & 0xFF);
+}
+
+static unsigned int
+bit_isset(unsigned char *array, unsigned int index) {
+ unsigned int byte, shift, mask;
+
+ byte = array[index / 8];
+ shift = 7 - (index % 8);
+ mask = 1 << shift;
+
+ return ((byte & mask) != 0);
+}
+
+isc_result_t
+dns_nsec_buildrdata(dns_db_t *db, dns_dbversion_t *version,
+ dns_dbnode_t *node, dns_name_t *target,
+ unsigned char *buffer, dns_rdata_t *rdata)
+{
+ isc_result_t result;
+ dns_rdataset_t rdataset;
+ isc_region_t r;
+ unsigned int i, window;
+ int octet;
+
+ unsigned char *nsec_bits, *bm;
+ unsigned int max_type;
+ dns_rdatasetiter_t *rdsiter;
+
+ memset(buffer, 0, DNS_NSEC_BUFFERSIZE);
+ dns_name_toregion(target, &r);
+ memcpy(buffer, r.base, r.length);
+ r.base = buffer;
+ /*
+ * Use the end of the space for a raw bitmap leaving enough
+ * space for the window identifiers and length octets.
+ */
+ bm = r.base + r.length + 512;
+ nsec_bits = r.base + r.length;
+ set_bit(bm, dns_rdatatype_rrsig, 1);
+ set_bit(bm, dns_rdatatype_nsec, 1);
+ max_type = dns_rdatatype_nsec;
+ dns_rdataset_init(&rdataset);
+ rdsiter = NULL;
+ result = dns_db_allrdatasets(db, node, version, 0, &rdsiter);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ for (result = dns_rdatasetiter_first(rdsiter);
+ result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(rdsiter))
+ {
+ dns_rdatasetiter_current(rdsiter, &rdataset);
+ if (rdataset.type != dns_rdatatype_nsec &&
+ rdataset.type != dns_rdatatype_nsec3 &&
+ rdataset.type != dns_rdatatype_rrsig) {
+ if (rdataset.type > max_type)
+ max_type = rdataset.type;
+ set_bit(bm, rdataset.type, 1);
+ }
+ dns_rdataset_disassociate(&rdataset);
+ }
+
+ /*
+ * At zone cuts, deny the existence of glue in the parent zone.
+ */
+ if (bit_isset(bm, dns_rdatatype_ns) &&
+ ! bit_isset(bm, dns_rdatatype_soa)) {
+ for (i = 0; i <= max_type; i++) {
+ if (bit_isset(bm, i) &&
+ ! dns_rdatatype_iszonecutauth((dns_rdatatype_t)i))
+ set_bit(bm, i, 0);
+ }
+ }
+
+ dns_rdatasetiter_destroy(&rdsiter);
+ if (result != ISC_R_NOMORE)
+ return (result);
+
+ for (window = 0; window < 256; window++) {
+ if (window * 256 > max_type)
+ break;
+ for (octet = 31; octet >= 0; octet--)
+ if (bm[window * 32 + octet] != 0)
+ break;
+ if (octet < 0)
+ continue;
+ nsec_bits[0] = window;
+ nsec_bits[1] = octet + 1;
+ /*
+ * Note: potential overlapping move.
+ */
+ memmove(&nsec_bits[2], &bm[window * 32], octet + 1);
+ nsec_bits += 3 + octet;
+ }
+ r.length = nsec_bits - r.base;
+ INSIST(r.length <= DNS_NSEC_BUFFERSIZE);
+ dns_rdata_fromregion(rdata,
+ dns_db_class(db),
+ dns_rdatatype_nsec,
+ &r);
+
+ return (ISC_R_SUCCESS);
+}
+
+
+isc_result_t
+dns_nsec_build(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node,
+ dns_name_t *target, dns_ttl_t ttl)
+{
+ isc_result_t result;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ unsigned char data[DNS_NSEC_BUFFERSIZE];
+ dns_rdatalist_t rdatalist;
+ dns_rdataset_t rdataset;
+
+ dns_rdataset_init(&rdataset);
+ dns_rdata_init(&rdata);
+
+ RETERR(dns_nsec_buildrdata(db, version, node, target, data, &rdata));
+
+ rdatalist.rdclass = dns_db_class(db);
+ rdatalist.type = dns_rdatatype_nsec;
+ rdatalist.covers = 0;
+ rdatalist.ttl = ttl;
+ ISC_LIST_INIT(rdatalist.rdata);
+ ISC_LIST_APPEND(rdatalist.rdata, &rdata, link);
+ RETERR(dns_rdatalist_tordataset(&rdatalist, &rdataset));
+ result = dns_db_addrdataset(db, node, version, 0, &rdataset,
+ 0, NULL);
+ if (result == DNS_R_UNCHANGED)
+ result = ISC_R_SUCCESS;
+ RETERR(result);
+ failure:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ return (result);
+}
+
+isc_boolean_t
+dns_nsec_typepresent(dns_rdata_t *nsec, dns_rdatatype_t type) {
+ dns_rdata_nsec_t nsecstruct;
+ isc_result_t result;
+ isc_boolean_t present;
+ unsigned int i, len, window;
+
+ REQUIRE(nsec != NULL);
+ REQUIRE(nsec->type == dns_rdatatype_nsec);
+
+ /* This should never fail */
+ result = dns_rdata_tostruct(nsec, &nsecstruct, NULL);
+ INSIST(result == ISC_R_SUCCESS);
+
+ present = ISC_FALSE;
+ for (i = 0; i < nsecstruct.len; i += len) {
+ INSIST(i + 2 <= nsecstruct.len);
+ window = nsecstruct.typebits[i];
+ len = nsecstruct.typebits[i + 1];
+ INSIST(len > 0 && len <= 32);
+ i += 2;
+ INSIST(i + len <= nsecstruct.len);
+ if (window * 256 > type)
+ break;
+ if ((window + 1) * 256 <= type)
+ continue;
+ if (type < (window * 256) + len * 8)
+ present = ISC_TF(bit_isset(&nsecstruct.typebits[i],
+ type % 256));
+ break;
+ }
+ dns_rdata_freestruct(&nsec);
+ return (present);
+}
+
+isc_result_t
+dns_nsec_nseconly(dns_db_t *db, dns_dbversion_t *version,
+ isc_boolean_t *answer)
+{
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t rdataset;
+ dns_rdata_dnskey_t dnskey;
+ isc_result_t result;
+
+ REQUIRE(answer != NULL);
+
+ dns_rdataset_init(&rdataset);
+
+ result = dns_db_getoriginnode(db, &node);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_db_findrdataset(db, node, version, dns_rdatatype_dnskey,
+ 0, 0, &rdataset, NULL);
+ dns_db_detachnode(db, &node);
+
+ if (result == ISC_R_NOTFOUND) {
+ *answer = ISC_FALSE;
+ return (ISC_R_SUCCESS);
+ }
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset)) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ dns_rdataset_current(&rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &dnskey, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ if (dnskey.algorithm == DST_ALG_RSAMD5 ||
+ dnskey.algorithm == DST_ALG_RSASHA1 ||
+ dnskey.algorithm == DST_ALG_DSA ||
+ dnskey.algorithm == DST_ALG_ECC)
+ break;
+ }
+ dns_rdataset_disassociate(&rdataset);
+ if (result == ISC_R_SUCCESS)
+ *answer = ISC_TRUE;
+ if (result == ISC_R_NOMORE) {
+ *answer = ISC_FALSE;
+ result = ISC_R_SUCCESS;
+ }
+ return (result);
+}
diff --git a/lib/dns/nsec3.c b/lib/dns/nsec3.c
new file mode 100644
index 0000000..54a6993
--- /dev/null
+++ b/lib/dns/nsec3.c
@@ -0,0 +1,1377 @@
+/*
+ * Copyright (C) 2006, 2008 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 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.
+ */
+
+/* $Id: nsec3.c,v 1.6 2008/11/17 23:46:42 marka Exp $ */
+
+#include <config.h>
+
+#include <isc/base32.h>
+#include <isc/buffer.h>
+#include <isc/hex.h>
+#include <isc/iterated_hash.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dst/dst.h>
+
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/diff.h>
+#include <dns/fixedname.h>
+#include <dns/nsec3.h>
+#include <dns/rdata.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/rdatastruct.h>
+#include <dns/result.h>
+
+#define CHECK(x) do { \
+ result = (x); \
+ if (result != ISC_R_SUCCESS) \
+ goto failure; \
+ } while (0)
+
+#define OPTOUT(x) (((x) & DNS_NSEC3FLAG_OPTOUT) != 0)
+#define CREATE(x) (((x) & DNS_NSEC3FLAG_CREATE) != 0)
+#define REMOVE(x) (((x) & DNS_NSEC3FLAG_REMOVE) != 0)
+
+static void
+set_bit(unsigned char *array, unsigned int index, unsigned int bit) {
+ unsigned int shift, mask;
+
+ shift = 7 - (index % 8);
+ mask = 1 << shift;
+
+ if (bit != 0)
+ array[index / 8] |= mask;
+ else
+ array[index / 8] &= (~mask & 0xFF);
+}
+
+static unsigned int
+bit_isset(unsigned char *array, unsigned int index) {
+ unsigned int byte, shift, mask;
+
+ byte = array[index / 8];
+ shift = 7 - (index % 8);
+ mask = 1 << shift;
+
+ return ((byte & mask) != 0);
+}
+
+isc_result_t
+dns_nsec3_buildrdata(dns_db_t *db, dns_dbversion_t *version,
+ dns_dbnode_t *node, unsigned int hashalg,
+ unsigned int flags, unsigned int iterations,
+ const unsigned char *salt, size_t salt_length,
+ const unsigned char *nexthash, size_t hash_length,
+ unsigned char *buffer, dns_rdata_t *rdata)
+{
+ isc_result_t result;
+ dns_rdataset_t rdataset;
+ isc_region_t r;
+ unsigned int i, window;
+ int octet;
+ isc_boolean_t found;
+
+ unsigned char *nsec_bits, *bm;
+ unsigned int max_type;
+ dns_rdatasetiter_t *rdsiter;
+ unsigned char *p;
+
+ REQUIRE(salt_length < 256U);
+ REQUIRE(hash_length < 256U);
+ REQUIRE(flags <= 0xffU);
+ REQUIRE(hashalg <= 0xffU);
+ REQUIRE(iterations <= 0xffffU);
+
+ switch (hashalg) {
+ case dns_hash_sha1:
+ REQUIRE(hash_length == ISC_SHA1_DIGESTLENGTH);
+ break;
+ }
+
+ memset(buffer, 0, DNS_NSEC3_BUFFERSIZE);
+
+ p = buffer;
+
+ *p++ = hashalg;
+ *p++ = flags;
+
+ *p++ = iterations >> 8;
+ *p++ = iterations;
+
+ *p++ = salt_length;
+ memcpy(p, salt, salt_length);
+ p += salt_length;
+
+ *p++ = hash_length;
+ memcpy(p, nexthash, hash_length);
+ p += hash_length;
+
+ r.length = p - buffer;
+ r.base = buffer;
+
+ /*
+ * Use the end of the space for a raw bitmap leaving enough
+ * space for the window identifiers and length octets.
+ */
+ bm = r.base + r.length + 512;
+ nsec_bits = r.base + r.length;
+ max_type = 0;
+ if (node == NULL)
+ goto collapse_bitmap;
+ dns_rdataset_init(&rdataset);
+ rdsiter = NULL;
+ result = dns_db_allrdatasets(db, node, version, 0, &rdsiter);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ found = ISC_FALSE;
+ for (result = dns_rdatasetiter_first(rdsiter);
+ result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(rdsiter))
+ {
+ dns_rdatasetiter_current(rdsiter, &rdataset);
+ if (rdataset.type != dns_rdatatype_nsec &&
+ rdataset.type != dns_rdatatype_nsec3 &&
+ rdataset.type != dns_rdatatype_rrsig) {
+ if (rdataset.type > max_type)
+ max_type = rdataset.type;
+ set_bit(bm, rdataset.type, 1);
+ /* Don't set RRSIG for insecure delegation. */
+ if (rdataset.type != dns_rdatatype_ns)
+ found = ISC_TRUE;
+ }
+ dns_rdataset_disassociate(&rdataset);
+ }
+ if (found) {
+ if (dns_rdatatype_rrsig > max_type)
+ max_type = dns_rdatatype_rrsig;
+ set_bit(bm, dns_rdatatype_rrsig, 1);
+ }
+
+ /*
+ * At zone cuts, deny the existence of glue in the parent zone.
+ */
+ if (bit_isset(bm, dns_rdatatype_ns) &&
+ ! bit_isset(bm, dns_rdatatype_soa)) {
+ for (i = 0; i <= max_type; i++) {
+ if (bit_isset(bm, i) &&
+ ! dns_rdatatype_iszonecutauth((dns_rdatatype_t)i))
+ set_bit(bm, i, 0);
+ }
+ }
+
+ dns_rdatasetiter_destroy(&rdsiter);
+ if (result != ISC_R_NOMORE)
+ return (result);
+
+ collapse_bitmap:
+ for (window = 0; window < 256; window++) {
+ if (window * 256 > max_type)
+ break;
+ for (octet = 31; octet >= 0; octet--)
+ if (bm[window * 32 + octet] != 0)
+ break;
+ if (octet < 0)
+ continue;
+ nsec_bits[0] = window;
+ nsec_bits[1] = octet + 1;
+ /*
+ * Note: potentially overlapping move.
+ */
+ memmove(&nsec_bits[2], &bm[window * 32], octet + 1);
+ nsec_bits += 3 + octet;
+ }
+ r.length = nsec_bits - r.base;
+ INSIST(r.length <= DNS_NSEC3_BUFFERSIZE);
+ dns_rdata_fromregion(rdata, dns_db_class(db), dns_rdatatype_nsec3, &r);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_boolean_t
+dns_nsec3_typepresent(dns_rdata_t *rdata, dns_rdatatype_t type) {
+ dns_rdata_nsec3_t nsec3;
+ isc_result_t result;
+ isc_boolean_t present;
+ unsigned int i, len, window;
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(rdata->type == dns_rdatatype_nsec3);
+
+ /* This should never fail */
+ result = dns_rdata_tostruct(rdata, &nsec3, NULL);
+ INSIST(result == ISC_R_SUCCESS);
+
+ present = ISC_FALSE;
+ for (i = 0; i < nsec3.len; i += len) {
+ INSIST(i + 2 <= nsec3.len);
+ window = nsec3.typebits[i];
+ len = nsec3.typebits[i + 1];
+ INSIST(len > 0 && len <= 32);
+ i += 2;
+ INSIST(i + len <= nsec3.len);
+ if (window * 256 > type)
+ break;
+ if ((window + 1) * 256 <= type)
+ continue;
+ if (type < (window * 256) + len * 8)
+ present = ISC_TF(bit_isset(&nsec3.typebits[i],
+ type % 256));
+ break;
+ }
+ dns_rdata_freestruct(&nsec3);
+ return (present);
+}
+
+isc_result_t
+dns_nsec3_hashname(dns_fixedname_t *result,
+ unsigned char rethash[NSEC3_MAX_HASH_LENGTH],
+ size_t *hash_length, dns_name_t *name, dns_name_t *origin,
+ dns_hash_t hashalg, unsigned int iterations,
+ const unsigned char *salt, size_t saltlength)
+{
+ unsigned char hash[NSEC3_MAX_HASH_LENGTH];
+ unsigned char nametext[DNS_NAME_FORMATSIZE];
+ dns_fixedname_t fixed;
+ dns_name_t *downcased;
+ isc_buffer_t namebuffer;
+ isc_region_t region;
+ size_t len;
+
+ if (rethash == NULL)
+ rethash = hash;
+
+ memset(rethash, 0, NSEC3_MAX_HASH_LENGTH);
+
+ dns_fixedname_init(&fixed);
+ downcased = dns_fixedname_name(&fixed);
+ dns_name_downcase(name, downcased, NULL);
+
+ /* hash the node name */
+ len = isc_iterated_hash(rethash, hashalg, iterations, salt, saltlength,
+ downcased->ndata, downcased->length);
+ if (len == 0U)
+ return (DNS_R_BADALG);
+
+ if (hash_length != NULL)
+ *hash_length = len;
+
+ /* convert the hash to base32hex */
+ region.base = rethash;
+ region.length = len;
+ isc_buffer_init(&namebuffer, nametext, sizeof nametext);
+ isc_base32hex_totext(&region, 1, "", &namebuffer);
+
+ /* convert the hex to a domain name */
+ dns_fixedname_init(result);
+ return (dns_name_fromtext(dns_fixedname_name(result), &namebuffer,
+ origin, 0, NULL));
+}
+
+unsigned int
+dns_nsec3_hashlength(dns_hash_t hash) {
+
+ switch (hash) {
+ case dns_hash_sha1: return(ISC_SHA1_DIGESTLENGTH);
+ }
+ return (0);
+}
+
+isc_boolean_t
+dns_nsec3_supportedhash(dns_hash_t hash) {
+ switch (hash) {
+ case dns_hash_sha1: return (ISC_TRUE);
+ }
+ return (ISC_FALSE);
+}
+
+/*%
+ * Update a single RR in version 'ver' of 'db' and log the
+ * update in 'diff'.
+ *
+ * Ensures:
+ * \li '*tuple' == NULL. Either the tuple is freed, or its
+ * ownership has been transferred to the diff.
+ */
+static isc_result_t
+do_one_tuple(dns_difftuple_t **tuple, dns_db_t *db, dns_dbversion_t *ver,
+ dns_diff_t *diff)
+{
+ dns_diff_t temp_diff;
+ isc_result_t result;
+
+ /*
+ * Create a singleton diff.
+ */
+ dns_diff_init(diff->mctx, &temp_diff);
+ temp_diff.resign = diff->resign;
+ ISC_LIST_APPEND(temp_diff.tuples, *tuple, link);
+
+ /*
+ * Apply it to the database.
+ */
+ result = dns_diff_apply(&temp_diff, db, ver);
+ ISC_LIST_UNLINK(temp_diff.tuples, *tuple, link);
+ if (result != ISC_R_SUCCESS) {
+ dns_difftuple_free(tuple);
+ return (result);
+ }
+
+ /*
+ * Merge it into the current pending journal entry.
+ */
+ dns_diff_appendminimal(diff, tuple);
+
+ /*
+ * Do not clear temp_diff.
+ */
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Set '*exists' to true iff the given name exists, to false otherwise.
+ */
+static isc_result_t
+name_exists(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
+ isc_boolean_t *exists)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ dns_rdatasetiter_t *iter = NULL;
+
+ result = dns_db_findnode(db, name, ISC_FALSE, &node);
+ if (result == ISC_R_NOTFOUND) {
+ *exists = ISC_FALSE;
+ return (ISC_R_SUCCESS);
+ }
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_db_allrdatasets(db, node, version,
+ (isc_stdtime_t) 0, &iter);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_node;
+
+ result = dns_rdatasetiter_first(iter);
+ if (result == ISC_R_SUCCESS) {
+ *exists = ISC_TRUE;
+ } else if (result == ISC_R_NOMORE) {
+ *exists = ISC_FALSE;
+ result = ISC_R_SUCCESS;
+ } else
+ *exists = ISC_FALSE;
+ dns_rdatasetiter_destroy(&iter);
+
+ cleanup_node:
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+static isc_boolean_t
+match_nsec3param(const dns_rdata_nsec3_t *nsec3,
+ const dns_rdata_nsec3param_t *nsec3param)
+{
+ if (nsec3->hash == nsec3param->hash &&
+ nsec3->iterations == nsec3param->iterations &&
+ nsec3->salt_length == nsec3param->salt_length &&
+ !memcmp(nsec3->salt, nsec3param->salt, nsec3->salt_length))
+ return (ISC_TRUE);
+ return (ISC_FALSE);
+}
+
+/*%
+ * Delete NSEC3 records at "name" which match "param", recording the
+ * change in "diff".
+ */
+static isc_result_t
+delete(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
+ const dns_rdata_nsec3param_t *nsec3param, dns_diff_t *diff)
+{
+ dns_dbnode_t *node = NULL ;
+ dns_difftuple_t *tuple = NULL;
+ dns_rdata_nsec3_t nsec3;
+ dns_rdataset_t rdataset;
+ isc_result_t result;
+
+ result = dns_db_findnsec3node(db, name, ISC_FALSE, &node);
+ if (result == ISC_R_NOTFOUND)
+ return (ISC_R_SUCCESS);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ dns_rdataset_init(&rdataset);
+ result = dns_db_findrdataset(db, node, version, dns_rdatatype_nsec3, 0,
+ (isc_stdtime_t) 0, &rdataset, NULL);
+
+ if (result == ISC_R_NOTFOUND) {
+ result = ISC_R_SUCCESS;
+ goto cleanup_node;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_node;
+
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset))
+ {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_current(&rdataset, &rdata);
+ CHECK(dns_rdata_tostruct(&rdata, &nsec3, NULL));
+
+ if (!match_nsec3param(&nsec3, nsec3param))
+ continue;
+
+ result = dns_difftuple_create(diff->mctx, DNS_DIFFOP_DEL, name,
+ rdataset.ttl, &rdata, &tuple);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ result = do_one_tuple(&tuple, db, version, diff);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ }
+ if (result != ISC_R_NOMORE)
+ goto failure;
+ result = ISC_R_SUCCESS;
+
+ failure:
+ dns_rdataset_disassociate(&rdataset);
+ cleanup_node:
+ dns_db_detachnode(db, &node);
+
+ return (result);
+}
+
+#ifndef RFC5155_STRICT
+static isc_boolean_t
+better_param(dns_rdataset_t *nsec3paramset, dns_rdata_t *param) {
+ dns_rdataset_t rdataset;
+ isc_result_t result;
+
+ if (REMOVE(param->data[1]))
+ return (ISC_TRUE);
+
+ dns_rdataset_init(&rdataset);
+ dns_rdataset_clone(nsec3paramset, &rdataset);
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset)) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_current(&rdataset, &rdata);
+ if (rdata.length != param->length)
+ continue;
+ if (rdata.data[0] != param->data[0] ||
+ REMOVE(rdata.data[1]) ||
+ rdata.data[2] != param->data[2] ||
+ rdata.data[3] != param->data[3] ||
+ rdata.data[4] != param->data[4] ||
+ memcmp(&rdata.data[5], &param->data[5], param->data[4]))
+ continue;
+ if (CREATE(rdata.data[1]) && !CREATE(param->data[1])) {
+ dns_rdataset_disassociate(&rdataset);
+ return (ISC_TRUE);
+ }
+ }
+ dns_rdataset_disassociate(&rdataset);
+ return (ISC_FALSE);
+}
+#endif
+
+static isc_result_t
+find_nsec3(dns_rdata_nsec3_t *nsec3, dns_rdataset_t *rdataset,
+ const dns_rdata_nsec3param_t *nsec3param)
+{
+ isc_result_t result;
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset)) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ dns_rdataset_current(rdataset, &rdata);
+ CHECK(dns_rdata_tostruct(&rdata, nsec3, NULL));
+ dns_rdata_reset(&rdata);
+ if (match_nsec3param(nsec3, nsec3param))
+ break;
+ }
+ failure:
+ return (result);
+}
+
+isc_result_t
+dns_nsec3_addnsec3(dns_db_t *db, dns_dbversion_t *version,
+ dns_name_t *name, const dns_rdata_nsec3param_t *nsec3param,
+ dns_ttl_t nsecttl, isc_boolean_t unsecure, dns_diff_t *diff)
+{
+ dns_dbiterator_t *dbit = NULL;
+ dns_dbnode_t *node = NULL;
+ dns_dbnode_t *newnode = NULL;
+ dns_difftuple_t *tuple = NULL;
+ dns_fixedname_t fixed;
+ dns_fixedname_t fprev;
+ dns_hash_t hash;
+ dns_name_t *hashname;
+ dns_name_t *origin;
+ dns_name_t *prev;
+ dns_name_t empty;
+ dns_rdata_nsec3_t nsec3;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_t rdataset;
+ int pass;
+ isc_boolean_t exists;
+ isc_boolean_t remove_unsecure = ISC_FALSE;
+ isc_uint8_t flags;
+ isc_buffer_t buffer;
+ isc_result_t result;
+ unsigned char *old_next;
+ unsigned char *salt;
+ unsigned char nexthash[NSEC3_MAX_HASH_LENGTH];
+ unsigned char nsec3buf[DNS_NSEC3_BUFFERSIZE];
+ unsigned int iterations;
+ unsigned int labels;
+ size_t next_length;
+ unsigned int old_length;
+ unsigned int salt_length;
+
+ dns_fixedname_init(&fixed);
+ hashname = dns_fixedname_name(&fixed);
+ dns_fixedname_init(&fprev);
+ prev = dns_fixedname_name(&fprev);
+
+ dns_rdataset_init(&rdataset);
+
+ origin = dns_db_origin(db);
+
+ /*
+ * Chain parameters.
+ */
+ hash = nsec3param->hash;
+ iterations = nsec3param->iterations;
+ salt_length = nsec3param->salt_length;
+ salt = nsec3param->salt;
+
+ /*
+ * Default flags for a new chain.
+ */
+ flags = nsec3param->flags & DNS_NSEC3FLAG_OPTOUT;
+
+ /*
+ * If this is the first NSEC3 in the chain nexthash will
+ * remain pointing to itself.
+ */
+ next_length = sizeof(nexthash);
+ CHECK(dns_nsec3_hashname(&fixed, nexthash, &next_length,
+ name, origin, hash, iterations,
+ salt, salt_length));
+
+ /*
+ * Create the node if it doesn't exist and hold
+ * a reference to it until we have added the NSEC3.
+ */
+ CHECK(dns_db_findnsec3node(db, hashname, ISC_TRUE, &newnode));
+
+ /*
+ * Seek the iterator to the 'newnode'.
+ */
+ CHECK(dns_db_createiterator(db, DNS_DB_NSEC3ONLY, &dbit));
+ CHECK(dns_dbiterator_seek(dbit, hashname));
+ CHECK(dns_dbiterator_pause(dbit));
+ result = dns_db_findrdataset(db, newnode, version, dns_rdatatype_nsec3,
+ 0, (isc_stdtime_t) 0, &rdataset, NULL);
+ /*
+ * If we updating a existing NSEC3 then find its
+ * next field.
+ */
+ if (result == ISC_R_SUCCESS) {
+ result = find_nsec3(&nsec3, &rdataset, nsec3param);
+ if (result == ISC_R_SUCCESS) {
+ if (!CREATE(nsec3param->flags))
+ flags = nsec3.flags;
+ next_length = nsec3.next_length;
+ INSIST(next_length <= sizeof(nexthash));
+ memcpy(nexthash, nsec3.next, next_length);
+ dns_rdataset_disassociate(&rdataset);
+ /*
+ * If the NSEC3 is not for a unsecure delegation then
+ * we are just updating it. If it is for a unsecure
+ * delegation then we need find out if we need to
+ * remove the NSEC3 record or not by examining the
+ * previous NSEC3 record.
+ */
+ if (!unsecure)
+ goto addnsec3;
+ else
+ remove_unsecure = ISC_TRUE;
+ } else {
+ dns_rdataset_disassociate(&rdataset);
+ if (result != ISC_R_NOMORE)
+ goto failure;
+ }
+ }
+
+ /*
+ * Find the previous NSEC3 (if any) and update it if required.
+ */
+ pass = 0;
+ do {
+ result = dns_dbiterator_prev(dbit);
+ if (result == ISC_R_NOMORE) {
+ pass++;
+ CHECK(dns_dbiterator_last(dbit));
+ }
+ CHECK(dns_dbiterator_current(dbit, &node, prev));
+ CHECK(dns_dbiterator_pause(dbit));
+ result = dns_db_findrdataset(db, node, version,
+ dns_rdatatype_nsec3, 0,
+ (isc_stdtime_t) 0, &rdataset,
+ NULL);
+ dns_db_detachnode(db, &node);
+ if (result != ISC_R_SUCCESS)
+ continue;
+
+ result = find_nsec3(&nsec3, &rdataset, nsec3param);
+ if (result == ISC_R_NOMORE) {
+ dns_rdataset_disassociate(&rdataset);
+ continue;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ if (remove_unsecure) {
+ dns_rdataset_disassociate(&rdataset);
+ /*
+ * We have found the previous NSEC3 record and can now
+ * see if the existing NSEC3 record needs to be
+ * updated or deleted.
+ */
+ if (!OPTOUT(nsec3.flags)) {
+ /*
+ * Just update the NSEC3 record.
+ */
+ goto addnsec3;
+ } else {
+ /*
+ * This is actually a deletion not a add.
+ */
+ result = dns_nsec3_delnsec3(db, version, name,
+ nsec3param, diff);
+ goto failure;
+ }
+ } else {
+ /*
+ * Is this is a unsecure delegation we are adding?
+ * If so no change is required.
+ */
+ if (OPTOUT(nsec3.flags) && unsecure) {
+ dns_rdataset_disassociate(&rdataset);
+ goto failure;
+ }
+ }
+
+ old_next = nsec3.next;
+ old_length = nsec3.next_length;
+
+ /*
+ * Delete the old previous NSEC3.
+ */
+ CHECK(delete(db, version, prev, nsec3param, diff));
+
+ /*
+ * Fixup the previous NSEC3.
+ */
+ nsec3.next = nexthash;
+ nsec3.next_length = next_length;
+ isc_buffer_init(&buffer, nsec3buf, sizeof(nsec3buf));
+ CHECK(dns_rdata_fromstruct(&rdata, rdataset.rdclass,
+ dns_rdatatype_nsec3, &nsec3,
+ &buffer));
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, prev,
+ rdataset.ttl, &rdata, &tuple));
+ CHECK(do_one_tuple(&tuple, db, version, diff));
+ INSIST(old_length <= sizeof(nexthash));
+ memcpy(nexthash, old_next, old_length);
+ if (!CREATE(nsec3param->flags))
+ flags = nsec3.flags;
+ dns_rdata_reset(&rdata);
+ dns_rdataset_disassociate(&rdataset);
+ break;
+ } while (pass < 2);
+
+ addnsec3:
+ /*
+ * Create the NSEC3 RDATA.
+ */
+ CHECK(dns_db_findnode(db, name, ISC_FALSE, &node));
+ CHECK(dns_nsec3_buildrdata(db, version, node, hash, flags, iterations,
+ salt, salt_length, nexthash, next_length,
+ nsec3buf, &rdata));
+ dns_db_detachnode(db, &node);
+
+ /*
+ * Delete the old NSEC3 and record the change.
+ */
+ CHECK(delete(db, version, hashname, nsec3param, diff));
+ /*
+ * Add the new NSEC3 and record the change.
+ */
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD,
+ hashname, nsecttl, &rdata, &tuple));
+ CHECK(do_one_tuple(&tuple, db, version, diff));
+ INSIST(tuple == NULL);
+ dns_rdata_reset(&rdata);
+ dns_db_detachnode(db, &newnode);
+
+ /*
+ * Add missing NSEC3 records for empty nodes
+ */
+ dns_name_init(&empty, NULL);
+ dns_name_clone(name, &empty);
+ do {
+ labels = dns_name_countlabels(&empty) - 1;
+ if (labels <= dns_name_countlabels(origin))
+ break;
+ dns_name_getlabelsequence(&empty, 1, labels, &empty);
+ CHECK(name_exists(db, version, &empty, &exists));
+ if (exists)
+ break;
+ CHECK(dns_nsec3_hashname(&fixed, nexthash, &next_length,
+ &empty, origin, hash, iterations,
+ salt, salt_length));
+
+ /*
+ * Create the node if it doesn't exist and hold
+ * a reference to it until we have added the NSEC3
+ * or we discover we don't need to add make a change.
+ */
+ CHECK(dns_db_findnsec3node(db, hashname, ISC_TRUE, &newnode));
+ result = dns_db_findrdataset(db, newnode, version,
+ dns_rdatatype_nsec3, 0,
+ (isc_stdtime_t) 0, &rdataset,
+ NULL);
+ if (result == ISC_R_SUCCESS) {
+ result = find_nsec3(&nsec3, &rdataset, nsec3param);
+ dns_rdataset_disassociate(&rdataset);
+ if (result == ISC_R_SUCCESS) {
+ dns_db_detachnode(db, &newnode);
+ break;
+ }
+ if (result != ISC_R_NOMORE)
+ goto failure;
+ }
+
+ /*
+ * Find the previous NSEC3 and update it.
+ */
+ CHECK(dns_dbiterator_seek(dbit, hashname));
+ pass = 0;
+ do {
+ result = dns_dbiterator_prev(dbit);
+ if (result == ISC_R_NOMORE) {
+ pass++;
+ CHECK(dns_dbiterator_last(dbit));
+ }
+ CHECK(dns_dbiterator_current(dbit, &node, prev));
+ CHECK(dns_dbiterator_pause(dbit));
+ result = dns_db_findrdataset(db, node, version,
+ dns_rdatatype_nsec3, 0,
+ (isc_stdtime_t) 0,
+ &rdataset, NULL);
+ dns_db_detachnode(db, &node);
+ if (result != ISC_R_SUCCESS)
+ continue;
+ result = find_nsec3(&nsec3, &rdataset, nsec3param);
+ if (result == ISC_R_NOMORE) {
+ dns_rdataset_disassociate(&rdataset);
+ continue;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ old_next = nsec3.next;
+ old_length = nsec3.next_length;
+
+ /*
+ * Delete the old previous NSEC3.
+ */
+ CHECK(delete(db, version, prev, nsec3param, diff));
+
+ /*
+ * Fixup the previous NSEC3.
+ */
+ nsec3.next = nexthash;
+ nsec3.next_length = next_length;
+ isc_buffer_init(&buffer, nsec3buf,
+ sizeof(nsec3buf));
+ CHECK(dns_rdata_fromstruct(&rdata, rdataset.rdclass,
+ dns_rdatatype_nsec3, &nsec3,
+ &buffer));
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD,
+ prev, rdataset.ttl, &rdata,
+ &tuple));
+ CHECK(do_one_tuple(&tuple, db, version, diff));
+ INSIST(old_length <= sizeof(nexthash));
+ memcpy(nexthash, old_next, old_length);
+ if (!CREATE(nsec3param->flags))
+ flags = nsec3.flags;
+ dns_rdata_reset(&rdata);
+ dns_rdataset_disassociate(&rdataset);
+ break;
+ } while (pass < 2);
+
+ INSIST(pass < 2);
+
+ /*
+ * Create the NSEC3 RDATA for the empty node.
+ */
+ CHECK(dns_nsec3_buildrdata(db, version, NULL, hash, flags,
+ iterations, salt, salt_length,
+ nexthash, next_length, nsec3buf,
+ &rdata));
+ /*
+ * Delete the old NSEC3 and record the change.
+ */
+ CHECK(delete(db, version, hashname, nsec3param, diff));
+
+ /*
+ * Add the new NSEC3 and record the change.
+ */
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD,
+ hashname, nsecttl, &rdata, &tuple));
+ CHECK(do_one_tuple(&tuple, db, version, diff));
+ INSIST(tuple == NULL);
+ dns_rdata_reset(&rdata);
+ dns_db_detachnode(db, &newnode);
+ } while (1);
+
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+
+ failure:
+ if (dbit != NULL)
+ dns_dbiterator_destroy(&dbit);
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (newnode != NULL)
+ dns_db_detachnode(db, &newnode);
+ return (result);
+}
+
+/*%
+ * Add NSEC3 records for "name", recording the change in "diff".
+ * The existing NSEC3 records are removed.
+ */
+isc_result_t
+dns_nsec3_addnsec3s(dns_db_t *db, dns_dbversion_t *version,
+ dns_name_t *name, dns_ttl_t nsecttl,
+ isc_boolean_t unsecure, dns_diff_t *diff)
+{
+ dns_dbnode_t *node = NULL;
+ dns_rdata_nsec3param_t nsec3param;
+ dns_rdataset_t rdataset;
+ isc_result_t result;
+
+ dns_rdataset_init(&rdataset);
+
+ /*
+ * Find the NSEC3 parameters for this zone.
+ */
+ result = dns_db_getoriginnode(db, &node);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_db_findrdataset(db, node, version,
+ dns_rdatatype_nsec3param, 0, 0,
+ &rdataset, NULL);
+ dns_db_detachnode(db, &node);
+ if (result == ISC_R_NOTFOUND)
+ return (ISC_R_SUCCESS);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * Update each active NSEC3 chain.
+ */
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset)) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ dns_rdataset_current(&rdataset, &rdata);
+ dns_rdata_tostruct(&rdata, &nsec3param, NULL);
+
+#ifdef RFC5155_STRICT
+ if (nsec3param.flags != 0)
+ continue;
+#else
+ if ((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0)
+ continue;
+ if (better_param(&rdataset, &rdata))
+ continue;
+#endif
+
+ /*
+ * We have a active chain. Update it.
+ */
+ CHECK(dns_nsec3_addnsec3(db, version, name, &nsec3param,
+ nsecttl, unsecure, diff));
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+
+ failure:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+
+ return (result);
+}
+
+isc_result_t
+dns_nsec3_delnsec3(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
+ const dns_rdata_nsec3param_t *nsec3param, dns_diff_t *diff)
+{
+ dns_dbiterator_t *dbit = NULL;
+ dns_dbnode_t *node = NULL;
+ dns_difftuple_t *tuple = NULL;
+ dns_fixedname_t fixed;
+ dns_fixedname_t fprev;
+ dns_hash_t hash;
+ dns_name_t *hashname;
+ dns_name_t *origin;
+ dns_name_t *prev;
+ dns_name_t empty;
+ dns_rdata_nsec3_t nsec3;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_t rdataset;
+ int pass;
+ isc_boolean_t exists;
+ isc_buffer_t buffer;
+ isc_result_t result;
+ unsigned char *salt;
+ unsigned char nexthash[NSEC3_MAX_HASH_LENGTH];
+ unsigned char nsec3buf[DNS_NSEC3_BUFFERSIZE];
+ unsigned int iterations;
+ unsigned int labels;
+ size_t next_length;
+ unsigned int salt_length;
+
+ dns_fixedname_init(&fixed);
+ hashname = dns_fixedname_name(&fixed);
+ dns_fixedname_init(&fprev);
+ prev = dns_fixedname_name(&fprev);
+
+ dns_rdataset_init(&rdataset);
+
+ origin = dns_db_origin(db);
+
+ /*
+ * Chain parameters.
+ */
+ hash = nsec3param->hash;
+ iterations = nsec3param->iterations;
+ salt_length = nsec3param->salt_length;
+ salt = nsec3param->salt;
+
+ /*
+ * If this is the first NSEC3 in the chain nexthash will
+ * remain pointing to itself.
+ */
+ next_length = sizeof(nexthash);
+ CHECK(dns_nsec3_hashname(&fixed, nexthash, &next_length,
+ name, origin, hash, iterations,
+ salt, salt_length));
+
+ CHECK(dns_db_createiterator(db, DNS_DB_NSEC3ONLY, &dbit));
+
+ result = dns_dbiterator_seek(dbit, hashname);
+ if (result == ISC_R_NOTFOUND)
+ goto success;
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ CHECK(dns_dbiterator_current(dbit, &node, NULL));
+ CHECK(dns_dbiterator_pause(dbit));
+ result = dns_db_findrdataset(db, node, version, dns_rdatatype_nsec3,
+ 0, (isc_stdtime_t) 0, &rdataset, NULL);
+ dns_db_detachnode(db, &node);
+ if (result == ISC_R_NOTFOUND)
+ goto success;
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ /*
+ * If we find a existing NSEC3 for this chain then save the
+ * next field.
+ */
+ result = find_nsec3(&nsec3, &rdataset, nsec3param);
+ if (result == ISC_R_SUCCESS) {
+ next_length = nsec3.next_length;
+ INSIST(next_length <= sizeof(nexthash));
+ memcpy(nexthash, nsec3.next, next_length);
+ }
+ dns_rdataset_disassociate(&rdataset);
+ if (result == ISC_R_NOMORE)
+ goto success;
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ /*
+ * Find the previous NSEC3 and update it.
+ */
+ pass = 0;
+ do {
+ result = dns_dbiterator_prev(dbit);
+ if (result == ISC_R_NOMORE) {
+ pass++;
+ CHECK(dns_dbiterator_last(dbit));
+ }
+ CHECK(dns_dbiterator_current(dbit, &node, prev));
+ CHECK(dns_dbiterator_pause(dbit));
+ result = dns_db_findrdataset(db, node, version,
+ dns_rdatatype_nsec3, 0,
+ (isc_stdtime_t) 0, &rdataset,
+ NULL);
+ dns_db_detachnode(db, &node);
+ if (result != ISC_R_SUCCESS)
+ continue;
+ result = find_nsec3(&nsec3, &rdataset, nsec3param);
+ if (result == ISC_R_NOMORE) {
+ dns_rdataset_disassociate(&rdataset);
+ continue;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ /*
+ * Delete the old previous NSEC3.
+ */
+ CHECK(delete(db, version, prev, nsec3param, diff));
+
+ /*
+ * Fixup the previous NSEC3.
+ */
+ nsec3.next = nexthash;
+ nsec3.next_length = next_length;
+ isc_buffer_init(&buffer, nsec3buf, sizeof(nsec3buf));
+ CHECK(dns_rdata_fromstruct(&rdata, rdataset.rdclass,
+ dns_rdatatype_nsec3, &nsec3,
+ &buffer));
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, prev,
+ rdataset.ttl, &rdata, &tuple));
+ CHECK(do_one_tuple(&tuple, db, version, diff));
+ dns_rdata_reset(&rdata);
+ dns_rdataset_disassociate(&rdataset);
+ break;
+ } while (pass < 2);
+
+ /*
+ * Delete the old NSEC3 and record the change.
+ */
+ CHECK(delete(db, version, hashname, nsec3param, diff));
+
+ /*
+ * Delete NSEC3 records for now non active nodes.
+ */
+ dns_name_init(&empty, NULL);
+ dns_name_clone(name, &empty);
+ do {
+ labels = dns_name_countlabels(&empty) - 1;
+ if (labels <= dns_name_countlabels(origin))
+ break;
+ dns_name_getlabelsequence(&empty, 1, labels, &empty);
+ CHECK(name_exists(db, version, &empty, &exists));
+ if (exists)
+ break;
+
+ CHECK(dns_nsec3_hashname(&fixed, nexthash, &next_length,
+ &empty, origin, hash, iterations,
+ salt, salt_length));
+ result = dns_dbiterator_seek(dbit, hashname);
+ if (result == ISC_R_NOTFOUND)
+ goto success;
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ CHECK(dns_dbiterator_current(dbit, &node, NULL));
+ CHECK(dns_dbiterator_pause(dbit));
+ result = dns_db_findrdataset(db, node, version,
+ dns_rdatatype_nsec3, 0,
+ (isc_stdtime_t) 0, &rdataset,
+ NULL);
+ dns_db_detachnode(db, &node);
+ if (result == ISC_R_NOTFOUND)
+ goto success;
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ result = find_nsec3(&nsec3, &rdataset, nsec3param);
+ if (result == ISC_R_SUCCESS) {
+ next_length = nsec3.next_length;
+ INSIST(next_length <= sizeof(nexthash));
+ memcpy(nexthash, nsec3.next, next_length);
+ }
+ dns_rdataset_disassociate(&rdataset);
+ if (result == ISC_R_NOMORE)
+ goto success;
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ pass = 0;
+ do {
+ result = dns_dbiterator_prev(dbit);
+ if (result == ISC_R_NOMORE) {
+ pass++;
+ CHECK(dns_dbiterator_last(dbit));
+ }
+ CHECK(dns_dbiterator_current(dbit, &node, prev));
+ CHECK(dns_dbiterator_pause(dbit));
+ result = dns_db_findrdataset(db, node, version,
+ dns_rdatatype_nsec3, 0,
+ (isc_stdtime_t) 0,
+ &rdataset, NULL);
+ dns_db_detachnode(db, &node);
+ if (result != ISC_R_SUCCESS)
+ continue;
+ result = find_nsec3(&nsec3, &rdataset, nsec3param);
+ if (result == ISC_R_NOMORE) {
+ dns_rdataset_disassociate(&rdataset);
+ continue;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ /*
+ * Delete the old previous NSEC3.
+ */
+ CHECK(delete(db, version, prev, nsec3param, diff));
+
+ /*
+ * Fixup the previous NSEC3.
+ */
+ nsec3.next = nexthash;
+ nsec3.next_length = next_length;
+ isc_buffer_init(&buffer, nsec3buf,
+ sizeof(nsec3buf));
+ CHECK(dns_rdata_fromstruct(&rdata, rdataset.rdclass,
+ dns_rdatatype_nsec3, &nsec3,
+ &buffer));
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD,
+ prev, rdataset.ttl, &rdata,
+ &tuple));
+ CHECK(do_one_tuple(&tuple, db, version, diff));
+ dns_rdata_reset(&rdata);
+ dns_rdataset_disassociate(&rdataset);
+ break;
+ } while (pass < 2);
+
+ INSIST(pass < 2);
+
+ /*
+ * Delete the old NSEC3 and record the change.
+ */
+ CHECK(delete(db, version, hashname, nsec3param, diff));
+ } while (1);
+
+ success:
+ result = ISC_R_SUCCESS;
+
+ failure:
+ if (dbit != NULL)
+ dns_dbiterator_destroy(&dbit);
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+isc_result_t
+dns_nsec3_delnsec3s(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
+ dns_diff_t *diff)
+{
+ dns_dbnode_t *node = NULL;
+ dns_rdata_nsec3param_t nsec3param;
+ dns_rdataset_t rdataset;
+ isc_result_t result;
+
+ dns_rdataset_init(&rdataset);
+
+ /*
+ * Find the NSEC3 parameters for this zone.
+ */
+ result = dns_db_getoriginnode(db, &node);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_db_findrdataset(db, node, version,
+ dns_rdatatype_nsec3param, 0, 0,
+ &rdataset, NULL);
+ dns_db_detachnode(db, &node);
+ if (result == ISC_R_NOTFOUND)
+ return (ISC_R_SUCCESS);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * Update each active NSEC3 chain.
+ */
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset)) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ dns_rdataset_current(&rdataset, &rdata);
+ dns_rdata_tostruct(&rdata, &nsec3param, NULL);
+
+#ifdef RFC5155_STRICT
+ if (nsec3param.flags != 0)
+ continue;
+#else
+ if ((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0)
+ continue;
+ if (better_param(&rdataset, &rdata))
+ continue;
+#endif
+
+ /*
+ * We have a active chain. Update it.
+ */
+ CHECK(dns_nsec3_delnsec3(db, version, name, &nsec3param, diff));
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+
+ failure:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+
+ return (result);
+}
+
+isc_result_t
+dns_nsec3_active(dns_db_t *db, dns_dbversion_t *version,
+ isc_boolean_t complete, isc_boolean_t *answer)
+{
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t rdataset;
+ dns_rdata_nsec3param_t nsec3param;
+ isc_result_t result;
+
+ REQUIRE(answer != NULL);
+
+ dns_rdataset_init(&rdataset);
+
+ result = dns_db_getoriginnode(db, &node);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_db_findrdataset(db, node, version,
+ dns_rdatatype_nsec3param, 0, 0,
+ &rdataset, NULL);
+ dns_db_detachnode(db, &node);
+
+ if (result == ISC_R_NOTFOUND) {
+ *answer = ISC_FALSE;
+ return (ISC_R_SUCCESS);
+ }
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset)) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ dns_rdataset_current(&rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &nsec3param, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ if ((nsec3param.flags) == 0 ||
+ (!complete && CREATE(nsec3param.flags)))
+ break;
+ }
+ dns_rdataset_disassociate(&rdataset);
+ if (result == ISC_R_SUCCESS)
+ *answer = ISC_TRUE;
+ if (result == ISC_R_NOMORE) {
+ *answer = ISC_FALSE;
+ result = ISC_R_SUCCESS;
+ }
+ return (result);
+}
+
+isc_result_t
+dns_nsec3_maxiterations(dns_db_t *db, dns_dbversion_t *version,
+ isc_mem_t *mctx, unsigned int *iterationsp)
+{
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t rdataset;
+ dst_key_t *key = NULL;
+ isc_buffer_t buffer;
+ isc_result_t result;
+ isc_uint16_t bits, minbits = 4096;
+
+ result = dns_db_getoriginnode(db, &node);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ dns_rdataset_init(&rdataset);
+ result = dns_db_findrdataset(db, node, version, dns_rdatatype_dnskey,
+ 0, 0, &rdataset, NULL);
+ dns_db_detachnode(db, &node);
+ if (result == ISC_R_NOTFOUND) {
+ *iterationsp = 0;
+ return (ISC_R_SUCCESS);
+ }
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset)) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ dns_rdataset_current(&rdataset, &rdata);
+ isc_buffer_init(&buffer, rdata.data, rdata.length);
+ isc_buffer_add(&buffer, rdata.length);
+ CHECK(dst_key_fromdns(dns_db_origin(db), rdataset.rdclass,
+ &buffer, mctx, &key));
+ bits = dst_key_getbits(key);
+ dst_key_free(&key);
+ if (minbits > bits)
+ minbits = bits;
+ }
+ if (result != ISC_R_NOMORE)
+ goto failure;
+
+ if (minbits <= 1024)
+ *iterationsp = 150;
+ else if (minbits <= 2048)
+ *iterationsp = 500;
+ else
+ *iterationsp = 2500;
+ result = ISC_R_SUCCESS;
+
+ failure:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ return (result);
+}
diff --git a/lib/dns/openssl_link.c b/lib/dns/openssl_link.c
new file mode 100644
index 0000000..1ddb75d
--- /dev/null
+++ b/lib/dns/openssl_link.c
@@ -0,0 +1,447 @@
+/*
+ * Portions Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 NETWORK ASSOCIATES 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.
+ *
+ * Portions Copyright (C) 1995-2000 by Network Associates, 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 NETWORK ASSOCIATES 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.
+ */
+
+/*
+ * Principal Author: Brian Wellington
+ * $Id: openssl_link.c,v 1.22 2008/04/05 23:47:11 tbox Exp $
+ */
+#ifdef OPENSSL
+
+#include <config.h>
+
+#include <isc/entropy.h>
+#include <isc/mem.h>
+#include <isc/mutex.h>
+#include <isc/mutexblock.h>
+#include <isc/string.h>
+#include <isc/thread.h>
+#include <isc/util.h>
+
+#include "dst_internal.h"
+#include "dst_openssl.h"
+
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/evp.h>
+#include <openssl/conf.h>
+#include <openssl/crypto.h>
+
+#if defined(CRYPTO_LOCK_ENGINE) && (OPENSSL_VERSION_NUMBER >= 0x0090707f)
+#define USE_ENGINE 1
+#endif
+
+#ifdef USE_ENGINE
+#include <openssl/engine.h>
+
+#ifdef ENGINE_ID
+const char *engine_id = ENGINE_ID;
+#else
+const char *engine_id;
+#endif
+#endif
+
+static RAND_METHOD *rm = NULL;
+
+static isc_mutex_t *locks = NULL;
+static int nlocks;
+
+#ifdef USE_ENGINE
+static ENGINE *e;
+static ENGINE *he;
+#endif
+
+#ifdef USE_PKCS11
+static isc_result_t
+dst__openssl_load_engine(const char *name, const char *engine_id,
+ const char **pre_cmds, int pre_num,
+ const char **post_cmds, int post_num);
+#endif
+
+static int
+entropy_get(unsigned char *buf, int num) {
+ isc_result_t result;
+ if (num < 0)
+ return (-1);
+ result = dst__entropy_getdata(buf, (unsigned int) num, ISC_FALSE);
+ return (result == ISC_R_SUCCESS ? num : -1);
+}
+
+static int
+entropy_status(void) {
+ return (dst__entropy_status() > 32);
+}
+
+static int
+entropy_getpseudo(unsigned char *buf, int num) {
+ isc_result_t result;
+ if (num < 0)
+ return (-1);
+ result = dst__entropy_getdata(buf, (unsigned int) num, ISC_TRUE);
+ return (result == ISC_R_SUCCESS ? num : -1);
+}
+
+static void
+entropy_add(const void *buf, int num, double entropy) {
+ /*
+ * Do nothing. The only call to this provides no useful data anyway.
+ */
+ UNUSED(buf);
+ UNUSED(num);
+ UNUSED(entropy);
+}
+
+static void
+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 unsigned long
+id_callback(void) {
+ return ((unsigned long)isc_thread_self());
+}
+
+static void *
+mem_alloc(size_t size) {
+ INSIST(dst__memory_pool != NULL);
+ return (isc_mem_allocate(dst__memory_pool, size));
+}
+
+static void
+mem_free(void *ptr) {
+ INSIST(dst__memory_pool != NULL);
+ if (ptr != NULL)
+ isc_mem_free(dst__memory_pool, ptr);
+}
+
+static void *
+mem_realloc(void *ptr, size_t size) {
+ void *p;
+
+ INSIST(dst__memory_pool != NULL);
+ p = NULL;
+ if (size > 0U) {
+ p = mem_alloc(size);
+ if (p != NULL && ptr != NULL)
+ memcpy(p, ptr, size);
+ }
+ if (ptr != NULL)
+ mem_free(ptr);
+ return (p);
+}
+
+isc_result_t
+dst__openssl_init() {
+ isc_result_t result;
+#ifdef USE_ENGINE
+ /* const char *name; */
+ ENGINE *re;
+#endif
+
+#ifdef DNS_CRYPTO_LEAKS
+ CRYPTO_malloc_debug_init();
+ CRYPTO_set_mem_debug_options(V_CRYPTO_MDEBUG_ALL);
+ CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
+#endif
+ CRYPTO_set_mem_functions(mem_alloc, mem_realloc, mem_free);
+ nlocks = CRYPTO_num_locks();
+ locks = mem_alloc(sizeof(isc_mutex_t) * nlocks);
+ if (locks == NULL)
+ return (ISC_R_NOMEMORY);
+ result = isc_mutexblock_init(locks, nlocks);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_mutexalloc;
+ CRYPTO_set_locking_callback(lock_callback);
+ CRYPTO_set_id_callback(id_callback);
+
+ rm = mem_alloc(sizeof(RAND_METHOD));
+ if (rm == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_mutexinit;
+ }
+ rm->seed = NULL;
+ rm->bytes = entropy_get;
+ rm->cleanup = NULL;
+ rm->add = entropy_add;
+ rm->pseudorand = entropy_getpseudo;
+ rm->status = entropy_status;
+#ifdef USE_ENGINE
+ OPENSSL_config(NULL);
+#ifdef USE_PKCS11
+#ifndef PKCS11_SO_PATH
+#define PKCS11_SO_PATH "/usr/local/lib/engines/engine_pkcs11.so"
+#endif
+#ifndef PKCS11_MODULE_PATH
+#define PKCS11_MODULE_PATH "/usr/lib/libpkcs11.so"
+#endif
+ {
+ /*
+ * to use this to config the PIN, add in openssl.cnf:
+ * - at the beginning: "openssl_conf = openssl_def"
+ * - at any place these sections:
+ * [ openssl_def ]
+ * engines = engine_section
+ * [ engine_section ]
+ * pkcs11 = pkcs11_section
+ * [ pkcs11_section ]
+ * PIN = my___pin
+ */
+
+ const char *pre_cmds[] = {
+ "SO_PATH", PKCS11_SO_PATH,
+ "LOAD", NULL,
+ "MODULE_PATH", PKCS11_MODULE_PATH
+ };
+ const char *post_cmds[] = {
+ /* "PIN", "my___pin" */
+ };
+ result = dst__openssl_load_engine("pkcs11", "pkcs11",
+ pre_cmds, 0,
+ post_cmds, /*1*/ 0);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_rm;
+ }
+#endif /* USE_PKCS11 */
+ if (engine_id != NULL) {
+ e = ENGINE_by_id(engine_id);
+ if (e == NULL) {
+ result = ISC_R_NOTFOUND;
+ goto cleanup_rm;
+ }
+ if (!ENGINE_init(e)) {
+ result = ISC_R_FAILURE;
+ ENGINE_free(e);
+ goto cleanup_rm;
+ }
+ ENGINE_set_default(e, ENGINE_METHOD_ALL);
+ ENGINE_free(e);
+ } else {
+ ENGINE_register_all_complete();
+ for (e = ENGINE_get_first(); e != NULL; e = ENGINE_get_next(e)) {
+
+ /*
+ * Something wierd here. If we call ENGINE_finish()
+ * ENGINE_get_default_RAND() will fail.
+ */
+ if (ENGINE_init(e)) {
+ if (he == NULL)
+ he = e;
+ }
+ }
+ }
+ re = ENGINE_get_default_RAND();
+ if (re == NULL) {
+ re = ENGINE_new();
+ if (re == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_rm;
+ }
+ ENGINE_set_RAND(re, rm);
+ ENGINE_set_default_RAND(re);
+ ENGINE_free(re);
+ } else
+ ENGINE_finish(re);
+
+#else
+ RAND_set_rand_method(rm);
+#endif /* USE_ENGINE */
+ return (ISC_R_SUCCESS);
+
+#ifdef USE_ENGINE
+ cleanup_rm:
+ mem_free(rm);
+#endif
+ cleanup_mutexinit:
+ CRYPTO_set_locking_callback(NULL);
+ DESTROYMUTEXBLOCK(locks, nlocks);
+ cleanup_mutexalloc:
+ mem_free(locks);
+ return (result);
+}
+
+void
+dst__openssl_destroy() {
+
+ /*
+ * Sequence taken from apps_shutdown() in <apps/apps.h>.
+ */
+#if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
+ CONF_modules_unload(1);
+#endif
+ EVP_cleanup();
+#if defined(USE_ENGINE)
+ if (e != NULL) {
+ ENGINE_finish(e);
+ e = NULL;
+ }
+#if defined(USE_ENGINE) && OPENSSL_VERSION_NUMBER >= 0x00907000L
+ ENGINE_cleanup();
+#endif
+#endif
+#if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
+ CRYPTO_cleanup_all_ex_data();
+#endif
+ ERR_clear_error();
+ ERR_free_strings();
+ ERR_remove_state(0);
+
+#ifdef DNS_CRYPTO_LEAKS
+ CRYPTO_mem_leaks_fp(stderr);
+#endif
+
+ if (rm != NULL) {
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L
+ RAND_cleanup();
+#endif
+ mem_free(rm);
+ }
+ if (locks != NULL) {
+ CRYPTO_set_locking_callback(NULL);
+ DESTROYMUTEXBLOCK(locks, nlocks);
+ mem_free(locks);
+ }
+}
+
+isc_result_t
+dst__openssl_toresult(isc_result_t fallback) {
+ isc_result_t result = fallback;
+ int err = ERR_get_error();
+
+ switch (ERR_GET_REASON(err)) {
+ case ERR_R_MALLOC_FAILURE:
+ result = ISC_R_NOMEMORY;
+ break;
+ default:
+ break;
+ }
+ ERR_clear_error();
+ return (result);
+}
+
+ENGINE *
+dst__openssl_getengine(const char *name) {
+
+ UNUSED(name);
+
+
+#if defined(USE_ENGINE)
+ return (he);
+#else
+ return (NULL);
+#endif
+}
+
+isc_result_t
+dst__openssl_setdefault(const char *name) {
+
+ UNUSED(name);
+
+#if defined(USE_ENGINE)
+ ENGINE_set_default(e, ENGINE_METHOD_ALL);
+#endif
+ /*
+ * XXXMPA If the engine does not have a default RAND method
+ * restore our method.
+ */
+ return (ISC_R_SUCCESS);
+}
+
+#ifdef USE_PKCS11
+/*
+ * 'name' is the name the engine is known by to the dst library.
+ * This may or may not match the name the engine is known by to
+ * openssl. It is the name that is stored in the private key file.
+ *
+ * 'engine_id' is the openssl engine name.
+ *
+ * pre_cmds and post_cmds a sequence if command arguement pairs
+ * pre_num and post_num are a count of those pairs.
+ *
+ * "SO_PATH", PKCS11_SO_PATH ("/usr/local/lib/engines/engine_pkcs11.so")
+ * "LOAD", NULL
+ * "MODULE_PATH", PKCS11_MODULE_PATH ("/usr/lib/libpkcs11.so")
+ */
+static isc_result_t
+dst__openssl_load_engine(const char *name, const char *engine_id,
+ const char **pre_cmds, int pre_num,
+ const char **post_cmds, int post_num)
+{
+ ENGINE *e;
+
+ UNUSED(name);
+
+ if (!strcasecmp(engine_id, "dynamic"))
+ ENGINE_load_dynamic();
+ e = ENGINE_by_id(engine_id);
+ if (e == NULL)
+ return (ISC_R_NOTFOUND);
+ while (pre_num--) {
+ if (!ENGINE_ctrl_cmd_string(e, pre_cmds[0], pre_cmds[1], 0)) {
+ ENGINE_free(e);
+ return (ISC_R_FAILURE);
+ }
+ pre_cmds += 2;
+ }
+ if (!ENGINE_init(e)) {
+ ENGINE_free(e);
+ return (ISC_R_FAILURE);
+ }
+ /*
+ * ENGINE_init() returned a functional reference, so free the
+ * structural reference from ENGINE_by_id().
+ */
+ ENGINE_free(e);
+ while (post_num--) {
+ if (!ENGINE_ctrl_cmd_string(e, post_cmds[0], post_cmds[1], 0)) {
+ ENGINE_free(e);
+ return (ISC_R_FAILURE);
+ }
+ post_cmds += 2;
+ }
+ if (he != NULL)
+ ENGINE_finish(he);
+ he = e;
+ return (ISC_R_SUCCESS);
+}
+#endif /* USE_PKCS11 */
+
+#else /* OPENSSL */
+
+#include <isc/util.h>
+
+EMPTY_TRANSLATION_UNIT
+
+#endif /* OPENSSL */
+/*! \file */
diff --git a/lib/dns/openssldh_link.c b/lib/dns/openssldh_link.c
new file mode 100644
index 0000000..abc3b7c
--- /dev/null
+++ b/lib/dns/openssldh_link.c
@@ -0,0 +1,638 @@
+/*
+ * Portions Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 NETWORK ASSOCIATES 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.
+ *
+ * Portions Copyright (C) 1995-2000 by Network Associates, 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 NETWORK ASSOCIATES 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.
+ */
+
+/*
+ * Principal Author: Brian Wellington
+ * $Id: openssldh_link.c,v 1.14 2008/04/01 23:47:10 tbox Exp $
+ */
+
+#ifdef OPENSSL
+
+#include <config.h>
+
+#include <ctype.h>
+
+#include <isc/mem.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dst/result.h>
+
+#include "dst_internal.h"
+#include "dst_openssl.h"
+#include "dst_parse.h"
+
+#define PRIME768 "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088" \
+ "A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25" \
+ "F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF"
+
+#define PRIME1024 "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" \
+ "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF2" \
+ "5F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406" \
+ "B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF"
+
+#define PRIME1536 "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
+ "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
+ "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
+ "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
+ "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
+ "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
+ "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
+ "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF"
+
+
+static isc_result_t openssldh_todns(const dst_key_t *key, isc_buffer_t *data);
+
+static BIGNUM bn2, bn768, bn1024, bn1536;
+
+static isc_result_t
+openssldh_computesecret(const dst_key_t *pub, const dst_key_t *priv,
+ isc_buffer_t *secret)
+{
+ DH *dhpub, *dhpriv;
+ int ret;
+ isc_region_t r;
+ unsigned int len;
+
+ REQUIRE(pub->keydata.dh != NULL);
+ REQUIRE(priv->keydata.dh != NULL);
+
+ dhpub = pub->keydata.dh;
+ dhpriv = priv->keydata.dh;
+
+ len = DH_size(dhpriv);
+ isc_buffer_availableregion(secret, &r);
+ if (r.length < len)
+ return (ISC_R_NOSPACE);
+ ret = DH_compute_key(r.base, dhpub->pub_key, dhpriv);
+ if (ret == 0)
+ return (dst__openssl_toresult(DST_R_COMPUTESECRETFAILURE));
+ isc_buffer_add(secret, len);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_boolean_t
+openssldh_compare(const dst_key_t *key1, const dst_key_t *key2) {
+ int status;
+ DH *dh1, *dh2;
+
+ dh1 = key1->keydata.dh;
+ dh2 = key2->keydata.dh;
+
+ if (dh1 == NULL && dh2 == NULL)
+ return (ISC_TRUE);
+ else if (dh1 == NULL || dh2 == NULL)
+ return (ISC_FALSE);
+
+ status = BN_cmp(dh1->p, dh2->p) ||
+ BN_cmp(dh1->g, dh2->g) ||
+ BN_cmp(dh1->pub_key, dh2->pub_key);
+
+ if (status != 0)
+ return (ISC_FALSE);
+
+ if (dh1->priv_key != NULL || dh2->priv_key != NULL) {
+ if (dh1->priv_key == NULL || dh2->priv_key == NULL)
+ return (ISC_FALSE);
+ if (BN_cmp(dh1->priv_key, dh2->priv_key) != 0)
+ return (ISC_FALSE);
+ }
+ return (ISC_TRUE);
+}
+
+static isc_boolean_t
+openssldh_paramcompare(const dst_key_t *key1, const dst_key_t *key2) {
+ int status;
+ DH *dh1, *dh2;
+
+ dh1 = key1->keydata.dh;
+ dh2 = key2->keydata.dh;
+
+ if (dh1 == NULL && dh2 == NULL)
+ return (ISC_TRUE);
+ else if (dh1 == NULL || dh2 == NULL)
+ return (ISC_FALSE);
+
+ status = BN_cmp(dh1->p, dh2->p) ||
+ BN_cmp(dh1->g, dh2->g);
+
+ if (status != 0)
+ return (ISC_FALSE);
+ return (ISC_TRUE);
+}
+
+static isc_result_t
+openssldh_generate(dst_key_t *key, int generator) {
+#if OPENSSL_VERSION_NUMBER > 0x00908000L
+ BN_GENCB cb;
+#endif
+ DH *dh = NULL;
+
+ if (generator == 0) {
+ if (key->key_size == 768 ||
+ key->key_size == 1024 ||
+ key->key_size == 1536)
+ {
+ dh = DH_new();
+ if (dh == NULL)
+ return (dst__openssl_toresult(ISC_R_NOMEMORY));
+ if (key->key_size == 768)
+ dh->p = &bn768;
+ else if (key->key_size == 1024)
+ dh->p = &bn1024;
+ else
+ dh->p = &bn1536;
+ dh->g = &bn2;
+ } else
+ generator = 2;
+ }
+
+ if (generator != 0) {
+#if OPENSSL_VERSION_NUMBER > 0x00908000L
+ dh = DH_new();
+ if (dh == NULL)
+ return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+
+ BN_GENCB_set_old(&cb, NULL, NULL);
+
+ if (!DH_generate_parameters_ex(dh, key->key_size, generator,
+ &cb)) {
+ DH_free(dh);
+ return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+ }
+#else
+ dh = DH_generate_parameters(key->key_size, generator,
+ NULL, NULL);
+#endif
+ }
+
+ if (dh == NULL)
+ return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+
+ if (DH_generate_key(dh) == 0) {
+ DH_free(dh);
+ return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+ }
+ dh->flags &= ~DH_FLAG_CACHE_MONT_P;
+
+ key->keydata.dh = dh;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_boolean_t
+openssldh_isprivate(const dst_key_t *key) {
+ DH *dh = key->keydata.dh;
+ return (ISC_TF(dh != NULL && dh->priv_key != NULL));
+}
+
+static void
+openssldh_destroy(dst_key_t *key) {
+ DH *dh = key->keydata.dh;
+
+ if (dh == NULL)
+ return;
+
+ if (dh->p == &bn768 || dh->p == &bn1024 || dh->p == &bn1536)
+ dh->p = NULL;
+ if (dh->g == &bn2)
+ dh->g = NULL;
+ DH_free(dh);
+ key->keydata.dh = NULL;
+}
+
+static void
+uint16_toregion(isc_uint16_t val, isc_region_t *region) {
+ *region->base++ = (val & 0xff00) >> 8;
+ *region->base++ = (val & 0x00ff);
+}
+
+static isc_uint16_t
+uint16_fromregion(isc_region_t *region) {
+ isc_uint16_t val;
+ unsigned char *cp = region->base;
+
+ val = ((unsigned int)(cp[0])) << 8;
+ val |= ((unsigned int)(cp[1]));
+
+ region->base += 2;
+ return (val);
+}
+
+static isc_result_t
+openssldh_todns(const dst_key_t *key, isc_buffer_t *data) {
+ DH *dh;
+ isc_region_t r;
+ isc_uint16_t dnslen, plen, glen, publen;
+
+ REQUIRE(key->keydata.dh != NULL);
+
+ dh = key->keydata.dh;
+
+ isc_buffer_availableregion(data, &r);
+
+ if (dh->g == &bn2 &&
+ (dh->p == &bn768 || dh->p == &bn1024 || dh->p == &bn1536)) {
+ plen = 1;
+ glen = 0;
+ }
+ else {
+ plen = BN_num_bytes(dh->p);
+ glen = BN_num_bytes(dh->g);
+ }
+ publen = BN_num_bytes(dh->pub_key);
+ dnslen = plen + glen + publen + 6;
+ if (r.length < (unsigned int) dnslen)
+ return (ISC_R_NOSPACE);
+
+ uint16_toregion(plen, &r);
+ if (plen == 1) {
+ if (dh->p == &bn768)
+ *r.base = 1;
+ else if (dh->p == &bn1024)
+ *r.base = 2;
+ else
+ *r.base = 3;
+ }
+ else
+ BN_bn2bin(dh->p, r.base);
+ r.base += plen;
+
+ uint16_toregion(glen, &r);
+ if (glen > 0)
+ BN_bn2bin(dh->g, r.base);
+ r.base += glen;
+
+ uint16_toregion(publen, &r);
+ BN_bn2bin(dh->pub_key, r.base);
+ r.base += publen;
+
+ isc_buffer_add(data, dnslen);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+openssldh_fromdns(dst_key_t *key, isc_buffer_t *data) {
+ DH *dh;
+ isc_region_t r;
+ isc_uint16_t plen, glen, publen;
+ int special = 0;
+
+ isc_buffer_remainingregion(data, &r);
+ if (r.length == 0)
+ return (ISC_R_SUCCESS);
+
+ dh = DH_new();
+ if (dh == NULL)
+ return (dst__openssl_toresult(ISC_R_NOMEMORY));
+ dh->flags &= ~DH_FLAG_CACHE_MONT_P;
+
+ /*
+ * Read the prime length. 1 & 2 are table entries, > 16 means a
+ * prime follows, otherwise an error.
+ */
+ if (r.length < 2) {
+ DH_free(dh);
+ return (DST_R_INVALIDPUBLICKEY);
+ }
+ plen = uint16_fromregion(&r);
+ if (plen < 16 && plen != 1 && plen != 2) {
+ DH_free(dh);
+ return (DST_R_INVALIDPUBLICKEY);
+ }
+ if (r.length < plen) {
+ DH_free(dh);
+ return (DST_R_INVALIDPUBLICKEY);
+ }
+ if (plen == 1 || plen == 2) {
+ if (plen == 1)
+ special = *r.base++;
+ else
+ special = uint16_fromregion(&r);
+ switch (special) {
+ case 1:
+ dh->p = &bn768;
+ break;
+ case 2:
+ dh->p = &bn1024;
+ break;
+ case 3:
+ dh->p = &bn1536;
+ break;
+ default:
+ DH_free(dh);
+ return (DST_R_INVALIDPUBLICKEY);
+ }
+ }
+ else {
+ dh->p = BN_bin2bn(r.base, plen, NULL);
+ r.base += plen;
+ }
+
+ /*
+ * Read the generator length. This should be 0 if the prime was
+ * special, but it might not be. If it's 0 and the prime is not
+ * special, we have a problem.
+ */
+ if (r.length < 2) {
+ DH_free(dh);
+ return (DST_R_INVALIDPUBLICKEY);
+ }
+ glen = uint16_fromregion(&r);
+ if (r.length < glen) {
+ DH_free(dh);
+ return (DST_R_INVALIDPUBLICKEY);
+ }
+ if (special != 0) {
+ if (glen == 0)
+ dh->g = &bn2;
+ else {
+ dh->g = BN_bin2bn(r.base, glen, NULL);
+ if (BN_cmp(dh->g, &bn2) == 0) {
+ BN_free(dh->g);
+ dh->g = &bn2;
+ }
+ else {
+ DH_free(dh);
+ return (DST_R_INVALIDPUBLICKEY);
+ }
+ }
+ }
+ else {
+ if (glen == 0) {
+ DH_free(dh);
+ return (DST_R_INVALIDPUBLICKEY);
+ }
+ dh->g = BN_bin2bn(r.base, glen, NULL);
+ }
+ r.base += glen;
+
+ if (r.length < 2) {
+ DH_free(dh);
+ return (DST_R_INVALIDPUBLICKEY);
+ }
+ publen = uint16_fromregion(&r);
+ if (r.length < publen) {
+ DH_free(dh);
+ return (DST_R_INVALIDPUBLICKEY);
+ }
+ dh->pub_key = BN_bin2bn(r.base, publen, NULL);
+ r.base += publen;
+
+ key->key_size = BN_num_bits(dh->p);
+
+ isc_buffer_forward(data, plen + glen + publen + 6);
+
+ key->keydata.dh = dh;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+openssldh_tofile(const dst_key_t *key, const char *directory) {
+ int i;
+ DH *dh;
+ dst_private_t priv;
+ unsigned char *bufs[4];
+ isc_result_t result;
+
+ if (key->keydata.dh == NULL)
+ return (DST_R_NULLKEY);
+
+ dh = key->keydata.dh;
+
+ for (i = 0; i < 4; i++) {
+ bufs[i] = isc_mem_get(key->mctx, BN_num_bytes(dh->p));
+ if (bufs[i] == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto fail;
+ }
+ }
+
+ i = 0;
+
+ priv.elements[i].tag = TAG_DH_PRIME;
+ priv.elements[i].length = BN_num_bytes(dh->p);
+ BN_bn2bin(dh->p, bufs[i]);
+ priv.elements[i].data = bufs[i];
+ i++;
+
+ priv.elements[i].tag = TAG_DH_GENERATOR;
+ priv.elements[i].length = BN_num_bytes(dh->g);
+ BN_bn2bin(dh->g, bufs[i]);
+ priv.elements[i].data = bufs[i];
+ i++;
+
+ priv.elements[i].tag = TAG_DH_PRIVATE;
+ priv.elements[i].length = BN_num_bytes(dh->priv_key);
+ BN_bn2bin(dh->priv_key, bufs[i]);
+ priv.elements[i].data = bufs[i];
+ i++;
+
+ priv.elements[i].tag = TAG_DH_PUBLIC;
+ priv.elements[i].length = BN_num_bytes(dh->pub_key);
+ BN_bn2bin(dh->pub_key, bufs[i]);
+ priv.elements[i].data = bufs[i];
+ i++;
+
+ priv.nelements = i;
+ result = dst__privstruct_writefile(key, &priv, directory);
+ fail:
+ for (i = 0; i < 4; i++) {
+ if (bufs[i] == NULL)
+ break;
+ isc_mem_put(key->mctx, bufs[i], BN_num_bytes(dh->p));
+ }
+ return (result);
+}
+
+static isc_result_t
+openssldh_parse(dst_key_t *key, isc_lex_t *lexer) {
+ dst_private_t priv;
+ isc_result_t ret;
+ int i;
+ DH *dh = NULL;
+ isc_mem_t *mctx;
+#define DST_RET(a) {ret = a; goto err;}
+
+ mctx = key->mctx;
+
+ /* read private key file */
+ ret = dst__privstruct_parse(key, DST_ALG_DH, lexer, mctx, &priv);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ dh = DH_new();
+ if (dh == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ dh->flags &= ~DH_FLAG_CACHE_MONT_P;
+ key->keydata.dh = dh;
+
+ for (i = 0; i < priv.nelements; i++) {
+ BIGNUM *bn;
+ bn = BN_bin2bn(priv.elements[i].data,
+ priv.elements[i].length, NULL);
+ if (bn == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+
+ switch (priv.elements[i].tag) {
+ case TAG_DH_PRIME:
+ dh->p = bn;
+ break;
+ case TAG_DH_GENERATOR:
+ dh->g = bn;
+ break;
+ case TAG_DH_PRIVATE:
+ dh->priv_key = bn;
+ break;
+ case TAG_DH_PUBLIC:
+ dh->pub_key = bn;
+ break;
+ }
+ }
+ dst__privstruct_free(&priv, mctx);
+
+ key->key_size = BN_num_bits(dh->p);
+
+ if ((key->key_size == 768 ||
+ key->key_size == 1024 ||
+ key->key_size == 1536) &&
+ BN_cmp(dh->g, &bn2) == 0)
+ {
+ if (key->key_size == 768 && BN_cmp(dh->p, &bn768) == 0) {
+ BN_free(dh->p);
+ BN_free(dh->g);
+ dh->p = &bn768;
+ dh->g = &bn2;
+ } else if (key->key_size == 1024 &&
+ BN_cmp(dh->p, &bn1024) == 0) {
+ BN_free(dh->p);
+ BN_free(dh->g);
+ dh->p = &bn1024;
+ dh->g = &bn2;
+ } else if (key->key_size == 1536 &&
+ BN_cmp(dh->p, &bn1536) == 0) {
+ BN_free(dh->p);
+ BN_free(dh->g);
+ dh->p = &bn1536;
+ dh->g = &bn2;
+ }
+ }
+
+ return (ISC_R_SUCCESS);
+
+ err:
+ openssldh_destroy(key);
+ dst__privstruct_free(&priv, mctx);
+ memset(&priv, 0, sizeof(priv));
+ return (ret);
+}
+
+static void
+BN_fromhex(BIGNUM *b, const char *str) {
+ static const char hexdigits[] = "0123456789abcdef";
+ unsigned char data[512];
+ unsigned int i;
+ BIGNUM *out;
+
+ RUNTIME_CHECK(strlen(str) < 1024U && strlen(str) % 2 == 0U);
+ for (i = 0; i < strlen(str); i += 2) {
+ char *s;
+ unsigned int high, low;
+
+ s = strchr(hexdigits, tolower((unsigned char)str[i]));
+ RUNTIME_CHECK(s != NULL);
+ high = s - hexdigits;
+
+ s = strchr(hexdigits, tolower((unsigned char)str[i + 1]));
+ RUNTIME_CHECK(s != NULL);
+ low = s - hexdigits;
+
+ data[i/2] = (unsigned char)((high << 4) + low);
+ }
+ out = BN_bin2bn(data, strlen(str)/2, b);
+ RUNTIME_CHECK(out != NULL);
+}
+
+static void
+openssldh_cleanup(void) {
+ BN_free(&bn2);
+ BN_free(&bn768);
+ BN_free(&bn1024);
+ BN_free(&bn1536);
+}
+
+static dst_func_t openssldh_functions = {
+ NULL, /*%< createctx */
+ NULL, /*%< destroyctx */
+ NULL, /*%< adddata */
+ NULL, /*%< openssldh_sign */
+ NULL, /*%< openssldh_verify */
+ openssldh_computesecret,
+ openssldh_compare,
+ openssldh_paramcompare,
+ openssldh_generate,
+ openssldh_isprivate,
+ openssldh_destroy,
+ openssldh_todns,
+ openssldh_fromdns,
+ openssldh_tofile,
+ openssldh_parse,
+ openssldh_cleanup,
+ NULL, /*%< fromlabel */
+};
+
+isc_result_t
+dst__openssldh_init(dst_func_t **funcp) {
+ REQUIRE(funcp != NULL);
+ if (*funcp == NULL) {
+ BN_init(&bn2);
+ BN_init(&bn768);
+ BN_init(&bn1024);
+ BN_init(&bn1536);
+ BN_set_word(&bn2, 2);
+ BN_fromhex(&bn768, PRIME768);
+ BN_fromhex(&bn1024, PRIME1024);
+ BN_fromhex(&bn1536, PRIME1536);
+ *funcp = &openssldh_functions;
+ }
+ return (ISC_R_SUCCESS);
+}
+
+#else /* OPENSSL */
+
+#include <isc/util.h>
+
+EMPTY_TRANSLATION_UNIT
+
+#endif /* OPENSSL */
+/*! \file */
diff --git a/lib/dns/openssldsa_link.c b/lib/dns/openssldsa_link.c
new file mode 100644
index 0000000..2e121ef
--- /dev/null
+++ b/lib/dns/openssldsa_link.c
@@ -0,0 +1,607 @@
+/*
+ * Portions Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 NETWORK ASSOCIATES 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.
+ *
+ * Portions Copyright (C) 1995-2000 by Network Associates, 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 NETWORK ASSOCIATES 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.
+ */
+
+/* $Id: openssldsa_link.c,v 1.13.126.1 2008/12/24 00:20:14 marka Exp $ */
+
+#ifdef OPENSSL
+#ifndef USE_EVP
+#define USE_EVP 1
+#endif
+
+#include <config.h>
+
+#include <string.h>
+
+#include <isc/entropy.h>
+#include <isc/mem.h>
+#include <isc/sha1.h>
+#include <isc/util.h>
+
+#include <dst/result.h>
+
+#include "dst_internal.h"
+#include "dst_openssl.h"
+#include "dst_parse.h"
+
+#include <openssl/dsa.h>
+
+static isc_result_t openssldsa_todns(const dst_key_t *key, isc_buffer_t *data);
+
+static isc_result_t
+openssldsa_createctx(dst_key_t *key, dst_context_t *dctx) {
+#if USE_EVP
+ EVP_MD_CTX *evp_md_ctx;
+
+ UNUSED(key);
+
+ evp_md_ctx = EVP_MD_CTX_create();
+ if (evp_md_ctx == NULL)
+ return (ISC_R_NOMEMORY);
+
+ if (!EVP_DigestInit_ex(evp_md_ctx, EVP_dss1(), NULL)) {
+ EVP_MD_CTX_destroy(evp_md_ctx);
+ return (ISC_R_FAILURE);
+ }
+
+ dctx->ctxdata.evp_md_ctx = evp_md_ctx;
+
+ return (ISC_R_SUCCESS);
+#else
+ isc_sha1_t *sha1ctx;
+
+ UNUSED(key);
+
+ sha1ctx = isc_mem_get(dctx->mctx, sizeof(isc_sha1_t));
+ isc_sha1_init(sha1ctx);
+ dctx->ctxdata.sha1ctx = sha1ctx;
+ return (ISC_R_SUCCESS);
+#endif
+}
+
+static void
+openssldsa_destroyctx(dst_context_t *dctx) {
+#if USE_EVP
+ EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
+
+ if (evp_md_ctx != NULL) {
+ EVP_MD_CTX_destroy(evp_md_ctx);
+ dctx->ctxdata.evp_md_ctx = NULL;
+ }
+#else
+ isc_sha1_t *sha1ctx = dctx->ctxdata.sha1ctx;
+
+ if (sha1ctx != NULL) {
+ isc_sha1_invalidate(sha1ctx);
+ isc_mem_put(dctx->mctx, sha1ctx, sizeof(isc_sha1_t));
+ dctx->ctxdata.sha1ctx = NULL;
+ }
+#endif
+}
+
+static isc_result_t
+openssldsa_adddata(dst_context_t *dctx, const isc_region_t *data) {
+#if USE_EVP
+ EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
+
+ if (!EVP_DigestUpdate(evp_md_ctx, data->base, data->length)) {
+ return (ISC_R_FAILURE);
+ }
+#else
+ isc_sha1_t *sha1ctx = dctx->ctxdata.sha1ctx;
+
+ isc_sha1_update(sha1ctx, data->base, data->length);
+#endif
+ return (ISC_R_SUCCESS);
+}
+
+static int
+BN_bn2bin_fixed(BIGNUM *bn, unsigned char *buf, int size) {
+ int bytes = size - BN_num_bytes(bn);
+ while (bytes-- > 0)
+ *buf++ = 0;
+ BN_bn2bin(bn, buf);
+ return (size);
+}
+
+static isc_result_t
+openssldsa_sign(dst_context_t *dctx, isc_buffer_t *sig) {
+ dst_key_t *key = dctx->key;
+ DSA *dsa = key->keydata.dsa;
+ isc_region_t r;
+ DSA_SIG *dsasig;
+#if USE_EVP
+ EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
+ EVP_PKEY *pkey;
+ unsigned char *sigbuf;
+ const unsigned char *sb;
+ unsigned int siglen;
+#else
+ isc_sha1_t *sha1ctx = dctx->ctxdata.sha1ctx;
+ unsigned char digest[ISC_SHA1_DIGESTLENGTH];
+#endif
+
+ isc_buffer_availableregion(sig, &r);
+ if (r.length < ISC_SHA1_DIGESTLENGTH * 2 + 1)
+ return (ISC_R_NOSPACE);
+
+#if USE_EVP
+ pkey = EVP_PKEY_new();
+ if (pkey == NULL)
+ return (ISC_R_NOMEMORY);
+ if (!EVP_PKEY_set1_DSA(pkey, dsa)) {
+ EVP_PKEY_free(pkey);
+ return (ISC_R_FAILURE);
+ }
+ sigbuf = malloc(EVP_PKEY_size(pkey));
+ if (sigbuf == NULL) {
+ EVP_PKEY_free(pkey);
+ return (ISC_R_NOMEMORY);
+ }
+ if (!EVP_SignFinal(evp_md_ctx, sigbuf, &siglen, pkey)) {
+ EVP_PKEY_free(pkey);
+ free(sigbuf);
+ return (ISC_R_FAILURE);
+ }
+ INSIST(EVP_PKEY_size(pkey) >= (int) siglen);
+ EVP_PKEY_free(pkey);
+ /* Convert from Dss-Sig-Value (RFC2459). */
+ dsasig = DSA_SIG_new();
+ if (dsasig == NULL) {
+ free(sigbuf);
+ return (ISC_R_NOMEMORY);
+ }
+ sb = sigbuf;
+ if (d2i_DSA_SIG(&dsasig, &sb, (long) siglen) == NULL) {
+ free(sigbuf);
+ return (ISC_R_FAILURE);
+ }
+ free(sigbuf);
+#elif 0
+ /* Only use EVP for the Digest */
+ if (!EVP_DigestFinal_ex(evp_md_ctx, digest, &siglen)) {
+ return (ISC_R_FAILURE);
+ }
+ dsasig = DSA_do_sign(digest, ISC_SHA1_DIGESTLENGTH, dsa);
+ if (dsasig == NULL)
+ return (dst__openssl_toresult(DST_R_SIGNFAILURE));
+#else
+ isc_sha1_final(sha1ctx, digest);
+
+ dsasig = DSA_do_sign(digest, ISC_SHA1_DIGESTLENGTH, dsa);
+ if (dsasig == NULL)
+ return (dst__openssl_toresult(DST_R_SIGNFAILURE));
+#endif
+ *r.base++ = (key->key_size - 512)/64;
+ BN_bn2bin_fixed(dsasig->r, r.base, ISC_SHA1_DIGESTLENGTH);
+ r.base += ISC_SHA1_DIGESTLENGTH;
+ BN_bn2bin_fixed(dsasig->s, r.base, ISC_SHA1_DIGESTLENGTH);
+ r.base += ISC_SHA1_DIGESTLENGTH;
+ DSA_SIG_free(dsasig);
+ isc_buffer_add(sig, ISC_SHA1_DIGESTLENGTH * 2 + 1);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+openssldsa_verify(dst_context_t *dctx, const isc_region_t *sig) {
+ dst_key_t *key = dctx->key;
+ DSA *dsa = key->keydata.dsa;
+ int status = 0;
+ unsigned char *cp = sig->base;
+ DSA_SIG *dsasig;
+#if USE_EVP
+ EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
+#if 0
+ EVP_PKEY *pkey;
+ unsigned char *sigbuf;
+#endif
+ unsigned int siglen;
+#else
+ isc_sha1_t *sha1ctx = dctx->ctxdata.sha1ctx;
+#endif
+ unsigned char digest[ISC_SHA1_DIGESTLENGTH];
+
+
+#if USE_EVP
+#if 1
+ /* Only use EVP for the digest */
+ if (!EVP_DigestFinal_ex(evp_md_ctx, digest, &siglen)) {
+ return (ISC_R_FAILURE);
+ }
+#endif
+#else
+ isc_sha1_final(sha1ctx, digest);
+#endif
+
+ if (sig->length != 2 * ISC_SHA1_DIGESTLENGTH + 1) {
+ return (DST_R_VERIFYFAILURE);
+ }
+
+ cp++; /*%< Skip T */
+ dsasig = DSA_SIG_new();
+ if (dsasig == NULL)
+ return (ISC_R_NOMEMORY);
+ dsasig->r = BN_bin2bn(cp, ISC_SHA1_DIGESTLENGTH, NULL);
+ cp += ISC_SHA1_DIGESTLENGTH;
+ dsasig->s = BN_bin2bn(cp, ISC_SHA1_DIGESTLENGTH, NULL);
+ cp += ISC_SHA1_DIGESTLENGTH;
+
+#if 0
+ pkey = EVP_PKEY_new();
+ if (pkey == NULL)
+ return (ISC_R_NOMEMORY);
+ if (!EVP_PKEY_set1_DSA(pkey, dsa)) {
+ EVP_PKEY_free(pkey);
+ return (ISC_R_FAILURE);
+ }
+ /* Convert to Dss-Sig-Value (RFC2459). */
+ sigbuf = malloc(EVP_PKEY_size(pkey) + 50);
+ if (sigbuf == NULL) {
+ EVP_PKEY_free(pkey);
+ return (ISC_R_NOMEMORY);
+ }
+ siglen = (unsigned) i2d_DSA_SIG(dsasig, &sigbuf);
+ INSIST(EVP_PKEY_size(pkey) >= (int) siglen);
+ status = EVP_VerifyFinal(evp_md_ctx, sigbuf, siglen, pkey);
+ EVP_PKEY_free(pkey);
+ free(sigbuf);
+#else
+ status = DSA_do_verify(digest, ISC_SHA1_DIGESTLENGTH, dsasig, dsa);
+#endif
+ DSA_SIG_free(dsasig);
+ if (status != 1)
+ return (dst__openssl_toresult(DST_R_VERIFYFAILURE));
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_boolean_t
+openssldsa_compare(const dst_key_t *key1, const dst_key_t *key2) {
+ int status;
+ DSA *dsa1, *dsa2;
+
+ dsa1 = key1->keydata.dsa;
+ dsa2 = key2->keydata.dsa;
+
+ if (dsa1 == NULL && dsa2 == NULL)
+ return (ISC_TRUE);
+ else if (dsa1 == NULL || dsa2 == NULL)
+ return (ISC_FALSE);
+
+ status = BN_cmp(dsa1->p, dsa2->p) ||
+ BN_cmp(dsa1->q, dsa2->q) ||
+ BN_cmp(dsa1->g, dsa2->g) ||
+ BN_cmp(dsa1->pub_key, dsa2->pub_key);
+
+ if (status != 0)
+ return (ISC_FALSE);
+
+ if (dsa1->priv_key != NULL || dsa2->priv_key != NULL) {
+ if (dsa1->priv_key == NULL || dsa2->priv_key == NULL)
+ return (ISC_FALSE);
+ if (BN_cmp(dsa1->priv_key, dsa2->priv_key))
+ return (ISC_FALSE);
+ }
+ return (ISC_TRUE);
+}
+
+static isc_result_t
+openssldsa_generate(dst_key_t *key, int unused) {
+#if OPENSSL_VERSION_NUMBER > 0x00908000L
+ BN_GENCB cb;
+#endif
+ DSA *dsa;
+ unsigned char rand_array[ISC_SHA1_DIGESTLENGTH];
+ isc_result_t result;
+
+ UNUSED(unused);
+
+ result = dst__entropy_getdata(rand_array, sizeof(rand_array),
+ ISC_FALSE);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+#if OPENSSL_VERSION_NUMBER > 0x00908000L
+ dsa = DSA_new();
+ if (dsa == NULL)
+ return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+
+ BN_GENCB_set_old(&cb, NULL, NULL);
+
+ if (!DSA_generate_parameters_ex(dsa, key->key_size, rand_array,
+ ISC_SHA1_DIGESTLENGTH, NULL, NULL,
+ &cb))
+ {
+ DSA_free(dsa);
+ return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+ }
+#else
+ dsa = DSA_generate_parameters(key->key_size, rand_array,
+ ISC_SHA1_DIGESTLENGTH, NULL, NULL,
+ NULL, NULL);
+ if (dsa == NULL)
+ return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+#endif
+
+ if (DSA_generate_key(dsa) == 0) {
+ DSA_free(dsa);
+ return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+ }
+ dsa->flags &= ~DSA_FLAG_CACHE_MONT_P;
+
+ key->keydata.dsa = dsa;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_boolean_t
+openssldsa_isprivate(const dst_key_t *key) {
+ DSA *dsa = key->keydata.dsa;
+ return (ISC_TF(dsa != NULL && dsa->priv_key != NULL));
+}
+
+static void
+openssldsa_destroy(dst_key_t *key) {
+ DSA *dsa = key->keydata.dsa;
+ DSA_free(dsa);
+ key->keydata.dsa = NULL;
+}
+
+
+static isc_result_t
+openssldsa_todns(const dst_key_t *key, isc_buffer_t *data) {
+ DSA *dsa;
+ isc_region_t r;
+ int dnslen;
+ unsigned int t, p_bytes;
+
+ REQUIRE(key->keydata.dsa != NULL);
+
+ dsa = key->keydata.dsa;
+
+ isc_buffer_availableregion(data, &r);
+
+ t = (BN_num_bytes(dsa->p) - 64) / 8;
+ if (t > 8)
+ return (DST_R_INVALIDPUBLICKEY);
+ p_bytes = 64 + 8 * t;
+
+ dnslen = 1 + (key->key_size * 3)/8 + ISC_SHA1_DIGESTLENGTH;
+ if (r.length < (unsigned int) dnslen)
+ return (ISC_R_NOSPACE);
+
+ *r.base++ = t;
+ BN_bn2bin_fixed(dsa->q, r.base, ISC_SHA1_DIGESTLENGTH);
+ r.base += ISC_SHA1_DIGESTLENGTH;
+ BN_bn2bin_fixed(dsa->p, r.base, key->key_size/8);
+ r.base += p_bytes;
+ BN_bn2bin_fixed(dsa->g, r.base, key->key_size/8);
+ r.base += p_bytes;
+ BN_bn2bin_fixed(dsa->pub_key, r.base, key->key_size/8);
+ r.base += p_bytes;
+
+ isc_buffer_add(data, dnslen);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+openssldsa_fromdns(dst_key_t *key, isc_buffer_t *data) {
+ DSA *dsa;
+ isc_region_t r;
+ unsigned int t, p_bytes;
+ isc_mem_t *mctx = key->mctx;
+
+ UNUSED(mctx);
+
+ isc_buffer_remainingregion(data, &r);
+ if (r.length == 0)
+ return (ISC_R_SUCCESS);
+
+ dsa = DSA_new();
+ if (dsa == NULL)
+ return (ISC_R_NOMEMORY);
+ dsa->flags &= ~DSA_FLAG_CACHE_MONT_P;
+
+ t = (unsigned int) *r.base++;
+ if (t > 8) {
+ DSA_free(dsa);
+ return (DST_R_INVALIDPUBLICKEY);
+ }
+ p_bytes = 64 + 8 * t;
+
+ if (r.length < 1 + ISC_SHA1_DIGESTLENGTH + 3 * p_bytes) {
+ DSA_free(dsa);
+ return (DST_R_INVALIDPUBLICKEY);
+ }
+
+ dsa->q = BN_bin2bn(r.base, ISC_SHA1_DIGESTLENGTH, NULL);
+ r.base += ISC_SHA1_DIGESTLENGTH;
+
+ dsa->p = BN_bin2bn(r.base, p_bytes, NULL);
+ r.base += p_bytes;
+
+ dsa->g = BN_bin2bn(r.base, p_bytes, NULL);
+ r.base += p_bytes;
+
+ dsa->pub_key = BN_bin2bn(r.base, p_bytes, NULL);
+ r.base += p_bytes;
+
+ key->key_size = p_bytes * 8;
+
+ isc_buffer_forward(data, 1 + ISC_SHA1_DIGESTLENGTH + 3 * p_bytes);
+
+ key->keydata.dsa = dsa;
+
+ return (ISC_R_SUCCESS);
+}
+
+
+static isc_result_t
+openssldsa_tofile(const dst_key_t *key, const char *directory) {
+ int cnt = 0;
+ DSA *dsa;
+ dst_private_t priv;
+ unsigned char bufs[5][128];
+
+ if (key->keydata.dsa == NULL)
+ return (DST_R_NULLKEY);
+
+ dsa = key->keydata.dsa;
+
+ priv.elements[cnt].tag = TAG_DSA_PRIME;
+ priv.elements[cnt].length = BN_num_bytes(dsa->p);
+ BN_bn2bin(dsa->p, bufs[cnt]);
+ priv.elements[cnt].data = bufs[cnt];
+ cnt++;
+
+ priv.elements[cnt].tag = TAG_DSA_SUBPRIME;
+ priv.elements[cnt].length = BN_num_bytes(dsa->q);
+ BN_bn2bin(dsa->q, bufs[cnt]);
+ priv.elements[cnt].data = bufs[cnt];
+ cnt++;
+
+ priv.elements[cnt].tag = TAG_DSA_BASE;
+ priv.elements[cnt].length = BN_num_bytes(dsa->g);
+ BN_bn2bin(dsa->g, bufs[cnt]);
+ priv.elements[cnt].data = bufs[cnt];
+ cnt++;
+
+ priv.elements[cnt].tag = TAG_DSA_PRIVATE;
+ priv.elements[cnt].length = BN_num_bytes(dsa->priv_key);
+ BN_bn2bin(dsa->priv_key, bufs[cnt]);
+ priv.elements[cnt].data = bufs[cnt];
+ cnt++;
+
+ priv.elements[cnt].tag = TAG_DSA_PUBLIC;
+ priv.elements[cnt].length = BN_num_bytes(dsa->pub_key);
+ BN_bn2bin(dsa->pub_key, bufs[cnt]);
+ priv.elements[cnt].data = bufs[cnt];
+ cnt++;
+
+ priv.nelements = cnt;
+ return (dst__privstruct_writefile(key, &priv, directory));
+}
+
+static isc_result_t
+openssldsa_parse(dst_key_t *key, isc_lex_t *lexer) {
+ dst_private_t priv;
+ isc_result_t ret;
+ int i;
+ DSA *dsa = NULL;
+ isc_mem_t *mctx = key->mctx;
+#define DST_RET(a) {ret = a; goto err;}
+
+ /* read private key file */
+ ret = dst__privstruct_parse(key, DST_ALG_DSA, lexer, mctx, &priv);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ dsa = DSA_new();
+ if (dsa == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ dsa->flags &= ~DSA_FLAG_CACHE_MONT_P;
+ key->keydata.dsa = dsa;
+
+ for (i=0; i < priv.nelements; i++) {
+ BIGNUM *bn;
+ bn = BN_bin2bn(priv.elements[i].data,
+ priv.elements[i].length, NULL);
+ if (bn == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+
+ switch (priv.elements[i].tag) {
+ case TAG_DSA_PRIME:
+ dsa->p = bn;
+ break;
+ case TAG_DSA_SUBPRIME:
+ dsa->q = bn;
+ break;
+ case TAG_DSA_BASE:
+ dsa->g = bn;
+ break;
+ case TAG_DSA_PRIVATE:
+ dsa->priv_key = bn;
+ break;
+ case TAG_DSA_PUBLIC:
+ dsa->pub_key = bn;
+ break;
+ }
+ }
+ dst__privstruct_free(&priv, mctx);
+
+ key->key_size = BN_num_bits(dsa->p);
+
+ return (ISC_R_SUCCESS);
+
+ err:
+ openssldsa_destroy(key);
+ dst__privstruct_free(&priv, mctx);
+ memset(&priv, 0, sizeof(priv));
+ return (ret);
+}
+
+static dst_func_t openssldsa_functions = {
+ openssldsa_createctx,
+ openssldsa_destroyctx,
+ openssldsa_adddata,
+ openssldsa_sign,
+ openssldsa_verify,
+ NULL, /*%< computesecret */
+ openssldsa_compare,
+ NULL, /*%< paramcompare */
+ openssldsa_generate,
+ openssldsa_isprivate,
+ openssldsa_destroy,
+ openssldsa_todns,
+ openssldsa_fromdns,
+ openssldsa_tofile,
+ openssldsa_parse,
+ NULL, /*%< cleanup */
+ NULL, /*%< fromlabel */
+};
+
+isc_result_t
+dst__openssldsa_init(dst_func_t **funcp) {
+ REQUIRE(funcp != NULL);
+ if (*funcp == NULL)
+ *funcp = &openssldsa_functions;
+ return (ISC_R_SUCCESS);
+}
+
+#else /* OPENSSL */
+
+#include <isc/util.h>
+
+EMPTY_TRANSLATION_UNIT
+
+#endif /* OPENSSL */
+/*! \file */
diff --git a/lib/dns/opensslrsa_link.c b/lib/dns/opensslrsa_link.c
new file mode 100644
index 0000000..aa5fb34
--- /dev/null
+++ b/lib/dns/opensslrsa_link.c
@@ -0,0 +1,986 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/*
+ * Principal Author: Brian Wellington
+ * $Id: opensslrsa_link.c,v 1.20.56.1 2008/12/24 00:20:14 marka Exp $
+ */
+#ifdef OPENSSL
+#ifndef USE_EVP
+#define USE_EVP 1
+#endif
+#if USE_EVP
+#define USE_EVP_RSA 1
+#endif
+
+#include <config.h>
+
+#include <isc/entropy.h>
+#include <isc/md5.h>
+#include <isc/sha1.h>
+#include <isc/mem.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dst/result.h>
+
+#include "dst_internal.h"
+#include "dst_openssl.h"
+#include "dst_parse.h"
+
+#include <openssl/err.h>
+#include <openssl/objects.h>
+#include <openssl/rsa.h>
+#if OPENSSL_VERSION_NUMBER > 0x00908000L
+#include <openssl/bn.h>
+#endif
+#include <openssl/engine.h>
+
+/*
+ * We don't use configure for windows so enforce the OpenSSL version
+ * here. Unlike with configure we don't support overriding this test.
+ */
+#ifdef WIN32
+#if !((OPENSSL_VERSION_NUMBER >= 0x009070cfL && \
+ OPENSSL_VERSION_NUMBER < 0x00908000L) || \
+ OPENSSL_VERSION_NUMBER >= 0x0090804fL)
+#error Please upgrade OpenSSL to 0.9.8d/0.9.7l or greater.
+#endif
+#endif
+
+
+ /*
+ * XXXMPA Temporarially disable RSA_BLINDING as it requires
+ * good quality random data that cannot currently be guarenteed.
+ * XXXMPA Find which versions of openssl use pseudo random data
+ * and set RSA_FLAG_BLINDING for those.
+ */
+
+#if 0
+#if OPENSSL_VERSION_NUMBER < 0x0090601fL
+#define SET_FLAGS(rsa) \
+ do { \
+ (rsa)->flags &= ~(RSA_FLAG_CACHE_PUBLIC | RSA_FLAG_CACHE_PRIVATE); \
+ (rsa)->flags |= RSA_FLAG_BLINDING; \
+ } while (0)
+#else
+#define SET_FLAGS(rsa) \
+ do { \
+ (rsa)->flags |= RSA_FLAG_BLINDING; \
+ } while (0)
+#endif
+#endif
+
+#if OPENSSL_VERSION_NUMBER < 0x0090601fL
+#define SET_FLAGS(rsa) \
+ do { \
+ (rsa)->flags &= ~(RSA_FLAG_CACHE_PUBLIC | RSA_FLAG_CACHE_PRIVATE); \
+ (rsa)->flags &= ~RSA_FLAG_BLINDING; \
+ } while (0)
+#elif defined(RSA_FLAG_NO_BLINDING)
+#define SET_FLAGS(rsa) \
+ do { \
+ (rsa)->flags &= ~RSA_FLAG_BLINDING; \
+ (rsa)->flags |= RSA_FLAG_NO_BLINDING; \
+ } while (0)
+#else
+#define SET_FLAGS(rsa) \
+ do { \
+ (rsa)->flags &= ~RSA_FLAG_BLINDING; \
+ } while (0)
+#endif
+
+#define DST_RET(a) {ret = a; goto err;}
+
+static isc_result_t opensslrsa_todns(const dst_key_t *key, isc_buffer_t *data);
+
+static isc_result_t
+opensslrsa_createctx(dst_key_t *key, dst_context_t *dctx) {
+#if USE_EVP
+ EVP_MD_CTX *evp_md_ctx;
+ const EVP_MD *type;
+#endif
+
+ UNUSED(key);
+ REQUIRE(dctx->key->key_alg == DST_ALG_RSAMD5 ||
+ dctx->key->key_alg == DST_ALG_RSASHA1 ||
+ dctx->key->key_alg == DST_ALG_NSEC3RSASHA1);
+
+#if USE_EVP
+ evp_md_ctx = EVP_MD_CTX_create();
+ if (evp_md_ctx == NULL)
+ return (ISC_R_NOMEMORY);
+
+ if (dctx->key->key_alg == DST_ALG_RSAMD5)
+ type = EVP_md5(); /* MD5 + RSA */
+ else
+ type = EVP_sha1(); /* SHA1 + RSA */
+
+ if (!EVP_DigestInit_ex(evp_md_ctx, type, NULL)) {
+ EVP_MD_CTX_destroy(evp_md_ctx);
+ return (ISC_R_FAILURE);
+ }
+ dctx->ctxdata.evp_md_ctx = evp_md_ctx;
+#else
+ if (dctx->key->key_alg == DST_ALG_RSAMD5) {
+ isc_md5_t *md5ctx;
+
+ md5ctx = isc_mem_get(dctx->mctx, sizeof(isc_md5_t));
+ if (md5ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ isc_md5_init(md5ctx);
+ dctx->ctxdata.md5ctx = md5ctx;
+ } else {
+ isc_sha1_t *sha1ctx;
+
+ sha1ctx = isc_mem_get(dctx->mctx, sizeof(isc_sha1_t));
+ if (sha1ctx == NULL)
+ return (ISC_R_NOMEMORY);
+ isc_sha1_init(sha1ctx);
+ dctx->ctxdata.sha1ctx = sha1ctx;
+ }
+#endif
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+opensslrsa_destroyctx(dst_context_t *dctx) {
+#if USE_EVP
+ EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
+#endif
+
+ REQUIRE(dctx->key->key_alg == DST_ALG_RSAMD5 ||
+ dctx->key->key_alg == DST_ALG_RSASHA1 ||
+ dctx->key->key_alg == DST_ALG_NSEC3RSASHA1);
+
+#if USE_EVP
+ if (evp_md_ctx != NULL) {
+ EVP_MD_CTX_destroy(evp_md_ctx);
+ dctx->ctxdata.evp_md_ctx = NULL;
+ }
+#else
+ if (dctx->key->key_alg == DST_ALG_RSAMD5) {
+ isc_md5_t *md5ctx = dctx->ctxdata.md5ctx;
+
+ if (md5ctx != NULL) {
+ isc_md5_invalidate(md5ctx);
+ isc_mem_put(dctx->mctx, md5ctx, sizeof(isc_md5_t));
+ dctx->ctxdata.md5ctx = NULL;
+ }
+ } else {
+ isc_sha1_t *sha1ctx = dctx->ctxdata.sha1ctx;
+
+ if (sha1ctx != NULL) {
+ isc_sha1_invalidate(sha1ctx);
+ isc_mem_put(dctx->mctx, sha1ctx, sizeof(isc_sha1_t));
+ dctx->ctxdata.sha1ctx = NULL;
+ }
+ }
+#endif
+}
+
+static isc_result_t
+opensslrsa_adddata(dst_context_t *dctx, const isc_region_t *data) {
+#if USE_EVP
+ EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
+#endif
+
+ REQUIRE(dctx->key->key_alg == DST_ALG_RSAMD5 ||
+ dctx->key->key_alg == DST_ALG_RSASHA1 ||
+ dctx->key->key_alg == DST_ALG_NSEC3RSASHA1);
+
+#if USE_EVP
+ if (!EVP_DigestUpdate(evp_md_ctx, data->base, data->length)) {
+ return (ISC_R_FAILURE);
+ }
+#else
+ if (dctx->key->key_alg == DST_ALG_RSAMD5) {
+ isc_md5_t *md5ctx = dctx->ctxdata.md5ctx;
+ isc_md5_update(md5ctx, data->base, data->length);
+ } else {
+ isc_sha1_t *sha1ctx = dctx->ctxdata.sha1ctx;
+ isc_sha1_update(sha1ctx, data->base, data->length);
+ }
+#endif
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+opensslrsa_sign(dst_context_t *dctx, isc_buffer_t *sig) {
+ dst_key_t *key = dctx->key;
+ isc_region_t r;
+ unsigned int siglen = 0;
+#if USE_EVP
+ EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
+ EVP_PKEY *pkey = key->keydata.pkey;
+#else
+ RSA *rsa = key->keydata.rsa;
+ /* note: ISC_SHA1_DIGESTLENGTH > ISC_MD5_DIGESTLENGTH */
+ unsigned char digest[ISC_SHA1_DIGESTLENGTH];
+ int status;
+ int type;
+ unsigned int digestlen;
+ char *message;
+ unsigned long err;
+ const char* file;
+ int line;
+#endif
+
+ REQUIRE(dctx->key->key_alg == DST_ALG_RSAMD5 ||
+ dctx->key->key_alg == DST_ALG_RSASHA1 ||
+ dctx->key->key_alg == DST_ALG_NSEC3RSASHA1);
+
+ isc_buffer_availableregion(sig, &r);
+
+#if USE_EVP
+ if (r.length < (unsigned int) EVP_PKEY_size(pkey))
+ return (ISC_R_NOSPACE);
+
+ if (!EVP_SignFinal(evp_md_ctx, r.base, &siglen, pkey)) {
+ return (ISC_R_FAILURE);
+ }
+#else
+ if (r.length < (unsigned int) RSA_size(rsa))
+ return (ISC_R_NOSPACE);
+
+ if (dctx->key->key_alg == DST_ALG_RSAMD5) {
+ isc_md5_t *md5ctx = dctx->ctxdata.md5ctx;
+ isc_md5_final(md5ctx, digest);
+ type = NID_md5;
+ digestlen = ISC_MD5_DIGESTLENGTH;
+ } else {
+ isc_sha1_t *sha1ctx = dctx->ctxdata.sha1ctx;
+ isc_sha1_final(sha1ctx, digest);
+ type = NID_sha1;
+ digestlen = ISC_SHA1_DIGESTLENGTH;
+ }
+
+ status = RSA_sign(type, digest, digestlen, r.base, &siglen, rsa);
+ if (status == 0) {
+ err = ERR_peek_error_line(&file, &line);
+ if (err != 0U) {
+ message = ERR_error_string(err, NULL);
+ }
+ return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+ }
+#endif
+
+ isc_buffer_add(sig, siglen);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+opensslrsa_verify(dst_context_t *dctx, const isc_region_t *sig) {
+ dst_key_t *key = dctx->key;
+ int status = 0;
+#if USE_EVP
+ EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
+ EVP_PKEY *pkey = key->keydata.pkey;
+#else
+ /* note: ISC_SHA1_DIGESTLENGTH > ISC_MD5_DIGESTLENGTH */
+ unsigned char digest[ISC_SHA1_DIGESTLENGTH];
+ int type;
+ unsigned int digestlen;
+ RSA *rsa = key->keydata.rsa;
+#endif
+
+ REQUIRE(dctx->key->key_alg == DST_ALG_RSAMD5 ||
+ dctx->key->key_alg == DST_ALG_RSASHA1 ||
+ dctx->key->key_alg == DST_ALG_NSEC3RSASHA1);
+
+#if USE_EVP
+ status = EVP_VerifyFinal(evp_md_ctx, sig->base, sig->length, pkey);
+#else
+ if (dctx->key->key_alg == DST_ALG_RSAMD5) {
+ isc_md5_t *md5ctx = dctx->ctxdata.md5ctx;
+ isc_md5_final(md5ctx, digest);
+ type = NID_md5;
+ digestlen = ISC_MD5_DIGESTLENGTH;
+ } else {
+ isc_sha1_t *sha1ctx = dctx->ctxdata.sha1ctx;
+ isc_sha1_final(sha1ctx, digest);
+ type = NID_sha1;
+ digestlen = ISC_SHA1_DIGESTLENGTH;
+ }
+
+ if (sig->length < (unsigned int) RSA_size(rsa))
+ return (DST_R_VERIFYFAILURE);
+
+ status = RSA_verify(type, digest, digestlen, sig->base,
+ RSA_size(rsa), rsa);
+#endif
+ if (status != 1)
+ return (dst__openssl_toresult(DST_R_VERIFYFAILURE));
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_boolean_t
+opensslrsa_compare(const dst_key_t *key1, const dst_key_t *key2) {
+ int status;
+ RSA *rsa1 = NULL, *rsa2 = NULL;
+#if USE_EVP
+ EVP_PKEY *pkey1, *pkey2;
+#endif
+
+#if USE_EVP
+ pkey1 = key1->keydata.pkey;
+ pkey2 = key2->keydata.pkey;
+ /*
+ * The pkey reference will keep these around after
+ * the RSA_free() call.
+ */
+ if (pkey1 != NULL) {
+ rsa1 = EVP_PKEY_get1_RSA(pkey1);
+ RSA_free(rsa1);
+ }
+ if (pkey2 != NULL) {
+ rsa2 = EVP_PKEY_get1_RSA(pkey2);
+ RSA_free(rsa2);
+ }
+#else
+ rsa1 = key1->keydata.rsa;
+ rsa2 = key2->keydata.rsa;
+#endif
+
+ if (rsa1 == NULL && rsa2 == NULL)
+ return (ISC_TRUE);
+ else if (rsa1 == NULL || rsa2 == NULL)
+ return (ISC_FALSE);
+
+ status = BN_cmp(rsa1->n, rsa2->n) ||
+ BN_cmp(rsa1->e, rsa2->e);
+
+ if (status != 0)
+ return (ISC_FALSE);
+
+#if USE_EVP
+ if ((rsa1->flags & RSA_FLAG_EXT_PKEY) != 0 ||
+ (rsa2->flags & RSA_FLAG_EXT_PKEY) != 0) {
+ if ((rsa1->flags & RSA_FLAG_EXT_PKEY) == 0 ||
+ (rsa2->flags & RSA_FLAG_EXT_PKEY) == 0)
+ return (ISC_FALSE);
+ /*
+ * Can't compare private parameters, BTW does it make sense?
+ */
+ return (ISC_TRUE);
+ }
+#endif
+
+ if (rsa1->d != NULL || rsa2->d != NULL) {
+ if (rsa1->d == NULL || rsa2->d == NULL)
+ return (ISC_FALSE);
+ status = BN_cmp(rsa1->d, rsa2->d) ||
+ BN_cmp(rsa1->p, rsa2->p) ||
+ BN_cmp(rsa1->q, rsa2->q);
+
+ if (status != 0)
+ return (ISC_FALSE);
+ }
+ return (ISC_TRUE);
+}
+
+static isc_result_t
+opensslrsa_generate(dst_key_t *key, int exp) {
+#if OPENSSL_VERSION_NUMBER > 0x00908000L
+ BN_GENCB cb;
+ RSA *rsa = RSA_new();
+ BIGNUM *e = BN_new();
+#if USE_EVP
+ EVP_PKEY *pkey = EVP_PKEY_new();
+#endif
+
+ if (rsa == NULL || e == NULL)
+ goto err;
+#if USE_EVP
+ if (pkey == NULL)
+ goto err;
+ if (!EVP_PKEY_set1_RSA(pkey, rsa))
+ goto err;
+#endif
+
+ if (exp == 0) {
+ /* RSA_F4 0x10001 */
+ BN_set_bit(e, 0);
+ BN_set_bit(e, 16);
+ } else {
+ /* F5 0x100000001 */
+ BN_set_bit(e, 0);
+ BN_set_bit(e, 32);
+ }
+
+ BN_GENCB_set_old(&cb, NULL, NULL);
+
+ if (RSA_generate_key_ex(rsa, key->key_size, e, &cb)) {
+ BN_free(e);
+ SET_FLAGS(rsa);
+#if USE_EVP
+ key->keydata.pkey = pkey;
+
+ RSA_free(rsa);
+#else
+ key->keydata.rsa = rsa;
+#endif
+ return (ISC_R_SUCCESS);
+ }
+
+err:
+#if USE_EVP
+ if (pkey != NULL)
+ EVP_PKEY_free(pkey);
+#endif
+ if (e != NULL)
+ BN_free(e);
+ if (rsa != NULL)
+ RSA_free(rsa);
+ return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+#else
+ RSA *rsa;
+ unsigned long e;
+#if USE_EVP
+ EVP_PKEY *pkey = EVP_PKEY_new();
+
+ if (pkey == NULL)
+ return (ISC_R_NOMEMORY);
+#endif
+
+ if (exp == 0)
+ e = RSA_F4;
+ else
+ e = 0x40000003;
+ rsa = RSA_generate_key(key->key_size, e, NULL, NULL);
+ if (rsa == NULL) {
+#if USE_EVP
+ EVP_PKEY_free(pkey);
+#endif
+ return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+ }
+ SET_FLAGS(rsa);
+#if USE_EVP
+ if (!EVP_PKEY_set1_RSA(pkey, rsa)) {
+ EVP_PKEY_free(pkey);
+ RSA_free(rsa);
+ return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+ }
+ key->keydata.pkey = pkey;
+ RSA_free(rsa);
+#else
+ key->keydata.rsa = rsa;
+#endif
+
+ return (ISC_R_SUCCESS);
+#endif
+}
+
+static isc_boolean_t
+opensslrsa_isprivate(const dst_key_t *key) {
+#if USE_EVP
+ RSA *rsa = EVP_PKEY_get1_RSA(key->keydata.pkey);
+ INSIST(rsa != NULL);
+ RSA_free(rsa);
+ /* key->keydata.pkey still has a reference so rsa is still valid. */
+#else
+ RSA *rsa = key->keydata.rsa;
+#endif
+ if (rsa != NULL && (rsa->flags & RSA_FLAG_EXT_PKEY) != 0)
+ return (ISC_TRUE);
+ return (ISC_TF(rsa != NULL && rsa->d != NULL));
+}
+
+static void
+opensslrsa_destroy(dst_key_t *key) {
+#if USE_EVP
+ EVP_PKEY *pkey = key->keydata.pkey;
+ EVP_PKEY_free(pkey);
+ key->keydata.pkey = NULL;
+#else
+ RSA *rsa = key->keydata.rsa;
+ RSA_free(rsa);
+ key->keydata.rsa = NULL;
+#endif
+}
+
+
+static isc_result_t
+opensslrsa_todns(const dst_key_t *key, isc_buffer_t *data) {
+ isc_region_t r;
+ unsigned int e_bytes;
+ unsigned int mod_bytes;
+ isc_result_t ret;
+ RSA *rsa;
+#if USE_EVP
+ EVP_PKEY *pkey;
+#endif
+
+#if USE_EVP
+ REQUIRE(key->keydata.pkey != NULL);
+#else
+ REQUIRE(key->keydata.rsa != NULL);
+#endif
+
+#if USE_EVP
+ pkey = key->keydata.pkey;
+ rsa = EVP_PKEY_get1_RSA(pkey);
+ if (rsa == NULL)
+ return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+#else
+ rsa = key->keydata.rsa;
+#endif
+
+ isc_buffer_availableregion(data, &r);
+
+ e_bytes = BN_num_bytes(rsa->e);
+ mod_bytes = BN_num_bytes(rsa->n);
+
+ if (e_bytes < 256) { /*%< key exponent is <= 2040 bits */
+ if (r.length < 1)
+ DST_RET(ISC_R_NOSPACE);
+ isc_buffer_putuint8(data, (isc_uint8_t) e_bytes);
+ } else {
+ if (r.length < 3)
+ DST_RET(ISC_R_NOSPACE);
+ isc_buffer_putuint8(data, 0);
+ isc_buffer_putuint16(data, (isc_uint16_t) e_bytes);
+ }
+
+ if (r.length < e_bytes + mod_bytes)
+ return (ISC_R_NOSPACE);
+ isc_buffer_availableregion(data, &r);
+
+ BN_bn2bin(rsa->e, r.base);
+ r.base += e_bytes;
+ BN_bn2bin(rsa->n, r.base);
+
+ isc_buffer_add(data, e_bytes + mod_bytes);
+
+ ret = ISC_R_SUCCESS;
+ err:
+#if USE_EVP
+ if (rsa != NULL)
+ RSA_free(rsa);
+#endif
+ return (ret);
+}
+
+static isc_result_t
+opensslrsa_fromdns(dst_key_t *key, isc_buffer_t *data) {
+ RSA *rsa;
+ isc_region_t r;
+ unsigned int e_bytes;
+#if USE_EVP
+ EVP_PKEY *pkey;
+#endif
+
+ isc_buffer_remainingregion(data, &r);
+ if (r.length == 0)
+ return (ISC_R_SUCCESS);
+
+ rsa = RSA_new();
+ if (rsa == NULL)
+ return (dst__openssl_toresult(ISC_R_NOMEMORY));
+ SET_FLAGS(rsa);
+
+ if (r.length < 1) {
+ RSA_free(rsa);
+ return (DST_R_INVALIDPUBLICKEY);
+ }
+ e_bytes = *r.base++;
+ r.length--;
+
+ if (e_bytes == 0) {
+ if (r.length < 2) {
+ RSA_free(rsa);
+ return (DST_R_INVALIDPUBLICKEY);
+ }
+ e_bytes = ((*r.base++) << 8);
+ e_bytes += *r.base++;
+ r.length -= 2;
+ }
+
+ if (r.length < e_bytes) {
+ RSA_free(rsa);
+ return (DST_R_INVALIDPUBLICKEY);
+ }
+ rsa->e = BN_bin2bn(r.base, e_bytes, NULL);
+ r.base += e_bytes;
+ r.length -= e_bytes;
+
+ rsa->n = BN_bin2bn(r.base, r.length, NULL);
+
+ key->key_size = BN_num_bits(rsa->n);
+
+ isc_buffer_forward(data, r.length);
+
+#if USE_EVP
+ pkey = EVP_PKEY_new();
+ if (pkey == NULL) {
+ RSA_free(rsa);
+ return (ISC_R_NOMEMORY);
+ }
+ if (!EVP_PKEY_set1_RSA(pkey, rsa)) {
+ EVP_PKEY_free(pkey);
+ RSA_free(rsa);
+ return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+ }
+ key->keydata.pkey = pkey;
+ RSA_free(rsa);
+#else
+ key->keydata.rsa = rsa;
+#endif
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+opensslrsa_tofile(const dst_key_t *key, const char *directory) {
+ int i;
+ RSA *rsa;
+ dst_private_t priv;
+ unsigned char *bufs[8];
+ isc_result_t result;
+
+#if USE_EVP
+ if (key->keydata.pkey == NULL)
+ return (DST_R_NULLKEY);
+ rsa = EVP_PKEY_get1_RSA(key->keydata.pkey);
+ if (rsa == NULL)
+ return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+#else
+ if (key->keydata.rsa == NULL)
+ return (DST_R_NULLKEY);
+ rsa = key->keydata.rsa;
+#endif
+
+ for (i = 0; i < 8; i++) {
+ bufs[i] = isc_mem_get(key->mctx, BN_num_bytes(rsa->n));
+ if (bufs[i] == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto fail;
+ }
+ }
+
+ i = 0;
+
+ priv.elements[i].tag = TAG_RSA_MODULUS;
+ priv.elements[i].length = BN_num_bytes(rsa->n);
+ BN_bn2bin(rsa->n, bufs[i]);
+ priv.elements[i].data = bufs[i];
+ i++;
+
+ priv.elements[i].tag = TAG_RSA_PUBLICEXPONENT;
+ priv.elements[i].length = BN_num_bytes(rsa->e);
+ BN_bn2bin(rsa->e, bufs[i]);
+ priv.elements[i].data = bufs[i];
+ i++;
+
+ if (rsa->d != NULL) {
+ priv.elements[i].tag = TAG_RSA_PRIVATEEXPONENT;
+ priv.elements[i].length = BN_num_bytes(rsa->d);
+ BN_bn2bin(rsa->d, bufs[i]);
+ priv.elements[i].data = bufs[i];
+ i++;
+ }
+
+ if (rsa->p != NULL) {
+ priv.elements[i].tag = TAG_RSA_PRIME1;
+ priv.elements[i].length = BN_num_bytes(rsa->p);
+ BN_bn2bin(rsa->p, bufs[i]);
+ priv.elements[i].data = bufs[i];
+ i++;
+ }
+
+ if (rsa->q != NULL) {
+ priv.elements[i].tag = TAG_RSA_PRIME2;
+ priv.elements[i].length = BN_num_bytes(rsa->q);
+ BN_bn2bin(rsa->q, bufs[i]);
+ priv.elements[i].data = bufs[i];
+ i++;
+ }
+
+ if (rsa->dmp1 != NULL) {
+ priv.elements[i].tag = TAG_RSA_EXPONENT1;
+ priv.elements[i].length = BN_num_bytes(rsa->dmp1);
+ BN_bn2bin(rsa->dmp1, bufs[i]);
+ priv.elements[i].data = bufs[i];
+ i++;
+ }
+
+ if (rsa->dmq1 != NULL) {
+ priv.elements[i].tag = TAG_RSA_EXPONENT2;
+ priv.elements[i].length = BN_num_bytes(rsa->dmq1);
+ BN_bn2bin(rsa->dmq1, bufs[i]);
+ priv.elements[i].data = bufs[i];
+ i++;
+ }
+
+ if (rsa->iqmp != NULL) {
+ priv.elements[i].tag = TAG_RSA_COEFFICIENT;
+ priv.elements[i].length = BN_num_bytes(rsa->iqmp);
+ BN_bn2bin(rsa->iqmp, bufs[i]);
+ priv.elements[i].data = bufs[i];
+ i++;
+ }
+
+ if (key->engine != NULL) {
+ priv.elements[i].tag = TAG_RSA_ENGINE;
+ priv.elements[i].length = strlen(key->engine) + 1;
+ priv.elements[i].data = (unsigned char *)key->engine;
+ i++;
+ }
+
+ if (key->label != NULL) {
+ priv.elements[i].tag = TAG_RSA_LABEL;
+ priv.elements[i].length = strlen(key->label) + 1;
+ priv.elements[i].data = (unsigned char *)key->label;
+ i++;
+ }
+
+ priv.nelements = i;
+ result = dst__privstruct_writefile(key, &priv, directory);
+ fail:
+#if USE_EVP
+ RSA_free(rsa);
+#endif
+ for (i = 0; i < 8; i++) {
+ if (bufs[i] == NULL)
+ break;
+ isc_mem_put(key->mctx, bufs[i], BN_num_bytes(rsa->n));
+ }
+ return (result);
+}
+
+static isc_result_t
+opensslrsa_parse(dst_key_t *key, isc_lex_t *lexer) {
+ dst_private_t priv;
+ isc_result_t ret;
+ int i;
+ RSA *rsa = NULL;
+ ENGINE *e = NULL;
+ isc_mem_t *mctx = key->mctx;
+ const char *name = NULL, *label = NULL;
+ EVP_PKEY *pkey = NULL;
+
+ /* read private key file */
+ ret = dst__privstruct_parse(key, DST_ALG_RSA, lexer, mctx, &priv);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ for (i = 0; i < priv.nelements; i++) {
+ switch (priv.elements[i].tag) {
+ case TAG_RSA_ENGINE:
+ name = (char *)priv.elements[i].data;
+ break;
+ case TAG_RSA_LABEL:
+ label = (char *)priv.elements[i].data;
+ break;
+ default:
+ break;
+ }
+ }
+ /*
+ * Is this key is stored in a HSM?
+ * See if we can fetch it.
+ */
+ if (name != NULL || label != NULL) {
+ INSIST(name != NULL);
+ INSIST(label != NULL);
+ e = dst__openssl_getengine(name);
+ if (e == NULL)
+ DST_RET(DST_R_NOENGINE);
+ pkey = ENGINE_load_private_key(e, label, NULL, NULL);
+ if (pkey == NULL) {
+ ERR_print_errors_fp(stderr);
+ DST_RET(ISC_R_FAILURE);
+ }
+ key->engine = isc_mem_strdup(key->mctx, name);
+ if (key->engine == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ key->label = isc_mem_strdup(key->mctx, label);
+ if (key->label == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ key->key_size = EVP_PKEY_bits(pkey);
+#if USE_EVP
+ key->keydata.pkey = pkey;
+#else
+ key->keydata.rsa = EVP_PKEY_get1_RSA(pkey);
+ if (rsa == NULL)
+ DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+ EVP_PKEY_free(pkey);
+#endif
+ dst__privstruct_free(&priv, mctx);
+ return (ISC_R_SUCCESS);
+ }
+
+ rsa = RSA_new();
+ if (rsa == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ SET_FLAGS(rsa);
+
+#if USE_EVP
+ pkey = EVP_PKEY_new();
+ if (pkey == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ if (!EVP_PKEY_set1_RSA(pkey, rsa)) {
+ DST_RET(ISC_R_FAILURE);
+ }
+ key->keydata.pkey = pkey;
+#else
+ key->keydata.rsa = rsa;
+#endif
+
+ for (i = 0; i < priv.nelements; i++) {
+ BIGNUM *bn;
+ switch (priv.elements[i].tag) {
+ case TAG_RSA_ENGINE:
+ continue;
+ case TAG_RSA_LABEL:
+ continue;
+ case TAG_RSA_PIN:
+ continue;
+ default:
+ bn = BN_bin2bn(priv.elements[i].data,
+ priv.elements[i].length, NULL);
+ if (bn == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ }
+
+ switch (priv.elements[i].tag) {
+ case TAG_RSA_MODULUS:
+ rsa->n = bn;
+ break;
+ case TAG_RSA_PUBLICEXPONENT:
+ rsa->e = bn;
+ break;
+ case TAG_RSA_PRIVATEEXPONENT:
+ rsa->d = bn;
+ break;
+ case TAG_RSA_PRIME1:
+ rsa->p = bn;
+ break;
+ case TAG_RSA_PRIME2:
+ rsa->q = bn;
+ break;
+ case TAG_RSA_EXPONENT1:
+ rsa->dmp1 = bn;
+ break;
+ case TAG_RSA_EXPONENT2:
+ rsa->dmq1 = bn;
+ break;
+ case TAG_RSA_COEFFICIENT:
+ rsa->iqmp = bn;
+ break;
+ }
+ }
+ dst__privstruct_free(&priv, mctx);
+
+ key->key_size = BN_num_bits(rsa->n);
+#if USE_EVP
+ RSA_free(rsa);
+#endif
+
+ return (ISC_R_SUCCESS);
+
+ err:
+#if USE_EVP
+ if (pkey != NULL)
+ EVP_PKEY_free(pkey);
+#endif
+ if (rsa != NULL)
+ RSA_free(rsa);
+ opensslrsa_destroy(key);
+ dst__privstruct_free(&priv, mctx);
+ memset(&priv, 0, sizeof(priv));
+ return (ret);
+}
+
+static isc_result_t
+opensslrsa_fromlabel(dst_key_t *key, const char *engine, const char *label,
+ const char *pin)
+{
+ ENGINE *e = NULL;
+ isc_result_t ret;
+ EVP_PKEY *pkey = NULL;
+
+ UNUSED(pin);
+
+ e = dst__openssl_getengine(engine);
+ if (e == NULL)
+ DST_RET(DST_R_NOENGINE);
+ pkey = ENGINE_load_private_key(e, label, NULL, NULL);
+ if (pkey == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ key->engine = isc_mem_strdup(key->mctx, label);
+ if (key->engine == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ key->label = isc_mem_strdup(key->mctx, label);
+ if (key->label == NULL)
+ DST_RET(ISC_R_NOMEMORY);
+ key->key_size = EVP_PKEY_bits(pkey);
+#if USE_EVP
+ key->keydata.pkey = pkey;
+#else
+ key->keydata.rsa = EVP_PKEY_get1_RSA(pkey);
+ EVP_PKEY_free(pkey);
+ if (key->keydata.rsa == NULL)
+ return (dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+#endif
+ return (ISC_R_SUCCESS);
+
+ err:
+ if (pkey != NULL)
+ EVP_PKEY_free(pkey);
+ return (ret);
+}
+
+static dst_func_t opensslrsa_functions = {
+ opensslrsa_createctx,
+ opensslrsa_destroyctx,
+ opensslrsa_adddata,
+ opensslrsa_sign,
+ opensslrsa_verify,
+ NULL, /*%< computesecret */
+ opensslrsa_compare,
+ NULL, /*%< paramcompare */
+ opensslrsa_generate,
+ opensslrsa_isprivate,
+ opensslrsa_destroy,
+ opensslrsa_todns,
+ opensslrsa_fromdns,
+ opensslrsa_tofile,
+ opensslrsa_parse,
+ NULL, /*%< cleanup */
+ opensslrsa_fromlabel,
+};
+
+isc_result_t
+dst__opensslrsa_init(dst_func_t **funcp) {
+ REQUIRE(funcp != NULL);
+ if (*funcp == NULL)
+ *funcp = &opensslrsa_functions;
+ return (ISC_R_SUCCESS);
+}
+
+#else /* OPENSSL */
+
+#include <isc/util.h>
+
+EMPTY_TRANSLATION_UNIT
+
+#endif /* OPENSSL */
+/*! \file */
diff --git a/lib/dns/order.c b/lib/dns/order.c
new file mode 100644
index 0000000..853b001
--- /dev/null
+++ b/lib/dns/order.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: order.c,v 1.10 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/types.h>
+#include <isc/util.h>
+#include <isc/refcount.h>
+
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/order.h>
+#include <dns/rdataset.h>
+#include <dns/types.h>
+
+typedef struct dns_order_ent dns_order_ent_t;
+struct dns_order_ent {
+ dns_fixedname_t name;
+ dns_rdataclass_t rdclass;
+ dns_rdatatype_t rdtype;
+ unsigned int mode;
+ ISC_LINK(dns_order_ent_t) link;
+};
+
+struct dns_order {
+ unsigned int magic;
+ isc_refcount_t references;
+ ISC_LIST(dns_order_ent_t) ents;
+ isc_mem_t *mctx;
+};
+
+#define DNS_ORDER_MAGIC ISC_MAGIC('O','r','d','r')
+#define DNS_ORDER_VALID(order) ISC_MAGIC_VALID(order, DNS_ORDER_MAGIC)
+
+isc_result_t
+dns_order_create(isc_mem_t *mctx, dns_order_t **orderp) {
+ dns_order_t *order;
+ isc_result_t result;
+
+ REQUIRE(orderp != NULL && *orderp == NULL);
+
+ order = isc_mem_get(mctx, sizeof(*order));
+ if (order == NULL)
+ return (ISC_R_NOMEMORY);
+
+ ISC_LIST_INIT(order->ents);
+
+ /* Implicit attach. */
+ result = isc_refcount_init(&order->references, 1);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, order, sizeof(*order));
+ return (result);
+ }
+
+ order->mctx = NULL;
+ isc_mem_attach(mctx, &order->mctx);
+ order->magic = DNS_ORDER_MAGIC;
+ *orderp = order;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_order_add(dns_order_t *order, dns_name_t *name,
+ dns_rdatatype_t rdtype, dns_rdataclass_t rdclass,
+ unsigned int mode)
+{
+ dns_order_ent_t *ent;
+
+ REQUIRE(DNS_ORDER_VALID(order));
+ REQUIRE(mode == DNS_RDATASETATTR_RANDOMIZE ||
+ mode == DNS_RDATASETATTR_FIXEDORDER ||
+ mode == 0 /* DNS_RDATASETATTR_CYCLIC */ );
+
+ ent = isc_mem_get(order->mctx, sizeof(*ent));
+ if (ent == NULL)
+ return (ISC_R_NOMEMORY);
+
+ dns_fixedname_init(&ent->name);
+ RUNTIME_CHECK(dns_name_copy(name, dns_fixedname_name(&ent->name), NULL)
+ == ISC_R_SUCCESS);
+ ent->rdtype = rdtype;
+ ent->rdclass = rdclass;
+ ent->mode = mode;
+ ISC_LINK_INIT(ent, link);
+ ISC_LIST_INITANDAPPEND(order->ents, ent, link);
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_boolean_t
+match(dns_name_t *name1, dns_name_t *name2) {
+
+ if (dns_name_iswildcard(name2))
+ return(dns_name_matcheswildcard(name1, name2));
+ return (dns_name_equal(name1, name2));
+}
+
+unsigned int
+dns_order_find(dns_order_t *order, dns_name_t *name,
+ dns_rdatatype_t rdtype, dns_rdataclass_t rdclass)
+{
+ dns_order_ent_t *ent;
+ REQUIRE(DNS_ORDER_VALID(order));
+
+ for (ent = ISC_LIST_HEAD(order->ents);
+ ent != NULL;
+ ent = ISC_LIST_NEXT(ent, link)) {
+ if (ent->rdtype != rdtype && ent->rdtype != dns_rdatatype_any)
+ continue;
+ if (ent->rdclass != rdclass &&
+ ent->rdclass != dns_rdataclass_any)
+ continue;
+ if (match(name, dns_fixedname_name(&ent->name)))
+ return (ent->mode);
+ }
+ return (0);
+}
+
+void
+dns_order_attach(dns_order_t *source, dns_order_t **target) {
+ REQUIRE(DNS_ORDER_VALID(source));
+ REQUIRE(target != NULL && *target == NULL);
+ isc_refcount_increment(&source->references, NULL);
+ *target = source;
+}
+
+void
+dns_order_detach(dns_order_t **orderp) {
+ dns_order_t *order;
+ dns_order_ent_t *ent;
+ unsigned int references;
+
+ REQUIRE(orderp != NULL);
+ order = *orderp;
+ REQUIRE(DNS_ORDER_VALID(order));
+ isc_refcount_decrement(&order->references, &references);
+ *orderp = NULL;
+ if (references != 0)
+ return;
+
+ order->magic = 0;
+ while ((ent = ISC_LIST_HEAD(order->ents)) != NULL) {
+ ISC_LIST_UNLINK(order->ents, ent, link);
+ isc_mem_put(order->mctx, ent, sizeof(*ent));
+ }
+ isc_refcount_destroy(&order->references);
+ isc_mem_putanddetach(&order->mctx, order, sizeof(*order));
+}
diff --git a/lib/dns/peer.c b/lib/dns/peer.c
new file mode 100644
index 0000000..12474cb
--- /dev/null
+++ b/lib/dns/peer.c
@@ -0,0 +1,712 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001, 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: peer.c,v 1.31 2008/04/03 06:09:04 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/mem.h>
+#include <isc/string.h>
+#include <isc/util.h>
+#include <isc/sockaddr.h>
+
+#include <dns/bit.h>
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/peer.h>
+
+/*%
+ * Bit positions in the dns_peer_t structure flags field
+ */
+#define BOGUS_BIT 0
+#define SERVER_TRANSFER_FORMAT_BIT 1
+#define TRANSFERS_BIT 2
+#define PROVIDE_IXFR_BIT 3
+#define REQUEST_IXFR_BIT 4
+#define SUPPORT_EDNS_BIT 5
+#define SERVER_UDPSIZE_BIT 6
+#define SERVER_MAXUDP_BIT 7
+#define REQUEST_NSID_BIT 8
+
+static void
+peerlist_delete(dns_peerlist_t **list);
+
+static void
+peer_delete(dns_peer_t **peer);
+
+isc_result_t
+dns_peerlist_new(isc_mem_t *mem, dns_peerlist_t **list) {
+ dns_peerlist_t *l;
+
+ REQUIRE(list != NULL);
+
+ l = isc_mem_get(mem, sizeof(*l));
+ if (l == NULL)
+ return (ISC_R_NOMEMORY);
+
+ ISC_LIST_INIT(l->elements);
+ l->mem = mem;
+ l->refs = 1;
+ l->magic = DNS_PEERLIST_MAGIC;
+
+ *list = l;
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_peerlist_attach(dns_peerlist_t *source, dns_peerlist_t **target) {
+ REQUIRE(DNS_PEERLIST_VALID(source));
+ REQUIRE(target != NULL);
+ REQUIRE(*target == NULL);
+
+ source->refs++;
+
+ ENSURE(source->refs != 0xffffffffU);
+
+ *target = source;
+}
+
+void
+dns_peerlist_detach(dns_peerlist_t **list) {
+ dns_peerlist_t *plist;
+
+ REQUIRE(list != NULL);
+ REQUIRE(*list != NULL);
+ REQUIRE(DNS_PEERLIST_VALID(*list));
+
+ plist = *list;
+ *list = NULL;
+
+ REQUIRE(plist->refs > 0);
+
+ plist->refs--;
+
+ if (plist->refs == 0)
+ peerlist_delete(&plist);
+}
+
+static void
+peerlist_delete(dns_peerlist_t **list) {
+ dns_peerlist_t *l;
+ dns_peer_t *server, *stmp;
+
+ REQUIRE(list != NULL);
+ REQUIRE(DNS_PEERLIST_VALID(*list));
+
+ l = *list;
+
+ REQUIRE(l->refs == 0);
+
+ server = ISC_LIST_HEAD(l->elements);
+ while (server != NULL) {
+ stmp = ISC_LIST_NEXT(server, next);
+ ISC_LIST_UNLINK(l->elements, server, next);
+ dns_peer_detach(&server);
+ server = stmp;
+ }
+
+ l->magic = 0;
+ isc_mem_put(l->mem, l, sizeof(*l));
+
+ *list = NULL;
+}
+
+void
+dns_peerlist_addpeer(dns_peerlist_t *peers, dns_peer_t *peer) {
+ dns_peer_t *p = NULL;
+
+ dns_peer_attach(peer, &p);
+
+ /*
+ * More specifics to front of list.
+ */
+ for (p = ISC_LIST_HEAD(peers->elements);
+ p != NULL;
+ p = ISC_LIST_NEXT(p, next))
+ if (p->prefixlen < peer->prefixlen)
+ break;
+
+ if (p != NULL)
+ ISC_LIST_INSERTBEFORE(peers->elements, p, peer, next);
+ else
+ ISC_LIST_APPEND(peers->elements, peer, next);
+
+}
+
+isc_result_t
+dns_peerlist_peerbyaddr(dns_peerlist_t *servers,
+ isc_netaddr_t *addr, dns_peer_t **retval)
+{
+ dns_peer_t *server;
+ isc_result_t res;
+
+ REQUIRE(retval != NULL);
+ REQUIRE(DNS_PEERLIST_VALID(servers));
+
+ server = ISC_LIST_HEAD(servers->elements);
+ while (server != NULL) {
+ if (isc_netaddr_eqprefix(addr, &server->address,
+ server->prefixlen))
+ break;
+
+ server = ISC_LIST_NEXT(server, next);
+ }
+
+ if (server != NULL) {
+ *retval = server;
+ res = ISC_R_SUCCESS;
+ } else {
+ res = ISC_R_NOTFOUND;
+ }
+
+ return (res);
+}
+
+
+
+isc_result_t
+dns_peerlist_currpeer(dns_peerlist_t *peers, dns_peer_t **retval) {
+ dns_peer_t *p = NULL;
+
+ p = ISC_LIST_TAIL(peers->elements);
+
+ dns_peer_attach(p, retval);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_peer_new(isc_mem_t *mem, isc_netaddr_t *addr, dns_peer_t **peerptr) {
+ unsigned int prefixlen = 0;
+
+ REQUIRE(peerptr != NULL);
+ switch(addr->family) {
+ case AF_INET:
+ prefixlen = 32;
+ break;
+ case AF_INET6:
+ prefixlen = 128;
+ break;
+ default:
+ INSIST(0);
+ }
+
+ return (dns_peer_newprefix(mem, addr, prefixlen, peerptr));
+}
+
+isc_result_t
+dns_peer_newprefix(isc_mem_t *mem, isc_netaddr_t *addr, unsigned int prefixlen,
+ dns_peer_t **peerptr)
+{
+ dns_peer_t *peer;
+
+ REQUIRE(peerptr != NULL);
+
+ peer = isc_mem_get(mem, sizeof(*peer));
+ if (peer == NULL)
+ return (ISC_R_NOMEMORY);
+
+ peer->magic = DNS_PEER_MAGIC;
+ peer->address = *addr;
+ peer->prefixlen = prefixlen;
+ peer->mem = mem;
+ peer->bogus = ISC_FALSE;
+ peer->transfer_format = dns_one_answer;
+ peer->transfers = 0;
+ peer->request_ixfr = ISC_FALSE;
+ peer->provide_ixfr = ISC_FALSE;
+ peer->key = NULL;
+ peer->refs = 1;
+ peer->transfer_source = NULL;
+ peer->notify_source = NULL;
+ peer->query_source = NULL;
+
+ memset(&peer->bitflags, 0x0, sizeof(peer->bitflags));
+
+ ISC_LINK_INIT(peer, next);
+
+ *peerptr = peer;
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_peer_attach(dns_peer_t *source, dns_peer_t **target) {
+ REQUIRE(DNS_PEER_VALID(source));
+ REQUIRE(target != NULL);
+ REQUIRE(*target == NULL);
+
+ source->refs++;
+
+ ENSURE(source->refs != 0xffffffffU);
+
+ *target = source;
+}
+
+void
+dns_peer_detach(dns_peer_t **peer) {
+ dns_peer_t *p;
+
+ REQUIRE(peer != NULL);
+ REQUIRE(*peer != NULL);
+ REQUIRE(DNS_PEER_VALID(*peer));
+
+ p = *peer;
+
+ REQUIRE(p->refs > 0);
+
+ *peer = NULL;
+ p->refs--;
+
+ if (p->refs == 0)
+ peer_delete(&p);
+}
+
+static void
+peer_delete(dns_peer_t **peer) {
+ dns_peer_t *p;
+ isc_mem_t *mem;
+
+ REQUIRE(peer != NULL);
+ REQUIRE(DNS_PEER_VALID(*peer));
+
+ p = *peer;
+
+ REQUIRE(p->refs == 0);
+
+ mem = p->mem;
+ p->mem = NULL;
+ p->magic = 0;
+
+ if (p->key != NULL) {
+ dns_name_free(p->key, mem);
+ isc_mem_put(mem, p->key, sizeof(dns_name_t));
+ }
+
+ if (p->transfer_source != NULL) {
+ isc_mem_put(mem, p->transfer_source,
+ sizeof(*p->transfer_source));
+ }
+
+ isc_mem_put(mem, p, sizeof(*p));
+
+ *peer = NULL;
+}
+
+isc_result_t
+dns_peer_setbogus(dns_peer_t *peer, isc_boolean_t newval) {
+ isc_boolean_t existed;
+
+ REQUIRE(DNS_PEER_VALID(peer));
+
+ existed = DNS_BIT_CHECK(BOGUS_BIT, &peer->bitflags);
+
+ peer->bogus = newval;
+ DNS_BIT_SET(BOGUS_BIT, &peer->bitflags);
+
+ return (existed ? ISC_R_EXISTS : ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_peer_getbogus(dns_peer_t *peer, isc_boolean_t *retval) {
+ REQUIRE(DNS_PEER_VALID(peer));
+ REQUIRE(retval != NULL);
+
+ if (DNS_BIT_CHECK(BOGUS_BIT, &peer->bitflags)) {
+ *retval = peer->bogus;
+ return (ISC_R_SUCCESS);
+ } else
+ return (ISC_R_NOTFOUND);
+}
+
+
+isc_result_t
+dns_peer_setprovideixfr(dns_peer_t *peer, isc_boolean_t newval) {
+ isc_boolean_t existed;
+
+ REQUIRE(DNS_PEER_VALID(peer));
+
+ existed = DNS_BIT_CHECK(PROVIDE_IXFR_BIT, &peer->bitflags);
+
+ peer->provide_ixfr = newval;
+ DNS_BIT_SET(PROVIDE_IXFR_BIT, &peer->bitflags);
+
+ return (existed ? ISC_R_EXISTS : ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_peer_getprovideixfr(dns_peer_t *peer, isc_boolean_t *retval) {
+ REQUIRE(DNS_PEER_VALID(peer));
+ REQUIRE(retval != NULL);
+
+ if (DNS_BIT_CHECK(PROVIDE_IXFR_BIT, &peer->bitflags)) {
+ *retval = peer->provide_ixfr;
+ return (ISC_R_SUCCESS);
+ } else {
+ return (ISC_R_NOTFOUND);
+ }
+}
+
+isc_result_t
+dns_peer_setrequestixfr(dns_peer_t *peer, isc_boolean_t newval) {
+ isc_boolean_t existed;
+
+ REQUIRE(DNS_PEER_VALID(peer));
+
+ existed = DNS_BIT_CHECK(REQUEST_IXFR_BIT, &peer->bitflags);
+
+ peer->request_ixfr = newval;
+ DNS_BIT_SET(REQUEST_IXFR_BIT, &peer->bitflags);
+
+ return (existed ? ISC_R_EXISTS : ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_peer_getrequestixfr(dns_peer_t *peer, isc_boolean_t *retval) {
+ REQUIRE(DNS_PEER_VALID(peer));
+ REQUIRE(retval != NULL);
+
+ if (DNS_BIT_CHECK(REQUEST_IXFR_BIT, &peer->bitflags)) {
+ *retval = peer->request_ixfr;
+ return (ISC_R_SUCCESS);
+ } else
+ return (ISC_R_NOTFOUND);
+}
+
+isc_result_t
+dns_peer_setsupportedns(dns_peer_t *peer, isc_boolean_t newval) {
+ isc_boolean_t existed;
+
+ REQUIRE(DNS_PEER_VALID(peer));
+
+ existed = DNS_BIT_CHECK(SUPPORT_EDNS_BIT, &peer->bitflags);
+
+ peer->support_edns = newval;
+ DNS_BIT_SET(SUPPORT_EDNS_BIT, &peer->bitflags);
+
+ return (existed ? ISC_R_EXISTS : ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_peer_getsupportedns(dns_peer_t *peer, isc_boolean_t *retval) {
+ REQUIRE(DNS_PEER_VALID(peer));
+ REQUIRE(retval != NULL);
+
+ if (DNS_BIT_CHECK(SUPPORT_EDNS_BIT, &peer->bitflags)) {
+ *retval = peer->support_edns;
+ return (ISC_R_SUCCESS);
+ } else
+ return (ISC_R_NOTFOUND);
+}
+
+isc_result_t
+dns_peer_setrequestnsid(dns_peer_t *peer, isc_boolean_t newval) {
+ isc_boolean_t existed;
+
+ REQUIRE(DNS_PEER_VALID(peer));
+
+ existed = DNS_BIT_CHECK(REQUEST_NSID_BIT, &peer->bitflags);
+
+ peer->request_nsid = newval;
+ DNS_BIT_SET(REQUEST_NSID_BIT, &peer->bitflags);
+
+ return (existed ? ISC_R_EXISTS : ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_peer_getrequestnsid(dns_peer_t *peer, isc_boolean_t *retval) {
+ REQUIRE(DNS_PEER_VALID(peer));
+ REQUIRE(retval != NULL);
+
+ if (DNS_BIT_CHECK(REQUEST_NSID_BIT, &peer->bitflags)) {
+ *retval = peer->request_nsid;
+ return (ISC_R_SUCCESS);
+ } else
+ return (ISC_R_NOTFOUND);
+}
+
+isc_result_t
+dns_peer_settransfers(dns_peer_t *peer, isc_uint32_t newval) {
+ isc_boolean_t existed;
+
+ REQUIRE(DNS_PEER_VALID(peer));
+
+ existed = DNS_BIT_CHECK(TRANSFERS_BIT, &peer->bitflags);
+
+ peer->transfers = newval;
+ DNS_BIT_SET(TRANSFERS_BIT, &peer->bitflags);
+
+ return (existed ? ISC_R_EXISTS : ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_peer_gettransfers(dns_peer_t *peer, isc_uint32_t *retval) {
+ REQUIRE(DNS_PEER_VALID(peer));
+ REQUIRE(retval != NULL);
+
+ if (DNS_BIT_CHECK(TRANSFERS_BIT, &peer->bitflags)) {
+ *retval = peer->transfers;
+ return (ISC_R_SUCCESS);
+ } else {
+ return (ISC_R_NOTFOUND);
+ }
+}
+
+isc_result_t
+dns_peer_settransferformat(dns_peer_t *peer, dns_transfer_format_t newval) {
+ isc_boolean_t existed;
+
+ REQUIRE(DNS_PEER_VALID(peer));
+
+ existed = DNS_BIT_CHECK(SERVER_TRANSFER_FORMAT_BIT,
+ &peer->bitflags);
+
+ peer->transfer_format = newval;
+ DNS_BIT_SET(SERVER_TRANSFER_FORMAT_BIT, &peer->bitflags);
+
+ return (existed ? ISC_R_EXISTS : ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_peer_gettransferformat(dns_peer_t *peer, dns_transfer_format_t *retval) {
+ REQUIRE(DNS_PEER_VALID(peer));
+ REQUIRE(retval != NULL);
+
+ if (DNS_BIT_CHECK(SERVER_TRANSFER_FORMAT_BIT, &peer->bitflags)) {
+ *retval = peer->transfer_format;
+ return (ISC_R_SUCCESS);
+ } else {
+ return (ISC_R_NOTFOUND);
+ }
+}
+
+isc_result_t
+dns_peer_getkey(dns_peer_t *peer, dns_name_t **retval) {
+ REQUIRE(DNS_PEER_VALID(peer));
+ REQUIRE(retval != NULL);
+
+ if (peer->key != NULL) {
+ *retval = peer->key;
+ }
+
+ return (peer->key == NULL ? ISC_R_NOTFOUND : ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_peer_setkey(dns_peer_t *peer, dns_name_t **keyval) {
+ isc_boolean_t exists = ISC_FALSE;
+
+ if (peer->key != NULL) {
+ dns_name_free(peer->key, peer->mem);
+ isc_mem_put(peer->mem, peer->key, sizeof(dns_name_t));
+ exists = ISC_TRUE;
+ }
+
+ peer->key = *keyval;
+ *keyval = NULL;
+
+ return (exists ? ISC_R_EXISTS : ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_peer_setkeybycharp(dns_peer_t *peer, const char *keyval) {
+ isc_buffer_t b;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+ isc_result_t result;
+
+ dns_fixedname_init(&fname);
+ isc_buffer_init(&b, keyval, strlen(keyval));
+ isc_buffer_add(&b, strlen(keyval));
+ result = dns_name_fromtext(dns_fixedname_name(&fname), &b,
+ dns_rootname, ISC_FALSE, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ name = isc_mem_get(peer->mem, sizeof(dns_name_t));
+ if (name == NULL)
+ return (ISC_R_NOMEMORY);
+
+ dns_name_init(name, NULL);
+ result = dns_name_dup(dns_fixedname_name(&fname), peer->mem, name);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(peer->mem, name, sizeof(dns_name_t));
+ return (result);
+ }
+
+ result = dns_peer_setkey(peer, &name);
+ if (result != ISC_R_SUCCESS)
+ isc_mem_put(peer->mem, name, sizeof(dns_name_t));
+
+ return (result);
+}
+
+isc_result_t
+dns_peer_settransfersource(dns_peer_t *peer,
+ const isc_sockaddr_t *transfer_source)
+{
+ REQUIRE(DNS_PEER_VALID(peer));
+
+ if (peer->transfer_source != NULL) {
+ isc_mem_put(peer->mem, peer->transfer_source,
+ sizeof(*peer->transfer_source));
+ peer->transfer_source = NULL;
+ }
+ if (transfer_source != NULL) {
+ peer->transfer_source = isc_mem_get(peer->mem,
+ sizeof(*peer->transfer_source));
+ if (peer->transfer_source == NULL)
+ return (ISC_R_NOMEMORY);
+
+ *peer->transfer_source = *transfer_source;
+ }
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_peer_gettransfersource(dns_peer_t *peer, isc_sockaddr_t *transfer_source) {
+ REQUIRE(DNS_PEER_VALID(peer));
+ REQUIRE(transfer_source != NULL);
+
+ if (peer->transfer_source == NULL)
+ return (ISC_R_NOTFOUND);
+ *transfer_source = *peer->transfer_source;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_peer_setnotifysource(dns_peer_t *peer,
+ const isc_sockaddr_t *notify_source)
+{
+ REQUIRE(DNS_PEER_VALID(peer));
+
+ if (peer->notify_source != NULL) {
+ isc_mem_put(peer->mem, peer->notify_source,
+ sizeof(*peer->notify_source));
+ peer->notify_source = NULL;
+ }
+ if (notify_source != NULL) {
+ peer->notify_source = isc_mem_get(peer->mem,
+ sizeof(*peer->notify_source));
+ if (peer->notify_source == NULL)
+ return (ISC_R_NOMEMORY);
+
+ *peer->notify_source = *notify_source;
+ }
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_peer_getnotifysource(dns_peer_t *peer, isc_sockaddr_t *notify_source) {
+ REQUIRE(DNS_PEER_VALID(peer));
+ REQUIRE(notify_source != NULL);
+
+ if (peer->notify_source == NULL)
+ return (ISC_R_NOTFOUND);
+ *notify_source = *peer->notify_source;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_peer_setquerysource(dns_peer_t *peer, const isc_sockaddr_t *query_source) {
+ REQUIRE(DNS_PEER_VALID(peer));
+
+ if (peer->query_source != NULL) {
+ isc_mem_put(peer->mem, peer->query_source,
+ sizeof(*peer->query_source));
+ peer->query_source = NULL;
+ }
+ if (query_source != NULL) {
+ peer->query_source = isc_mem_get(peer->mem,
+ sizeof(*peer->query_source));
+ if (peer->query_source == NULL)
+ return (ISC_R_NOMEMORY);
+
+ *peer->query_source = *query_source;
+ }
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_peer_getquerysource(dns_peer_t *peer, isc_sockaddr_t *query_source) {
+ REQUIRE(DNS_PEER_VALID(peer));
+ REQUIRE(query_source != NULL);
+
+ if (peer->query_source == NULL)
+ return (ISC_R_NOTFOUND);
+ *query_source = *peer->query_source;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_peer_setudpsize(dns_peer_t *peer, isc_uint16_t udpsize) {
+ isc_boolean_t existed;
+
+ REQUIRE(DNS_PEER_VALID(peer));
+
+ existed = DNS_BIT_CHECK(SERVER_UDPSIZE_BIT, &peer->bitflags);
+
+ peer->udpsize = udpsize;
+ DNS_BIT_SET(SERVER_UDPSIZE_BIT, &peer->bitflags);
+
+ return (existed ? ISC_R_EXISTS : ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_peer_getudpsize(dns_peer_t *peer, isc_uint16_t *udpsize) {
+
+ REQUIRE(DNS_PEER_VALID(peer));
+ REQUIRE(udpsize != NULL);
+
+ if (DNS_BIT_CHECK(SERVER_UDPSIZE_BIT, &peer->bitflags)) {
+ *udpsize = peer->udpsize;
+ return (ISC_R_SUCCESS);
+ } else {
+ return (ISC_R_NOTFOUND);
+ }
+}
+
+isc_result_t
+dns_peer_setmaxudp(dns_peer_t *peer, isc_uint16_t maxudp) {
+ isc_boolean_t existed;
+
+ REQUIRE(DNS_PEER_VALID(peer));
+
+ existed = DNS_BIT_CHECK(SERVER_MAXUDP_BIT, &peer->bitflags);
+
+ peer->maxudp = maxudp;
+ DNS_BIT_SET(SERVER_MAXUDP_BIT, &peer->bitflags);
+
+ return (existed ? ISC_R_EXISTS : ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_peer_getmaxudp(dns_peer_t *peer, isc_uint16_t *maxudp) {
+
+ REQUIRE(DNS_PEER_VALID(peer));
+ REQUIRE(maxudp != NULL);
+
+ if (DNS_BIT_CHECK(SERVER_MAXUDP_BIT, &peer->bitflags)) {
+ *maxudp = peer->maxudp;
+ return (ISC_R_SUCCESS);
+ } else {
+ return (ISC_R_NOTFOUND);
+ }
+}
diff --git a/lib/dns/portlist.c b/lib/dns/portlist.c
new file mode 100644
index 0000000..5bc89f4
--- /dev/null
+++ b/lib/dns/portlist.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: portlist.c,v 1.13 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdlib.h>
+
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/mutex.h>
+#include <isc/net.h>
+#include <isc/refcount.h>
+#include <isc/result.h>
+#include <isc/string.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+#include <dns/types.h>
+#include <dns/portlist.h>
+
+#define DNS_PORTLIST_MAGIC ISC_MAGIC('P','L','S','T')
+#define DNS_VALID_PORTLIST(p) ISC_MAGIC_VALID(p, DNS_PORTLIST_MAGIC)
+
+typedef struct dns_element {
+ in_port_t port;
+ isc_uint16_t flags;
+} dns_element_t;
+
+struct dns_portlist {
+ unsigned int magic;
+ isc_mem_t *mctx;
+ isc_refcount_t refcount;
+ isc_mutex_t lock;
+ dns_element_t *list;
+ unsigned int allocated;
+ unsigned int active;
+};
+
+#define DNS_PL_INET 0x0001
+#define DNS_PL_INET6 0x0002
+#define DNS_PL_ALLOCATE 16
+
+static int
+compare(const void *arg1, const void *arg2) {
+ const dns_element_t *e1 = (const dns_element_t *)arg1;
+ const dns_element_t *e2 = (const dns_element_t *)arg2;
+
+ if (e1->port < e2->port)
+ return (-1);
+ if (e1->port > e2->port)
+ return (1);
+ return (0);
+}
+
+isc_result_t
+dns_portlist_create(isc_mem_t *mctx, dns_portlist_t **portlistp) {
+ dns_portlist_t *portlist;
+ isc_result_t result;
+
+ REQUIRE(portlistp != NULL && *portlistp == NULL);
+
+ portlist = isc_mem_get(mctx, sizeof(*portlist));
+ if (portlist == NULL)
+ return (ISC_R_NOMEMORY);
+ result = isc_mutex_init(&portlist->lock);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, portlist, sizeof(*portlist));
+ return (result);
+ }
+ result = isc_refcount_init(&portlist->refcount, 1);
+ if (result != ISC_R_SUCCESS) {
+ DESTROYLOCK(&portlist->lock);
+ isc_mem_put(mctx, portlist, sizeof(*portlist));
+ return (result);
+ }
+ portlist->list = NULL;
+ portlist->allocated = 0;
+ portlist->active = 0;
+ portlist->mctx = NULL;
+ isc_mem_attach(mctx, &portlist->mctx);
+ portlist->magic = DNS_PORTLIST_MAGIC;
+ *portlistp = portlist;
+ return (ISC_R_SUCCESS);
+}
+
+static dns_element_t *
+find_port(dns_element_t *list, unsigned int len, in_port_t port) {
+ unsigned int xtry = len / 2;
+ unsigned int min = 0;
+ unsigned int max = len - 1;
+ unsigned int last = len;
+
+ for (;;) {
+ if (list[xtry].port == port)
+ return (&list[xtry]);
+ if (port > list[xtry].port) {
+ if (xtry == max)
+ break;
+ min = xtry;
+ xtry = xtry + (max - xtry + 1) / 2;
+ INSIST(xtry <= max);
+ if (xtry == last)
+ break;
+ last = min;
+ } else {
+ if (xtry == min)
+ break;
+ max = xtry;
+ xtry = xtry - (xtry - min + 1) / 2;
+ INSIST(xtry >= min);
+ if (xtry == last)
+ break;
+ last = max;
+ }
+ }
+ return (NULL);
+}
+
+isc_result_t
+dns_portlist_add(dns_portlist_t *portlist, int af, in_port_t port) {
+ dns_element_t *el;
+ isc_result_t result;
+
+ REQUIRE(DNS_VALID_PORTLIST(portlist));
+ REQUIRE(af == AF_INET || af == AF_INET6);
+
+ LOCK(&portlist->lock);
+ if (portlist->active != 0) {
+ el = find_port(portlist->list, portlist->active, port);
+ if (el != NULL) {
+ if (af == AF_INET)
+ el->flags |= DNS_PL_INET;
+ else
+ el->flags |= DNS_PL_INET6;
+ result = ISC_R_SUCCESS;
+ goto unlock;
+ }
+ }
+
+ if (portlist->allocated <= portlist->active) {
+ unsigned int allocated;
+ allocated = portlist->allocated + DNS_PL_ALLOCATE;
+ el = isc_mem_get(portlist->mctx, sizeof(*el) * allocated);
+ if (el == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto unlock;
+ }
+ if (portlist->list != NULL) {
+ memcpy(el, portlist->list,
+ portlist->allocated * sizeof(*el));
+ isc_mem_put(portlist->mctx, portlist->list,
+ portlist->allocated * sizeof(*el));
+ }
+ portlist->list = el;
+ portlist->allocated = allocated;
+ }
+ portlist->list[portlist->active].port = port;
+ if (af == AF_INET)
+ portlist->list[portlist->active].flags = DNS_PL_INET;
+ else
+ portlist->list[portlist->active].flags = DNS_PL_INET6;
+ portlist->active++;
+ qsort(portlist->list, portlist->active, sizeof(*el), compare);
+ result = ISC_R_SUCCESS;
+ unlock:
+ UNLOCK(&portlist->lock);
+ return (result);
+}
+
+void
+dns_portlist_remove(dns_portlist_t *portlist, int af, in_port_t port) {
+ dns_element_t *el;
+
+ REQUIRE(DNS_VALID_PORTLIST(portlist));
+ REQUIRE(af == AF_INET || af == AF_INET6);
+
+ LOCK(&portlist->lock);
+ if (portlist->active != 0) {
+ el = find_port(portlist->list, portlist->active, port);
+ if (el != NULL) {
+ if (af == AF_INET)
+ el->flags &= ~DNS_PL_INET;
+ else
+ el->flags &= ~DNS_PL_INET6;
+ if (el->flags == 0) {
+ *el = portlist->list[portlist->active];
+ portlist->active--;
+ qsort(portlist->list, portlist->active,
+ sizeof(*el), compare);
+ }
+ }
+ }
+ UNLOCK(&portlist->lock);
+}
+
+isc_boolean_t
+dns_portlist_match(dns_portlist_t *portlist, int af, in_port_t port) {
+ dns_element_t *el;
+ isc_boolean_t result = ISC_FALSE;
+
+ REQUIRE(DNS_VALID_PORTLIST(portlist));
+ REQUIRE(af == AF_INET || af == AF_INET6);
+ LOCK(&portlist->lock);
+ if (portlist->active != 0) {
+ el = find_port(portlist->list, portlist->active, port);
+ if (el != NULL) {
+ if (af == AF_INET && (el->flags & DNS_PL_INET) != 0)
+ result = ISC_TRUE;
+ if (af == AF_INET6 && (el->flags & DNS_PL_INET6) != 0)
+ result = ISC_TRUE;
+ }
+ }
+ UNLOCK(&portlist->lock);
+ return (result);
+}
+
+void
+dns_portlist_attach(dns_portlist_t *portlist, dns_portlist_t **portlistp) {
+
+ REQUIRE(DNS_VALID_PORTLIST(portlist));
+ REQUIRE(portlistp != NULL && *portlistp == NULL);
+
+ isc_refcount_increment(&portlist->refcount, NULL);
+ *portlistp = portlist;
+}
+
+void
+dns_portlist_detach(dns_portlist_t **portlistp) {
+ dns_portlist_t *portlist;
+ unsigned int count;
+
+ REQUIRE(portlistp != NULL);
+ portlist = *portlistp;
+ REQUIRE(DNS_VALID_PORTLIST(portlist));
+ *portlistp = NULL;
+ isc_refcount_decrement(&portlist->refcount, &count);
+ if (count == 0) {
+ portlist->magic = 0;
+ isc_refcount_destroy(&portlist->refcount);
+ if (portlist->list != NULL)
+ isc_mem_put(portlist->mctx, portlist->list,
+ portlist->allocated *
+ sizeof(*portlist->list));
+ DESTROYLOCK(&portlist->lock);
+ isc_mem_putanddetach(&portlist->mctx, portlist,
+ sizeof(*portlist));
+ }
+}
diff --git a/lib/dns/rbt.c b/lib/dns/rbt.c
new file mode 100644
index 0000000..112591e
--- /dev/null
+++ b/lib/dns/rbt.c
@@ -0,0 +1,2657 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rbt.c,v 1.142 2008/09/24 02:46:22 marka Exp $ */
+
+/*! \file */
+
+/* Principal Authors: DCL */
+
+#include <config.h>
+
+#include <isc/mem.h>
+#include <isc/platform.h>
+#include <isc/print.h>
+#include <isc/refcount.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+/*%
+ * This define is so dns/name.h (included by dns/fixedname.h) uses more
+ * efficient macro calls instead of functions for a few operations.
+ */
+#define DNS_NAME_USEINLINE 1
+
+#include <dns/fixedname.h>
+#include <dns/log.h>
+#include <dns/rbt.h>
+#include <dns/result.h>
+
+#define RBT_MAGIC ISC_MAGIC('R', 'B', 'T', '+')
+#define VALID_RBT(rbt) ISC_MAGIC_VALID(rbt, RBT_MAGIC)
+
+/*
+ * XXXDCL Since parent pointers were added in again, I could remove all of the
+ * chain junk, and replace with dns_rbt_firstnode, _previousnode, _nextnode,
+ * _lastnode. This would involve pretty major change to the API.
+ */
+#define CHAIN_MAGIC ISC_MAGIC('0', '-', '0', '-')
+#define VALID_CHAIN(chain) ISC_MAGIC_VALID(chain, CHAIN_MAGIC)
+
+#define RBT_HASH_SIZE 64
+
+#ifdef RBT_MEM_TEST
+#undef RBT_HASH_SIZE
+#define RBT_HASH_SIZE 2 /*%< To give the reallocation code a workout. */
+#endif
+
+struct dns_rbt {
+ unsigned int magic;
+ isc_mem_t * mctx;
+ dns_rbtnode_t * root;
+ void (*data_deleter)(void *, void *);
+ void * deleter_arg;
+ unsigned int nodecount;
+ unsigned int hashsize;
+ dns_rbtnode_t ** hashtable;
+};
+
+#define RED 0
+#define BLACK 1
+
+/*%
+ * Elements of the rbtnode structure.
+ */
+#define PARENT(node) ((node)->parent)
+#define LEFT(node) ((node)->left)
+#define RIGHT(node) ((node)->right)
+#define DOWN(node) ((node)->down)
+#define DATA(node) ((node)->data)
+#define HASHNEXT(node) ((node)->hashnext)
+#define HASHVAL(node) ((node)->hashval)
+#define COLOR(node) ((node)->color)
+#define NAMELEN(node) ((node)->namelen)
+#define OFFSETLEN(node) ((node)->offsetlen)
+#define ATTRS(node) ((node)->attributes)
+#define PADBYTES(node) ((node)->padbytes)
+#define IS_ROOT(node) ISC_TF((node)->is_root == 1)
+#define FINDCALLBACK(node) ISC_TF((node)->find_callback == 1)
+
+/*%
+ * Structure elements from the rbtdb.c, not
+ * used as part of the rbt.c algorithms.
+ */
+#define DIRTY(node) ((node)->dirty)
+#define WILD(node) ((node)->wild)
+#define LOCKNUM(node) ((node)->locknum)
+
+/*%
+ * The variable length stuff stored after the node.
+ */
+#define NAME(node) ((unsigned char *)((node) + 1))
+#define OFFSETS(node) (NAME(node) + NAMELEN(node))
+
+#define NODE_SIZE(node) (sizeof(*node) + \
+ NAMELEN(node) + OFFSETLEN(node) + PADBYTES(node))
+
+/*%
+ * Color management.
+ */
+#define IS_RED(node) ((node) != NULL && (node)->color == RED)
+#define IS_BLACK(node) ((node) == NULL || (node)->color == BLACK)
+#define MAKE_RED(node) ((node)->color = RED)
+#define MAKE_BLACK(node) ((node)->color = BLACK)
+
+/*%
+ * Chain management.
+ *
+ * The "ancestors" member of chains were removed, with their job now
+ * being wholy handled by parent pointers (which didn't exist, because
+ * of memory concerns, when chains were first implemented).
+ */
+#define ADD_LEVEL(chain, node) \
+ (chain)->levels[(chain)->level_count++] = (node)
+
+/*%
+ * The following macros directly access normally private name variables.
+ * These macros are used to avoid a lot of function calls in the critical
+ * path of the tree traversal code.
+ */
+
+#define NODENAME(node, name) \
+do { \
+ (name)->length = NAMELEN(node); \
+ (name)->labels = OFFSETLEN(node); \
+ (name)->ndata = NAME(node); \
+ (name)->offsets = OFFSETS(node); \
+ (name)->attributes = ATTRS(node); \
+ (name)->attributes |= DNS_NAMEATTR_READONLY; \
+} while (0)
+
+#ifdef DNS_RBT_USEHASH
+static isc_result_t
+inithash(dns_rbt_t *rbt);
+#endif
+
+#ifdef DEBUG
+#define inline
+/*
+ * A little something to help out in GDB.
+ */
+dns_name_t Name(dns_rbtnode_t *node);
+dns_name_t
+Name(dns_rbtnode_t *node) {
+ dns_name_t name;
+
+ dns_name_init(&name, NULL);
+ if (node != NULL)
+ NODENAME(node, &name);
+
+ return (name);
+}
+
+static void dns_rbt_printnodename(dns_rbtnode_t *node);
+#endif
+
+static inline dns_rbtnode_t *
+find_up(dns_rbtnode_t *node) {
+ dns_rbtnode_t *root;
+
+ /*
+ * Return the node in the level above the argument node that points
+ * to the level the argument node is in. If the argument node is in
+ * the top level, the return value is NULL.
+ */
+ for (root = node; ! IS_ROOT(root); root = PARENT(root))
+ ; /* Nothing. */
+
+ return (PARENT(root));
+}
+
+/*
+ * Forward declarations.
+ */
+static isc_result_t
+create_node(isc_mem_t *mctx, dns_name_t *name, dns_rbtnode_t **nodep);
+
+#ifdef DNS_RBT_USEHASH
+static inline void
+hash_node(dns_rbt_t *rbt, dns_rbtnode_t *node, dns_name_t *name);
+static inline void
+unhash_node(dns_rbt_t *rbt, dns_rbtnode_t *node);
+#else
+#define hash_node(rbt, node, name) (ISC_R_SUCCESS)
+#define unhash_node(rbt, node)
+#endif
+
+static inline void
+rotate_left(dns_rbtnode_t *node, dns_rbtnode_t **rootp);
+static inline void
+rotate_right(dns_rbtnode_t *node, dns_rbtnode_t **rootp);
+
+static void
+dns_rbt_addonlevel(dns_rbtnode_t *node, dns_rbtnode_t *current, int order,
+ dns_rbtnode_t **rootp);
+
+static void
+dns_rbt_deletefromlevel(dns_rbtnode_t *delete, dns_rbtnode_t **rootp);
+
+static isc_result_t
+dns_rbt_deletetree(dns_rbt_t *rbt, dns_rbtnode_t *node);
+
+static void
+dns_rbt_deletetreeflat(dns_rbt_t *rbt, unsigned int quantum,
+ dns_rbtnode_t **nodep);
+
+/*
+ * Initialize a red/black tree of trees.
+ */
+isc_result_t
+dns_rbt_create(isc_mem_t *mctx, void (*deleter)(void *, void *),
+ void *deleter_arg, dns_rbt_t **rbtp)
+{
+#ifdef DNS_RBT_USEHASH
+ isc_result_t result;
+#endif
+ dns_rbt_t *rbt;
+
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(rbtp != NULL && *rbtp == NULL);
+ REQUIRE(deleter == NULL ? deleter_arg == NULL : 1);
+
+ rbt = (dns_rbt_t *)isc_mem_get(mctx, sizeof(*rbt));
+ if (rbt == NULL)
+ return (ISC_R_NOMEMORY);
+
+ rbt->mctx = mctx;
+ rbt->data_deleter = deleter;
+ rbt->deleter_arg = deleter_arg;
+ rbt->root = NULL;
+ rbt->nodecount = 0;
+ rbt->hashtable = NULL;
+ rbt->hashsize = 0;
+
+#ifdef DNS_RBT_USEHASH
+ result = inithash(rbt);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, rbt, sizeof(*rbt));
+ return (result);
+ }
+#endif
+
+ rbt->magic = RBT_MAGIC;
+
+ *rbtp = rbt;
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Deallocate a red/black tree of trees.
+ */
+void
+dns_rbt_destroy(dns_rbt_t **rbtp) {
+ RUNTIME_CHECK(dns_rbt_destroy2(rbtp, 0) == ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_rbt_destroy2(dns_rbt_t **rbtp, unsigned int quantum) {
+ dns_rbt_t *rbt;
+
+ REQUIRE(rbtp != NULL && VALID_RBT(*rbtp));
+
+ rbt = *rbtp;
+
+ dns_rbt_deletetreeflat(rbt, quantum, &rbt->root);
+ if (rbt->root != NULL)
+ return (ISC_R_QUOTA);
+
+ INSIST(rbt->nodecount == 0);
+
+ if (rbt->hashtable != NULL)
+ isc_mem_put(rbt->mctx, rbt->hashtable,
+ rbt->hashsize * sizeof(dns_rbtnode_t *));
+
+ rbt->magic = 0;
+
+ isc_mem_put(rbt->mctx, rbt, sizeof(*rbt));
+ *rbtp = NULL;
+ return (ISC_R_SUCCESS);
+}
+
+unsigned int
+dns_rbt_nodecount(dns_rbt_t *rbt) {
+ REQUIRE(VALID_RBT(rbt));
+ return (rbt->nodecount);
+}
+
+static inline isc_result_t
+chain_name(dns_rbtnodechain_t *chain, dns_name_t *name,
+ isc_boolean_t include_chain_end)
+{
+ dns_name_t nodename;
+ isc_result_t result = ISC_R_SUCCESS;
+ int i;
+
+ dns_name_init(&nodename, NULL);
+
+ if (include_chain_end && chain->end != NULL) {
+ NODENAME(chain->end, &nodename);
+ result = dns_name_copy(&nodename, name, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ } else
+ dns_name_reset(name);
+
+ for (i = (int)chain->level_count - 1; i >= 0; i--) {
+ NODENAME(chain->levels[i], &nodename);
+ result = dns_name_concatenate(name, &nodename, name, NULL);
+
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ return (result);
+}
+
+static inline isc_result_t
+move_chain_to_last(dns_rbtnodechain_t *chain, dns_rbtnode_t *node) {
+ do {
+ /*
+ * Go as far right and then down as much as possible,
+ * as long as the rightmost node has a down pointer.
+ */
+ while (RIGHT(node) != NULL)
+ node = RIGHT(node);
+
+ if (DOWN(node) == NULL)
+ break;
+
+ ADD_LEVEL(chain, node);
+ node = DOWN(node);
+ } while (1);
+
+ chain->end = node;
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Add 'name' to tree, initializing its data pointer with 'data'.
+ */
+
+isc_result_t
+dns_rbt_addnode(dns_rbt_t *rbt, dns_name_t *name, dns_rbtnode_t **nodep) {
+ /*
+ * Does this thing have too many variables or what?
+ */
+ dns_rbtnode_t **root, *parent, *child, *current, *new_current;
+ dns_name_t *add_name, *new_name, current_name, *prefix, *suffix;
+ dns_fixedname_t fixedcopy, fixedprefix, fixedsuffix, fnewname;
+ dns_offsets_t current_offsets;
+ dns_namereln_t compared;
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_rbtnodechain_t chain;
+ unsigned int common_labels;
+ unsigned int nlabels, hlabels;
+ int order;
+
+ REQUIRE(VALID_RBT(rbt));
+ REQUIRE(dns_name_isabsolute(name));
+ REQUIRE(nodep != NULL && *nodep == NULL);
+
+ /*
+ * Create a copy of the name so the original name structure is
+ * not modified.
+ */
+ dns_fixedname_init(&fixedcopy);
+ add_name = dns_fixedname_name(&fixedcopy);
+ dns_name_clone(name, add_name);
+
+ if (rbt->root == NULL) {
+ result = create_node(rbt->mctx, add_name, &new_current);
+ if (result == ISC_R_SUCCESS) {
+ rbt->nodecount++;
+ new_current->is_root = 1;
+ rbt->root = new_current;
+ *nodep = new_current;
+ hash_node(rbt, new_current, name);
+ }
+ return (result);
+ }
+
+ dns_rbtnodechain_init(&chain, rbt->mctx);
+
+ dns_fixedname_init(&fixedprefix);
+ dns_fixedname_init(&fixedsuffix);
+ prefix = dns_fixedname_name(&fixedprefix);
+ suffix = dns_fixedname_name(&fixedsuffix);
+
+ root = &rbt->root;
+ INSIST(IS_ROOT(*root));
+ parent = NULL;
+ current = NULL;
+ child = *root;
+ dns_name_init(&current_name, current_offsets);
+ dns_fixedname_init(&fnewname);
+ new_name = dns_fixedname_name(&fnewname);
+ nlabels = dns_name_countlabels(name);
+ hlabels = 0;
+
+ do {
+ current = child;
+
+ NODENAME(current, &current_name);
+ compared = dns_name_fullcompare(add_name, &current_name,
+ &order, &common_labels);
+
+ if (compared == dns_namereln_equal) {
+ *nodep = current;
+ result = ISC_R_EXISTS;
+ break;
+
+ }
+
+ if (compared == dns_namereln_none) {
+
+ if (order < 0) {
+ parent = current;
+ child = LEFT(current);
+
+ } else if (order > 0) {
+ parent = current;
+ child = RIGHT(current);
+
+ }
+
+ } else {
+ /*
+ * This name has some suffix in common with the
+ * name at the current node. If the name at
+ * the current node is shorter, that means the
+ * new name should be in a subtree. If the
+ * name at the current node is longer, that means
+ * the down pointer to this tree should point
+ * to a new tree that has the common suffix, and
+ * the non-common parts of these two names should
+ * start a new tree.
+ */
+ hlabels += common_labels;
+ if (compared == dns_namereln_subdomain) {
+ /*
+ * All of the existing labels are in common,
+ * so the new name is in a subtree.
+ * Whack off the common labels for the
+ * not-in-common part to be searched for
+ * in the next level.
+ */
+ dns_name_split(add_name, common_labels,
+ add_name, NULL);
+
+ /*
+ * Follow the down pointer (possibly NULL).
+ */
+ root = &DOWN(current);
+
+ INSIST(*root == NULL ||
+ (IS_ROOT(*root) &&
+ PARENT(*root) == current));
+
+ parent = NULL;
+ child = DOWN(current);
+ ADD_LEVEL(&chain, current);
+
+ } else {
+ /*
+ * The number of labels in common is fewer
+ * than the number of labels at the current
+ * node, so the current node must be adjusted
+ * to have just the common suffix, and a down
+ * pointer made to a new tree.
+ */
+
+ INSIST(compared == dns_namereln_commonancestor
+ || compared == dns_namereln_contains);
+
+ /*
+ * Ensure the number of levels in the tree
+ * does not exceed the number of logical
+ * levels allowed by DNSSEC.
+ *
+ * XXXDCL need a better error result?
+ *
+ * XXXDCL Since chain ancestors were removed,
+ * no longer used by dns_rbt_addonlevel(),
+ * this is the only real use of chains in the
+ * function. It could be done instead with
+ * a simple integer variable, but I am pressed
+ * for time.
+ */
+ if (chain.level_count ==
+ (sizeof(chain.levels) /
+ sizeof(*chain.levels))) {
+ result = ISC_R_NOSPACE;
+ break;
+ }
+
+ /*
+ * Split the name into two parts, a prefix
+ * which is the not-in-common parts of the
+ * two names and a suffix that is the common
+ * parts of them.
+ */
+ dns_name_split(&current_name, common_labels,
+ prefix, suffix);
+ result = create_node(rbt->mctx, suffix,
+ &new_current);
+
+ if (result != ISC_R_SUCCESS)
+ break;
+
+ /*
+ * Reproduce the tree attributes of the
+ * current node.
+ */
+ new_current->is_root = current->is_root;
+ new_current->nsec3 = current->nsec3;
+ PARENT(new_current) = PARENT(current);
+ LEFT(new_current) = LEFT(current);
+ RIGHT(new_current) = RIGHT(current);
+ COLOR(new_current) = COLOR(current);
+
+ /*
+ * Fix pointers that were to the current node.
+ */
+ if (parent != NULL) {
+ if (LEFT(parent) == current)
+ LEFT(parent) = new_current;
+ else
+ RIGHT(parent) = new_current;
+ }
+ if (LEFT(new_current) != NULL)
+ PARENT(LEFT(new_current)) =
+ new_current;
+ if (RIGHT(new_current) != NULL)
+ PARENT(RIGHT(new_current)) =
+ new_current;
+ if (*root == current)
+ *root = new_current;
+
+ NAMELEN(current) = prefix->length;
+ OFFSETLEN(current) = prefix->labels;
+ memcpy(OFFSETS(current), prefix->offsets,
+ prefix->labels);
+ PADBYTES(current) +=
+ (current_name.length - prefix->length) +
+ (current_name.labels - prefix->labels);
+
+ /*
+ * Set up the new root of the next level.
+ * By definition it will not be the top
+ * level tree, so clear DNS_NAMEATTR_ABSOLUTE.
+ */
+ current->is_root = 1;
+ PARENT(current) = new_current;
+ DOWN(new_current) = current;
+ root = &DOWN(new_current);
+
+ ADD_LEVEL(&chain, new_current);
+
+ LEFT(current) = NULL;
+ RIGHT(current) = NULL;
+
+ MAKE_BLACK(current);
+ ATTRS(current) &= ~DNS_NAMEATTR_ABSOLUTE;
+
+ rbt->nodecount++;
+ dns_name_getlabelsequence(name,
+ nlabels - hlabels,
+ hlabels, new_name);
+ hash_node(rbt, new_current, new_name);
+
+ if (common_labels ==
+ dns_name_countlabels(add_name)) {
+ /*
+ * The name has been added by pushing
+ * the not-in-common parts down to
+ * a new level.
+ */
+ *nodep = new_current;
+ return (ISC_R_SUCCESS);
+
+ } else {
+ /*
+ * The current node has no data,
+ * because it is just a placeholder.
+ * Its data pointer is already NULL
+ * from create_node()), so there's
+ * nothing more to do to it.
+ */
+
+ /*
+ * The not-in-common parts of the new
+ * name will be inserted into the new
+ * level following this loop (unless
+ * result != ISC_R_SUCCESS, which
+ * is tested after the loop ends).
+ */
+ dns_name_split(add_name, common_labels,
+ add_name, NULL);
+
+ break;
+ }
+
+ }
+
+ }
+
+ } while (child != NULL);
+
+ if (result == ISC_R_SUCCESS)
+ result = create_node(rbt->mctx, add_name, &new_current);
+
+ if (result == ISC_R_SUCCESS) {
+ dns_rbt_addonlevel(new_current, current, order, root);
+ rbt->nodecount++;
+ *nodep = new_current;
+ hash_node(rbt, new_current, name);
+ }
+
+ return (result);
+}
+
+/*
+ * Add a name to the tree of trees, associating it with some data.
+ */
+isc_result_t
+dns_rbt_addname(dns_rbt_t *rbt, dns_name_t *name, void *data) {
+ isc_result_t result;
+ dns_rbtnode_t *node;
+
+ REQUIRE(VALID_RBT(rbt));
+ REQUIRE(dns_name_isabsolute(name));
+
+ node = NULL;
+
+ result = dns_rbt_addnode(rbt, name, &node);
+
+ /*
+ * dns_rbt_addnode will report the node exists even when
+ * it does not have data associated with it, but the
+ * dns_rbt_*name functions all behave depending on whether
+ * there is data associated with a node.
+ */
+ if (result == ISC_R_SUCCESS ||
+ (result == ISC_R_EXISTS && DATA(node) == NULL)) {
+ DATA(node) = data;
+ result = ISC_R_SUCCESS;
+ }
+
+ return (result);
+}
+
+/*
+ * Find the node for "name" in the tree of trees.
+ */
+isc_result_t
+dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
+ dns_rbtnode_t **node, dns_rbtnodechain_t *chain,
+ unsigned int options, dns_rbtfindcallback_t callback,
+ void *callback_arg)
+{
+ dns_rbtnode_t *current, *last_compared, *current_root;
+ dns_rbtnodechain_t localchain;
+ dns_name_t *search_name, current_name, *callback_name;
+ dns_fixedname_t fixedcallbackname, fixedsearchname;
+ dns_namereln_t compared;
+ isc_result_t result, saved_result;
+ unsigned int common_labels;
+ unsigned int hlabels = 0;
+ int order;
+
+ REQUIRE(VALID_RBT(rbt));
+ REQUIRE(dns_name_isabsolute(name));
+ REQUIRE(node != NULL && *node == NULL);
+ REQUIRE((options & (DNS_RBTFIND_NOEXACT | DNS_RBTFIND_NOPREDECESSOR))
+ != (DNS_RBTFIND_NOEXACT | DNS_RBTFIND_NOPREDECESSOR));
+
+ /*
+ * If there is a chain it needs to appear to be in a sane state,
+ * otherwise a chain is still needed to generate foundname and
+ * callback_name.
+ */
+ if (chain == NULL) {
+ options |= DNS_RBTFIND_NOPREDECESSOR;
+ chain = &localchain;
+ dns_rbtnodechain_init(chain, rbt->mctx);
+ } else
+ dns_rbtnodechain_reset(chain);
+
+ if (rbt->root == NULL)
+ return (ISC_R_NOTFOUND);
+ else {
+ /*
+ * Appease GCC about variables it incorrectly thinks are
+ * possibly used uninitialized.
+ */
+ compared = dns_namereln_none;
+ last_compared = NULL;
+ }
+
+ dns_fixedname_init(&fixedcallbackname);
+ callback_name = dns_fixedname_name(&fixedcallbackname);
+
+ /*
+ * search_name is the name segment being sought in each tree level.
+ * By using a fixedname, the search_name will definitely have offsets
+ * for use by any splitting.
+ * By using dns_name_clone, no name data should be copied thanks to
+ * the lack of bitstring labels.
+ */
+ dns_fixedname_init(&fixedsearchname);
+ search_name = dns_fixedname_name(&fixedsearchname);
+ dns_name_clone(name, search_name);
+
+ dns_name_init(&current_name, NULL);
+
+ saved_result = ISC_R_SUCCESS;
+ current = rbt->root;
+ current_root = rbt->root;
+
+ while (current != NULL) {
+ NODENAME(current, &current_name);
+ compared = dns_name_fullcompare(search_name, &current_name,
+ &order, &common_labels);
+ last_compared = current;
+
+ if (compared == dns_namereln_equal)
+ break;
+
+ if (compared == dns_namereln_none) {
+#ifdef DNS_RBT_USEHASH
+ dns_name_t hash_name;
+ dns_rbtnode_t *hnode;
+ dns_rbtnode_t *up_current;
+ unsigned int nlabels;
+ unsigned int tlabels = 1;
+ unsigned int hash;
+
+ /*
+ * If there is no hash table, hashing can't be done.
+ */
+ if (rbt->hashtable == NULL)
+ goto nohash;
+
+ /*
+ * The case of current != current_root, that
+ * means a left or right pointer was followed,
+ * only happens when the algorithm fell through to
+ * the traditional binary search because of a
+ * bitstring label. Since we dropped the bitstring
+ * support, this should not happen.
+ */
+ INSIST(current == current_root);
+
+ nlabels = dns_name_countlabels(search_name);
+
+ /*
+ * current_root is the root of the current level, so
+ * it's parent is the same as it's "up" pointer.
+ */
+ up_current = PARENT(current_root);
+ dns_name_init(&hash_name, NULL);
+
+ hashagain:
+ /*
+ * Hash includes tail.
+ */
+ dns_name_getlabelsequence(name,
+ nlabels - tlabels,
+ hlabels + tlabels,
+ &hash_name);
+ hash = dns_name_fullhash(&hash_name, ISC_FALSE);
+ dns_name_getlabelsequence(search_name,
+ nlabels - tlabels,
+ tlabels, &hash_name);
+
+ for (hnode = rbt->hashtable[hash % rbt->hashsize];
+ hnode != NULL;
+ hnode = hnode->hashnext)
+ {
+ dns_name_t hnode_name;
+
+ if (hash != HASHVAL(hnode))
+ continue;
+ if (find_up(hnode) != up_current)
+ continue;
+ dns_name_init(&hnode_name, NULL);
+ NODENAME(hnode, &hnode_name);
+ if (dns_name_equal(&hnode_name, &hash_name))
+ break;
+ }
+
+ if (hnode != NULL) {
+ current = hnode;
+ /*
+ * This is an optimization. If hashing found
+ * the right node, the next call to
+ * dns_name_fullcompare() would obviously
+ * return _equal or _subdomain. Determine
+ * which of those would be the case by
+ * checking if the full name was hashed. Then
+ * make it look like dns_name_fullcompare
+ * was called and jump to the right place.
+ */
+ if (tlabels == nlabels) {
+ compared = dns_namereln_equal;
+ break;
+ } else {
+ common_labels = tlabels;
+ compared = dns_namereln_subdomain;
+ goto subdomain;
+ }
+ }
+
+ if (tlabels++ < nlabels)
+ goto hashagain;
+
+ /*
+ * All of the labels have been tried against the hash
+ * table. Since we dropped the support of bitstring
+ * labels, the name isn't in the table.
+ */
+ current = NULL;
+ continue;
+
+ nohash:
+#endif /* DNS_RBT_USEHASH */
+ /*
+ * Standard binary search tree movement.
+ */
+ if (order < 0)
+ current = LEFT(current);
+ else
+ current = RIGHT(current);
+
+ } else {
+ /*
+ * The names have some common suffix labels.
+ *
+ * If the number in common are equal in length to
+ * the current node's name length, then follow the
+ * down pointer and search in the new tree.
+ */
+ if (compared == dns_namereln_subdomain) {
+ subdomain:
+ /*
+ * Whack off the current node's common parts
+ * for the name to search in the next level.
+ */
+ dns_name_split(search_name, common_labels,
+ search_name, NULL);
+ hlabels += common_labels;
+ /*
+ * This might be the closest enclosing name.
+ */
+ if (DATA(current) != NULL ||
+ (options & DNS_RBTFIND_EMPTYDATA) != 0)
+ *node = current;
+
+ /*
+ * Point the chain to the next level. This
+ * needs to be done before 'current' is pointed
+ * there because the callback in the next
+ * block of code needs the current 'current',
+ * but in the event the callback requests that
+ * the search be stopped then the
+ * DNS_R_PARTIALMATCH code at the end of this
+ * function needs the chain pointed to the
+ * next level.
+ */
+ ADD_LEVEL(chain, current);
+
+ /*
+ * The caller may want to interrupt the
+ * downward search when certain special nodes
+ * are traversed. If this is a special node,
+ * the callback is used to learn what the
+ * caller wants to do.
+ */
+ if (callback != NULL &&
+ FINDCALLBACK(current)) {
+ result = chain_name(chain,
+ callback_name,
+ ISC_FALSE);
+ if (result != ISC_R_SUCCESS) {
+ dns_rbtnodechain_reset(chain);
+ return (result);
+ }
+
+ result = (callback)(current,
+ callback_name,
+ callback_arg);
+ if (result != DNS_R_CONTINUE) {
+ saved_result = result;
+ /*
+ * Treat this node as if it
+ * had no down pointer.
+ */
+ current = NULL;
+ break;
+ }
+ }
+
+ /*
+ * Finally, head to the next tree level.
+ */
+ current = DOWN(current);
+ current_root = current;
+
+ } else {
+ /*
+ * Though there are labels in common, the
+ * entire name at this node is not common
+ * with the search name so the search
+ * name does not exist in the tree.
+ */
+ INSIST(compared == dns_namereln_commonancestor
+ || compared == dns_namereln_contains);
+
+ current = NULL;
+ }
+ }
+ }
+
+ /*
+ * If current is not NULL, NOEXACT is not disallowing exact matches,
+ * and either the node has data or an empty node is ok, return
+ * ISC_R_SUCCESS to indicate an exact match.
+ */
+ if (current != NULL && (options & DNS_RBTFIND_NOEXACT) == 0 &&
+ (DATA(current) != NULL ||
+ (options & DNS_RBTFIND_EMPTYDATA) != 0)) {
+ /*
+ * Found an exact match.
+ */
+ chain->end = current;
+ chain->level_matches = chain->level_count;
+
+ if (foundname != NULL)
+ result = chain_name(chain, foundname, ISC_TRUE);
+ else
+ result = ISC_R_SUCCESS;
+
+ if (result == ISC_R_SUCCESS) {
+ *node = current;
+ result = saved_result;
+ } else
+ *node = NULL;
+ } else {
+ /*
+ * Did not find an exact match (or did not want one).
+ */
+ if (*node != NULL) {
+ /*
+ * ... but found a partially matching superdomain.
+ * Unwind the chain to the partial match node
+ * to set level_matches to the level above the node,
+ * and then to derive the name.
+ *
+ * chain->level_count is guaranteed to be at least 1
+ * here because by definition of finding a superdomain,
+ * the chain is pointed to at least the first subtree.
+ */
+ chain->level_matches = chain->level_count - 1;
+
+ while (chain->levels[chain->level_matches] != *node) {
+ INSIST(chain->level_matches > 0);
+ chain->level_matches--;
+ }
+
+ if (foundname != NULL) {
+ unsigned int saved_count = chain->level_count;
+
+ chain->level_count = chain->level_matches + 1;
+
+ result = chain_name(chain, foundname,
+ ISC_FALSE);
+
+ chain->level_count = saved_count;
+ } else
+ result = ISC_R_SUCCESS;
+
+ if (result == ISC_R_SUCCESS)
+ result = DNS_R_PARTIALMATCH;
+
+ } else
+ result = ISC_R_NOTFOUND;
+
+ if (current != NULL) {
+ /*
+ * There was an exact match but either
+ * DNS_RBTFIND_NOEXACT was set, or
+ * DNS_RBTFIND_EMPTYDATA was set and the node had no
+ * data. A policy decision was made to set the
+ * chain to the exact match, but this is subject
+ * to change if it becomes apparent that something
+ * else would be more useful. It is important that
+ * this case is handled here, because the predecessor
+ * setting code below assumes the match was not exact.
+ */
+ INSIST(((options & DNS_RBTFIND_NOEXACT) != 0) ||
+ ((options & DNS_RBTFIND_EMPTYDATA) == 0 &&
+ DATA(current) == NULL));
+ chain->end = current;
+
+ } else if ((options & DNS_RBTFIND_NOPREDECESSOR) != 0) {
+ /*
+ * Ensure the chain points nowhere.
+ */
+ chain->end = NULL;
+
+ } else {
+ /*
+ * Since there was no exact match, the chain argument
+ * needs to be pointed at the DNSSEC predecessor of
+ * the search name.
+ */
+ if (compared == dns_namereln_subdomain) {
+ /*
+ * Attempted to follow a down pointer that was
+ * NULL, which means the searched for name was
+ * a subdomain of a terminal name in the tree.
+ * Since there are no existing subdomains to
+ * order against, the terminal name is the
+ * predecessor.
+ */
+ INSIST(chain->level_count > 0);
+ INSIST(chain->level_matches <
+ chain->level_count);
+ chain->end =
+ chain->levels[--chain->level_count];
+
+ } else {
+ isc_result_t result2;
+
+ /*
+ * Point current to the node that stopped
+ * the search.
+ *
+ * With the hashing modification that has been
+ * added to the algorithm, the stop node of a
+ * standard binary search is not known. So it
+ * has to be found. There is probably a more
+ * clever way of doing this.
+ *
+ * The assignment of current to NULL when
+ * the relationship is *not* dns_namereln_none,
+ * even though it later gets set to the same
+ * last_compared anyway, is simply to not push
+ * the while loop in one more level of
+ * indentation.
+ */
+ if (compared == dns_namereln_none)
+ current = last_compared;
+ else
+ current = NULL;
+
+ while (current != NULL) {
+ NODENAME(current, &current_name);
+ compared = dns_name_fullcompare(
+ search_name,
+ &current_name,
+ &order,
+ &common_labels);
+
+ last_compared = current;
+
+ /*
+ * Standard binary search movement.
+ */
+ if (order < 0)
+ current = LEFT(current);
+ else
+ current = RIGHT(current);
+
+ }
+
+ current = last_compared;
+
+ /*
+ * Reached a point within a level tree that
+ * positively indicates the name is not
+ * present, but the stop node could be either
+ * less than the desired name (order > 0) or
+ * greater than the desired name (order < 0).
+ *
+ * If the stop node is less, it is not
+ * necessarily the predecessor. If the stop
+ * node has a down pointer, then the real
+ * predecessor is at the end of a level below
+ * (not necessarily the next level).
+ * Move down levels until the rightmost node
+ * does not have a down pointer.
+ *
+ * When the stop node is greater, it is
+ * the successor. All the logic for finding
+ * the predecessor is handily encapsulated
+ * in dns_rbtnodechain_prev. In the event
+ * that the search name is less than anything
+ * else in the tree, the chain is reset.
+ * XXX DCL What is the best way for the caller
+ * to know that the search name has
+ * no predecessor?
+ */
+
+
+ if (order > 0) {
+ if (DOWN(current) != NULL) {
+ ADD_LEVEL(chain, current);
+
+ result2 =
+ move_chain_to_last(chain,
+ DOWN(current));
+
+ if (result2 != ISC_R_SUCCESS)
+ result = result2;
+ } else
+ /*
+ * Ah, the pure and simple
+ * case. The stop node is the
+ * predecessor.
+ */
+ chain->end = current;
+
+ } else {
+ INSIST(order < 0);
+
+ chain->end = current;
+
+ result2 = dns_rbtnodechain_prev(chain,
+ NULL,
+ NULL);
+ if (result2 == ISC_R_SUCCESS ||
+ result2 == DNS_R_NEWORIGIN)
+ ; /* Nothing. */
+ else if (result2 == ISC_R_NOMORE)
+ /*
+ * There is no predecessor.
+ */
+ dns_rbtnodechain_reset(chain);
+ else
+ result = result2;
+ }
+
+ }
+ }
+ }
+
+ ENSURE(*node == NULL || DNS_RBTNODE_VALID(*node));
+
+ return (result);
+}
+
+/*
+ * Get the data pointer associated with 'name'.
+ */
+isc_result_t
+dns_rbt_findname(dns_rbt_t *rbt, dns_name_t *name, unsigned int options,
+ dns_name_t *foundname, void **data) {
+ dns_rbtnode_t *node = NULL;
+ isc_result_t result;
+
+ REQUIRE(data != NULL && *data == NULL);
+
+ result = dns_rbt_findnode(rbt, name, foundname, &node, NULL,
+ options, NULL, NULL);
+
+ if (node != NULL &&
+ (DATA(node) != NULL || (options & DNS_RBTFIND_EMPTYDATA) != 0))
+ *data = DATA(node);
+ else
+ result = ISC_R_NOTFOUND;
+
+ return (result);
+}
+
+/*
+ * Delete a name from the tree of trees.
+ */
+isc_result_t
+dns_rbt_deletename(dns_rbt_t *rbt, dns_name_t *name, isc_boolean_t recurse) {
+ dns_rbtnode_t *node = NULL;
+ isc_result_t result;
+
+ REQUIRE(VALID_RBT(rbt));
+ REQUIRE(dns_name_isabsolute(name));
+
+ /*
+ * First, find the node.
+ *
+ * When searching, the name might not have an exact match:
+ * consider a.b.a.com, b.b.a.com and c.b.a.com as the only
+ * elements of a tree, which would make layer 1 a single
+ * node tree of "b.a.com" and layer 2 a three node tree of
+ * a, b, and c. Deleting a.com would find only a partial depth
+ * match in the first layer. Should it be a requirement that
+ * that the name to be deleted have data? For now, it is.
+ *
+ * ->dirty, ->locknum and ->references are ignored; they are
+ * solely the province of rbtdb.c.
+ */
+ result = dns_rbt_findnode(rbt, name, NULL, &node, NULL,
+ DNS_RBTFIND_NOOPTIONS, NULL, NULL);
+
+ if (result == ISC_R_SUCCESS) {
+ if (DATA(node) != NULL)
+ result = dns_rbt_deletenode(rbt, node, recurse);
+ else
+ result = ISC_R_NOTFOUND;
+
+ } else if (result == DNS_R_PARTIALMATCH)
+ result = ISC_R_NOTFOUND;
+
+ return (result);
+}
+
+/*
+ * Remove a node from the tree of trees.
+ *
+ * NOTE WELL: deletion is *not* symmetric with addition; that is, reversing
+ * a sequence of additions to be deletions will not generally get the
+ * tree back to the state it started in. For example, if the addition
+ * of "b.c" caused the node "a.b.c" to be split, pushing "a" to its own level,
+ * then the subsequent deletion of "b.c" will not cause "a" to be pulled up,
+ * restoring "a.b.c". The RBT *used* to do this kind of rejoining, but it
+ * turned out to be a bad idea because it could corrupt an active nodechain
+ * that had "b.c" as one of its levels -- and the RBT has no idea what
+ * nodechains are in use by callers, so it can't even *try* to helpfully
+ * fix them up (which would probably be doomed to failure anyway).
+ *
+ * Similarly, it is possible to leave the tree in a state where a supposedly
+ * deleted node still exists. The first case of this is obvious; take
+ * the tree which has "b.c" on one level, pointing to "a". Now deleted "b.c".
+ * It was just established in the previous paragraph why we can't pull "a"
+ * back up to its parent level. But what happens when "a" then gets deleted?
+ * "b.c" is left hanging around without data or children. This condition
+ * is actually pretty easy to detect, but ... should it really be removed?
+ * Is a chain pointing to it? An iterator? Who knows! (Note that the
+ * references structure member cannot be looked at because it is private to
+ * rbtdb.) This is ugly and makes me unhappy, but after hours of trying to
+ * make it more aesthetically proper and getting nowhere, this is the way it
+ * is going to stay until such time as it proves to be a *real* problem.
+ *
+ * Finally, for reference, note that the original routine that did node
+ * joining was called join_nodes(). It has been excised, living now only
+ * in the CVS history, but comments have been left behind that point to it just
+ * in case someone wants to muck with this some more.
+ *
+ * The one positive aspect of all of this is that joining used to have a
+ * case where it might fail. Without trying to join, now this function always
+ * succeeds. It still returns isc_result_t, though, so the API wouldn't change.
+ */
+isc_result_t
+dns_rbt_deletenode(dns_rbt_t *rbt, dns_rbtnode_t *node, isc_boolean_t recurse)
+{
+ dns_rbtnode_t *parent;
+
+ REQUIRE(VALID_RBT(rbt));
+ REQUIRE(DNS_RBTNODE_VALID(node));
+
+ if (DOWN(node) != NULL) {
+ if (recurse)
+ RUNTIME_CHECK(dns_rbt_deletetree(rbt, DOWN(node))
+ == ISC_R_SUCCESS);
+ else {
+ if (DATA(node) != NULL && rbt->data_deleter != NULL)
+ rbt->data_deleter(DATA(node), rbt->deleter_arg);
+ DATA(node) = NULL;
+
+ /*
+ * Since there is at least one node below this one and
+ * no recursion was requested, the deletion is
+ * complete. The down node from this node might be all
+ * by itself on a single level, so join_nodes() could
+ * be used to collapse the tree (with all the caveats
+ * of the comment at the start of this function).
+ */
+ return (ISC_R_SUCCESS);
+ }
+ }
+
+ /*
+ * Note the node that points to the level of the node that is being
+ * deleted. If the deleted node is the top level, parent will be set
+ * to NULL.
+ */
+ parent = find_up(node);
+
+ /*
+ * This node now has no down pointer (either because it didn't
+ * have one to start, or because it was recursively removed).
+ * So now the node needs to be removed from this level.
+ */
+ dns_rbt_deletefromlevel(node, parent == NULL ? &rbt->root :
+ &DOWN(parent));
+
+ if (DATA(node) != NULL && rbt->data_deleter != NULL)
+ rbt->data_deleter(DATA(node), rbt->deleter_arg);
+
+ unhash_node(rbt, node);
+#if DNS_RBT_USEMAGIC
+ node->magic = 0;
+#endif
+ dns_rbtnode_refdestroy(node);
+ isc_mem_put(rbt->mctx, node, NODE_SIZE(node));
+ rbt->nodecount--;
+
+ /*
+ * There are now two special cases that can exist that would
+ * not have existed if the tree had been created using only
+ * the names that now exist in it. (This is all related to
+ * join_nodes() as described in this function's introductory comment.)
+ * Both cases exist when the deleted node's parent (the node
+ * that pointed to the deleted node's level) is not null but
+ * it has no data: parent != NULL && DATA(parent) == NULL.
+ *
+ * The first case is that the deleted node was the last on its level:
+ * DOWN(parent) == NULL. This case can only exist if the parent was
+ * previously deleted -- and so now, apparently, the parent should go
+ * away. That can't be done though because there might be external
+ * references to it, such as through a nodechain.
+ *
+ * The other case also involves a parent with no data, but with the
+ * deleted node being the next-to-last node instead of the last:
+ * LEFT(DOWN(parent)) == NULL && RIGHT(DOWN(parent)) == NULL.
+ * Presumably now the remaining node on the level should be joined
+ * with the parent, but it's already been described why that can't be
+ * done.
+ */
+
+ /*
+ * This function never fails.
+ */
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_rbt_namefromnode(dns_rbtnode_t *node, dns_name_t *name) {
+
+ REQUIRE(DNS_RBTNODE_VALID(node));
+ REQUIRE(name != NULL);
+ REQUIRE(name->offsets == NULL);
+
+ NODENAME(node, name);
+}
+
+isc_result_t
+dns_rbt_fullnamefromnode(dns_rbtnode_t *node, dns_name_t *name) {
+ dns_name_t current;
+ isc_result_t result;
+
+ REQUIRE(DNS_RBTNODE_VALID(node));
+ REQUIRE(name != NULL);
+ REQUIRE(name->buffer != NULL);
+
+ dns_name_init(&current, NULL);
+ dns_name_reset(name);
+
+ do {
+ INSIST(node != NULL);
+
+ NODENAME(node, &current);
+
+ result = dns_name_concatenate(name, &current, name, NULL);
+ if (result != ISC_R_SUCCESS)
+ break;
+
+ node = find_up(node);
+ } while (! dns_name_isabsolute(name));
+
+ return (result);
+}
+
+char *
+dns_rbt_formatnodename(dns_rbtnode_t *node, char *printname, unsigned int size)
+{
+ dns_fixedname_t fixedname;
+ dns_name_t *name;
+ isc_result_t result;
+
+ REQUIRE(DNS_RBTNODE_VALID(node));
+ REQUIRE(printname != NULL);
+
+ dns_fixedname_init(&fixedname);
+ name = dns_fixedname_name(&fixedname);
+ result = dns_rbt_fullnamefromnode(node, name);
+ if (result == ISC_R_SUCCESS)
+ dns_name_format(name, printname, size);
+ else
+ snprintf(printname, size, "<error building name: %s>",
+ dns_result_totext(result));
+
+ return (printname);
+}
+
+static isc_result_t
+create_node(isc_mem_t *mctx, dns_name_t *name, dns_rbtnode_t **nodep) {
+ dns_rbtnode_t *node;
+ isc_region_t region;
+ unsigned int labels;
+
+ REQUIRE(name->offsets != NULL);
+
+ dns_name_toregion(name, &region);
+ labels = dns_name_countlabels(name);
+ ENSURE(labels > 0);
+
+ /*
+ * Allocate space for the node structure, the name, and the offsets.
+ */
+ node = (dns_rbtnode_t *)isc_mem_get(mctx, sizeof(*node) +
+ region.length + labels);
+
+ if (node == NULL)
+ return (ISC_R_NOMEMORY);
+
+ node->is_root = 0;
+ PARENT(node) = NULL;
+ RIGHT(node) = NULL;
+ LEFT(node) = NULL;
+ DOWN(node) = NULL;
+ DATA(node) = NULL;
+#ifdef DNS_RBT_USEHASH
+ HASHNEXT(node) = NULL;
+ HASHVAL(node) = 0;
+#endif
+
+ ISC_LINK_INIT(node, deadlink);
+
+ LOCKNUM(node) = 0;
+ WILD(node) = 0;
+ DIRTY(node) = 0;
+ dns_rbtnode_refinit(node, 0);
+ node->find_callback = 0;
+ node->nsec3 = 0;
+
+ MAKE_BLACK(node);
+
+ /*
+ * The following is stored to make reconstructing a name from the
+ * stored value in the node easy: the length of the name, the number
+ * of labels, whether the name is absolute or not, the name itself,
+ * and the name's offsets table.
+ *
+ * XXX RTH
+ * The offsets table could be made smaller by eliminating the
+ * first offset, which is always 0. This requires changes to
+ * lib/dns/name.c.
+ */
+ NAMELEN(node) = region.length;
+ PADBYTES(node) = 0;
+ OFFSETLEN(node) = labels;
+ ATTRS(node) = name->attributes;
+
+ memcpy(NAME(node), region.base, region.length);
+ memcpy(OFFSETS(node), name->offsets, labels);
+
+#if DNS_RBT_USEMAGIC
+ node->magic = DNS_RBTNODE_MAGIC;
+#endif
+ *nodep = node;
+
+ return (ISC_R_SUCCESS);
+}
+
+#ifdef DNS_RBT_USEHASH
+static inline void
+hash_add_node(dns_rbt_t *rbt, dns_rbtnode_t *node, dns_name_t *name) {
+ unsigned int hash;
+
+ HASHVAL(node) = dns_name_fullhash(name, ISC_FALSE);
+
+ hash = HASHVAL(node) % rbt->hashsize;
+ HASHNEXT(node) = rbt->hashtable[hash];
+
+ rbt->hashtable[hash] = node;
+}
+
+static isc_result_t
+inithash(dns_rbt_t *rbt) {
+ unsigned int bytes;
+
+ rbt->hashsize = RBT_HASH_SIZE;
+ bytes = rbt->hashsize * sizeof(dns_rbtnode_t *);
+ rbt->hashtable = isc_mem_get(rbt->mctx, bytes);
+
+ if (rbt->hashtable == NULL)
+ return (ISC_R_NOMEMORY);
+
+ memset(rbt->hashtable, 0, bytes);
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+rehash(dns_rbt_t *rbt) {
+ unsigned int oldsize;
+ dns_rbtnode_t **oldtable;
+ dns_rbtnode_t *node;
+ unsigned int hash;
+ unsigned int i;
+
+ oldsize = rbt->hashsize;
+ oldtable = rbt->hashtable;
+ rbt->hashsize *= 2 + 1;
+ rbt->hashtable = isc_mem_get(rbt->mctx,
+ rbt->hashsize * sizeof(dns_rbtnode_t *));
+ if (rbt->hashtable == NULL) {
+ rbt->hashtable = oldtable;
+ rbt->hashsize = oldsize;
+ return;
+ }
+
+ for (i = 0; i < rbt->hashsize; i++)
+ rbt->hashtable[i] = NULL;
+
+ for (i = 0; i < oldsize; i++) {
+ node = oldtable[i];
+ while (node != NULL) {
+ hash = HASHVAL(node) % rbt->hashsize;
+ oldtable[i] = HASHNEXT(node);
+ HASHNEXT(node) = rbt->hashtable[hash];
+ rbt->hashtable[hash] = node;
+ node = oldtable[i];
+ }
+ }
+
+ isc_mem_put(rbt->mctx, oldtable, oldsize * sizeof(dns_rbtnode_t *));
+}
+
+static inline void
+hash_node(dns_rbt_t *rbt, dns_rbtnode_t *node, dns_name_t *name) {
+
+ REQUIRE(DNS_RBTNODE_VALID(node));
+
+ if (rbt->nodecount >= (rbt->hashsize *3))
+ rehash(rbt);
+
+ hash_add_node(rbt, node, name);
+}
+
+static inline void
+unhash_node(dns_rbt_t *rbt, dns_rbtnode_t *node) {
+ unsigned int bucket;
+ dns_rbtnode_t *bucket_node;
+
+ REQUIRE(DNS_RBTNODE_VALID(node));
+
+ if (rbt->hashtable != NULL) {
+ bucket = HASHVAL(node) % rbt->hashsize;
+ bucket_node = rbt->hashtable[bucket];
+
+ if (bucket_node == node)
+ rbt->hashtable[bucket] = HASHNEXT(node);
+ else {
+ while (HASHNEXT(bucket_node) != node) {
+ INSIST(HASHNEXT(bucket_node) != NULL);
+ bucket_node = HASHNEXT(bucket_node);
+ }
+ HASHNEXT(bucket_node) = HASHNEXT(node);
+ }
+ }
+}
+#endif /* DNS_RBT_USEHASH */
+
+static inline void
+rotate_left(dns_rbtnode_t *node, dns_rbtnode_t **rootp) {
+ dns_rbtnode_t *child;
+
+ REQUIRE(DNS_RBTNODE_VALID(node));
+ REQUIRE(rootp != NULL);
+
+ child = RIGHT(node);
+ INSIST(child != NULL);
+
+ RIGHT(node) = LEFT(child);
+ if (LEFT(child) != NULL)
+ PARENT(LEFT(child)) = node;
+ LEFT(child) = node;
+
+ if (child != NULL)
+ PARENT(child) = PARENT(node);
+
+ if (IS_ROOT(node)) {
+ *rootp = child;
+ child->is_root = 1;
+ node->is_root = 0;
+
+ } else {
+ if (LEFT(PARENT(node)) == node)
+ LEFT(PARENT(node)) = child;
+ else
+ RIGHT(PARENT(node)) = child;
+ }
+
+ PARENT(node) = child;
+}
+
+static inline void
+rotate_right(dns_rbtnode_t *node, dns_rbtnode_t **rootp) {
+ dns_rbtnode_t *child;
+
+ REQUIRE(DNS_RBTNODE_VALID(node));
+ REQUIRE(rootp != NULL);
+
+ child = LEFT(node);
+ INSIST(child != NULL);
+
+ LEFT(node) = RIGHT(child);
+ if (RIGHT(child) != NULL)
+ PARENT(RIGHT(child)) = node;
+ RIGHT(child) = node;
+
+ if (child != NULL)
+ PARENT(child) = PARENT(node);
+
+ if (IS_ROOT(node)) {
+ *rootp = child;
+ child->is_root = 1;
+ node->is_root = 0;
+
+ } else {
+ if (LEFT(PARENT(node)) == node)
+ LEFT(PARENT(node)) = child;
+ else
+ RIGHT(PARENT(node)) = child;
+ }
+
+ PARENT(node) = child;
+}
+
+/*
+ * This is the real workhorse of the insertion code, because it does the
+ * true red/black tree on a single level.
+ */
+static void
+dns_rbt_addonlevel(dns_rbtnode_t *node, dns_rbtnode_t *current, int order,
+ dns_rbtnode_t **rootp)
+{
+ dns_rbtnode_t *child, *root, *parent, *grandparent;
+ dns_name_t add_name, current_name;
+ dns_offsets_t add_offsets, current_offsets;
+
+ REQUIRE(rootp != NULL);
+ REQUIRE(DNS_RBTNODE_VALID(node) && LEFT(node) == NULL &&
+ RIGHT(node) == NULL);
+ REQUIRE(current != NULL);
+
+ root = *rootp;
+ if (root == NULL) {
+ /*
+ * First node of a level.
+ */
+ MAKE_BLACK(node);
+ node->is_root = 1;
+ PARENT(node) = current;
+ *rootp = node;
+ return;
+ }
+
+ child = root;
+
+ dns_name_init(&add_name, add_offsets);
+ NODENAME(node, &add_name);
+
+ dns_name_init(&current_name, current_offsets);
+ NODENAME(current, &current_name);
+
+ if (order < 0) {
+ INSIST(LEFT(current) == NULL);
+ LEFT(current) = node;
+ } else {
+ INSIST(RIGHT(current) == NULL);
+ RIGHT(current) = node;
+ }
+
+ INSIST(PARENT(node) == NULL);
+ PARENT(node) = current;
+
+ MAKE_RED(node);
+
+ while (node != root && IS_RED(PARENT(node))) {
+ /*
+ * XXXDCL could do away with separate parent and grandparent
+ * variables. They are vestiges of the days before parent
+ * pointers. However, they make the code a little clearer.
+ */
+
+ parent = PARENT(node);
+ grandparent = PARENT(parent);
+
+ if (parent == LEFT(grandparent)) {
+ child = RIGHT(grandparent);
+ if (child != NULL && IS_RED(child)) {
+ MAKE_BLACK(parent);
+ MAKE_BLACK(child);
+ MAKE_RED(grandparent);
+ node = grandparent;
+ } else {
+ if (node == RIGHT(parent)) {
+ rotate_left(parent, &root);
+ node = parent;
+ parent = PARENT(node);
+ grandparent = PARENT(parent);
+ }
+ MAKE_BLACK(parent);
+ MAKE_RED(grandparent);
+ rotate_right(grandparent, &root);
+ }
+ } else {
+ child = LEFT(grandparent);
+ if (child != NULL && IS_RED(child)) {
+ MAKE_BLACK(parent);
+ MAKE_BLACK(child);
+ MAKE_RED(grandparent);
+ node = grandparent;
+ } else {
+ if (node == LEFT(parent)) {
+ rotate_right(parent, &root);
+ node = parent;
+ parent = PARENT(node);
+ grandparent = PARENT(parent);
+ }
+ MAKE_BLACK(parent);
+ MAKE_RED(grandparent);
+ rotate_left(grandparent, &root);
+ }
+ }
+ }
+
+ MAKE_BLACK(root);
+ ENSURE(IS_ROOT(root));
+ *rootp = root;
+
+ return;
+}
+
+/*
+ * This is the real workhorse of the deletion code, because it does the
+ * true red/black tree on a single level.
+ */
+static void
+dns_rbt_deletefromlevel(dns_rbtnode_t *delete, dns_rbtnode_t **rootp) {
+ dns_rbtnode_t *child, *sibling, *parent;
+ dns_rbtnode_t *successor;
+
+ REQUIRE(delete != NULL);
+
+ /*
+ * Verify that the parent history is (apparently) correct.
+ */
+ INSIST((IS_ROOT(delete) && *rootp == delete) ||
+ (! IS_ROOT(delete) &&
+ (LEFT(PARENT(delete)) == delete ||
+ RIGHT(PARENT(delete)) == delete)));
+
+ child = NULL;
+
+ if (LEFT(delete) == NULL) {
+ if (RIGHT(delete) == NULL) {
+ if (IS_ROOT(delete)) {
+ /*
+ * This is the only item in the tree.
+ */
+ *rootp = NULL;
+ return;
+ }
+ } else
+ /*
+ * This node has one child, on the right.
+ */
+ child = RIGHT(delete);
+
+ } else if (RIGHT(delete) == NULL)
+ /*
+ * This node has one child, on the left.
+ */
+ child = LEFT(delete);
+ else {
+ dns_rbtnode_t holder, *tmp = &holder;
+
+ /*
+ * This node has two children, so it cannot be directly
+ * deleted. Find its immediate in-order successor and
+ * move it to this location, then do the deletion at the
+ * old site of the successor.
+ */
+ successor = RIGHT(delete);
+ while (LEFT(successor) != NULL)
+ successor = LEFT(successor);
+
+ /*
+ * The successor cannot possibly have a left child;
+ * if there is any child, it is on the right.
+ */
+ if (RIGHT(successor) != NULL)
+ child = RIGHT(successor);
+
+ /*
+ * Swap the two nodes; it would be simpler to just replace
+ * the value being deleted with that of the successor,
+ * but this rigamarole is done so the caller has complete
+ * control over the pointers (and memory allocation) of
+ * all of nodes. If just the key value were removed from
+ * the tree, the pointer to the node would be unchanged.
+ */
+
+ /*
+ * First, put the successor in the tree location of the
+ * node to be deleted. Save its existing tree pointer
+ * information, which will be needed when linking up
+ * delete to the successor's old location.
+ */
+ memcpy(tmp, successor, sizeof(dns_rbtnode_t));
+
+ if (IS_ROOT(delete)) {
+ *rootp = successor;
+ successor->is_root = ISC_TRUE;
+ delete->is_root = ISC_FALSE;
+
+ } else
+ if (LEFT(PARENT(delete)) == delete)
+ LEFT(PARENT(delete)) = successor;
+ else
+ RIGHT(PARENT(delete)) = successor;
+
+ PARENT(successor) = PARENT(delete);
+ LEFT(successor) = LEFT(delete);
+ RIGHT(successor) = RIGHT(delete);
+ COLOR(successor) = COLOR(delete);
+
+ if (LEFT(successor) != NULL)
+ PARENT(LEFT(successor)) = successor;
+ if (RIGHT(successor) != successor)
+ PARENT(RIGHT(successor)) = successor;
+
+ /*
+ * Now relink the node to be deleted into the
+ * successor's previous tree location. PARENT(tmp)
+ * is the successor's original parent.
+ */
+ INSIST(! IS_ROOT(delete));
+
+ if (PARENT(tmp) == delete) {
+ /*
+ * Node being deleted was successor's parent.
+ */
+ RIGHT(successor) = delete;
+ PARENT(delete) = successor;
+
+ } else {
+ LEFT(PARENT(tmp)) = delete;
+ PARENT(delete) = PARENT(tmp);
+ }
+
+ /*
+ * Original location of successor node has no left.
+ */
+ LEFT(delete) = NULL;
+ RIGHT(delete) = RIGHT(tmp);
+ COLOR(delete) = COLOR(tmp);
+ }
+
+ /*
+ * Remove the node by removing the links from its parent.
+ */
+ if (! IS_ROOT(delete)) {
+ if (LEFT(PARENT(delete)) == delete)
+ LEFT(PARENT(delete)) = child;
+ else
+ RIGHT(PARENT(delete)) = child;
+
+ if (child != NULL)
+ PARENT(child) = PARENT(delete);
+
+ } else {
+ /*
+ * This is the root being deleted, and at this point
+ * it is known to have just one child.
+ */
+ *rootp = child;
+ child->is_root = 1;
+ PARENT(child) = PARENT(delete);
+ }
+
+ /*
+ * Fix color violations.
+ */
+ if (IS_BLACK(delete)) {
+ parent = PARENT(delete);
+
+ while (child != *rootp && IS_BLACK(child)) {
+ INSIST(child == NULL || ! IS_ROOT(child));
+
+ if (LEFT(parent) == child) {
+ sibling = RIGHT(parent);
+
+ if (IS_RED(sibling)) {
+ MAKE_BLACK(sibling);
+ MAKE_RED(parent);
+ rotate_left(parent, rootp);
+ sibling = RIGHT(parent);
+ }
+
+ if (IS_BLACK(LEFT(sibling)) &&
+ IS_BLACK(RIGHT(sibling))) {
+ MAKE_RED(sibling);
+ child = parent;
+
+ } else {
+
+ if (IS_BLACK(RIGHT(sibling))) {
+ MAKE_BLACK(LEFT(sibling));
+ MAKE_RED(sibling);
+ rotate_right(sibling, rootp);
+ sibling = RIGHT(parent);
+ }
+
+ COLOR(sibling) = COLOR(parent);
+ MAKE_BLACK(parent);
+ MAKE_BLACK(RIGHT(sibling));
+ rotate_left(parent, rootp);
+ child = *rootp;
+ }
+
+ } else {
+ /*
+ * Child is parent's right child.
+ * Everything is doen the same as above,
+ * except mirrored.
+ */
+ sibling = LEFT(parent);
+
+ if (IS_RED(sibling)) {
+ MAKE_BLACK(sibling);
+ MAKE_RED(parent);
+ rotate_right(parent, rootp);
+ sibling = LEFT(parent);
+ }
+
+ if (IS_BLACK(LEFT(sibling)) &&
+ IS_BLACK(RIGHT(sibling))) {
+ MAKE_RED(sibling);
+ child = parent;
+
+ } else {
+ if (IS_BLACK(LEFT(sibling))) {
+ MAKE_BLACK(RIGHT(sibling));
+ MAKE_RED(sibling);
+ rotate_left(sibling, rootp);
+ sibling = LEFT(parent);
+ }
+
+ COLOR(sibling) = COLOR(parent);
+ MAKE_BLACK(parent);
+ MAKE_BLACK(LEFT(sibling));
+ rotate_right(parent, rootp);
+ child = *rootp;
+ }
+ }
+
+ parent = PARENT(child);
+ }
+
+ if (IS_RED(child))
+ MAKE_BLACK(child);
+ }
+}
+
+/*
+ * This should only be used on the root of a tree, because no color fixup
+ * is done at all.
+ *
+ * NOTE: No root pointer maintenance is done, because the function is only
+ * used for two cases:
+ * + deleting everything DOWN from a node that is itself being deleted, and
+ * + deleting the entire tree of trees from dns_rbt_destroy.
+ * In each case, the root pointer is no longer relevant, so there
+ * is no need for a root parameter to this function.
+ *
+ * If the function is ever intended to be used to delete something where
+ * a pointer needs to be told that this tree no longer exists,
+ * this function would need to adjusted accordingly.
+ */
+static isc_result_t
+dns_rbt_deletetree(dns_rbt_t *rbt, dns_rbtnode_t *node) {
+ isc_result_t result = ISC_R_SUCCESS;
+ REQUIRE(VALID_RBT(rbt));
+
+ if (node == NULL)
+ return (result);
+
+ if (LEFT(node) != NULL) {
+ result = dns_rbt_deletetree(rbt, LEFT(node));
+ if (result != ISC_R_SUCCESS)
+ goto done;
+ LEFT(node) = NULL;
+ }
+ if (RIGHT(node) != NULL) {
+ result = dns_rbt_deletetree(rbt, RIGHT(node));
+ if (result != ISC_R_SUCCESS)
+ goto done;
+ RIGHT(node) = NULL;
+ }
+ if (DOWN(node) != NULL) {
+ result = dns_rbt_deletetree(rbt, DOWN(node));
+ if (result != ISC_R_SUCCESS)
+ goto done;
+ DOWN(node) = NULL;
+ }
+ done:
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (DATA(node) != NULL && rbt->data_deleter != NULL)
+ rbt->data_deleter(DATA(node), rbt->deleter_arg);
+
+ unhash_node(rbt, node);
+#if DNS_RBT_USEMAGIC
+ node->magic = 0;
+#endif
+
+ isc_mem_put(rbt->mctx, node, NODE_SIZE(node));
+ rbt->nodecount--;
+ return (result);
+}
+
+static void
+dns_rbt_deletetreeflat(dns_rbt_t *rbt, unsigned int quantum,
+ dns_rbtnode_t **nodep)
+{
+ dns_rbtnode_t *parent;
+ dns_rbtnode_t *node = *nodep;
+ REQUIRE(VALID_RBT(rbt));
+
+ again:
+ if (node == NULL) {
+ *nodep = NULL;
+ return;
+ }
+
+ traverse:
+ if (LEFT(node) != NULL) {
+ node = LEFT(node);
+ goto traverse;
+ }
+ if (DOWN(node) != NULL) {
+ node = DOWN(node);
+ goto traverse;
+ }
+
+ if (DATA(node) != NULL && rbt->data_deleter != NULL)
+ rbt->data_deleter(DATA(node), rbt->deleter_arg);
+
+ /*
+ * Note: we don't call unhash_node() here as we are destroying
+ * the complete rbt tree.
+ */
+#if DNS_RBT_USEMAGIC
+ node->magic = 0;
+#endif
+ parent = PARENT(node);
+ if (RIGHT(node) != NULL)
+ PARENT(RIGHT(node)) = parent;
+ if (parent != NULL) {
+ if (LEFT(parent) == node)
+ LEFT(parent) = RIGHT(node);
+ else if (DOWN(parent) == node)
+ DOWN(parent) = RIGHT(node);
+ } else
+ parent = RIGHT(node);
+
+ isc_mem_put(rbt->mctx, node, NODE_SIZE(node));
+ rbt->nodecount--;
+ node = parent;
+ if (quantum != 0 && --quantum == 0) {
+ *nodep = node;
+ return;
+ }
+ goto again;
+}
+
+static void
+dns_rbt_indent(int depth) {
+ int i;
+
+ for (i = 0; i < depth; i++)
+ putchar('\t');
+}
+
+static void
+dns_rbt_printnodename(dns_rbtnode_t *node) {
+ isc_region_t r;
+ dns_name_t name;
+ char buffer[DNS_NAME_FORMATSIZE];
+ dns_offsets_t offsets;
+
+ r.length = NAMELEN(node);
+ r.base = NAME(node);
+
+ dns_name_init(&name, offsets);
+ dns_name_fromregion(&name, &r);
+
+ dns_name_format(&name, buffer, sizeof(buffer));
+
+ printf("%s", buffer);
+}
+
+static void
+dns_rbt_printtree(dns_rbtnode_t *root, dns_rbtnode_t *parent, int depth) {
+ dns_rbt_indent(depth);
+
+ if (root != NULL) {
+ dns_rbt_printnodename(root);
+ printf(" (%s", IS_RED(root) ? "RED" : "black");
+ if (parent) {
+ printf(" from ");
+ dns_rbt_printnodename(parent);
+ }
+
+ if ((! IS_ROOT(root) && PARENT(root) != parent) ||
+ ( IS_ROOT(root) && depth > 0 &&
+ DOWN(PARENT(root)) != root)) {
+
+ printf(" (BAD parent pointer! -> ");
+ if (PARENT(root) != NULL)
+ dns_rbt_printnodename(PARENT(root));
+ else
+ printf("NULL");
+ printf(")");
+ }
+
+ printf(")\n");
+
+
+ depth++;
+
+ if (DOWN(root)) {
+ dns_rbt_indent(depth);
+ printf("++ BEG down from ");
+ dns_rbt_printnodename(root);
+ printf("\n");
+ dns_rbt_printtree(DOWN(root), NULL, depth);
+ dns_rbt_indent(depth);
+ printf("-- END down from ");
+ dns_rbt_printnodename(root);
+ printf("\n");
+ }
+
+ if (IS_RED(root) && IS_RED(LEFT(root)))
+ printf("** Red/Red color violation on left\n");
+ dns_rbt_printtree(LEFT(root), root, depth);
+
+ if (IS_RED(root) && IS_RED(RIGHT(root)))
+ printf("** Red/Red color violation on right\n");
+ dns_rbt_printtree(RIGHT(root), root, depth);
+
+ } else
+ printf("NULL\n");
+}
+
+void
+dns_rbt_printall(dns_rbt_t *rbt) {
+ REQUIRE(VALID_RBT(rbt));
+
+ dns_rbt_printtree(rbt->root, NULL, 0);
+}
+
+/*
+ * Chain Functions
+ */
+
+void
+dns_rbtnodechain_init(dns_rbtnodechain_t *chain, isc_mem_t *mctx) {
+ /*
+ * Initialize 'chain'.
+ */
+
+ REQUIRE(chain != NULL);
+
+ chain->mctx = mctx;
+ chain->end = NULL;
+ chain->level_count = 0;
+ chain->level_matches = 0;
+ memset(chain->levels, 0, sizeof(chain->levels));
+
+ chain->magic = CHAIN_MAGIC;
+}
+
+isc_result_t
+dns_rbtnodechain_current(dns_rbtnodechain_t *chain, dns_name_t *name,
+ dns_name_t *origin, dns_rbtnode_t **node)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+
+ REQUIRE(VALID_CHAIN(chain));
+
+ if (node != NULL)
+ *node = chain->end;
+
+ if (chain->end == NULL)
+ return (ISC_R_NOTFOUND);
+
+ if (name != NULL) {
+ NODENAME(chain->end, name);
+
+ if (chain->level_count == 0) {
+ /*
+ * Names in the top level tree are all absolute.
+ * Always make 'name' relative.
+ */
+ INSIST(dns_name_isabsolute(name));
+
+ /*
+ * This is cheaper than dns_name_getlabelsequence().
+ */
+ name->labels--;
+ name->length--;
+ name->attributes &= ~DNS_NAMEATTR_ABSOLUTE;
+ }
+ }
+
+ if (origin != NULL) {
+ if (chain->level_count > 0)
+ result = chain_name(chain, origin, ISC_FALSE);
+ else
+ result = dns_name_copy(dns_rootname, origin, NULL);
+ }
+
+ return (result);
+}
+
+isc_result_t
+dns_rbtnodechain_prev(dns_rbtnodechain_t *chain, dns_name_t *name,
+ dns_name_t *origin)
+{
+ dns_rbtnode_t *current, *previous, *predecessor;
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_boolean_t new_origin = ISC_FALSE;
+
+ REQUIRE(VALID_CHAIN(chain) && chain->end != NULL);
+
+ predecessor = NULL;
+
+ current = chain->end;
+
+ if (LEFT(current) != NULL) {
+ /*
+ * Moving left one then right as far as possible is the
+ * previous node, at least for this level.
+ */
+ current = LEFT(current);
+
+ while (RIGHT(current) != NULL)
+ current = RIGHT(current);
+
+ predecessor = current;
+
+ } else {
+ /*
+ * No left links, so move toward the root. If at any point on
+ * the way there the link from parent to child is a right
+ * link, then the parent is the previous node, at least
+ * for this level.
+ */
+ while (! IS_ROOT(current)) {
+ previous = current;
+ current = PARENT(current);
+
+ if (RIGHT(current) == previous) {
+ predecessor = current;
+ break;
+ }
+ }
+ }
+
+ if (predecessor != NULL) {
+ /*
+ * Found a predecessor node in this level. It might not
+ * really be the predecessor, however.
+ */
+ if (DOWN(predecessor) != NULL) {
+ /*
+ * The predecessor is really down at least one level.
+ * Go down and as far right as possible, and repeat
+ * as long as the rightmost node has a down pointer.
+ */
+ do {
+ /*
+ * XXX DCL Need to do something about origins
+ * here. See whether to go down, and if so
+ * whether it is truly what Bob calls a
+ * new origin.
+ */
+ ADD_LEVEL(chain, predecessor);
+ predecessor = DOWN(predecessor);
+
+ /* XXX DCL duplicated from above; clever
+ * way to unduplicate? */
+
+ while (RIGHT(predecessor) != NULL)
+ predecessor = RIGHT(predecessor);
+ } while (DOWN(predecessor) != NULL);
+
+ /* XXX DCL probably needs work on the concept */
+ if (origin != NULL)
+ new_origin = ISC_TRUE;
+ }
+
+ } else if (chain->level_count > 0) {
+ /*
+ * Dang, didn't find a predecessor in this level.
+ * Got to the root of this level without having traversed
+ * any right links. Ascend the tree one level; the
+ * node that points to this tree is the predecessor.
+ */
+ INSIST(chain->level_count > 0 && IS_ROOT(current));
+ predecessor = chain->levels[--chain->level_count];
+
+ /* XXX DCL probably needs work on the concept */
+ /*
+ * Don't declare an origin change when the new origin is "."
+ * at the top level tree, because "." is declared as the origin
+ * for the second level tree.
+ */
+ if (origin != NULL &&
+ (chain->level_count > 0 || OFFSETLEN(predecessor) > 1))
+ new_origin = ISC_TRUE;
+ }
+
+ if (predecessor != NULL) {
+ chain->end = predecessor;
+
+ if (new_origin) {
+ result = dns_rbtnodechain_current(chain, name, origin,
+ NULL);
+ if (result == ISC_R_SUCCESS)
+ result = DNS_R_NEWORIGIN;
+
+ } else
+ result = dns_rbtnodechain_current(chain, name, NULL,
+ NULL);
+
+ } else
+ result = ISC_R_NOMORE;
+
+ return (result);
+}
+
+isc_result_t
+dns_rbtnodechain_down(dns_rbtnodechain_t *chain, dns_name_t *name,
+ dns_name_t *origin)
+{
+ dns_rbtnode_t *current, *successor;
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_boolean_t new_origin = ISC_FALSE;
+
+ REQUIRE(VALID_CHAIN(chain) && chain->end != NULL);
+
+ successor = NULL;
+
+ current = chain->end;
+
+ if (DOWN(current) != NULL) {
+ /*
+ * Don't declare an origin change when the new origin is "."
+ * at the second level tree, because "." is already declared
+ * as the origin for the top level tree.
+ */
+ if (chain->level_count > 0 ||
+ OFFSETLEN(current) > 1)
+ new_origin = ISC_TRUE;
+
+ ADD_LEVEL(chain, current);
+ current = DOWN(current);
+
+ while (LEFT(current) != NULL)
+ current = LEFT(current);
+
+ successor = current;
+ }
+
+ if (successor != NULL) {
+ chain->end = successor;
+
+ /*
+ * It is not necessary to use dns_rbtnodechain_current like
+ * the other functions because this function will never
+ * find a node in the topmost level. This is because the
+ * root level will never be more than one name, and everything
+ * in the megatree is a successor to that node, down at
+ * the second level or below.
+ */
+
+ if (name != NULL)
+ NODENAME(chain->end, name);
+
+ if (new_origin) {
+ if (origin != NULL)
+ result = chain_name(chain, origin, ISC_FALSE);
+
+ if (result == ISC_R_SUCCESS)
+ result = DNS_R_NEWORIGIN;
+
+ } else
+ result = ISC_R_SUCCESS;
+
+ } else
+ result = ISC_R_NOMORE;
+
+ return (result);
+}
+
+isc_result_t
+dns_rbtnodechain_nextflat(dns_rbtnodechain_t *chain, dns_name_t *name) {
+ dns_rbtnode_t *current, *previous, *successor;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ REQUIRE(VALID_CHAIN(chain) && chain->end != NULL);
+
+ successor = NULL;
+
+ current = chain->end;
+
+ if (RIGHT(current) == NULL) {
+ while (! IS_ROOT(current)) {
+ previous = current;
+ current = PARENT(current);
+
+ if (LEFT(current) == previous) {
+ successor = current;
+ break;
+ }
+ }
+ } else {
+ current = RIGHT(current);
+
+ while (LEFT(current) != NULL)
+ current = LEFT(current);
+
+ successor = current;
+ }
+
+ if (successor != NULL) {
+ chain->end = successor;
+
+ if (name != NULL)
+ NODENAME(chain->end, name);
+
+ result = ISC_R_SUCCESS;
+ } else
+ result = ISC_R_NOMORE;
+
+ return (result);
+}
+
+isc_result_t
+dns_rbtnodechain_next(dns_rbtnodechain_t *chain, dns_name_t *name,
+ dns_name_t *origin)
+{
+ dns_rbtnode_t *current, *previous, *successor;
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_boolean_t new_origin = ISC_FALSE;
+
+ REQUIRE(VALID_CHAIN(chain) && chain->end != NULL);
+
+ successor = NULL;
+
+ current = chain->end;
+
+ /*
+ * If there is a level below this node, the next node is the leftmost
+ * node of the next level.
+ */
+ if (DOWN(current) != NULL) {
+ /*
+ * Don't declare an origin change when the new origin is "."
+ * at the second level tree, because "." is already declared
+ * as the origin for the top level tree.
+ */
+ if (chain->level_count > 0 ||
+ OFFSETLEN(current) > 1)
+ new_origin = ISC_TRUE;
+
+ ADD_LEVEL(chain, current);
+ current = DOWN(current);
+
+ while (LEFT(current) != NULL)
+ current = LEFT(current);
+
+ successor = current;
+
+ } else if (RIGHT(current) == NULL) {
+ /*
+ * The successor is up, either in this level or a previous one.
+ * Head back toward the root of the tree, looking for any path
+ * that was via a left link; the successor is the node that has
+ * that left link. In the event the root of the level is
+ * reached without having traversed any left links, ascend one
+ * level and look for either a right link off the point of
+ * ascent, or search for a left link upward again, repeating
+ * ascents until either case is true.
+ */
+ do {
+ while (! IS_ROOT(current)) {
+ previous = current;
+ current = PARENT(current);
+
+ if (LEFT(current) == previous) {
+ successor = current;
+ break;
+ }
+ }
+
+ if (successor == NULL) {
+ /*
+ * Reached the root without having traversed
+ * any left pointers, so this level is done.
+ */
+ if (chain->level_count == 0)
+ break;
+
+ current = chain->levels[--chain->level_count];
+ new_origin = ISC_TRUE;
+
+ if (RIGHT(current) != NULL)
+ break;
+ }
+ } while (successor == NULL);
+ }
+
+ if (successor == NULL && RIGHT(current) != NULL) {
+ current = RIGHT(current);
+
+ while (LEFT(current) != NULL)
+ current = LEFT(current);
+
+ successor = current;
+ }
+
+ if (successor != NULL) {
+ chain->end = successor;
+
+ /*
+ * It is not necessary to use dns_rbtnodechain_current like
+ * the other functions because this function will never
+ * find a node in the topmost level. This is because the
+ * root level will never be more than one name, and everything
+ * in the megatree is a successor to that node, down at
+ * the second level or below.
+ */
+
+ if (name != NULL)
+ NODENAME(chain->end, name);
+
+ if (new_origin) {
+ if (origin != NULL)
+ result = chain_name(chain, origin, ISC_FALSE);
+
+ if (result == ISC_R_SUCCESS)
+ result = DNS_R_NEWORIGIN;
+
+ } else
+ result = ISC_R_SUCCESS;
+
+ } else
+ result = ISC_R_NOMORE;
+
+ return (result);
+}
+
+isc_result_t
+dns_rbtnodechain_first(dns_rbtnodechain_t *chain, dns_rbt_t *rbt,
+ dns_name_t *name, dns_name_t *origin)
+
+{
+ isc_result_t result;
+
+ REQUIRE(VALID_RBT(rbt));
+ REQUIRE(VALID_CHAIN(chain));
+
+ dns_rbtnodechain_reset(chain);
+
+ chain->end = rbt->root;
+
+ result = dns_rbtnodechain_current(chain, name, origin, NULL);
+
+ if (result == ISC_R_SUCCESS)
+ result = DNS_R_NEWORIGIN;
+
+ return (result);
+}
+
+isc_result_t
+dns_rbtnodechain_last(dns_rbtnodechain_t *chain, dns_rbt_t *rbt,
+ dns_name_t *name, dns_name_t *origin)
+
+{
+ isc_result_t result;
+
+ REQUIRE(VALID_RBT(rbt));
+ REQUIRE(VALID_CHAIN(chain));
+
+ dns_rbtnodechain_reset(chain);
+
+ result = move_chain_to_last(chain, rbt->root);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_rbtnodechain_current(chain, name, origin, NULL);
+
+ if (result == ISC_R_SUCCESS)
+ result = DNS_R_NEWORIGIN;
+
+ return (result);
+}
+
+
+void
+dns_rbtnodechain_reset(dns_rbtnodechain_t *chain) {
+ /*
+ * Free any dynamic storage associated with 'chain', and then
+ * reinitialize 'chain'.
+ */
+
+ REQUIRE(VALID_CHAIN(chain));
+
+ chain->end = NULL;
+ chain->level_count = 0;
+ chain->level_matches = 0;
+}
+
+void
+dns_rbtnodechain_invalidate(dns_rbtnodechain_t *chain) {
+ /*
+ * Free any dynamic storage associated with 'chain', and then
+ * invalidate 'chain'.
+ */
+
+ dns_rbtnodechain_reset(chain);
+
+ chain->magic = 0;
+}
diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c
new file mode 100644
index 0000000..5cdbcad
--- /dev/null
+++ b/lib/dns/rbtdb.c
@@ -0,0 +1,8435 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rbtdb.c,v 1.270 2008/11/14 14:07:48 marka Exp $ */
+
+/*! \file */
+
+/*
+ * Principal Author: Bob Halley
+ */
+
+#include <config.h>
+
+/* #define inline */
+
+#include <isc/event.h>
+#include <isc/heap.h>
+#include <isc/mem.h>
+#include <isc/mutex.h>
+#include <isc/platform.h>
+#include <isc/print.h>
+#include <isc/random.h>
+#include <isc/refcount.h>
+#include <isc/rwlock.h>
+#include <isc/serial.h>
+#include <isc/string.h>
+#include <isc/task.h>
+#include <isc/time.h>
+#include <isc/util.h>
+
+#include <dns/acache.h>
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/events.h>
+#include <dns/fixedname.h>
+#include <dns/lib.h>
+#include <dns/log.h>
+#include <dns/masterdump.h>
+#include <dns/nsec.h>
+#include <dns/nsec3.h>
+#include <dns/rbt.h>
+#include <dns/rdata.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/rdataslab.h>
+#include <dns/rdatastruct.h>
+#include <dns/result.h>
+#include <dns/stats.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+#include <dns/zonekey.h>
+
+#ifdef DNS_RBTDB_VERSION64
+#include "rbtdb64.h"
+#else
+#include "rbtdb.h"
+#endif
+
+#ifdef DNS_RBTDB_VERSION64
+#define RBTDB_MAGIC ISC_MAGIC('R', 'B', 'D', '8')
+#else
+#define RBTDB_MAGIC ISC_MAGIC('R', 'B', 'D', '4')
+#endif
+
+/*%
+ * Note that "impmagic" is not the first four bytes of the struct, so
+ * ISC_MAGIC_VALID cannot be used.
+ */
+#define VALID_RBTDB(rbtdb) ((rbtdb) != NULL && \
+ (rbtdb)->common.impmagic == RBTDB_MAGIC)
+
+#ifdef DNS_RBTDB_VERSION64
+typedef isc_uint64_t rbtdb_serial_t;
+/*%
+ * Make casting easier in symbolic debuggers by using different names
+ * for the 64 bit version.
+ */
+#define dns_rbtdb_t dns_rbtdb64_t
+#define rdatasetheader_t rdatasetheader64_t
+#define rbtdb_version_t rbtdb_version64_t
+#else
+typedef isc_uint32_t rbtdb_serial_t;
+#endif
+
+typedef isc_uint32_t rbtdb_rdatatype_t;
+
+#define RBTDB_RDATATYPE_BASE(type) ((dns_rdatatype_t)((type) & 0xFFFF))
+#define RBTDB_RDATATYPE_EXT(type) ((dns_rdatatype_t)((type) >> 16))
+#define RBTDB_RDATATYPE_VALUE(b, e) ((rbtdb_rdatatype_t)((e) << 16) | (b))
+
+#define RBTDB_RDATATYPE_SIGNSEC \
+ RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_nsec)
+#define RBTDB_RDATATYPE_SIGNSEC3 \
+ RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_nsec3)
+#define RBTDB_RDATATYPE_SIGNS \
+ RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_ns)
+#define RBTDB_RDATATYPE_SIGCNAME \
+ RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_cname)
+#define RBTDB_RDATATYPE_SIGDNAME \
+ RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_dname)
+#define RBTDB_RDATATYPE_NCACHEANY \
+ RBTDB_RDATATYPE_VALUE(0, dns_rdatatype_any)
+
+/*
+ * We use rwlock for DB lock only when ISC_RWLOCK_USEATOMIC is non 0.
+ * Using rwlock is effective with regard to lookup performance only when
+ * it is implemented in an efficient way.
+ * Otherwise, it is generally wise to stick to the simple locking since rwlock
+ * would require more memory or can even make lookups slower due to its own
+ * overhead (when it internally calls mutex locks).
+ */
+#ifdef ISC_RWLOCK_USEATOMIC
+#define DNS_RBTDB_USERWLOCK 1
+#else
+#define DNS_RBTDB_USERWLOCK 0
+#endif
+
+#if DNS_RBTDB_USERWLOCK
+#define RBTDB_INITLOCK(l) isc_rwlock_init((l), 0, 0)
+#define RBTDB_DESTROYLOCK(l) isc_rwlock_destroy(l)
+#define RBTDB_LOCK(l, t) RWLOCK((l), (t))
+#define RBTDB_UNLOCK(l, t) RWUNLOCK((l), (t))
+#else
+#define RBTDB_INITLOCK(l) isc_mutex_init(l)
+#define RBTDB_DESTROYLOCK(l) DESTROYLOCK(l)
+#define RBTDB_LOCK(l, t) LOCK(l)
+#define RBTDB_UNLOCK(l, t) UNLOCK(l)
+#endif
+
+/*
+ * Since node locking is sensitive to both performance and memory footprint,
+ * we need some trick here. If we have both high-performance rwlock and
+ * high performance and small-memory reference counters, we use rwlock for
+ * node lock and isc_refcount for node references. In this case, we don't have
+ * to protect the access to the counters by locks.
+ * Otherwise, we simply use ordinary mutex lock for node locking, and use
+ * simple integers as reference counters which is protected by the lock.
+ * In most cases, we can simply use wrapper macros such as NODE_LOCK and
+ * NODE_UNLOCK. In some other cases, however, we need to protect reference
+ * counters first and then protect other parts of a node as read-only data.
+ * Special additional macros, NODE_STRONGLOCK(), NODE_WEAKLOCK(), etc, are also
+ * provided for these special cases. When we can use the efficient backend
+ * routines, we should only protect the "other members" by NODE_WEAKLOCK(read).
+ * Otherwise, we should use NODE_STRONGLOCK() to protect the entire critical
+ * section including the access to the reference counter.
+ * Note that we cannot use NODE_LOCK()/NODE_UNLOCK() wherever the protected
+ * section is also protected by NODE_STRONGLOCK().
+ */
+#if defined(ISC_RWLOCK_USEATOMIC) && defined(DNS_RBT_USEISCREFCOUNT)
+typedef isc_rwlock_t nodelock_t;
+
+#define NODE_INITLOCK(l) isc_rwlock_init((l), 0, 0)
+#define NODE_DESTROYLOCK(l) isc_rwlock_destroy(l)
+#define NODE_LOCK(l, t) RWLOCK((l), (t))
+#define NODE_UNLOCK(l, t) RWUNLOCK((l), (t))
+#define NODE_TRYUPGRADE(l) isc_rwlock_tryupgrade(l)
+
+#define NODE_STRONGLOCK(l) ((void)0)
+#define NODE_STRONGUNLOCK(l) ((void)0)
+#define NODE_WEAKLOCK(l, t) NODE_LOCK(l, t)
+#define NODE_WEAKUNLOCK(l, t) NODE_UNLOCK(l, t)
+#define NODE_WEAKDOWNGRADE(l) isc_rwlock_downgrade(l)
+#else
+typedef isc_mutex_t nodelock_t;
+
+#define NODE_INITLOCK(l) isc_mutex_init(l)
+#define NODE_DESTROYLOCK(l) DESTROYLOCK(l)
+#define NODE_LOCK(l, t) LOCK(l)
+#define NODE_UNLOCK(l, t) UNLOCK(l)
+#define NODE_TRYUPGRADE(l) ISC_R_SUCCESS
+
+#define NODE_STRONGLOCK(l) LOCK(l)
+#define NODE_STRONGUNLOCK(l) UNLOCK(l)
+#define NODE_WEAKLOCK(l, t) ((void)0)
+#define NODE_WEAKUNLOCK(l, t) ((void)0)
+#define NODE_WEAKDOWNGRADE(l) ((void)0)
+#endif
+
+/*
+ * Allow clients with a virtual time of up to 5 minutes in the past to see
+ * records that would have otherwise have expired.
+ */
+#define RBTDB_VIRTUAL 300
+
+struct noqname {
+ dns_name_t name;
+ void * neg;
+ void * negsig;
+ dns_rdatatype_t type;
+};
+
+typedef struct acachectl acachectl_t;
+
+typedef struct rdatasetheader {
+ /*%
+ * Locked by the owning node's lock.
+ */
+ rbtdb_serial_t serial;
+ dns_ttl_t rdh_ttl;
+ rbtdb_rdatatype_t type;
+ isc_uint16_t attributes;
+ dns_trust_t trust;
+ struct noqname *noqname;
+ struct noqname *closest;
+ /*%<
+ * We don't use the LIST macros, because the LIST structure has
+ * both head and tail pointers, and is doubly linked.
+ */
+
+ struct rdatasetheader *next;
+ /*%<
+ * If this is the top header for an rdataset, 'next' points
+ * to the top header for the next rdataset (i.e., the next type).
+ * Otherwise, it points up to the header whose down pointer points
+ * at this header.
+ */
+
+ struct rdatasetheader *down;
+ /*%<
+ * Points to the header for the next older version of
+ * this rdataset.
+ */
+
+ isc_uint32_t count;
+ /*%<
+ * Monotonously increased every time this rdataset is bound so that
+ * it is used as the base of the starting point in DNS responses
+ * when the "cyclic" rrset-order is required. Since the ordering
+ * should not be so crucial, no lock is set for the counter for
+ * performance reasons.
+ */
+
+ acachectl_t *additional_auth;
+ acachectl_t *additional_glue;
+
+ dns_rbtnode_t *node;
+ isc_stdtime_t last_used;
+ ISC_LINK(struct rdatasetheader) lru_link;
+ /*%<
+ * Used for LRU-based cache management. We should probably make
+ * these cache-DB specific. We might also make it a pointer and
+ * ensure only the top header has a valid link to save memory.
+ * The linked-list is locked by the rbtdb->lrulock.
+ */
+
+ /*
+ * It's possible this should not be here anymore, but instead
+ * referenced from the bucket's heap directly.
+ */
+#if 0
+ isc_heap_t *heap;
+#endif
+ unsigned int heap_index;
+ /*%<
+ * Used for TTL-based cache cleaning.
+ */
+ isc_stdtime_t resign;
+} rdatasetheader_t;
+
+typedef ISC_LIST(rdatasetheader_t) rdatasetheaderlist_t;
+typedef ISC_LIST(dns_rbtnode_t) rbtnodelist_t;
+
+#define RDATASET_ATTR_NONEXISTENT 0x0001
+#define RDATASET_ATTR_STALE 0x0002
+#define RDATASET_ATTR_IGNORE 0x0004
+#define RDATASET_ATTR_RETAIN 0x0008
+#define RDATASET_ATTR_NXDOMAIN 0x0010
+#define RDATASET_ATTR_RESIGN 0x0020
+#define RDATASET_ATTR_STATCOUNT 0x0040
+#define RDATASET_ATTR_OPTOUT 0x0080
+
+typedef struct acache_cbarg {
+ dns_rdatasetadditional_t type;
+ unsigned int count;
+ dns_db_t *db;
+ dns_dbnode_t *node;
+ rdatasetheader_t *header;
+} acache_cbarg_t;
+
+struct acachectl {
+ dns_acacheentry_t *entry;
+ acache_cbarg_t *cbarg;
+};
+
+/*
+ * XXX
+ * When the cache will pre-expire data (due to memory low or other
+ * situations) before the rdataset's TTL has expired, it MUST
+ * respect the RETAIN bit and not expire the data until its TTL is
+ * expired.
+ */
+
+#undef IGNORE /* WIN32 winbase.h defines this. */
+
+#define EXISTS(header) \
+ (((header)->attributes & RDATASET_ATTR_NONEXISTENT) == 0)
+#define NONEXISTENT(header) \
+ (((header)->attributes & RDATASET_ATTR_NONEXISTENT) != 0)
+#define IGNORE(header) \
+ (((header)->attributes & RDATASET_ATTR_IGNORE) != 0)
+#define RETAIN(header) \
+ (((header)->attributes & RDATASET_ATTR_RETAIN) != 0)
+#define NXDOMAIN(header) \
+ (((header)->attributes & RDATASET_ATTR_NXDOMAIN) != 0)
+#define RESIGN(header) \
+ (((header)->attributes & RDATASET_ATTR_RESIGN) != 0)
+#define OPTOUT(header) \
+ (((header)->attributes & RDATASET_ATTR_OPTOUT) != 0)
+
+#define DEFAULT_NODE_LOCK_COUNT 7 /*%< Should be prime. */
+
+/*%
+ * Number of buckets for cache DB entries (locks, LRU lists, TTL heaps).
+ * There is a tradeoff issue about configuring this value: if this is too
+ * small, it may cause heavier contention between threads; if this is too large,
+ * LRU purge algorithm won't work well (entries tend to be purged prematurely).
+ * The default value should work well for most environments, but this can
+ * also be configurable at compilation time via the
+ * DNS_RBTDB_CACHE_NODE_LOCK_COUNT variable. This value must be larger than
+ * 1 due to the assumption of overmem_purge().
+ */
+#ifdef DNS_RBTDB_CACHE_NODE_LOCK_COUNT
+#if DNS_RBTDB_CACHE_NODE_LOCK_COUNT <= 1
+#error "DNS_RBTDB_CACHE_NODE_LOCK_COUNT must be larger 1"
+#else
+#define DEFAULT_CACHE_NODE_LOCK_COUNT DNS_RBTDB_CACHE_NODE_LOCK_COUNT
+#endif
+#else
+#define DEFAULT_CACHE_NODE_LOCK_COUNT 16
+#endif /* DNS_RBTDB_CACHE_NODE_LOCK_COUNT */
+
+typedef struct {
+ nodelock_t lock;
+ /* Protected in the refcount routines. */
+ isc_refcount_t references;
+ /* Locked by lock. */
+ isc_boolean_t exiting;
+} rbtdb_nodelock_t;
+
+typedef struct rbtdb_changed {
+ dns_rbtnode_t * node;
+ isc_boolean_t dirty;
+ ISC_LINK(struct rbtdb_changed) link;
+} rbtdb_changed_t;
+
+typedef ISC_LIST(rbtdb_changed_t) rbtdb_changedlist_t;
+
+typedef enum {
+ dns_db_insecure,
+ dns_db_partial,
+ dns_db_secure
+} dns_db_secure_t;
+
+typedef struct rbtdb_version {
+ /* Not locked */
+ rbtdb_serial_t serial;
+ /*
+ * Protected in the refcount routines.
+ * XXXJT: should we change the lock policy based on the refcount
+ * performance?
+ */
+ isc_refcount_t references;
+ /* Locked by database lock. */
+ isc_boolean_t writer;
+ isc_boolean_t commit_ok;
+ rbtdb_changedlist_t changed_list;
+ rdatasetheaderlist_t resigned_list;
+ ISC_LINK(struct rbtdb_version) link;
+ dns_db_secure_t secure;
+ isc_boolean_t havensec3;
+ /* NSEC3 parameters */
+ dns_hash_t hash;
+ isc_uint8_t flags;
+ isc_uint16_t iterations;
+ isc_uint8_t salt_length;
+ unsigned char salt[NSEC3_MAX_HASH_LENGTH];
+} rbtdb_version_t;
+
+typedef ISC_LIST(rbtdb_version_t) rbtdb_versionlist_t;
+
+typedef struct {
+ /* Unlocked. */
+ dns_db_t common;
+#if DNS_RBTDB_USERWLOCK
+ isc_rwlock_t lock;
+#else
+ isc_mutex_t lock;
+#endif
+ isc_rwlock_t tree_lock;
+ unsigned int node_lock_count;
+ rbtdb_nodelock_t * node_locks;
+ dns_rbtnode_t * origin_node;
+ dns_stats_t * rrsetstats; /* cache DB only */
+ /* Locked by lock. */
+ unsigned int active;
+ isc_refcount_t references;
+ unsigned int attributes;
+ rbtdb_serial_t current_serial;
+ rbtdb_serial_t least_serial;
+ rbtdb_serial_t next_serial;
+ rbtdb_version_t * current_version;
+ rbtdb_version_t * future_version;
+ rbtdb_versionlist_t open_versions;
+ isc_boolean_t overmem;
+ isc_task_t * task;
+ dns_dbnode_t *soanode;
+ dns_dbnode_t *nsnode;
+
+ /*
+ * This is a linked list used to implement the LRU cache. There will
+ * be node_lock_count linked lists here. Nodes in bucket 1 will be
+ * placed on the linked list rdatasets[1].
+ */
+ rdatasetheaderlist_t *rdatasets;
+
+ /*%
+ * Temporary storage for stale cache nodes and dynamically deleted
+ * nodes that await being cleaned up.
+ */
+ rbtnodelist_t *deadnodes;
+
+ /*
+ * Heaps. Each of these is used for TTL based expiry.
+ */
+ isc_heap_t **heaps;
+
+ /* Locked by tree_lock. */
+ dns_rbt_t * tree;
+ dns_rbt_t * nsec3;
+
+ /* Unlocked */
+ unsigned int quantum;
+} dns_rbtdb_t;
+
+#define RBTDB_ATTR_LOADED 0x01
+#define RBTDB_ATTR_LOADING 0x02
+
+/*%
+ * Search Context
+ */
+typedef struct {
+ dns_rbtdb_t * rbtdb;
+ rbtdb_version_t * rbtversion;
+ rbtdb_serial_t serial;
+ unsigned int options;
+ dns_rbtnodechain_t chain;
+ isc_boolean_t copy_name;
+ isc_boolean_t need_cleanup;
+ isc_boolean_t wild;
+ dns_rbtnode_t * zonecut;
+ rdatasetheader_t * zonecut_rdataset;
+ rdatasetheader_t * zonecut_sigrdataset;
+ dns_fixedname_t zonecut_name;
+ isc_stdtime_t now;
+} rbtdb_search_t;
+
+/*%
+ * Load Context
+ */
+typedef struct {
+ dns_rbtdb_t * rbtdb;
+ isc_stdtime_t now;
+} rbtdb_load_t;
+
+static void rdataset_disassociate(dns_rdataset_t *rdataset);
+static isc_result_t rdataset_first(dns_rdataset_t *rdataset);
+static isc_result_t rdataset_next(dns_rdataset_t *rdataset);
+static void rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata);
+static void rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target);
+static unsigned int rdataset_count(dns_rdataset_t *rdataset);
+static isc_result_t rdataset_getnoqname(dns_rdataset_t *rdataset,
+ dns_name_t *name,
+ dns_rdataset_t *neg,
+ dns_rdataset_t *negsig);
+static isc_result_t rdataset_getclosest(dns_rdataset_t *rdataset,
+ dns_name_t *name,
+ dns_rdataset_t *neg,
+ dns_rdataset_t *negsig);
+static isc_result_t rdataset_getadditional(dns_rdataset_t *rdataset,
+ dns_rdatasetadditional_t type,
+ dns_rdatatype_t qtype,
+ dns_acache_t *acache,
+ dns_zone_t **zonep,
+ dns_db_t **dbp,
+ dns_dbversion_t **versionp,
+ dns_dbnode_t **nodep,
+ dns_name_t *fname,
+ dns_message_t *msg,
+ isc_stdtime_t now);
+static isc_result_t rdataset_setadditional(dns_rdataset_t *rdataset,
+ dns_rdatasetadditional_t type,
+ dns_rdatatype_t qtype,
+ dns_acache_t *acache,
+ dns_zone_t *zone,
+ dns_db_t *db,
+ dns_dbversion_t *version,
+ dns_dbnode_t *node,
+ dns_name_t *fname);
+static isc_result_t rdataset_putadditional(dns_acache_t *acache,
+ dns_rdataset_t *rdataset,
+ dns_rdatasetadditional_t type,
+ dns_rdatatype_t qtype);
+static inline isc_boolean_t need_headerupdate(rdatasetheader_t *header,
+ isc_stdtime_t now);
+static void update_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header,
+ isc_stdtime_t now);
+static void expire_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header,
+ isc_boolean_t tree_locked);
+static void overmem_purge(dns_rbtdb_t *rbtdb, unsigned int locknum_start,
+ isc_stdtime_t now, isc_boolean_t tree_locked);
+static isc_result_t resign_insert(dns_rbtdb_t *rbtdb, int idx,
+ rdatasetheader_t *newheader);
+
+static dns_rdatasetmethods_t rdataset_methods = {
+ rdataset_disassociate,
+ rdataset_first,
+ rdataset_next,
+ rdataset_current,
+ rdataset_clone,
+ rdataset_count,
+ NULL,
+ rdataset_getnoqname,
+ NULL,
+ rdataset_getclosest,
+ rdataset_getadditional,
+ rdataset_setadditional,
+ rdataset_putadditional
+};
+
+static void rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp);
+static isc_result_t rdatasetiter_first(dns_rdatasetiter_t *iterator);
+static isc_result_t rdatasetiter_next(dns_rdatasetiter_t *iterator);
+static void rdatasetiter_current(dns_rdatasetiter_t *iterator,
+ dns_rdataset_t *rdataset);
+
+static dns_rdatasetitermethods_t rdatasetiter_methods = {
+ rdatasetiter_destroy,
+ rdatasetiter_first,
+ rdatasetiter_next,
+ rdatasetiter_current
+};
+
+typedef struct rbtdb_rdatasetiter {
+ dns_rdatasetiter_t common;
+ rdatasetheader_t * current;
+} rbtdb_rdatasetiter_t;
+
+static void dbiterator_destroy(dns_dbiterator_t **iteratorp);
+static isc_result_t dbiterator_first(dns_dbiterator_t *iterator);
+static isc_result_t dbiterator_last(dns_dbiterator_t *iterator);
+static isc_result_t dbiterator_seek(dns_dbiterator_t *iterator,
+ dns_name_t *name);
+static isc_result_t dbiterator_prev(dns_dbiterator_t *iterator);
+static isc_result_t dbiterator_next(dns_dbiterator_t *iterator);
+static isc_result_t dbiterator_current(dns_dbiterator_t *iterator,
+ dns_dbnode_t **nodep,
+ dns_name_t *name);
+static isc_result_t dbiterator_pause(dns_dbiterator_t *iterator);
+static isc_result_t dbiterator_origin(dns_dbiterator_t *iterator,
+ dns_name_t *name);
+
+static dns_dbiteratormethods_t dbiterator_methods = {
+ dbiterator_destroy,
+ dbiterator_first,
+ dbiterator_last,
+ dbiterator_seek,
+ dbiterator_prev,
+ dbiterator_next,
+ dbiterator_current,
+ dbiterator_pause,
+ dbiterator_origin
+};
+
+#define DELETION_BATCH_MAX 64
+
+/*
+ * If 'paused' is ISC_TRUE, then the tree lock is not being held.
+ */
+typedef struct rbtdb_dbiterator {
+ dns_dbiterator_t common;
+ isc_boolean_t paused;
+ isc_boolean_t new_origin;
+ isc_rwlocktype_t tree_locked;
+ isc_result_t result;
+ dns_fixedname_t name;
+ dns_fixedname_t origin;
+ dns_rbtnodechain_t chain;
+ dns_rbtnodechain_t nsec3chain;
+ dns_rbtnodechain_t *current;
+ dns_rbtnode_t *node;
+ dns_rbtnode_t *deletions[DELETION_BATCH_MAX];
+ int delete;
+ isc_boolean_t nsec3only;
+ isc_boolean_t nonsec3;
+} rbtdb_dbiterator_t;
+
+
+#define IS_STUB(rbtdb) (((rbtdb)->common.attributes & DNS_DBATTR_STUB) != 0)
+#define IS_CACHE(rbtdb) (((rbtdb)->common.attributes & DNS_DBATTR_CACHE) != 0)
+
+static void free_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log,
+ isc_event_t *event);
+static void overmem(dns_db_t *db, isc_boolean_t overmem);
+static void setnsec3parameters(dns_db_t *db, rbtdb_version_t *version,
+ isc_boolean_t *nsec3createflag);
+
+/*%
+ * 'init_count' is used to initialize 'newheader->count' which inturn
+ * is used to determine where in the cycle rrset-order cyclic starts.
+ * We don't lock this as we don't care about simultanious updates.
+ *
+ * Note:
+ * Both init_count and header->count can be ISC_UINT32_MAX.
+ * The count on the returned rdataset however can't be as
+ * that indicates that the database does not implement cyclic
+ * processing.
+ */
+static unsigned int init_count;
+
+/*
+ * Locking
+ *
+ * If a routine is going to lock more than one lock in this module, then
+ * the locking must be done in the following order:
+ *
+ * Tree Lock
+ *
+ * Node Lock (Only one from the set may be locked at one time by
+ * any caller)
+ *
+ * Database Lock
+ *
+ * Failure to follow this hierarchy can result in deadlock.
+ */
+
+/*
+ * Deleting Nodes
+ *
+ * For zone databases the node for the origin of the zone MUST NOT be deleted.
+ */
+
+
+/*
+ * DB Routines
+ */
+
+static void
+attach(dns_db_t *source, dns_db_t **targetp) {
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)source;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ isc_refcount_increment(&rbtdb->references, NULL);
+
+ *targetp = source;
+}
+
+static void
+free_rbtdb_callback(isc_task_t *task, isc_event_t *event) {
+ dns_rbtdb_t *rbtdb = event->ev_arg;
+
+ UNUSED(task);
+
+ free_rbtdb(rbtdb, ISC_TRUE, event);
+}
+
+static void
+update_rrsetstats(dns_rbtdb_t *rbtdb, rdatasetheader_t *header,
+ isc_boolean_t increment)
+{
+ dns_rdatastatstype_t statattributes = 0;
+ dns_rdatastatstype_t base = 0;
+ dns_rdatastatstype_t type;
+
+ /* At the moment we count statistics only for cache DB */
+ INSIST(IS_CACHE(rbtdb));
+
+ if (NXDOMAIN(header))
+ statattributes = DNS_RDATASTATSTYPE_ATTR_NXDOMAIN;
+ else if (RBTDB_RDATATYPE_BASE(header->type) == 0) {
+ statattributes = DNS_RDATASTATSTYPE_ATTR_NXRRSET;
+ base = RBTDB_RDATATYPE_EXT(header->type);
+ } else
+ base = RBTDB_RDATATYPE_BASE(header->type);
+
+ type = DNS_RDATASTATSTYPE_VALUE(base, statattributes);
+ if (increment)
+ dns_rdatasetstats_increment(rbtdb->rrsetstats, type);
+ else
+ dns_rdatasetstats_decrement(rbtdb->rrsetstats, type);
+}
+
+static void
+set_ttl(dns_rbtdb_t *rbtdb, rdatasetheader_t *header, dns_ttl_t newttl) {
+ int idx;
+ isc_heap_t *heap;
+ dns_ttl_t oldttl;
+
+ oldttl = header->rdh_ttl;
+ header->rdh_ttl = newttl;
+
+ if (!IS_CACHE(rbtdb))
+ return;
+
+ /*
+ * It's possible the rbtdb is not a cache. If this is the case,
+ * we will not have a heap, and we move on. If we do, though,
+ * we might need to adjust things.
+ */
+ if (header->heap_index == 0 || newttl == oldttl)
+ return;
+ idx = header->node->locknum;
+ if (rbtdb->heaps == NULL || rbtdb->heaps[idx] == NULL)
+ return;
+ heap = rbtdb->heaps[idx];
+
+ if (newttl < oldttl)
+ isc_heap_increased(heap, header->heap_index);
+ else
+ isc_heap_decreased(heap, header->heap_index);
+}
+
+/*%
+ * These functions allow the heap code to rank the priority of each
+ * element. It returns ISC_TRUE if v1 happens "sooner" than v2.
+ */
+static isc_boolean_t
+ttl_sooner(void *v1, void *v2) {
+ rdatasetheader_t *h1 = v1;
+ rdatasetheader_t *h2 = v2;
+
+ if (h1->rdh_ttl < h2->rdh_ttl)
+ return (ISC_TRUE);
+ return (ISC_FALSE);
+}
+
+static isc_boolean_t
+resign_sooner(void *v1, void *v2) {
+ rdatasetheader_t *h1 = v1;
+ rdatasetheader_t *h2 = v2;
+
+ if (h1->resign < h2->resign)
+ return (ISC_TRUE);
+ return (ISC_FALSE);
+}
+
+/*%
+ * This function sets the heap index into the header.
+ */
+static void
+set_index(void *what, unsigned int index) {
+ rdatasetheader_t *h = what;
+
+ h->heap_index = index;
+}
+
+/*%
+ * Work out how many nodes can be deleted in the time between two
+ * requests to the nameserver. Smooth the resulting number and use it
+ * as a estimate for the number of nodes to be deleted in the next
+ * iteration.
+ */
+static unsigned int
+adjust_quantum(unsigned int old, isc_time_t *start) {
+ unsigned int pps = dns_pps; /* packets per second */
+ unsigned int interval;
+ isc_uint64_t usecs;
+ isc_time_t end;
+ unsigned int new;
+
+ if (pps < 100)
+ pps = 100;
+ isc_time_now(&end);
+
+ interval = 1000000 / pps; /* interval in usec */
+ if (interval == 0)
+ interval = 1;
+ usecs = isc_time_microdiff(&end, start);
+ if (usecs == 0) {
+ /*
+ * We were unable to measure the amount of time taken.
+ * Double the nodes deleted next time.
+ */
+ old *= 2;
+ if (old > 1000)
+ old = 1000;
+ return (old);
+ }
+ new = old * interval;
+ new /= (unsigned int)usecs;
+ if (new == 0)
+ new = 1;
+ else if (new > 1000)
+ new = 1000;
+
+ /* Smooth */
+ new = (new + old * 3) / 4;
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
+ ISC_LOG_DEBUG(1), "adjust_quantum -> %d", new);
+
+ return (new);
+}
+
+static void
+free_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log, isc_event_t *event) {
+ unsigned int i;
+ isc_ondestroy_t ondest;
+ isc_result_t result;
+ char buf[DNS_NAME_FORMATSIZE];
+ isc_time_t start;
+
+ if (IS_CACHE(rbtdb) && rbtdb->common.rdclass == dns_rdataclass_in)
+ overmem((dns_db_t *)rbtdb, (isc_boolean_t)-1);
+
+ REQUIRE(rbtdb->current_version != NULL || EMPTY(rbtdb->open_versions));
+ REQUIRE(rbtdb->future_version == NULL);
+
+ if (rbtdb->current_version != NULL) {
+ unsigned int refs;
+
+ isc_refcount_decrement(&rbtdb->current_version->references,
+ &refs);
+ INSIST(refs == 0);
+ UNLINK(rbtdb->open_versions, rbtdb->current_version, link);
+ isc_refcount_destroy(&rbtdb->current_version->references);
+ isc_mem_put(rbtdb->common.mctx, rbtdb->current_version,
+ sizeof(rbtdb_version_t));
+ }
+
+ /*
+ * We assume the number of remaining dead nodes is reasonably small;
+ * the overhead of unlinking all nodes here should be negligible.
+ */
+ for (i = 0; i < rbtdb->node_lock_count; i++) {
+ dns_rbtnode_t *node;
+
+ node = ISC_LIST_HEAD(rbtdb->deadnodes[i]);
+ while (node != NULL) {
+ ISC_LIST_UNLINK(rbtdb->deadnodes[i], node, deadlink);
+ node = ISC_LIST_HEAD(rbtdb->deadnodes[i]);
+ }
+ }
+
+ if (event == NULL)
+ rbtdb->quantum = (rbtdb->task != NULL) ? 100 : 0;
+ again:
+ if (rbtdb->tree != NULL) {
+ isc_time_now(&start);
+ result = dns_rbt_destroy2(&rbtdb->tree, rbtdb->quantum);
+ if (result == ISC_R_QUOTA) {
+ INSIST(rbtdb->task != NULL);
+ if (rbtdb->quantum != 0)
+ rbtdb->quantum = adjust_quantum(rbtdb->quantum,
+ &start);
+ if (event == NULL)
+ event = isc_event_allocate(rbtdb->common.mctx,
+ NULL,
+ DNS_EVENT_FREESTORAGE,
+ free_rbtdb_callback,
+ rbtdb,
+ sizeof(isc_event_t));
+ if (event == NULL)
+ goto again;
+ isc_task_send(rbtdb->task, &event);
+ return;
+ }
+ INSIST(result == ISC_R_SUCCESS && rbtdb->tree == NULL);
+ }
+
+ if (rbtdb->nsec3 != NULL) {
+ isc_time_now(&start);
+ result = dns_rbt_destroy2(&rbtdb->nsec3, rbtdb->quantum);
+ if (result == ISC_R_QUOTA) {
+ INSIST(rbtdb->task != NULL);
+ if (rbtdb->quantum != 0)
+ rbtdb->quantum = adjust_quantum(rbtdb->quantum,
+ &start);
+ if (event == NULL)
+ event = isc_event_allocate(rbtdb->common.mctx,
+ NULL,
+ DNS_EVENT_FREESTORAGE,
+ free_rbtdb_callback,
+ rbtdb,
+ sizeof(isc_event_t));
+ if (event == NULL)
+ goto again;
+ isc_task_send(rbtdb->task, &event);
+ return;
+ }
+ INSIST(result == ISC_R_SUCCESS && rbtdb->nsec3 == NULL);
+ }
+
+ if (event != NULL)
+ isc_event_free(&event);
+ if (log) {
+ if (dns_name_dynamic(&rbtdb->common.origin))
+ dns_name_format(&rbtdb->common.origin, buf,
+ sizeof(buf));
+ else
+ strcpy(buf, "<UNKNOWN>");
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1),
+ "done free_rbtdb(%s)", buf);
+ }
+ if (dns_name_dynamic(&rbtdb->common.origin))
+ dns_name_free(&rbtdb->common.origin, rbtdb->common.mctx);
+ for (i = 0; i < rbtdb->node_lock_count; i++) {
+ isc_refcount_destroy(&rbtdb->node_locks[i].references);
+ NODE_DESTROYLOCK(&rbtdb->node_locks[i].lock);
+ }
+
+ /*
+ * Clean up LRU / re-signing order lists.
+ */
+ if (rbtdb->rdatasets != NULL) {
+ for (i = 0; i < rbtdb->node_lock_count; i++)
+ INSIST(ISC_LIST_EMPTY(rbtdb->rdatasets[i]));
+ isc_mem_put(rbtdb->common.mctx, rbtdb->rdatasets,
+ rbtdb->node_lock_count *
+ sizeof(rdatasetheaderlist_t));
+ }
+ /*
+ * Clean up dead node buckets.
+ */
+ if (rbtdb->deadnodes != NULL) {
+ for (i = 0; i < rbtdb->node_lock_count; i++)
+ INSIST(ISC_LIST_EMPTY(rbtdb->deadnodes[i]));
+ isc_mem_put(rbtdb->common.mctx, rbtdb->deadnodes,
+ rbtdb->node_lock_count * sizeof(rbtnodelist_t));
+ }
+ /*
+ * Clean up heap objects.
+ */
+ if (rbtdb->heaps != NULL) {
+ for (i = 0; i < rbtdb->node_lock_count; i++)
+ isc_heap_destroy(&rbtdb->heaps[i]);
+ isc_mem_put(rbtdb->common.mctx, rbtdb->heaps,
+ rbtdb->node_lock_count *
+ sizeof(isc_heap_t *));
+ }
+
+ if (rbtdb->rrsetstats != NULL)
+ dns_stats_detach(&rbtdb->rrsetstats);
+
+ isc_mem_put(rbtdb->common.mctx, rbtdb->node_locks,
+ rbtdb->node_lock_count * sizeof(rbtdb_nodelock_t));
+ isc_rwlock_destroy(&rbtdb->tree_lock);
+ isc_refcount_destroy(&rbtdb->references);
+ if (rbtdb->task != NULL)
+ isc_task_detach(&rbtdb->task);
+
+ RBTDB_DESTROYLOCK(&rbtdb->lock);
+ rbtdb->common.magic = 0;
+ rbtdb->common.impmagic = 0;
+ ondest = rbtdb->common.ondest;
+ isc_mem_putanddetach(&rbtdb->common.mctx, rbtdb, sizeof(*rbtdb));
+ isc_ondestroy_notify(&ondest, rbtdb);
+}
+
+static inline void
+maybe_free_rbtdb(dns_rbtdb_t *rbtdb) {
+ isc_boolean_t want_free = ISC_FALSE;
+ unsigned int i;
+ unsigned int inactive = 0;
+
+ /* XXX check for open versions here */
+
+ if (rbtdb->soanode != NULL)
+ dns_db_detachnode((dns_db_t *)rbtdb, &rbtdb->soanode);
+ if (rbtdb->nsnode != NULL)
+ dns_db_detachnode((dns_db_t *)rbtdb, &rbtdb->nsnode);
+
+ /*
+ * Even though there are no external direct references, there still
+ * may be nodes in use.
+ */
+ for (i = 0; i < rbtdb->node_lock_count; i++) {
+ NODE_LOCK(&rbtdb->node_locks[i].lock, isc_rwlocktype_write);
+ rbtdb->node_locks[i].exiting = ISC_TRUE;
+ NODE_UNLOCK(&rbtdb->node_locks[i].lock, isc_rwlocktype_write);
+ if (isc_refcount_current(&rbtdb->node_locks[i].references)
+ == 0) {
+ inactive++;
+ }
+ }
+
+ if (inactive != 0) {
+ RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write);
+ rbtdb->active -= inactive;
+ if (rbtdb->active == 0)
+ want_free = ISC_TRUE;
+ RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write);
+ if (want_free) {
+ char buf[DNS_NAME_FORMATSIZE];
+ if (dns_name_dynamic(&rbtdb->common.origin))
+ dns_name_format(&rbtdb->common.origin, buf,
+ sizeof(buf));
+ else
+ strcpy(buf, "<UNKNOWN>");
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1),
+ "calling free_rbtdb(%s)", buf);
+ free_rbtdb(rbtdb, ISC_TRUE, NULL);
+ }
+ }
+}
+
+static void
+detach(dns_db_t **dbp) {
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)(*dbp);
+ unsigned int refs;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ isc_refcount_decrement(&rbtdb->references, &refs);
+
+ if (refs == 0)
+ maybe_free_rbtdb(rbtdb);
+
+ *dbp = NULL;
+}
+
+static void
+currentversion(dns_db_t *db, dns_dbversion_t **versionp) {
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ rbtdb_version_t *version;
+ unsigned int refs;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_read);
+ version = rbtdb->current_version;
+ isc_refcount_increment(&version->references, &refs);
+ RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_read);
+
+ *versionp = (dns_dbversion_t *)version;
+}
+
+static inline rbtdb_version_t *
+allocate_version(isc_mem_t *mctx, rbtdb_serial_t serial,
+ unsigned int references, isc_boolean_t writer)
+{
+ isc_result_t result;
+ rbtdb_version_t *version;
+
+ version = isc_mem_get(mctx, sizeof(*version));
+ if (version == NULL)
+ return (NULL);
+ version->serial = serial;
+ result = isc_refcount_init(&version->references, references);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, version, sizeof(*version));
+ return (NULL);
+ }
+ version->writer = writer;
+ version->commit_ok = ISC_FALSE;
+ ISC_LIST_INIT(version->changed_list);
+ ISC_LIST_INIT(version->resigned_list);
+ ISC_LINK_INIT(version, link);
+
+ return (version);
+}
+
+static isc_result_t
+newversion(dns_db_t *db, dns_dbversion_t **versionp) {
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ rbtdb_version_t *version;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+ REQUIRE(versionp != NULL && *versionp == NULL);
+ REQUIRE(rbtdb->future_version == NULL);
+
+ RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write);
+ RUNTIME_CHECK(rbtdb->next_serial != 0); /* XXX Error? */
+ version = allocate_version(rbtdb->common.mctx, rbtdb->next_serial, 1,
+ ISC_TRUE);
+ if (version != NULL) {
+ version->commit_ok = ISC_TRUE;
+ version->secure = rbtdb->current_version->secure;
+ version->havensec3 = rbtdb->current_version->havensec3;
+ if (version->havensec3) {
+ version->flags = rbtdb->current_version->flags;
+ version->iterations =
+ rbtdb->current_version->iterations;
+ version->hash = rbtdb->current_version->hash;
+ version->salt_length =
+ rbtdb->current_version->salt_length;
+ memcpy(version->salt, rbtdb->current_version->salt,
+ version->salt_length);
+ } else {
+ version->flags = 0;
+ version->iterations = 0;
+ version->hash = 0;
+ version->salt_length = 0;
+ memset(version->salt, 0, sizeof(version->salt));
+ }
+ rbtdb->next_serial++;
+ rbtdb->future_version = version;
+ }
+ RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write);
+
+ if (version == NULL)
+ return (ISC_R_NOMEMORY);
+
+ *versionp = version;
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+attachversion(dns_db_t *db, dns_dbversion_t *source,
+ dns_dbversion_t **targetp)
+{
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ rbtdb_version_t *rbtversion = source;
+ unsigned int refs;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ isc_refcount_increment(&rbtversion->references, &refs);
+ INSIST(refs > 1);
+
+ *targetp = rbtversion;
+}
+
+static rbtdb_changed_t *
+add_changed(dns_rbtdb_t *rbtdb, rbtdb_version_t *version,
+ dns_rbtnode_t *node)
+{
+ rbtdb_changed_t *changed;
+ unsigned int refs;
+
+ /*
+ * Caller must be holding the node lock if its reference must be
+ * protected by the lock.
+ */
+
+ changed = isc_mem_get(rbtdb->common.mctx, sizeof(*changed));
+
+ RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write);
+
+ REQUIRE(version->writer);
+
+ if (changed != NULL) {
+ dns_rbtnode_refincrement(node, &refs);
+ INSIST(refs != 0);
+ changed->node = node;
+ changed->dirty = ISC_FALSE;
+ ISC_LIST_INITANDAPPEND(version->changed_list, changed, link);
+ } else
+ version->commit_ok = ISC_FALSE;
+
+ RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write);
+
+ return (changed);
+}
+
+static void
+free_acachearray(isc_mem_t *mctx, rdatasetheader_t *header,
+ acachectl_t *array)
+{
+ unsigned int count;
+ unsigned int i;
+ unsigned char *raw; /* RDATASLAB */
+
+ /*
+ * The caller must be holding the corresponding node lock.
+ */
+
+ if (array == NULL)
+ return;
+
+ raw = (unsigned char *)header + sizeof(*header);
+ count = raw[0] * 256 + raw[1];
+
+ /*
+ * Sanity check: since an additional cache entry has a reference to
+ * the original DB node (in the callback arg), there should be no
+ * acache entries when the node can be freed.
+ */
+ for (i = 0; i < count; i++)
+ INSIST(array[i].entry == NULL && array[i].cbarg == NULL);
+
+ isc_mem_put(mctx, array, count * sizeof(acachectl_t));
+}
+
+static inline void
+free_noqname(isc_mem_t *mctx, struct noqname **noqname) {
+
+ if (dns_name_dynamic(&(*noqname)->name))
+ dns_name_free(&(*noqname)->name, mctx);
+ if ((*noqname)->neg != NULL)
+ isc_mem_put(mctx, (*noqname)->neg,
+ dns_rdataslab_size((*noqname)->neg, 0));
+ if ((*noqname)->negsig != NULL)
+ isc_mem_put(mctx, (*noqname)->negsig,
+ dns_rdataslab_size((*noqname)->negsig, 0));
+ isc_mem_put(mctx, *noqname, sizeof(**noqname));
+ *noqname = NULL;
+}
+
+static inline void
+init_rdataset(dns_rbtdb_t *rbtdb, rdatasetheader_t *h)
+{
+ ISC_LINK_INIT(h, lru_link);
+ h->heap_index = 0;
+
+#if TRACE_HEADER
+ if (IS_CACHE(rbtdb) && rbtdb->common.rdclass == dns_rdataclass_in)
+ fprintf(stderr, "initialized header: %p\n", h);
+#else
+ UNUSED(rbtdb);
+#endif
+}
+
+static inline rdatasetheader_t *
+new_rdataset(dns_rbtdb_t *rbtdb, isc_mem_t *mctx)
+{
+ rdatasetheader_t *h;
+
+ h = isc_mem_get(mctx, sizeof(*h));
+ if (h == NULL)
+ return (NULL);
+
+#if TRACE_HEADER
+ if (IS_CACHE(rbtdb) && rbtdb->common.rdclass == dns_rdataclass_in)
+ fprintf(stderr, "allocated header: %p\n", h);
+#endif
+ init_rdataset(rbtdb, h);
+ return (h);
+}
+
+static inline void
+free_rdataset(dns_rbtdb_t *rbtdb, isc_mem_t *mctx, rdatasetheader_t *rdataset)
+{
+ unsigned int size;
+ int idx;
+
+ if (EXISTS(rdataset) &&
+ (rdataset->attributes & RDATASET_ATTR_STATCOUNT) != 0) {
+ update_rrsetstats(rbtdb, rdataset, ISC_FALSE);
+ }
+
+ idx = rdataset->node->locknum;
+ if (ISC_LINK_LINKED(rdataset, lru_link))
+ ISC_LIST_UNLINK(rbtdb->rdatasets[idx], rdataset, lru_link);
+ if (rdataset->heap_index != 0)
+ isc_heap_delete(rbtdb->heaps[idx], rdataset->heap_index);
+ rdataset->heap_index = 0;
+
+ if (rdataset->noqname != NULL)
+ free_noqname(mctx, &rdataset->noqname);
+ if (rdataset->closest != NULL)
+ free_noqname(mctx, &rdataset->closest);
+
+ free_acachearray(mctx, rdataset, rdataset->additional_auth);
+ free_acachearray(mctx, rdataset, rdataset->additional_glue);
+
+ if ((rdataset->attributes & RDATASET_ATTR_NONEXISTENT) != 0)
+ size = sizeof(*rdataset);
+ else
+ size = dns_rdataslab_size((unsigned char *)rdataset,
+ sizeof(*rdataset));
+ isc_mem_put(mctx, rdataset, size);
+}
+
+static inline void
+rollback_node(dns_rbtnode_t *node, rbtdb_serial_t serial) {
+ rdatasetheader_t *header, *dcurrent;
+ isc_boolean_t make_dirty = ISC_FALSE;
+
+ /*
+ * Caller must hold the node lock.
+ */
+
+ /*
+ * We set the IGNORE attribute on rdatasets with serial number
+ * 'serial'. When the reference count goes to zero, these rdatasets
+ * will be cleaned up; until that time, they will be ignored.
+ */
+ for (header = node->data; header != NULL; header = header->next) {
+ if (header->serial == serial) {
+ header->attributes |= RDATASET_ATTR_IGNORE;
+ make_dirty = ISC_TRUE;
+ }
+ for (dcurrent = header->down;
+ dcurrent != NULL;
+ dcurrent = dcurrent->down) {
+ if (dcurrent->serial == serial) {
+ dcurrent->attributes |= RDATASET_ATTR_IGNORE;
+ make_dirty = ISC_TRUE;
+ }
+ }
+ }
+ if (make_dirty)
+ node->dirty = 1;
+}
+
+static inline void
+clean_stale_headers(dns_rbtdb_t *rbtdb, isc_mem_t *mctx, rdatasetheader_t *top)
+{
+ rdatasetheader_t *d, *down_next;
+
+ for (d = top->down; d != NULL; d = down_next) {
+ down_next = d->down;
+ free_rdataset(rbtdb, mctx, d);
+ }
+ top->down = NULL;
+}
+
+static inline void
+clean_cache_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) {
+ rdatasetheader_t *current, *top_prev, *top_next;
+ isc_mem_t *mctx = rbtdb->common.mctx;
+
+ /*
+ * Caller must be holding the node lock.
+ */
+
+ top_prev = NULL;
+ for (current = node->data; current != NULL; current = top_next) {
+ top_next = current->next;
+ clean_stale_headers(rbtdb, mctx, current);
+ /*
+ * If current is nonexistent or stale, we can clean it up.
+ */
+ if ((current->attributes &
+ (RDATASET_ATTR_NONEXISTENT|RDATASET_ATTR_STALE)) != 0) {
+ if (top_prev != NULL)
+ top_prev->next = current->next;
+ else
+ node->data = current->next;
+ free_rdataset(rbtdb, mctx, current);
+ } else
+ top_prev = current;
+ }
+ node->dirty = 0;
+}
+
+static inline void
+clean_zone_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
+ rbtdb_serial_t least_serial)
+{
+ rdatasetheader_t *current, *dcurrent, *down_next, *dparent;
+ rdatasetheader_t *top_prev, *top_next;
+ isc_mem_t *mctx = rbtdb->common.mctx;
+ isc_boolean_t still_dirty = ISC_FALSE;
+
+ /*
+ * Caller must be holding the node lock.
+ */
+ REQUIRE(least_serial != 0);
+
+ top_prev = NULL;
+ for (current = node->data; current != NULL; current = top_next) {
+ top_next = current->next;
+
+ /*
+ * First, we clean up any instances of multiple rdatasets
+ * with the same serial number, or that have the IGNORE
+ * attribute.
+ */
+ dparent = current;
+ for (dcurrent = current->down;
+ dcurrent != NULL;
+ dcurrent = down_next) {
+ down_next = dcurrent->down;
+ INSIST(dcurrent->serial <= dparent->serial);
+ if (dcurrent->serial == dparent->serial ||
+ IGNORE(dcurrent)) {
+ if (down_next != NULL)
+ down_next->next = dparent;
+ dparent->down = down_next;
+ free_rdataset(rbtdb, mctx, dcurrent);
+ } else
+ dparent = dcurrent;
+ }
+
+ /*
+ * We've now eliminated all IGNORE datasets with the possible
+ * exception of current, which we now check.
+ */
+ if (IGNORE(current)) {
+ down_next = current->down;
+ if (down_next == NULL) {
+ if (top_prev != NULL)
+ top_prev->next = current->next;
+ else
+ node->data = current->next;
+ free_rdataset(rbtdb, mctx, current);
+ /*
+ * current no longer exists, so we can
+ * just continue with the loop.
+ */
+ continue;
+ } else {
+ /*
+ * Pull up current->down, making it the new
+ * current.
+ */
+ if (top_prev != NULL)
+ top_prev->next = down_next;
+ else
+ node->data = down_next;
+ down_next->next = top_next;
+ free_rdataset(rbtdb, mctx, current);
+ current = down_next;
+ }
+ }
+
+ /*
+ * We now try to find the first down node less than the
+ * least serial.
+ */
+ dparent = current;
+ for (dcurrent = current->down;
+ dcurrent != NULL;
+ dcurrent = down_next) {
+ down_next = dcurrent->down;
+ if (dcurrent->serial < least_serial)
+ break;
+ dparent = dcurrent;
+ }
+
+ /*
+ * If there is a such an rdataset, delete it and any older
+ * versions.
+ */
+ if (dcurrent != NULL) {
+ do {
+ down_next = dcurrent->down;
+ INSIST(dcurrent->serial <= least_serial);
+ free_rdataset(rbtdb, mctx, dcurrent);
+ dcurrent = down_next;
+ } while (dcurrent != NULL);
+ dparent->down = NULL;
+ }
+
+ /*
+ * Note. The serial number of 'current' might be less than
+ * least_serial too, but we cannot delete it because it is
+ * the most recent version, unless it is a NONEXISTENT
+ * rdataset.
+ */
+ if (current->down != NULL) {
+ still_dirty = ISC_TRUE;
+ top_prev = current;
+ } else {
+ /*
+ * If this is a NONEXISTENT rdataset, we can delete it.
+ */
+ if (NONEXISTENT(current)) {
+ if (top_prev != NULL)
+ top_prev->next = current->next;
+ else
+ node->data = current->next;
+ free_rdataset(rbtdb, mctx, current);
+ } else
+ top_prev = current;
+ }
+ }
+ if (!still_dirty)
+ node->dirty = 0;
+}
+
+/*%
+ * Clean up dead nodes. These are nodes which have no references, and
+ * have no data. They are dead but we could not or chose not to delete
+ * them when we deleted all the data at that node because we did not want
+ * to wait for the tree write lock.
+ *
+ * The caller must hold a tree write lock and bucketnum'th node (write) lock.
+ */
+static void
+cleanup_dead_nodes(dns_rbtdb_t *rbtdb, int bucketnum) {
+ dns_rbtnode_t *node;
+ isc_result_t result;
+ int count = 10; /* XXXJT: should be adjustable */
+
+ node = ISC_LIST_HEAD(rbtdb->deadnodes[bucketnum]);
+ while (node != NULL && count > 0) {
+ ISC_LIST_UNLINK(rbtdb->deadnodes[bucketnum], node, deadlink);
+
+ /*
+ * Since we're holding a tree write lock, it should be
+ * impossible for this node to be referenced by others.
+ */
+ INSIST(dns_rbtnode_refcurrent(node) == 0 &&
+ node->data == NULL);
+
+ INSIST(!ISC_LINK_LINKED(node, deadlink));
+ if (node->nsec3)
+ result = dns_rbt_deletenode(rbtdb->nsec3, node,
+ ISC_FALSE);
+ else
+ result = dns_rbt_deletenode(rbtdb->tree, node,
+ ISC_FALSE);
+ if (result != ISC_R_SUCCESS)
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
+ "cleanup_dead_nodes: "
+ "dns_rbt_deletenode: %s",
+ isc_result_totext(result));
+ node = ISC_LIST_HEAD(rbtdb->deadnodes[bucketnum]);
+ count--;
+ }
+}
+
+/*
+ * Caller must be holding the node lock if its reference must be protected
+ * by the lock.
+ */
+static inline void
+new_reference(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) {
+ unsigned int lockrefs, noderefs;
+ isc_refcount_t *lockref;
+
+ dns_rbtnode_refincrement0(node, &noderefs);
+ if (noderefs == 1) { /* this is the first reference to the node */
+ lockref = &rbtdb->node_locks[node->locknum].references;
+ isc_refcount_increment0(lockref, &lockrefs);
+ INSIST(lockrefs != 0);
+ }
+ INSIST(noderefs != 0);
+}
+
+/*
+ * This function is assumed to be called when a node is newly referenced
+ * and can be in the deadnode list. In that case the node must be retrieved
+ * from the list because the it is going to be used. In addition, if the caller
+ * happens to hold a write lock on the tree, it's a good chance to purge dead
+ * nodes.
+ * Note: while a new reference is gained in multiple places, there are only very
+ * few cases where the node can be in the deadnode list (only empty nodes can
+ * have been added to the list).
+ */
+static inline void
+reactivate_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
+ isc_rwlocktype_t treelocktype)
+{
+ isc_boolean_t need_relock = ISC_FALSE;
+
+ NODE_STRONGLOCK(&rbtdb->node_locks[node->locknum].lock);
+ new_reference(rbtdb, node);
+
+ NODE_WEAKLOCK(&rbtdb->node_locks[node->locknum].lock,
+ isc_rwlocktype_read);
+ if (ISC_LINK_LINKED(node, deadlink))
+ need_relock = ISC_TRUE;
+ else if (!ISC_LIST_EMPTY(rbtdb->deadnodes[node->locknum]) &&
+ treelocktype == isc_rwlocktype_write)
+ need_relock = ISC_TRUE;
+ NODE_WEAKUNLOCK(&rbtdb->node_locks[node->locknum].lock,
+ isc_rwlocktype_read);
+ if (need_relock) {
+ NODE_WEAKLOCK(&rbtdb->node_locks[node->locknum].lock,
+ isc_rwlocktype_write);
+ if (ISC_LINK_LINKED(node, deadlink))
+ ISC_LIST_UNLINK(rbtdb->deadnodes[node->locknum],
+ node, deadlink);
+ if (treelocktype == isc_rwlocktype_write)
+ cleanup_dead_nodes(rbtdb, node->locknum);
+ NODE_WEAKUNLOCK(&rbtdb->node_locks[node->locknum].lock,
+ isc_rwlocktype_write);
+ }
+
+ NODE_STRONGUNLOCK(&rbtdb->node_locks[node->locknum].lock);
+}
+
+/*
+ * Caller must be holding the node lock; either the "strong", read or write
+ * lock. Note that the lock must be held even when node references are
+ * atomically modified; in that case the decrement operation itself does not
+ * have to be protected, but we must avoid a race condition where multiple
+ * threads are decreasing the reference to zero simultaneously and at least
+ * one of them is going to free the node.
+ * This function returns ISC_TRUE if and only if the node reference decreases
+ * to zero.
+ */
+static isc_boolean_t
+decrement_reference(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
+ rbtdb_serial_t least_serial,
+ isc_rwlocktype_t nlock, isc_rwlocktype_t tlock)
+{
+ isc_result_t result;
+ isc_boolean_t write_locked;
+ rbtdb_nodelock_t *nodelock;
+ unsigned int refs, nrefs;
+ int bucket = node->locknum;
+
+ nodelock = &rbtdb->node_locks[bucket];
+
+ /* Handle easy and typical case first. */
+ if (!node->dirty && (node->data != NULL || node->down != NULL)) {
+ dns_rbtnode_refdecrement(node, &nrefs);
+ INSIST((int)nrefs >= 0);
+ if (nrefs == 0) {
+ isc_refcount_decrement(&nodelock->references, &refs);
+ INSIST((int)refs >= 0);
+ }
+ return ((nrefs == 0) ? ISC_TRUE : ISC_FALSE);
+ }
+
+ /* Upgrade the lock? */
+ if (nlock == isc_rwlocktype_read) {
+ NODE_WEAKUNLOCK(&nodelock->lock, isc_rwlocktype_read);
+ NODE_WEAKLOCK(&nodelock->lock, isc_rwlocktype_write);
+ }
+ dns_rbtnode_refdecrement(node, &nrefs);
+ INSIST((int)nrefs >= 0);
+ if (nrefs > 0) {
+ /* Restore the lock? */
+ if (nlock == isc_rwlocktype_read)
+ NODE_WEAKDOWNGRADE(&nodelock->lock);
+ return (ISC_FALSE);
+ }
+
+ if (node->dirty && dns_rbtnode_refcurrent(node) == 0) {
+ if (IS_CACHE(rbtdb))
+ clean_cache_node(rbtdb, node);
+ else {
+ if (least_serial == 0) {
+ /*
+ * Caller doesn't know the least serial.
+ * Get it.
+ */
+ RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_read);
+ least_serial = rbtdb->least_serial;
+ RBTDB_UNLOCK(&rbtdb->lock,
+ isc_rwlocktype_read);
+ }
+ clean_zone_node(rbtdb, node, least_serial);
+ }
+ }
+
+ isc_refcount_decrement(&nodelock->references, &refs);
+ INSIST((int)refs >= 0);
+
+ /*
+ * XXXDCL should this only be done for cache zones?
+ */
+ if (node->data != NULL || node->down != NULL) {
+ /* Restore the lock? */
+ if (nlock == isc_rwlocktype_read)
+ NODE_WEAKDOWNGRADE(&nodelock->lock);
+ return (ISC_TRUE);
+ }
+
+ /*
+ * Attempt to switch to a write lock on the tree. If this fails,
+ * we will add this node to a linked list of nodes in this locking
+ * bucket which we will free later.
+ */
+ if (tlock != isc_rwlocktype_write) {
+ /*
+ * Locking hierarchy notwithstanding, we don't need to free
+ * the node lock before acquiring the tree write lock because
+ * we only do a trylock.
+ */
+ if (tlock == isc_rwlocktype_read)
+ result = isc_rwlock_tryupgrade(&rbtdb->tree_lock);
+ else
+ result = isc_rwlock_trylock(&rbtdb->tree_lock,
+ isc_rwlocktype_write);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS ||
+ result == ISC_R_LOCKBUSY);
+
+ write_locked = ISC_TF(result == ISC_R_SUCCESS);
+ } else
+ write_locked = ISC_TRUE;
+
+ if (write_locked && dns_rbtnode_refcurrent(node) == 0) {
+ /*
+ * We can now delete the node if the reference counter is
+ * zero. This should be typically the case, but a different
+ * thread may still gain a (new) reference just before the
+ * current thread locks the tree (e.g., in findnode()).
+ */
+
+ if (isc_log_wouldlog(dns_lctx, ISC_LOG_DEBUG(1))) {
+ char printname[DNS_NAME_FORMATSIZE];
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1),
+ "decrement_reference: "
+ "delete from rbt: %p %s",
+ node,
+ dns_rbt_formatnodename(node, printname,
+ sizeof(printname)));
+ }
+
+ INSIST(!ISC_LINK_LINKED(node, deadlink));
+ if (node->nsec3)
+ result = dns_rbt_deletenode(rbtdb->nsec3, node,
+ ISC_FALSE);
+ else
+ result = dns_rbt_deletenode(rbtdb->tree, node,
+ ISC_FALSE);
+ if (result != ISC_R_SUCCESS)
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
+ "decrement_reference: "
+ "dns_rbt_deletenode: %s",
+ isc_result_totext(result));
+ } else if (dns_rbtnode_refcurrent(node) == 0) {
+ INSIST(!ISC_LINK_LINKED(node, deadlink));
+ ISC_LIST_APPEND(rbtdb->deadnodes[bucket], node, deadlink);
+ }
+
+ /* Restore the lock? */
+ if (nlock == isc_rwlocktype_read)
+ NODE_WEAKDOWNGRADE(&nodelock->lock);
+
+ /*
+ * Relock a read lock, or unlock the write lock if no lock was held.
+ */
+ if (tlock == isc_rwlocktype_none)
+ if (write_locked)
+ RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+
+ if (tlock == isc_rwlocktype_read)
+ if (write_locked)
+ isc_rwlock_downgrade(&rbtdb->tree_lock);
+
+ return (ISC_TRUE);
+}
+
+static inline void
+make_least_version(dns_rbtdb_t *rbtdb, rbtdb_version_t *version,
+ rbtdb_changedlist_t *cleanup_list)
+{
+ /*
+ * Caller must be holding the database lock.
+ */
+
+ rbtdb->least_serial = version->serial;
+ *cleanup_list = version->changed_list;
+ ISC_LIST_INIT(version->changed_list);
+}
+
+static inline void
+cleanup_nondirty(rbtdb_version_t *version, rbtdb_changedlist_t *cleanup_list) {
+ rbtdb_changed_t *changed, *next_changed;
+
+ /*
+ * If the changed record is dirty, then
+ * an update created multiple versions of
+ * a given rdataset. We keep this list
+ * until we're the least open version, at
+ * which point it's safe to get rid of any
+ * older versions.
+ *
+ * If the changed record isn't dirty, then
+ * we don't need it anymore since we're
+ * committing and not rolling back.
+ *
+ * The caller must be holding the database lock.
+ */
+ for (changed = HEAD(version->changed_list);
+ changed != NULL;
+ changed = next_changed) {
+ next_changed = NEXT(changed, link);
+ if (!changed->dirty) {
+ UNLINK(version->changed_list,
+ changed, link);
+ APPEND(*cleanup_list,
+ changed, link);
+ }
+ }
+}
+
+static void
+iszonesecure(dns_db_t *db, rbtdb_version_t *version, dns_dbnode_t *origin) {
+ dns_rdataset_t keyset;
+ dns_rdataset_t nsecset, signsecset;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_boolean_t haszonekey = ISC_FALSE;
+ isc_boolean_t hasnsec = ISC_FALSE;
+ isc_boolean_t hasoptbit = ISC_FALSE;
+ isc_boolean_t nsec3createflag = ISC_FALSE;
+ isc_result_t result;
+
+ dns_rdataset_init(&keyset);
+ result = dns_db_findrdataset(db, origin, version, dns_rdatatype_dnskey,
+ 0, 0, &keyset, NULL);
+ if (result == ISC_R_SUCCESS) {
+ dns_rdata_t keyrdata = DNS_RDATA_INIT;
+ result = dns_rdataset_first(&keyset);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdataset_current(&keyset, &keyrdata);
+ if (dns_zonekey_iszonekey(&keyrdata)) {
+ haszonekey = ISC_TRUE;
+ break;
+ }
+ result = dns_rdataset_next(&keyset);
+ }
+ dns_rdataset_disassociate(&keyset);
+ }
+ if (!haszonekey) {
+ version->secure = dns_db_insecure;
+ version->havensec3 = ISC_FALSE;
+ return;
+ }
+
+ dns_rdataset_init(&nsecset);
+ dns_rdataset_init(&signsecset);
+ result = dns_db_findrdataset(db, origin, version, dns_rdatatype_nsec,
+ 0, 0, &nsecset, &signsecset);
+ if (result == ISC_R_SUCCESS) {
+ if (dns_rdataset_isassociated(&signsecset)) {
+ hasnsec = ISC_TRUE;
+ result = dns_rdataset_first(&nsecset);
+ if (result == ISC_R_SUCCESS) {
+ dns_rdataset_current(&nsecset, &rdata);
+ hasoptbit = dns_nsec_typepresent(&rdata,
+ dns_rdatatype_opt);
+ }
+ dns_rdataset_disassociate(&signsecset);
+ }
+ dns_rdataset_disassociate(&nsecset);
+ }
+
+ setnsec3parameters(db, version, &nsec3createflag);
+
+ /*
+ * Do we have a valid NSEC/NSEC3 chain?
+ */
+ if (version->havensec3 || (hasnsec && !hasoptbit))
+ version->secure = dns_db_secure;
+ /*
+ * Do we have a NSEC/NSEC3 chain under creation?
+ */
+ else if (hasoptbit || nsec3createflag)
+ version->secure = dns_db_partial;
+ else
+ version->secure = dns_db_insecure;
+}
+
+/*%<
+ * Walk the origin node looking for NSEC3PARAM records.
+ * Cache the nsec3 parameters.
+ */
+static void
+setnsec3parameters(dns_db_t *db, rbtdb_version_t *version,
+ isc_boolean_t *nsec3createflag)
+{
+ dns_rbtnode_t *node;
+ dns_rdata_nsec3param_t nsec3param;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_region_t region;
+ isc_result_t result;
+ rdatasetheader_t *header, *header_next;
+ unsigned char *raw; /* RDATASLAB */
+ unsigned int count, length;
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+
+ RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+ version->havensec3 = ISC_FALSE;
+ node = rbtdb->origin_node;
+ NODE_LOCK(&(rbtdb->node_locks[node->locknum].lock),
+ isc_rwlocktype_read);
+ for (header = node->data;
+ header != NULL;
+ header = header_next) {
+ header_next = header->next;
+ do {
+ if (header->serial <= version->serial &&
+ !IGNORE(header)) {
+ if (NONEXISTENT(header))
+ header = NULL;
+ break;
+ } else
+ header = header->down;
+ } while (header != NULL);
+
+ if (header != NULL &&
+ header->type == dns_rdatatype_nsec3param) {
+ /*
+ * Find A NSEC3PARAM with a supported algorithm.
+ */
+ raw = (unsigned char *)header + sizeof(*header);
+ count = raw[0] * 256 + raw[1]; /* count */
+#if DNS_RDATASET_FIXED
+ raw += count * 4 + 2;
+#else
+ raw += 2;
+#endif
+ while (count-- > 0U) {
+ length = raw[0] * 256 + raw[1];
+#if DNS_RDATASET_FIXED
+ raw += 4;
+#else
+ raw += 2;
+#endif
+ region.base = raw;
+ region.length = length;
+ raw += length;
+ dns_rdata_fromregion(&rdata,
+ rbtdb->common.rdclass,
+ dns_rdatatype_nsec3param,
+ &region);
+ result = dns_rdata_tostruct(&rdata,
+ &nsec3param,
+ NULL);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_rdata_reset(&rdata);
+
+ if (nsec3param.hash != DNS_NSEC3_UNKNOWNALG &&
+ !dns_nsec3_supportedhash(nsec3param.hash))
+ continue;
+
+#ifdef RFC5155_STRICT
+ if (nsec3param.flags != 0)
+ continue;
+#else
+ if ((nsec3param.flags & DNS_NSEC3FLAG_CREATE)
+ != 0)
+ *nsec3createflag = ISC_TRUE;
+ if ((nsec3param.flags & ~DNS_NSEC3FLAG_OPTOUT)
+ != 0)
+ continue;
+#endif
+
+ INSIST(nsec3param.salt_length <=
+ sizeof(version->salt));
+ memcpy(version->salt, nsec3param.salt,
+ nsec3param.salt_length);
+ version->hash = nsec3param.hash;
+ version->salt_length = nsec3param.salt_length;
+ version->iterations = nsec3param.iterations;
+ version->flags = nsec3param.flags;
+ version->havensec3 = ISC_TRUE;
+ /*
+ * Look for a better algorithm than the
+ * unknown test algorithm.
+ */
+ if (nsec3param.hash != DNS_NSEC3_UNKNOWNALG)
+ goto unlock;
+ }
+ }
+ }
+ unlock:
+ NODE_UNLOCK(&(rbtdb->node_locks[node->locknum].lock),
+ isc_rwlocktype_read);
+ RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+}
+
+static void
+closeversion(dns_db_t *db, dns_dbversion_t **versionp, isc_boolean_t commit) {
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ rbtdb_version_t *version, *cleanup_version, *least_greater;
+ isc_boolean_t rollback = ISC_FALSE;
+ rbtdb_changedlist_t cleanup_list;
+ rdatasetheaderlist_t resigned_list;
+ rbtdb_changed_t *changed, *next_changed;
+ rbtdb_serial_t serial, least_serial;
+ dns_rbtnode_t *rbtnode;
+ unsigned int refs;
+ rdatasetheader_t *header;
+ isc_boolean_t writer;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+ version = (rbtdb_version_t *)*versionp;
+
+ cleanup_version = NULL;
+ ISC_LIST_INIT(cleanup_list);
+ ISC_LIST_INIT(resigned_list);
+
+ isc_refcount_decrement(&version->references, &refs);
+ if (refs > 0) { /* typical and easy case first */
+ if (commit) {
+ RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_read);
+ INSIST(!version->writer);
+ RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_read);
+ }
+ goto end;
+ }
+
+ RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write);
+ serial = version->serial;
+ writer = version->writer;
+ if (version->writer) {
+ if (commit) {
+ unsigned cur_ref;
+ rbtdb_version_t *cur_version;
+
+ INSIST(version->commit_ok);
+ INSIST(version == rbtdb->future_version);
+ /*
+ * The current version is going to be replaced.
+ * Release the (likely last) reference to it from the
+ * DB itself and unlink it from the open list.
+ */
+ cur_version = rbtdb->current_version;
+ isc_refcount_decrement(&cur_version->references,
+ &cur_ref);
+ if (cur_ref == 0) {
+ if (cur_version->serial == rbtdb->least_serial)
+ INSIST(EMPTY(cur_version->changed_list));
+ UNLINK(rbtdb->open_versions,
+ cur_version, link);
+ }
+ if (EMPTY(rbtdb->open_versions)) {
+ /*
+ * We're going to become the least open
+ * version.
+ */
+ make_least_version(rbtdb, version,
+ &cleanup_list);
+ } else {
+ /*
+ * Some other open version is the
+ * least version. We can't cleanup
+ * records that were changed in this
+ * version because the older versions
+ * may still be in use by an open
+ * version.
+ *
+ * We can, however, discard the
+ * changed records for things that
+ * we've added that didn't exist in
+ * prior versions.
+ */
+ cleanup_nondirty(version, &cleanup_list);
+ }
+ /*
+ * If the (soon to be former) current version
+ * isn't being used by anyone, we can clean
+ * it up.
+ */
+ if (cur_ref == 0) {
+ cleanup_version = cur_version;
+ APPENDLIST(version->changed_list,
+ cleanup_version->changed_list,
+ link);
+ }
+ /*
+ * Become the current version.
+ */
+ version->writer = ISC_FALSE;
+ rbtdb->current_version = version;
+ rbtdb->current_serial = version->serial;
+ rbtdb->future_version = NULL;
+
+ /*
+ * Keep the current version in the open list, and
+ * gain a reference for the DB itself (see the DB
+ * creation function below). This must be the only
+ * case where we need to increment the counter from
+ * zero and need to use isc_refcount_increment0().
+ */
+ isc_refcount_increment0(&version->references,
+ &cur_ref);
+ INSIST(cur_ref == 1);
+ PREPEND(rbtdb->open_versions,
+ rbtdb->current_version, link);
+ resigned_list = version->resigned_list;
+ ISC_LIST_INIT(version->resigned_list);
+ } else {
+ /*
+ * We're rolling back this transaction.
+ */
+ cleanup_list = version->changed_list;
+ ISC_LIST_INIT(version->changed_list);
+ resigned_list = version->resigned_list;
+ ISC_LIST_INIT(version->resigned_list);
+ rollback = ISC_TRUE;
+ cleanup_version = version;
+ rbtdb->future_version = NULL;
+ }
+ } else {
+ if (version != rbtdb->current_version) {
+ /*
+ * There are no external or internal references
+ * to this version and it can be cleaned up.
+ */
+ cleanup_version = version;
+
+ /*
+ * Find the version with the least serial
+ * number greater than ours.
+ */
+ least_greater = PREV(version, link);
+ if (least_greater == NULL)
+ least_greater = rbtdb->current_version;
+
+ INSIST(version->serial < least_greater->serial);
+ /*
+ * Is this the least open version?
+ */
+ if (version->serial == rbtdb->least_serial) {
+ /*
+ * Yes. Install the new least open
+ * version.
+ */
+ make_least_version(rbtdb,
+ least_greater,
+ &cleanup_list);
+ } else {
+ /*
+ * Add any unexecuted cleanups to
+ * those of the least greater version.
+ */
+ APPENDLIST(least_greater->changed_list,
+ version->changed_list,
+ link);
+ }
+ } else if (version->serial == rbtdb->least_serial)
+ INSIST(EMPTY(version->changed_list));
+ UNLINK(rbtdb->open_versions, version, link);
+ }
+ least_serial = rbtdb->least_serial;
+ RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write);
+
+ /*
+ * Update the zone's secure status.
+ */
+ if (writer && commit && !IS_CACHE(rbtdb))
+ iszonesecure(db, version, rbtdb->origin_node);
+
+ if (cleanup_version != NULL) {
+ INSIST(EMPTY(cleanup_version->changed_list));
+ isc_mem_put(rbtdb->common.mctx, cleanup_version,
+ sizeof(*cleanup_version));
+ }
+
+ /*
+ * Commit/rollback re-signed headers.
+ */
+ for (header = HEAD(resigned_list);
+ header != NULL;
+ header = HEAD(resigned_list)) {
+ ISC_LIST_UNLINK(resigned_list, header, lru_link);
+ if (rollback) {
+ nodelock_t *lock;
+ lock = &rbtdb->node_locks[header->node->locknum].lock;
+ NODE_LOCK(lock, isc_rwlocktype_write);
+ resign_insert(rbtdb, header->node->locknum, header);
+ NODE_UNLOCK(lock, isc_rwlocktype_write);
+ }
+ decrement_reference(rbtdb, header->node, least_serial,
+ isc_rwlocktype_write,
+ isc_rwlocktype_none);
+ }
+
+ if (!EMPTY(cleanup_list)) {
+ /*
+ * We acquire a tree write lock here in order to make sure
+ * that stale nodes will be removed in decrement_reference().
+ * If we didn't have the lock, those nodes could miss the
+ * chance to be removed until the server stops. The write lock
+ * is expensive, but this event should be rare enough to justify
+ * the cost.
+ */
+ RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+ for (changed = HEAD(cleanup_list);
+ changed != NULL;
+ changed = next_changed) {
+ nodelock_t *lock;
+
+ next_changed = NEXT(changed, link);
+ rbtnode = changed->node;
+ lock = &rbtdb->node_locks[rbtnode->locknum].lock;
+
+ NODE_LOCK(lock, isc_rwlocktype_write);
+ /*
+ * This is a good opportunity to purge any dead nodes,
+ * so use it.
+ */
+ cleanup_dead_nodes(rbtdb, rbtnode->locknum);
+
+ if (rollback)
+ rollback_node(rbtnode, serial);
+ decrement_reference(rbtdb, rbtnode, least_serial,
+ isc_rwlocktype_write,
+ isc_rwlocktype_write);
+
+ NODE_UNLOCK(lock, isc_rwlocktype_write);
+
+ isc_mem_put(rbtdb->common.mctx, changed,
+ sizeof(*changed));
+ }
+ RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+ }
+
+ end:
+ *versionp = NULL;
+}
+
+/*
+ * Add the necessary magic for the wildcard name 'name'
+ * to be found in 'rbtdb'.
+ *
+ * In order for wildcard matching to work correctly in
+ * zone_find(), we must ensure that a node for the wildcarding
+ * level exists in the database, and has its 'find_callback'
+ * and 'wild' bits set.
+ *
+ * E.g. if the wildcard name is "*.sub.example." then we
+ * must ensure that "sub.example." exists and is marked as
+ * a wildcard level.
+ */
+static isc_result_t
+add_wildcard_magic(dns_rbtdb_t *rbtdb, dns_name_t *name) {
+ isc_result_t result;
+ dns_name_t foundname;
+ dns_offsets_t offsets;
+ unsigned int n;
+ dns_rbtnode_t *node = NULL;
+
+ dns_name_init(&foundname, offsets);
+ n = dns_name_countlabels(name);
+ INSIST(n >= 2);
+ n--;
+ dns_name_getlabelsequence(name, 1, n, &foundname);
+ result = dns_rbt_addnode(rbtdb->tree, &foundname, &node);
+ if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS)
+ return (result);
+ node->nsec3 = 0;
+ node->find_callback = 1;
+ node->wild = 1;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+add_empty_wildcards(dns_rbtdb_t *rbtdb, dns_name_t *name) {
+ isc_result_t result;
+ dns_name_t foundname;
+ dns_offsets_t offsets;
+ unsigned int n, l, i;
+
+ dns_name_init(&foundname, offsets);
+ n = dns_name_countlabels(name);
+ l = dns_name_countlabels(&rbtdb->common.origin);
+ i = l + 1;
+ while (i < n) {
+ dns_rbtnode_t *node = NULL; /* dummy */
+ dns_name_getlabelsequence(name, n - i, i, &foundname);
+ if (dns_name_iswildcard(&foundname)) {
+ result = add_wildcard_magic(rbtdb, &foundname);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = dns_rbt_addnode(rbtdb->tree, &foundname,
+ &node);
+ if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS)
+ return (result);
+ node->nsec3 = 0;
+ }
+ i++;
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+findnode(dns_db_t *db, dns_name_t *name, isc_boolean_t create,
+ dns_dbnode_t **nodep)
+{
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ dns_rbtnode_t *node = NULL;
+ dns_name_t nodename;
+ isc_result_t result;
+ isc_rwlocktype_t locktype = isc_rwlocktype_read;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ dns_name_init(&nodename, NULL);
+ RWLOCK(&rbtdb->tree_lock, locktype);
+ result = dns_rbt_findnode(rbtdb->tree, name, NULL, &node, NULL,
+ DNS_RBTFIND_EMPTYDATA, NULL, NULL);
+ if (result != ISC_R_SUCCESS) {
+ RWUNLOCK(&rbtdb->tree_lock, locktype);
+ if (!create) {
+ if (result == DNS_R_PARTIALMATCH)
+ result = ISC_R_NOTFOUND;
+ return (result);
+ }
+ /*
+ * It would be nice to try to upgrade the lock instead of
+ * unlocking then relocking.
+ */
+ locktype = isc_rwlocktype_write;
+ RWLOCK(&rbtdb->tree_lock, locktype);
+ node = NULL;
+ result = dns_rbt_addnode(rbtdb->tree, name, &node);
+ if (result == ISC_R_SUCCESS) {
+ dns_rbt_namefromnode(node, &nodename);
+#ifdef DNS_RBT_USEHASH
+ node->locknum = node->hashval % rbtdb->node_lock_count;
+#else
+ node->locknum = dns_name_hash(&nodename, ISC_TRUE) %
+ rbtdb->node_lock_count;
+#endif
+ node->nsec3 = 0;
+ add_empty_wildcards(rbtdb, name);
+
+ if (dns_name_iswildcard(name)) {
+ result = add_wildcard_magic(rbtdb, name);
+ if (result != ISC_R_SUCCESS) {
+ RWUNLOCK(&rbtdb->tree_lock, locktype);
+ return (result);
+ }
+ }
+ } else if (result != ISC_R_EXISTS) {
+ RWUNLOCK(&rbtdb->tree_lock, locktype);
+ return (result);
+ }
+ }
+ reactivate_node(rbtdb, node, locktype);
+ RWUNLOCK(&rbtdb->tree_lock, locktype);
+
+ *nodep = (dns_dbnode_t *)node;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+findnsec3node(dns_db_t *db, dns_name_t *name, isc_boolean_t create,
+ dns_dbnode_t **nodep)
+{
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ dns_rbtnode_t *node = NULL;
+ dns_name_t nodename;
+ isc_result_t result;
+ isc_rwlocktype_t locktype = isc_rwlocktype_read;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ dns_name_init(&nodename, NULL);
+ RWLOCK(&rbtdb->tree_lock, locktype);
+ result = dns_rbt_findnode(rbtdb->nsec3, name, NULL, &node, NULL,
+ DNS_RBTFIND_EMPTYDATA, NULL, NULL);
+ if (result != ISC_R_SUCCESS) {
+ RWUNLOCK(&rbtdb->tree_lock, locktype);
+ if (!create) {
+ if (result == DNS_R_PARTIALMATCH)
+ result = ISC_R_NOTFOUND;
+ return (result);
+ }
+ /*
+ * It would be nice to try to upgrade the lock instead of
+ * unlocking then relocking.
+ */
+ locktype = isc_rwlocktype_write;
+ RWLOCK(&rbtdb->tree_lock, locktype);
+ node = NULL;
+ result = dns_rbt_addnode(rbtdb->nsec3, name, &node);
+ if (result == ISC_R_SUCCESS) {
+ dns_rbt_namefromnode(node, &nodename);
+#ifdef DNS_RBT_USEHASH
+ node->locknum = node->hashval % rbtdb->node_lock_count;
+#else
+ node->locknum = dns_name_hash(&nodename, ISC_TRUE) %
+ rbtdb->node_lock_count;
+#endif
+ node->nsec3 = 1U;
+ } else if (result != ISC_R_EXISTS) {
+ RWUNLOCK(&rbtdb->tree_lock, locktype);
+ return (result);
+ }
+ } else
+ INSIST(node->nsec3);
+ NODE_STRONGLOCK(&rbtdb->node_locks[node->locknum].lock);
+ new_reference(rbtdb, node);
+ NODE_STRONGUNLOCK(&rbtdb->node_locks[node->locknum].lock);
+ RWUNLOCK(&rbtdb->tree_lock, locktype);
+
+ *nodep = (dns_dbnode_t *)node;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+zone_zonecut_callback(dns_rbtnode_t *node, dns_name_t *name, void *arg) {
+ rbtdb_search_t *search = arg;
+ rdatasetheader_t *header, *header_next;
+ rdatasetheader_t *dname_header, *sigdname_header, *ns_header;
+ rdatasetheader_t *found;
+ isc_result_t result;
+ dns_rbtnode_t *onode;
+
+ /*
+ * We only want to remember the topmost zone cut, since it's the one
+ * that counts, so we'll just continue if we've already found a
+ * zonecut.
+ */
+ if (search->zonecut != NULL)
+ return (DNS_R_CONTINUE);
+
+ found = NULL;
+ result = DNS_R_CONTINUE;
+ onode = search->rbtdb->origin_node;
+
+ NODE_LOCK(&(search->rbtdb->node_locks[node->locknum].lock),
+ isc_rwlocktype_read);
+
+ /*
+ * Look for an NS or DNAME rdataset active in our version.
+ */
+ ns_header = NULL;
+ dname_header = NULL;
+ sigdname_header = NULL;
+ for (header = node->data; header != NULL; header = header_next) {
+ header_next = header->next;
+ if (header->type == dns_rdatatype_ns ||
+ header->type == dns_rdatatype_dname ||
+ header->type == RBTDB_RDATATYPE_SIGDNAME) {
+ do {
+ if (header->serial <= search->serial &&
+ !IGNORE(header)) {
+ /*
+ * Is this a "this rdataset doesn't
+ * exist" record?
+ */
+ if (NONEXISTENT(header))
+ header = NULL;
+ break;
+ } else
+ header = header->down;
+ } while (header != NULL);
+ if (header != NULL) {
+ if (header->type == dns_rdatatype_dname)
+ dname_header = header;
+ else if (header->type ==
+ RBTDB_RDATATYPE_SIGDNAME)
+ sigdname_header = header;
+ else if (node != onode ||
+ IS_STUB(search->rbtdb)) {
+ /*
+ * We've found an NS rdataset that
+ * isn't at the origin node. We check
+ * that they're not at the origin node,
+ * because otherwise we'd erroneously
+ * treat the zone top as if it were
+ * a delegation.
+ */
+ ns_header = header;
+ }
+ }
+ }
+ }
+
+ /*
+ * Did we find anything?
+ */
+ if (dname_header != NULL) {
+ /*
+ * Note that DNAME has precedence over NS if both exist.
+ */
+ found = dname_header;
+ search->zonecut_sigrdataset = sigdname_header;
+ } else if (ns_header != NULL) {
+ found = ns_header;
+ search->zonecut_sigrdataset = NULL;
+ }
+
+ if (found != NULL) {
+ /*
+ * We increment the reference count on node to ensure that
+ * search->zonecut_rdataset will still be valid later.
+ */
+ new_reference(search->rbtdb, node);
+ search->zonecut = node;
+ search->zonecut_rdataset = found;
+ search->need_cleanup = ISC_TRUE;
+ /*
+ * Since we've found a zonecut, anything beneath it is
+ * glue and is not subject to wildcard matching, so we
+ * may clear search->wild.
+ */
+ search->wild = ISC_FALSE;
+ if ((search->options & DNS_DBFIND_GLUEOK) == 0) {
+ /*
+ * If the caller does not want to find glue, then
+ * this is the best answer and the search should
+ * stop now.
+ */
+ result = DNS_R_PARTIALMATCH;
+ } else {
+ dns_name_t *zcname;
+
+ /*
+ * The search will continue beneath the zone cut.
+ * This may or may not be the best match. In case it
+ * is, we need to remember the node name.
+ */
+ zcname = dns_fixedname_name(&search->zonecut_name);
+ RUNTIME_CHECK(dns_name_copy(name, zcname, NULL) ==
+ ISC_R_SUCCESS);
+ search->copy_name = ISC_TRUE;
+ }
+ } else {
+ /*
+ * There is no zonecut at this node which is active in this
+ * version.
+ *
+ * If this is a "wild" node and the caller hasn't disabled
+ * wildcard matching, remember that we've seen a wild node
+ * in case we need to go searching for wildcard matches
+ * later on.
+ */
+ if (node->wild && (search->options & DNS_DBFIND_NOWILD) == 0)
+ search->wild = ISC_TRUE;
+ }
+
+ NODE_UNLOCK(&(search->rbtdb->node_locks[node->locknum].lock),
+ isc_rwlocktype_read);
+
+ return (result);
+}
+
+static inline void
+bind_rdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
+ rdatasetheader_t *header, isc_stdtime_t now,
+ dns_rdataset_t *rdataset)
+{
+ unsigned char *raw; /* RDATASLAB */
+
+ /*
+ * Caller must be holding the node reader lock.
+ * XXXJT: technically, we need a writer lock, since we'll increment
+ * the header count below. However, since the actual counter value
+ * doesn't matter, we prioritize performance here. (We may want to
+ * use atomic increment when available).
+ */
+
+ if (rdataset == NULL)
+ return;
+
+ new_reference(rbtdb, node);
+
+ INSIST(rdataset->methods == NULL); /* We must be disassociated. */
+
+ rdataset->methods = &rdataset_methods;
+ rdataset->rdclass = rbtdb->common.rdclass;
+ rdataset->type = RBTDB_RDATATYPE_BASE(header->type);
+ rdataset->covers = RBTDB_RDATATYPE_EXT(header->type);
+ rdataset->ttl = header->rdh_ttl - now;
+ rdataset->trust = header->trust;
+ if (NXDOMAIN(header))
+ rdataset->attributes |= DNS_RDATASETATTR_NXDOMAIN;
+ if (OPTOUT(header))
+ rdataset->attributes |= DNS_RDATASETATTR_OPTOUT;
+ rdataset->private1 = rbtdb;
+ rdataset->private2 = node;
+ raw = (unsigned char *)header + sizeof(*header);
+ rdataset->private3 = raw;
+ rdataset->count = header->count++;
+ if (rdataset->count == ISC_UINT32_MAX)
+ rdataset->count = 0;
+
+ /*
+ * Reset iterator state.
+ */
+ rdataset->privateuint4 = 0;
+ rdataset->private5 = NULL;
+
+ /*
+ * Add noqname proof.
+ */
+ rdataset->private6 = header->noqname;
+ if (rdataset->private6 != NULL)
+ rdataset->attributes |= DNS_RDATASETATTR_NOQNAME;
+ rdataset->private7 = header->closest;
+ if (rdataset->private7 != NULL)
+ rdataset->attributes |= DNS_RDATASETATTR_CLOSEST;
+
+ /*
+ * Copy out re-signing information.
+ */
+ if (RESIGN(header)) {
+ rdataset->attributes |= DNS_RDATASETATTR_RESIGN;
+ rdataset->resign = header->resign;
+ } else
+ rdataset->resign = 0;
+}
+
+static inline isc_result_t
+setup_delegation(rbtdb_search_t *search, dns_dbnode_t **nodep,
+ dns_name_t *foundname, dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset)
+{
+ isc_result_t result;
+ dns_name_t *zcname;
+ rbtdb_rdatatype_t type;
+ dns_rbtnode_t *node;
+
+ /*
+ * The caller MUST NOT be holding any node locks.
+ */
+
+ node = search->zonecut;
+ type = search->zonecut_rdataset->type;
+
+ /*
+ * If we have to set foundname, we do it before anything else.
+ * If we were to set foundname after we had set nodep or bound the
+ * rdataset, then we'd have to undo that work if dns_name_copy()
+ * failed. By setting foundname first, there's nothing to undo if
+ * we have trouble.
+ */
+ if (foundname != NULL && search->copy_name) {
+ zcname = dns_fixedname_name(&search->zonecut_name);
+ result = dns_name_copy(zcname, foundname, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ if (nodep != NULL) {
+ /*
+ * Note that we don't have to increment the node's reference
+ * count here because we're going to use the reference we
+ * already have in the search block.
+ */
+ *nodep = node;
+ search->need_cleanup = ISC_FALSE;
+ }
+ if (rdataset != NULL) {
+ NODE_LOCK(&(search->rbtdb->node_locks[node->locknum].lock),
+ isc_rwlocktype_read);
+ bind_rdataset(search->rbtdb, node, search->zonecut_rdataset,
+ search->now, rdataset);
+ if (sigrdataset != NULL && search->zonecut_sigrdataset != NULL)
+ bind_rdataset(search->rbtdb, node,
+ search->zonecut_sigrdataset,
+ search->now, sigrdataset);
+ NODE_UNLOCK(&(search->rbtdb->node_locks[node->locknum].lock),
+ isc_rwlocktype_read);
+ }
+
+ if (type == dns_rdatatype_dname)
+ return (DNS_R_DNAME);
+ return (DNS_R_DELEGATION);
+}
+
+static inline isc_boolean_t
+valid_glue(rbtdb_search_t *search, dns_name_t *name, rbtdb_rdatatype_t type,
+ dns_rbtnode_t *node)
+{
+ unsigned char *raw; /* RDATASLAB */
+ unsigned int count, size;
+ dns_name_t ns_name;
+ isc_boolean_t valid = ISC_FALSE;
+ dns_offsets_t offsets;
+ isc_region_t region;
+ rdatasetheader_t *header;
+
+ /*
+ * No additional locking is required.
+ */
+
+ /*
+ * Valid glue types are A, AAAA, A6. NS is also a valid glue type
+ * if it occurs at a zone cut, but is not valid below it.
+ */
+ if (type == dns_rdatatype_ns) {
+ if (node != search->zonecut) {
+ return (ISC_FALSE);
+ }
+ } else if (type != dns_rdatatype_a &&
+ type != dns_rdatatype_aaaa &&
+ type != dns_rdatatype_a6) {
+ return (ISC_FALSE);
+ }
+
+ header = search->zonecut_rdataset;
+ raw = (unsigned char *)header + sizeof(*header);
+ count = raw[0] * 256 + raw[1];
+#if DNS_RDATASET_FIXED
+ raw += 2 + (4 * count);
+#else
+ raw += 2;
+#endif
+
+ while (count > 0) {
+ count--;
+ size = raw[0] * 256 + raw[1];
+#if DNS_RDATASET_FIXED
+ raw += 4;
+#else
+ raw += 2;
+#endif
+ region.base = raw;
+ region.length = size;
+ raw += size;
+ /*
+ * XXX Until we have rdata structures, we have no choice but
+ * to directly access the rdata format.
+ */
+ dns_name_init(&ns_name, offsets);
+ dns_name_fromregion(&ns_name, &region);
+ if (dns_name_compare(&ns_name, name) == 0) {
+ valid = ISC_TRUE;
+ break;
+ }
+ }
+
+ return (valid);
+}
+
+static inline isc_boolean_t
+activeempty(rbtdb_search_t *search, dns_rbtnodechain_t *chain,
+ dns_name_t *name)
+{
+ dns_fixedname_t fnext;
+ dns_fixedname_t forigin;
+ dns_name_t *next;
+ dns_name_t *origin;
+ dns_name_t prefix;
+ dns_rbtdb_t *rbtdb;
+ dns_rbtnode_t *node;
+ isc_result_t result;
+ isc_boolean_t answer = ISC_FALSE;
+ rdatasetheader_t *header;
+
+ rbtdb = search->rbtdb;
+
+ dns_name_init(&prefix, NULL);
+ dns_fixedname_init(&fnext);
+ next = dns_fixedname_name(&fnext);
+ dns_fixedname_init(&forigin);
+ origin = dns_fixedname_name(&forigin);
+
+ result = dns_rbtnodechain_next(chain, NULL, NULL);
+ while (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) {
+ node = NULL;
+ result = dns_rbtnodechain_current(chain, &prefix,
+ origin, &node);
+ if (result != ISC_R_SUCCESS)
+ break;
+ NODE_LOCK(&(rbtdb->node_locks[node->locknum].lock),
+ isc_rwlocktype_read);
+ for (header = node->data;
+ header != NULL;
+ header = header->next) {
+ if (header->serial <= search->serial &&
+ !IGNORE(header) && EXISTS(header))
+ break;
+ }
+ NODE_UNLOCK(&(rbtdb->node_locks[node->locknum].lock),
+ isc_rwlocktype_read);
+ if (header != NULL)
+ break;
+ result = dns_rbtnodechain_next(chain, NULL, NULL);
+ }
+ if (result == ISC_R_SUCCESS)
+ result = dns_name_concatenate(&prefix, origin, next, NULL);
+ if (result == ISC_R_SUCCESS && dns_name_issubdomain(next, name))
+ answer = ISC_TRUE;
+ return (answer);
+}
+
+static inline isc_boolean_t
+activeemtpynode(rbtdb_search_t *search, dns_name_t *qname, dns_name_t *wname) {
+ dns_fixedname_t fnext;
+ dns_fixedname_t forigin;
+ dns_fixedname_t fprev;
+ dns_name_t *next;
+ dns_name_t *origin;
+ dns_name_t *prev;
+ dns_name_t name;
+ dns_name_t rname;
+ dns_name_t tname;
+ dns_rbtdb_t *rbtdb;
+ dns_rbtnode_t *node;
+ dns_rbtnodechain_t chain;
+ isc_boolean_t check_next = ISC_TRUE;
+ isc_boolean_t check_prev = ISC_TRUE;
+ isc_boolean_t answer = ISC_FALSE;
+ isc_result_t result;
+ rdatasetheader_t *header;
+ unsigned int n;
+
+ rbtdb = search->rbtdb;
+
+ dns_name_init(&name, NULL);
+ dns_name_init(&tname, NULL);
+ dns_name_init(&rname, NULL);
+ dns_fixedname_init(&fnext);
+ next = dns_fixedname_name(&fnext);
+ dns_fixedname_init(&fprev);
+ prev = dns_fixedname_name(&fprev);
+ dns_fixedname_init(&forigin);
+ origin = dns_fixedname_name(&forigin);
+
+ /*
+ * Find if qname is at or below a empty node.
+ * Use our own copy of the chain.
+ */
+
+ chain = search->chain;
+ do {
+ node = NULL;
+ result = dns_rbtnodechain_current(&chain, &name,
+ origin, &node);
+ if (result != ISC_R_SUCCESS)
+ break;
+ NODE_LOCK(&(rbtdb->node_locks[node->locknum].lock),
+ isc_rwlocktype_read);
+ for (header = node->data;
+ header != NULL;
+ header = header->next) {
+ if (header->serial <= search->serial &&
+ !IGNORE(header) && EXISTS(header))
+ break;
+ }
+ NODE_UNLOCK(&(rbtdb->node_locks[node->locknum].lock),
+ isc_rwlocktype_read);
+ if (header != NULL)
+ break;
+ result = dns_rbtnodechain_prev(&chain, NULL, NULL);
+ } while (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN);
+ if (result == ISC_R_SUCCESS)
+ result = dns_name_concatenate(&name, origin, prev, NULL);
+ if (result != ISC_R_SUCCESS)
+ check_prev = ISC_FALSE;
+
+ result = dns_rbtnodechain_next(&chain, NULL, NULL);
+ while (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) {
+ node = NULL;
+ result = dns_rbtnodechain_current(&chain, &name,
+ origin, &node);
+ if (result != ISC_R_SUCCESS)
+ break;
+ NODE_LOCK(&(rbtdb->node_locks[node->locknum].lock),
+ isc_rwlocktype_read);
+ for (header = node->data;
+ header != NULL;
+ header = header->next) {
+ if (header->serial <= search->serial &&
+ !IGNORE(header) && EXISTS(header))
+ break;
+ }
+ NODE_UNLOCK(&(rbtdb->node_locks[node->locknum].lock),
+ isc_rwlocktype_read);
+ if (header != NULL)
+ break;
+ result = dns_rbtnodechain_next(&chain, NULL, NULL);
+ }
+ if (result == ISC_R_SUCCESS)
+ result = dns_name_concatenate(&name, origin, next, NULL);
+ if (result != ISC_R_SUCCESS)
+ check_next = ISC_FALSE;
+
+ dns_name_clone(qname, &rname);
+
+ /*
+ * Remove the wildcard label to find the terminal name.
+ */
+ n = dns_name_countlabels(wname);
+ dns_name_getlabelsequence(wname, 1, n - 1, &tname);
+
+ do {
+ if ((check_prev && dns_name_issubdomain(prev, &rname)) ||
+ (check_next && dns_name_issubdomain(next, &rname))) {
+ answer = ISC_TRUE;
+ break;
+ }
+ /*
+ * Remove the left hand label.
+ */
+ n = dns_name_countlabels(&rname);
+ dns_name_getlabelsequence(&rname, 1, n - 1, &rname);
+ } while (!dns_name_equal(&rname, &tname));
+ return (answer);
+}
+
+static inline isc_result_t
+find_wildcard(rbtdb_search_t *search, dns_rbtnode_t **nodep,
+ dns_name_t *qname)
+{
+ unsigned int i, j;
+ dns_rbtnode_t *node, *level_node, *wnode;
+ rdatasetheader_t *header;
+ isc_result_t result = ISC_R_NOTFOUND;
+ dns_name_t name;
+ dns_name_t *wname;
+ dns_fixedname_t fwname;
+ dns_rbtdb_t *rbtdb;
+ isc_boolean_t done, wild, active;
+ dns_rbtnodechain_t wchain;
+
+ /*
+ * Caller must be holding the tree lock and MUST NOT be holding
+ * any node locks.
+ */
+
+ /*
+ * Examine each ancestor level. If the level's wild bit
+ * is set, then construct the corresponding wildcard name and
+ * search for it. If the wildcard node exists, and is active in
+ * this version, we're done. If not, then we next check to see
+ * if the ancestor is active in this version. If so, then there
+ * can be no possible wildcard match and again we're done. If not,
+ * continue the search.
+ */
+
+ rbtdb = search->rbtdb;
+ i = search->chain.level_matches;
+ done = ISC_FALSE;
+ node = *nodep;
+ do {
+ NODE_LOCK(&(rbtdb->node_locks[node->locknum].lock),
+ isc_rwlocktype_read);
+
+ /*
+ * First we try to figure out if this node is active in
+ * the search's version. We do this now, even though we
+ * may not need the information, because it simplifies the
+ * locking and code flow.
+ */
+ for (header = node->data;
+ header != NULL;
+ header = header->next) {
+ if (header->serial <= search->serial &&
+ !IGNORE(header) && EXISTS(header))
+ break;
+ }
+ if (header != NULL)
+ active = ISC_TRUE;
+ else
+ active = ISC_FALSE;
+
+ if (node->wild)
+ wild = ISC_TRUE;
+ else
+ wild = ISC_FALSE;
+
+ NODE_UNLOCK(&(rbtdb->node_locks[node->locknum].lock),
+ isc_rwlocktype_read);
+
+ if (wild) {
+ /*
+ * Construct the wildcard name for this level.
+ */
+ dns_name_init(&name, NULL);
+ dns_rbt_namefromnode(node, &name);
+ dns_fixedname_init(&fwname);
+ wname = dns_fixedname_name(&fwname);
+ result = dns_name_concatenate(dns_wildcardname, &name,
+ wname, NULL);
+ j = i;
+ while (result == ISC_R_SUCCESS && j != 0) {
+ j--;
+ level_node = search->chain.levels[j];
+ dns_name_init(&name, NULL);
+ dns_rbt_namefromnode(level_node, &name);
+ result = dns_name_concatenate(wname,
+ &name,
+ wname,
+ NULL);
+ }
+ if (result != ISC_R_SUCCESS)
+ break;
+
+ wnode = NULL;
+ dns_rbtnodechain_init(&wchain, NULL);
+ result = dns_rbt_findnode(rbtdb->tree, wname,
+ NULL, &wnode, &wchain,
+ DNS_RBTFIND_EMPTYDATA,
+ NULL, NULL);
+ if (result == ISC_R_SUCCESS) {
+ nodelock_t *lock;
+
+ /*
+ * We have found the wildcard node. If it
+ * is active in the search's version, we're
+ * done.
+ */
+ lock = &rbtdb->node_locks[wnode->locknum].lock;
+ NODE_LOCK(lock, isc_rwlocktype_read);
+ for (header = wnode->data;
+ header != NULL;
+ header = header->next) {
+ if (header->serial <= search->serial &&
+ !IGNORE(header) && EXISTS(header))
+ break;
+ }
+ NODE_UNLOCK(lock, isc_rwlocktype_read);
+ if (header != NULL ||
+ activeempty(search, &wchain, wname)) {
+ if (activeemtpynode(search, qname,
+ wname)) {
+ return (ISC_R_NOTFOUND);
+ }
+ /*
+ * The wildcard node is active!
+ *
+ * Note: result is still ISC_R_SUCCESS
+ * so we don't have to set it.
+ */
+ *nodep = wnode;
+ break;
+ }
+ } else if (result != ISC_R_NOTFOUND &&
+ result != DNS_R_PARTIALMATCH) {
+ /*
+ * An error has occurred. Bail out.
+ */
+ break;
+ }
+ }
+
+ if (active) {
+ /*
+ * The level node is active. Any wildcarding
+ * present at higher levels has no
+ * effect and we're done.
+ */
+ result = ISC_R_NOTFOUND;
+ break;
+ }
+
+ if (i > 0) {
+ i--;
+ node = search->chain.levels[i];
+ } else
+ done = ISC_TRUE;
+ } while (!done);
+
+ return (result);
+}
+
+static isc_boolean_t
+matchparams(rdatasetheader_t *header, rbtdb_search_t *search)
+{
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_nsec3_t nsec3;
+ unsigned char *raw; /* RDATASLAB */
+ unsigned int rdlen, count;
+ isc_region_t region;
+ isc_result_t result;
+
+ REQUIRE(header->type == dns_rdatatype_nsec3);
+
+ raw = (unsigned char *)header + sizeof(*header);
+ count = raw[0] * 256 + raw[1]; /* count */
+#if DNS_RDATASET_FIXED
+ raw += count * 4 + 2;
+#else
+ raw += 2;
+#endif
+ while (count-- > 0) {
+ rdlen = raw[0] * 256 + raw[1];
+#if DNS_RDATASET_FIXED
+ raw += 4;
+#else
+ raw += 2;
+#endif
+ region.base = raw;
+ region.length = rdlen;
+ dns_rdata_fromregion(&rdata, search->rbtdb->common.rdclass,
+ dns_rdatatype_nsec3, &region);
+ raw += rdlen;
+ result = dns_rdata_tostruct(&rdata, &nsec3, NULL);
+ INSIST(result == ISC_R_SUCCESS);
+ if (nsec3.hash == search->rbtversion->hash &&
+ nsec3.iterations == search->rbtversion->iterations &&
+ nsec3.salt_length == search->rbtversion->salt_length &&
+ memcmp(nsec3.salt, search->rbtversion->salt,
+ nsec3.salt_length) == 0)
+ return (ISC_TRUE);
+ dns_rdata_reset(&rdata);
+ }
+ return (ISC_FALSE);
+}
+
+static inline isc_result_t
+find_closest_nsec(rbtdb_search_t *search, dns_dbnode_t **nodep,
+ dns_name_t *foundname, dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset, dns_rbt_t *tree,
+ dns_db_secure_t secure)
+{
+ dns_rbtnode_t *node;
+ rdatasetheader_t *header, *header_next, *found, *foundsig;
+ isc_boolean_t empty_node;
+ isc_result_t result;
+ dns_fixedname_t fname, forigin;
+ dns_name_t *name, *origin;
+ dns_rdatatype_t type;
+ rbtdb_rdatatype_t sigtype;
+ isc_boolean_t wraps;
+ isc_boolean_t need_sig = ISC_TF(secure == dns_db_secure);
+
+ if (tree == search->rbtdb->nsec3) {
+ type = dns_rdatatype_nsec3;
+ sigtype = RBTDB_RDATATYPE_SIGNSEC3;
+ wraps = ISC_TRUE;
+ } else {
+ type = dns_rdatatype_nsec;
+ sigtype = RBTDB_RDATATYPE_SIGNSEC;
+ wraps = ISC_FALSE;
+ }
+
+ again:
+ do {
+ node = NULL;
+ dns_fixedname_init(&fname);
+ name = dns_fixedname_name(&fname);
+ dns_fixedname_init(&forigin);
+ origin = dns_fixedname_name(&forigin);
+ result = dns_rbtnodechain_current(&search->chain, name,
+ origin, &node);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ NODE_LOCK(&(search->rbtdb->node_locks[node->locknum].lock),
+ isc_rwlocktype_read);
+ found = NULL;
+ foundsig = NULL;
+ empty_node = ISC_TRUE;
+ for (header = node->data;
+ header != NULL;
+ header = header_next) {
+ header_next = header->next;
+ /*
+ * Look for an active, extant NSEC or RRSIG NSEC.
+ */
+ do {
+ if (header->serial <= search->serial &&
+ !IGNORE(header)) {
+ /*
+ * Is this a "this rdataset doesn't
+ * exist" record?
+ */
+ if (NONEXISTENT(header))
+ header = NULL;
+ break;
+ } else
+ header = header->down;
+ } while (header != NULL);
+ if (header != NULL) {
+ /*
+ * We now know that there is at least one
+ * active rdataset at this node.
+ */
+ empty_node = ISC_FALSE;
+ if (header->type == type) {
+ found = header;
+ if (foundsig != NULL)
+ break;
+ } else if (header->type == sigtype) {
+ foundsig = header;
+ if (found != NULL)
+ break;
+ }
+ }
+ }
+ if (!empty_node) {
+ if (found != NULL && search->rbtversion->havensec3 &&
+ found->type == dns_rdatatype_nsec3 &&
+ !matchparams(found, search)) {
+ empty_node = ISC_TRUE;
+ found = NULL;
+ foundsig = NULL;
+ result = dns_rbtnodechain_prev(&search->chain,
+ NULL, NULL);
+ } else if (found != NULL &&
+ (foundsig != NULL || !need_sig))
+ {
+ /*
+ * We've found the right NSEC/NSEC3 record.
+ *
+ * Note: for this to really be the right
+ * NSEC record, it's essential that the NSEC
+ * records of any nodes obscured by a zone
+ * cut have been removed; we assume this is
+ * the case.
+ */
+ result = dns_name_concatenate(name, origin,
+ foundname, NULL);
+ if (result == ISC_R_SUCCESS) {
+ if (nodep != NULL) {
+ new_reference(search->rbtdb,
+ node);
+ *nodep = node;
+ }
+ bind_rdataset(search->rbtdb, node,
+ found, search->now,
+ rdataset);
+ if (foundsig != NULL)
+ bind_rdataset(search->rbtdb,
+ node,
+ foundsig,
+ search->now,
+ sigrdataset);
+ }
+ } else if (found == NULL && foundsig == NULL) {
+ /*
+ * This node is active, but has no NSEC or
+ * RRSIG NSEC. That means it's glue or
+ * other obscured zone data that isn't
+ * relevant for our search. Treat the
+ * node as if it were empty and keep looking.
+ */
+ empty_node = ISC_TRUE;
+ result = dns_rbtnodechain_prev(&search->chain,
+ NULL, NULL);
+ } else {
+ /*
+ * We found an active node, but either the
+ * NSEC or the RRSIG NSEC is missing. This
+ * shouldn't happen.
+ */
+ result = DNS_R_BADDB;
+ }
+ } else {
+ /*
+ * This node isn't active. We've got to keep
+ * looking.
+ */
+ result = dns_rbtnodechain_prev(&search->chain, NULL,
+ NULL);
+ }
+ NODE_UNLOCK(&(search->rbtdb->node_locks[node->locknum].lock),
+ isc_rwlocktype_read);
+ } while (empty_node && result == ISC_R_SUCCESS);
+
+ if (result == ISC_R_NOMORE && wraps) {
+ result = dns_rbtnodechain_last(&search->chain, tree,
+ NULL, NULL);
+ if (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) {
+ wraps = ISC_FALSE;
+ goto again;
+ }
+ }
+
+ /*
+ * If the result is ISC_R_NOMORE, then we got to the beginning of
+ * the database and didn't find a NSEC record. This shouldn't
+ * happen.
+ */
+ if (result == ISC_R_NOMORE)
+ result = DNS_R_BADDB;
+
+ return (result);
+}
+
+static isc_result_t
+zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
+ dns_rdatatype_t type, unsigned int options, isc_stdtime_t now,
+ dns_dbnode_t **nodep, dns_name_t *foundname,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+ dns_rbtnode_t *node = NULL;
+ isc_result_t result;
+ rbtdb_search_t search;
+ isc_boolean_t cname_ok = ISC_TRUE;
+ isc_boolean_t close_version = ISC_FALSE;
+ isc_boolean_t maybe_zonecut = ISC_FALSE;
+ isc_boolean_t at_zonecut = ISC_FALSE;
+ isc_boolean_t wild;
+ isc_boolean_t empty_node;
+ rdatasetheader_t *header, *header_next, *found, *nsecheader;
+ rdatasetheader_t *foundsig, *cnamesig, *nsecsig;
+ rbtdb_rdatatype_t sigtype;
+ isc_boolean_t active;
+ dns_rbtnodechain_t chain;
+ nodelock_t *lock;
+ dns_rbt_t *tree;
+
+ search.rbtdb = (dns_rbtdb_t *)db;
+
+ REQUIRE(VALID_RBTDB(search.rbtdb));
+
+ /*
+ * We don't care about 'now'.
+ */
+ UNUSED(now);
+
+ /*
+ * If the caller didn't supply a version, attach to the current
+ * version.
+ */
+ if (version == NULL) {
+ currentversion(db, &version);
+ close_version = ISC_TRUE;
+ }
+
+ search.rbtversion = version;
+ search.serial = search.rbtversion->serial;
+ search.options = options;
+ search.copy_name = ISC_FALSE;
+ search.need_cleanup = ISC_FALSE;
+ search.wild = ISC_FALSE;
+ search.zonecut = NULL;
+ dns_fixedname_init(&search.zonecut_name);
+ dns_rbtnodechain_init(&search.chain, search.rbtdb->common.mctx);
+ search.now = 0;
+
+ /*
+ * 'wild' will be true iff. we've matched a wildcard.
+ */
+ wild = ISC_FALSE;
+
+ RWLOCK(&search.rbtdb->tree_lock, isc_rwlocktype_read);
+
+ /*
+ * Search down from the root of the tree. If, while going down, we
+ * encounter a callback node, zone_zonecut_callback() will search the
+ * rdatasets at the zone cut for active DNAME or NS rdatasets.
+ */
+ tree = (options & DNS_DBFIND_FORCENSEC3) != 0 ? search.rbtdb->nsec3 :
+ search.rbtdb->tree;
+ result = dns_rbt_findnode(tree, name, foundname, &node,
+ &search.chain, DNS_RBTFIND_EMPTYDATA,
+ zone_zonecut_callback, &search);
+
+ if (result == DNS_R_PARTIALMATCH) {
+ partial_match:
+ if (search.zonecut != NULL) {
+ result = setup_delegation(&search, nodep, foundname,
+ rdataset, sigrdataset);
+ goto tree_exit;
+ }
+
+ if (search.wild) {
+ /*
+ * At least one of the levels in the search chain
+ * potentially has a wildcard. For each such level,
+ * we must see if there's a matching wildcard active
+ * in the current version.
+ */
+ result = find_wildcard(&search, &node, name);
+ if (result == ISC_R_SUCCESS) {
+ result = dns_name_copy(name, foundname, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto tree_exit;
+ wild = ISC_TRUE;
+ goto found;
+ }
+ else if (result != ISC_R_NOTFOUND)
+ goto tree_exit;
+ }
+
+ chain = search.chain;
+ active = activeempty(&search, &chain, name);
+
+ /*
+ * If we're here, then the name does not exist, is not
+ * beneath a zonecut, and there's no matching wildcard.
+ */
+ if ((search.rbtversion->secure == dns_db_secure &&
+ !search.rbtversion->havensec3) ||
+ (search.options & DNS_DBFIND_FORCENSEC) != 0 ||
+ (search.options & DNS_DBFIND_FORCENSEC3) != 0)
+ {
+ result = find_closest_nsec(&search, nodep, foundname,
+ rdataset, sigrdataset, tree,
+ search.rbtversion->secure);
+ if (result == ISC_R_SUCCESS)
+ result = active ? DNS_R_EMPTYNAME :
+ DNS_R_NXDOMAIN;
+ } else
+ result = active ? DNS_R_EMPTYNAME : DNS_R_NXDOMAIN;
+ goto tree_exit;
+ } else if (result != ISC_R_SUCCESS)
+ goto tree_exit;
+
+ found:
+ /*
+ * We have found a node whose name is the desired name, or we
+ * have matched a wildcard.
+ */
+
+ if (search.zonecut != NULL) {
+ /*
+ * If we're beneath a zone cut, we don't want to look for
+ * CNAMEs because they're not legitimate zone glue.
+ */
+ cname_ok = ISC_FALSE;
+ } else {
+ /*
+ * The node may be a zone cut itself. If it might be one,
+ * make sure we check for it later.
+ */
+ if (node->find_callback &&
+ (node != search.rbtdb->origin_node ||
+ IS_STUB(search.rbtdb)) &&
+ !dns_rdatatype_atparent(type))
+ maybe_zonecut = ISC_TRUE;
+ }
+
+ /*
+ * Certain DNSSEC types are not subject to CNAME matching
+ * (RFC4035, section 2.5 and RFC3007).
+ *
+ * We don't check for RRSIG, because we don't store RRSIG records
+ * directly.
+ */
+ if (type == dns_rdatatype_key || type == dns_rdatatype_nsec)
+ cname_ok = ISC_FALSE;
+
+ /*
+ * We now go looking for rdata...
+ */
+
+ NODE_LOCK(&(search.rbtdb->node_locks[node->locknum].lock),
+ isc_rwlocktype_read);
+
+ found = NULL;
+ foundsig = NULL;
+ sigtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, type);
+ nsecheader = NULL;
+ nsecsig = NULL;
+ cnamesig = NULL;
+ empty_node = ISC_TRUE;
+ for (header = node->data; header != NULL; header = header_next) {
+ header_next = header->next;
+ /*
+ * Look for an active, extant rdataset.
+ */
+ do {
+ if (header->serial <= search.serial &&
+ !IGNORE(header)) {
+ /*
+ * Is this a "this rdataset doesn't
+ * exist" record?
+ */
+ if (NONEXISTENT(header))
+ header = NULL;
+ break;
+ } else
+ header = header->down;
+ } while (header != NULL);
+ if (header != NULL) {
+ /*
+ * We now know that there is at least one active
+ * rdataset at this node.
+ */
+ empty_node = ISC_FALSE;
+
+ /*
+ * Do special zone cut handling, if requested.
+ */
+ if (maybe_zonecut &&
+ header->type == dns_rdatatype_ns) {
+ /*
+ * We increment the reference count on node to
+ * ensure that search->zonecut_rdataset will
+ * still be valid later.
+ */
+ new_reference(search.rbtdb, node);
+ search.zonecut = node;
+ search.zonecut_rdataset = header;
+ search.zonecut_sigrdataset = NULL;
+ search.need_cleanup = ISC_TRUE;
+ maybe_zonecut = ISC_FALSE;
+ at_zonecut = ISC_TRUE;
+ /*
+ * It is not clear if KEY should still be
+ * allowed at the parent side of the zone
+ * cut or not. It is needed for RFC3007
+ * validated updates.
+ */
+ if ((search.options & DNS_DBFIND_GLUEOK) == 0
+ && type != dns_rdatatype_nsec
+ && type != dns_rdatatype_key) {
+ /*
+ * Glue is not OK, but any answer we
+ * could return would be glue. Return
+ * the delegation.
+ */
+ found = NULL;
+ break;
+ }
+ if (found != NULL && foundsig != NULL)
+ break;
+ }
+
+
+ /*
+ * If the NSEC3 record doesn't match the chain
+ * we are using behave as if it isn't here.
+ */
+ if (header->type == dns_rdatatype_nsec3 &&
+ !matchparams(header, &search))
+ goto partial_match;
+ /*
+ * If we found a type we were looking for,
+ * remember it.
+ */
+ if (header->type == type ||
+ type == dns_rdatatype_any ||
+ (header->type == dns_rdatatype_cname &&
+ cname_ok)) {
+ /*
+ * We've found the answer!
+ */
+ found = header;
+ if (header->type == dns_rdatatype_cname &&
+ cname_ok) {
+ /*
+ * We may be finding a CNAME instead
+ * of the desired type.
+ *
+ * If we've already got the CNAME RRSIG,
+ * use it, otherwise change sigtype
+ * so that we find it.
+ */
+ if (cnamesig != NULL)
+ foundsig = cnamesig;
+ else
+ sigtype =
+ RBTDB_RDATATYPE_SIGCNAME;
+ }
+ /*
+ * If we've got all we need, end the search.
+ */
+ if (!maybe_zonecut && foundsig != NULL)
+ break;
+ } else if (header->type == sigtype) {
+ /*
+ * We've found the RRSIG rdataset for our
+ * target type. Remember it.
+ */
+ foundsig = header;
+ /*
+ * If we've got all we need, end the search.
+ */
+ if (!maybe_zonecut && found != NULL)
+ break;
+ } else if (header->type == dns_rdatatype_nsec &&
+ !search.rbtversion->havensec3) {
+ /*
+ * Remember a NSEC rdataset even if we're
+ * not specifically looking for it, because
+ * we might need it later.
+ */
+ nsecheader = header;
+ } else if (header->type == RBTDB_RDATATYPE_SIGNSEC &&
+ !search.rbtversion->havensec3) {
+ /*
+ * If we need the NSEC rdataset, we'll also
+ * need its signature.
+ */
+ nsecsig = header;
+ } else if (cname_ok &&
+ header->type == RBTDB_RDATATYPE_SIGCNAME) {
+ /*
+ * If we get a CNAME match, we'll also need
+ * its signature.
+ */
+ cnamesig = header;
+ }
+ }
+ }
+
+ if (empty_node) {
+ /*
+ * We have an exact match for the name, but there are no
+ * active rdatasets in the desired version. That means that
+ * this node doesn't exist in the desired version, and that
+ * we really have a partial match.
+ */
+ if (!wild) {
+ lock = &search.rbtdb->node_locks[node->locknum].lock;
+ NODE_UNLOCK(lock, isc_rwlocktype_read);
+ goto partial_match;
+ }
+ }
+
+ /*
+ * If we didn't find what we were looking for...
+ */
+ if (found == NULL) {
+ if (search.zonecut != NULL) {
+ /*
+ * We were trying to find glue at a node beneath a
+ * zone cut, but didn't.
+ *
+ * Return the delegation.
+ */
+ lock = &search.rbtdb->node_locks[node->locknum].lock;
+ NODE_UNLOCK(lock, isc_rwlocktype_read);
+ result = setup_delegation(&search, nodep, foundname,
+ rdataset, sigrdataset);
+ goto tree_exit;
+ }
+ /*
+ * The desired type doesn't exist.
+ */
+ result = DNS_R_NXRRSET;
+ if (search.rbtversion->secure == dns_db_secure &&
+ !search.rbtversion->havensec3 &&
+ (nsecheader == NULL || nsecsig == NULL)) {
+ /*
+ * The zone is secure but there's no NSEC,
+ * or the NSEC has no signature!
+ */
+ if (!wild) {
+ result = DNS_R_BADDB;
+ goto node_exit;
+ }
+
+ lock = &search.rbtdb->node_locks[node->locknum].lock;
+ NODE_UNLOCK(lock, isc_rwlocktype_read);
+ result = find_closest_nsec(&search, nodep, foundname,
+ rdataset, sigrdataset,
+ search.rbtdb->tree,
+ search.rbtversion->secure);
+ if (result == ISC_R_SUCCESS)
+ result = DNS_R_EMPTYWILD;
+ goto tree_exit;
+ }
+ if ((search.options & DNS_DBFIND_FORCENSEC) != 0 &&
+ nsecheader == NULL)
+ {
+ /*
+ * There's no NSEC record, and we were told
+ * to find one.
+ */
+ result = DNS_R_BADDB;
+ goto node_exit;
+ }
+ if (nodep != NULL) {
+ new_reference(search.rbtdb, node);
+ *nodep = node;
+ }
+ if ((search.rbtversion->secure == dns_db_secure &&
+ !search.rbtversion->havensec3) ||
+ (search.options & DNS_DBFIND_FORCENSEC) != 0)
+ {
+ bind_rdataset(search.rbtdb, node, nsecheader,
+ 0, rdataset);
+ if (nsecsig != NULL)
+ bind_rdataset(search.rbtdb, node,
+ nsecsig, 0, sigrdataset);
+ }
+ if (wild)
+ foundname->attributes |= DNS_NAMEATTR_WILDCARD;
+ goto node_exit;
+ }
+
+ /*
+ * We found what we were looking for, or we found a CNAME.
+ */
+
+ if (type != found->type &&
+ type != dns_rdatatype_any &&
+ found->type == dns_rdatatype_cname) {
+ /*
+ * We weren't doing an ANY query and we found a CNAME instead
+ * of the type we were looking for, so we need to indicate
+ * that result to the caller.
+ */
+ result = DNS_R_CNAME;
+ } else if (search.zonecut != NULL) {
+ /*
+ * If we're beneath a zone cut, we must indicate that the
+ * result is glue, unless we're actually at the zone cut
+ * and the type is NSEC or KEY.
+ */
+ if (search.zonecut == node) {
+ /*
+ * It is not clear if KEY should still be
+ * allowed at the parent side of the zone
+ * cut or not. It is needed for RFC3007
+ * validated updates.
+ */
+ if (type == dns_rdatatype_nsec ||
+ type == dns_rdatatype_nsec3 ||
+ type == dns_rdatatype_key)
+ result = ISC_R_SUCCESS;
+ else if (type == dns_rdatatype_any)
+ result = DNS_R_ZONECUT;
+ else
+ result = DNS_R_GLUE;
+ } else
+ result = DNS_R_GLUE;
+ /*
+ * We might have found data that isn't glue, but was occluded
+ * by a dynamic update. If the caller cares about this, they
+ * will have told us to validate glue.
+ *
+ * XXX We should cache the glue validity state!
+ */
+ if (result == DNS_R_GLUE &&
+ (search.options & DNS_DBFIND_VALIDATEGLUE) != 0 &&
+ !valid_glue(&search, foundname, type, node)) {
+ lock = &search.rbtdb->node_locks[node->locknum].lock;
+ NODE_UNLOCK(lock, isc_rwlocktype_read);
+ result = setup_delegation(&search, nodep, foundname,
+ rdataset, sigrdataset);
+ goto tree_exit;
+ }
+ } else {
+ /*
+ * An ordinary successful query!
+ */
+ result = ISC_R_SUCCESS;
+ }
+
+ if (nodep != NULL) {
+ if (!at_zonecut)
+ new_reference(search.rbtdb, node);
+ else
+ search.need_cleanup = ISC_FALSE;
+ *nodep = node;
+ }
+
+ if (type != dns_rdatatype_any) {
+ bind_rdataset(search.rbtdb, node, found, 0, rdataset);
+ if (foundsig != NULL)
+ bind_rdataset(search.rbtdb, node, foundsig, 0,
+ sigrdataset);
+ }
+
+ if (wild)
+ foundname->attributes |= DNS_NAMEATTR_WILDCARD;
+
+ node_exit:
+ NODE_UNLOCK(&(search.rbtdb->node_locks[node->locknum].lock),
+ isc_rwlocktype_read);
+
+ tree_exit:
+ RWUNLOCK(&search.rbtdb->tree_lock, isc_rwlocktype_read);
+
+ /*
+ * If we found a zonecut but aren't going to use it, we have to
+ * let go of it.
+ */
+ if (search.need_cleanup) {
+ node = search.zonecut;
+ lock = &(search.rbtdb->node_locks[node->locknum].lock);
+
+ NODE_LOCK(lock, isc_rwlocktype_read);
+ decrement_reference(search.rbtdb, node, 0,
+ isc_rwlocktype_read, isc_rwlocktype_none);
+ NODE_UNLOCK(lock, isc_rwlocktype_read);
+ }
+
+ if (close_version)
+ closeversion(db, &version, ISC_FALSE);
+
+ dns_rbtnodechain_reset(&search.chain);
+
+ return (result);
+}
+
+static isc_result_t
+zone_findzonecut(dns_db_t *db, dns_name_t *name, unsigned int options,
+ isc_stdtime_t now, dns_dbnode_t **nodep,
+ dns_name_t *foundname,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+ UNUSED(db);
+ UNUSED(name);
+ UNUSED(options);
+ UNUSED(now);
+ UNUSED(nodep);
+ UNUSED(foundname);
+ UNUSED(rdataset);
+ UNUSED(sigrdataset);
+
+ FATAL_ERROR(__FILE__, __LINE__, "zone_findzonecut() called!");
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static isc_result_t
+cache_zonecut_callback(dns_rbtnode_t *node, dns_name_t *name, void *arg) {
+ rbtdb_search_t *search = arg;
+ rdatasetheader_t *header, *header_prev, *header_next;
+ rdatasetheader_t *dname_header, *sigdname_header;
+ isc_result_t result;
+ nodelock_t *lock;
+ isc_rwlocktype_t locktype;
+
+ /* XXX comment */
+
+ REQUIRE(search->zonecut == NULL);
+
+ /*
+ * Keep compiler silent.
+ */
+ UNUSED(name);
+
+ lock = &(search->rbtdb->node_locks[node->locknum].lock);
+ locktype = isc_rwlocktype_read;
+ NODE_LOCK(lock, locktype);
+
+ /*
+ * Look for a DNAME or RRSIG DNAME rdataset.
+ */
+ dname_header = NULL;
+ sigdname_header = NULL;
+ header_prev = NULL;
+ for (header = node->data; header != NULL; header = header_next) {
+ header_next = header->next;
+ if (header->rdh_ttl <= search->now) {
+ /*
+ * This rdataset is stale. If no one else is
+ * using the node, we can clean it up right
+ * now, otherwise we mark it as stale, and
+ * the node as dirty, so it will get cleaned
+ * up later.
+ */
+ if ((header->rdh_ttl <= search->now - RBTDB_VIRTUAL) &&
+ (locktype == isc_rwlocktype_write ||
+ NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) {
+ /*
+ * We update the node's status only when we
+ * can get write access; otherwise, we leave
+ * others to this work. Periodical cleaning
+ * will eventually take the job as the last
+ * resort.
+ * We won't downgrade the lock, since other
+ * rdatasets are probably stale, too.
+ */
+ locktype = isc_rwlocktype_write;
+
+ if (dns_rbtnode_refcurrent(node) == 0) {
+ isc_mem_t *mctx;
+
+ /*
+ * header->down can be non-NULL if the
+ * refcount has just decremented to 0
+ * but decrement_reference() has not
+ * performed clean_cache_node(), in
+ * which case we need to purge the
+ * stale headers first.
+ */
+ mctx = search->rbtdb->common.mctx;
+ clean_stale_headers(search->rbtdb,
+ mctx,
+ header);
+ if (header_prev != NULL)
+ header_prev->next =
+ header->next;
+ else
+ node->data = header->next;
+ free_rdataset(search->rbtdb, mctx,
+ header);
+ } else {
+ header->attributes |=
+ RDATASET_ATTR_STALE;
+ node->dirty = 1;
+ header_prev = header;
+ }
+ } else
+ header_prev = header;
+ } else if (header->type == dns_rdatatype_dname &&
+ EXISTS(header)) {
+ dname_header = header;
+ header_prev = header;
+ } else if (header->type == RBTDB_RDATATYPE_SIGDNAME &&
+ EXISTS(header)) {
+ sigdname_header = header;
+ header_prev = header;
+ } else
+ header_prev = header;
+ }
+
+ if (dname_header != NULL &&
+ (dname_header->trust != dns_trust_pending ||
+ (search->options & DNS_DBFIND_PENDINGOK) != 0)) {
+ /*
+ * We increment the reference count on node to ensure that
+ * search->zonecut_rdataset will still be valid later.
+ */
+ new_reference(search->rbtdb, node);
+ INSIST(!ISC_LINK_LINKED(node, deadlink));
+ search->zonecut = node;
+ search->zonecut_rdataset = dname_header;
+ search->zonecut_sigrdataset = sigdname_header;
+ search->need_cleanup = ISC_TRUE;
+ result = DNS_R_PARTIALMATCH;
+ } else
+ result = DNS_R_CONTINUE;
+
+ NODE_UNLOCK(lock, locktype);
+
+ return (result);
+}
+
+static inline isc_result_t
+find_deepest_zonecut(rbtdb_search_t *search, dns_rbtnode_t *node,
+ dns_dbnode_t **nodep, dns_name_t *foundname,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+ unsigned int i;
+ dns_rbtnode_t *level_node;
+ rdatasetheader_t *header, *header_prev, *header_next;
+ rdatasetheader_t *found, *foundsig;
+ isc_result_t result = ISC_R_NOTFOUND;
+ dns_name_t name;
+ dns_rbtdb_t *rbtdb;
+ isc_boolean_t done;
+ nodelock_t *lock;
+ isc_rwlocktype_t locktype;
+
+ /*
+ * Caller must be holding the tree lock.
+ */
+
+ rbtdb = search->rbtdb;
+ i = search->chain.level_matches;
+ done = ISC_FALSE;
+ do {
+ locktype = isc_rwlocktype_read;
+ lock = &rbtdb->node_locks[node->locknum].lock;
+ NODE_LOCK(lock, locktype);
+
+ /*
+ * Look for NS and RRSIG NS rdatasets.
+ */
+ found = NULL;
+ foundsig = NULL;
+ header_prev = NULL;
+ for (header = node->data;
+ header != NULL;
+ header = header_next) {
+ header_next = header->next;
+ if (header->rdh_ttl <= search->now) {
+ /*
+ * This rdataset is stale. If no one else is
+ * using the node, we can clean it up right
+ * now, otherwise we mark it as stale, and
+ * the node as dirty, so it will get cleaned
+ * up later.
+ */
+ if ((header->rdh_ttl <= search->now -
+ RBTDB_VIRTUAL) &&
+ (locktype == isc_rwlocktype_write ||
+ NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) {
+ /*
+ * We update the node's status only
+ * when we can get write access.
+ */
+ locktype = isc_rwlocktype_write;
+
+ if (dns_rbtnode_refcurrent(node)
+ == 0) {
+ isc_mem_t *m;
+
+ m = search->rbtdb->common.mctx;
+ clean_stale_headers(
+ search->rbtdb,
+ m, header);
+ if (header_prev != NULL)
+ header_prev->next =
+ header->next;
+ else
+ node->data =
+ header->next;
+ free_rdataset(rbtdb, m,
+ header);
+ } else {
+ header->attributes |=
+ RDATASET_ATTR_STALE;
+ node->dirty = 1;
+ header_prev = header;
+ }
+ } else
+ header_prev = header;
+ } else if (EXISTS(header)) {
+ /*
+ * We've found an extant rdataset. See if
+ * we're interested in it.
+ */
+ if (header->type == dns_rdatatype_ns) {
+ found = header;
+ if (foundsig != NULL)
+ break;
+ } else if (header->type ==
+ RBTDB_RDATATYPE_SIGNS) {
+ foundsig = header;
+ if (found != NULL)
+ break;
+ }
+ header_prev = header;
+ } else
+ header_prev = header;
+ }
+
+ if (found != NULL) {
+ /*
+ * If we have to set foundname, we do it before
+ * anything else. If we were to set foundname after
+ * we had set nodep or bound the rdataset, then we'd
+ * have to undo that work if dns_name_concatenate()
+ * failed. By setting foundname first, there's
+ * nothing to undo if we have trouble.
+ */
+ if (foundname != NULL) {
+ dns_name_init(&name, NULL);
+ dns_rbt_namefromnode(node, &name);
+ result = dns_name_copy(&name, foundname, NULL);
+ while (result == ISC_R_SUCCESS && i > 0) {
+ i--;
+ level_node = search->chain.levels[i];
+ dns_name_init(&name, NULL);
+ dns_rbt_namefromnode(level_node,
+ &name);
+ result =
+ dns_name_concatenate(foundname,
+ &name,
+ foundname,
+ NULL);
+ }
+ if (result != ISC_R_SUCCESS) {
+ *nodep = NULL;
+ goto node_exit;
+ }
+ }
+ result = DNS_R_DELEGATION;
+ if (nodep != NULL) {
+ new_reference(search->rbtdb, node);
+ *nodep = node;
+ }
+ bind_rdataset(search->rbtdb, node, found, search->now,
+ rdataset);
+ if (foundsig != NULL)
+ bind_rdataset(search->rbtdb, node, foundsig,
+ search->now, sigrdataset);
+ if (need_headerupdate(found, search->now) ||
+ (foundsig != NULL &&
+ need_headerupdate(foundsig, search->now))) {
+ if (locktype != isc_rwlocktype_write) {
+ NODE_UNLOCK(lock, locktype);
+ NODE_LOCK(lock, isc_rwlocktype_write);
+ locktype = isc_rwlocktype_write;
+ }
+ if (need_headerupdate(found, search->now))
+ update_header(search->rbtdb, found,
+ search->now);
+ if (foundsig != NULL &&
+ need_headerupdate(foundsig, search->now)) {
+ update_header(search->rbtdb, foundsig,
+ search->now);
+ }
+ }
+ }
+
+ node_exit:
+ NODE_UNLOCK(lock, locktype);
+
+ if (found == NULL && i > 0) {
+ i--;
+ node = search->chain.levels[i];
+ } else
+ done = ISC_TRUE;
+
+ } while (!done);
+
+ return (result);
+}
+
+static isc_result_t
+find_coveringnsec(rbtdb_search_t *search, dns_dbnode_t **nodep,
+ isc_stdtime_t now, dns_name_t *foundname,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+ dns_rbtnode_t *node;
+ rdatasetheader_t *header, *header_next, *header_prev;
+ rdatasetheader_t *found, *foundsig;
+ isc_boolean_t empty_node;
+ isc_result_t result;
+ dns_fixedname_t fname, forigin;
+ dns_name_t *name, *origin;
+ rbtdb_rdatatype_t matchtype, sigmatchtype;
+ nodelock_t *lock;
+ isc_rwlocktype_t locktype;
+
+ matchtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_nsec, 0);
+ sigmatchtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig,
+ dns_rdatatype_nsec);
+
+ do {
+ node = NULL;
+ dns_fixedname_init(&fname);
+ name = dns_fixedname_name(&fname);
+ dns_fixedname_init(&forigin);
+ origin = dns_fixedname_name(&forigin);
+ result = dns_rbtnodechain_current(&search->chain, name,
+ origin, &node);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ locktype = isc_rwlocktype_read;
+ lock = &(search->rbtdb->node_locks[node->locknum].lock);
+ NODE_LOCK(lock, locktype);
+ found = NULL;
+ foundsig = NULL;
+ empty_node = ISC_TRUE;
+ header_prev = NULL;
+ for (header = node->data;
+ header != NULL;
+ header = header_next) {
+ header_next = header->next;
+ if (header->rdh_ttl <= now) {
+ /*
+ * This rdataset is stale. If no one else is
+ * using the node, we can clean it up right
+ * now, otherwise we mark it as stale, and the
+ * node as dirty, so it will get cleaned up
+ * later.
+ */
+ if ((header->rdh_ttl <= now - RBTDB_VIRTUAL) &&
+ (locktype == isc_rwlocktype_write ||
+ NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) {
+ /*
+ * We update the node's status only
+ * when we can get write access.
+ */
+ locktype = isc_rwlocktype_write;
+
+ if (dns_rbtnode_refcurrent(node)
+ == 0) {
+ isc_mem_t *m;
+
+ m = search->rbtdb->common.mctx;
+ clean_stale_headers(
+ search->rbtdb,
+ m, header);
+ if (header_prev != NULL)
+ header_prev->next =
+ header->next;
+ else
+ node->data = header->next;
+ free_rdataset(search->rbtdb, m,
+ header);
+ } else {
+ header->attributes |=
+ RDATASET_ATTR_STALE;
+ node->dirty = 1;
+ header_prev = header;
+ }
+ } else
+ header_prev = header;
+ continue;
+ }
+ if (NONEXISTENT(header) ||
+ RBTDB_RDATATYPE_BASE(header->type) == 0) {
+ header_prev = header;
+ continue;
+ }
+ empty_node = ISC_FALSE;
+ if (header->type == matchtype)
+ found = header;
+ else if (header->type == sigmatchtype)
+ foundsig = header;
+ header_prev = header;
+ }
+ if (found != NULL) {
+ result = dns_name_concatenate(name, origin,
+ foundname, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto unlock_node;
+ bind_rdataset(search->rbtdb, node, found,
+ now, rdataset);
+ if (foundsig != NULL)
+ bind_rdataset(search->rbtdb, node, foundsig,
+ now, sigrdataset);
+ new_reference(search->rbtdb, node);
+ *nodep = node;
+ result = DNS_R_COVERINGNSEC;
+ } else if (!empty_node) {
+ result = ISC_R_NOTFOUND;
+ } else
+ result = dns_rbtnodechain_prev(&search->chain, NULL,
+ NULL);
+ unlock_node:
+ NODE_UNLOCK(lock, locktype);
+ } while (empty_node && result == ISC_R_SUCCESS);
+ return (result);
+}
+
+static isc_result_t
+cache_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
+ dns_rdatatype_t type, unsigned int options, isc_stdtime_t now,
+ dns_dbnode_t **nodep, dns_name_t *foundname,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+ dns_rbtnode_t *node = NULL;
+ isc_result_t result;
+ rbtdb_search_t search;
+ isc_boolean_t cname_ok = ISC_TRUE;
+ isc_boolean_t empty_node;
+ nodelock_t *lock;
+ isc_rwlocktype_t locktype;
+ rdatasetheader_t *header, *header_prev, *header_next;
+ rdatasetheader_t *found, *nsheader;
+ rdatasetheader_t *foundsig, *nssig, *cnamesig;
+ rdatasetheader_t *update, *updatesig;
+ rbtdb_rdatatype_t sigtype, negtype;
+
+ UNUSED(version);
+
+ search.rbtdb = (dns_rbtdb_t *)db;
+
+ REQUIRE(VALID_RBTDB(search.rbtdb));
+ REQUIRE(version == NULL);
+
+ if (now == 0)
+ isc_stdtime_get(&now);
+
+ search.rbtversion = NULL;
+ search.serial = 1;
+ search.options = options;
+ search.copy_name = ISC_FALSE;
+ search.need_cleanup = ISC_FALSE;
+ search.wild = ISC_FALSE;
+ search.zonecut = NULL;
+ dns_fixedname_init(&search.zonecut_name);
+ dns_rbtnodechain_init(&search.chain, search.rbtdb->common.mctx);
+ search.now = now;
+ update = NULL;
+ updatesig = NULL;
+
+ RWLOCK(&search.rbtdb->tree_lock, isc_rwlocktype_read);
+
+ /*
+ * Search down from the root of the tree. If, while going down, we
+ * encounter a callback node, cache_zonecut_callback() will search the
+ * rdatasets at the zone cut for a DNAME rdataset.
+ */
+ result = dns_rbt_findnode(search.rbtdb->tree, name, foundname, &node,
+ &search.chain, DNS_RBTFIND_EMPTYDATA,
+ cache_zonecut_callback, &search);
+
+ if (result == DNS_R_PARTIALMATCH) {
+ if ((search.options & DNS_DBFIND_COVERINGNSEC) != 0) {
+ result = find_coveringnsec(&search, nodep, now,
+ foundname, rdataset,
+ sigrdataset);
+ if (result == DNS_R_COVERINGNSEC)
+ goto tree_exit;
+ }
+ if (search.zonecut != NULL) {
+ result = setup_delegation(&search, nodep, foundname,
+ rdataset, sigrdataset);
+ goto tree_exit;
+ } else {
+ find_ns:
+ result = find_deepest_zonecut(&search, node, nodep,
+ foundname, rdataset,
+ sigrdataset);
+ goto tree_exit;
+ }
+ } else if (result != ISC_R_SUCCESS)
+ goto tree_exit;
+
+ /*
+ * Certain DNSSEC types are not subject to CNAME matching
+ * (RFC4035, section 2.5 and RFC3007).
+ *
+ * We don't check for RRSIG, because we don't store RRSIG records
+ * directly.
+ */
+ if (type == dns_rdatatype_key || type == dns_rdatatype_nsec)
+ cname_ok = ISC_FALSE;
+
+ /*
+ * We now go looking for rdata...
+ */
+
+ lock = &(search.rbtdb->node_locks[node->locknum].lock);
+ locktype = isc_rwlocktype_read;
+ NODE_LOCK(lock, locktype);
+
+ found = NULL;
+ foundsig = NULL;
+ sigtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, type);
+ negtype = RBTDB_RDATATYPE_VALUE(0, type);
+ nsheader = NULL;
+ nssig = NULL;
+ cnamesig = NULL;
+ empty_node = ISC_TRUE;
+ header_prev = NULL;
+ for (header = node->data; header != NULL; header = header_next) {
+ header_next = header->next;
+ if (header->rdh_ttl <= now) {
+ /*
+ * This rdataset is stale. If no one else is using the
+ * node, we can clean it up right now, otherwise we
+ * mark it as stale, and the node as dirty, so it will
+ * get cleaned up later.
+ */
+ if ((header->rdh_ttl <= now - RBTDB_VIRTUAL) &&
+ (locktype == isc_rwlocktype_write ||
+ NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) {
+ /*
+ * We update the node's status only when we
+ * can get write access.
+ */
+ locktype = isc_rwlocktype_write;
+
+ if (dns_rbtnode_refcurrent(node) == 0) {
+ isc_mem_t *mctx;
+
+ mctx = search.rbtdb->common.mctx;
+ clean_stale_headers(search.rbtdb, mctx,
+ header);
+ if (header_prev != NULL)
+ header_prev->next =
+ header->next;
+ else
+ node->data = header->next;
+ free_rdataset(search.rbtdb, mctx,
+ header);
+ } else {
+ header->attributes |=
+ RDATASET_ATTR_STALE;
+ node->dirty = 1;
+ header_prev = header;
+ }
+ } else
+ header_prev = header;
+ } else if (EXISTS(header)) {
+ /*
+ * We now know that there is at least one active
+ * non-stale rdataset at this node.
+ */
+ empty_node = ISC_FALSE;
+
+ /*
+ * If we found a type we were looking for, remember
+ * it.
+ */
+ if (header->type == type ||
+ (type == dns_rdatatype_any &&
+ RBTDB_RDATATYPE_BASE(header->type) != 0) ||
+ (cname_ok && header->type ==
+ dns_rdatatype_cname)) {
+ /*
+ * We've found the answer.
+ */
+ found = header;
+ if (header->type == dns_rdatatype_cname &&
+ cname_ok &&
+ cnamesig != NULL) {
+ /*
+ * If we've already got the CNAME RRSIG,
+ * use it, otherwise change sigtype
+ * so that we find it.
+ */
+ if (cnamesig != NULL)
+ foundsig = cnamesig;
+ else
+ sigtype =
+ RBTDB_RDATATYPE_SIGCNAME;
+ foundsig = cnamesig;
+ }
+ } else if (header->type == sigtype) {
+ /*
+ * We've found the RRSIG rdataset for our
+ * target type. Remember it.
+ */
+ foundsig = header;
+ } else if (header->type == RBTDB_RDATATYPE_NCACHEANY ||
+ header->type == negtype) {
+ /*
+ * We've found a negative cache entry.
+ */
+ found = header;
+ } else if (header->type == dns_rdatatype_ns) {
+ /*
+ * Remember a NS rdataset even if we're
+ * not specifically looking for it, because
+ * we might need it later.
+ */
+ nsheader = header;
+ } else if (header->type == RBTDB_RDATATYPE_SIGNS) {
+ /*
+ * If we need the NS rdataset, we'll also
+ * need its signature.
+ */
+ nssig = header;
+ } else if (cname_ok &&
+ header->type == RBTDB_RDATATYPE_SIGCNAME) {
+ /*
+ * If we get a CNAME match, we'll also need
+ * its signature.
+ */
+ cnamesig = header;
+ }
+ header_prev = header;
+ } else
+ header_prev = header;
+ }
+
+ if (empty_node) {
+ /*
+ * We have an exact match for the name, but there are no
+ * extant rdatasets. That means that this node doesn't
+ * meaningfully exist, and that we really have a partial match.
+ */
+ NODE_UNLOCK(lock, locktype);
+ goto find_ns;
+ }
+
+ /*
+ * If we didn't find what we were looking for...
+ */
+ if (found == NULL ||
+ (found->trust == dns_trust_glue &&
+ ((options & DNS_DBFIND_GLUEOK) == 0)) ||
+ (found->trust == dns_trust_pending &&
+ ((options & DNS_DBFIND_PENDINGOK) == 0))) {
+ /*
+ * If there is an NS rdataset at this node, then this is the
+ * deepest zone cut.
+ */
+ if (nsheader != NULL) {
+ if (nodep != NULL) {
+ new_reference(search.rbtdb, node);
+ INSIST(!ISC_LINK_LINKED(node, deadlink));
+ *nodep = node;
+ }
+ bind_rdataset(search.rbtdb, node, nsheader, search.now,
+ rdataset);
+ if (need_headerupdate(nsheader, search.now))
+ update = nsheader;
+ if (nssig != NULL) {
+ bind_rdataset(search.rbtdb, node, nssig,
+ search.now, sigrdataset);
+ if (need_headerupdate(nssig, search.now))
+ updatesig = nssig;
+ }
+ result = DNS_R_DELEGATION;
+ goto node_exit;
+ }
+
+ /*
+ * Go find the deepest zone cut.
+ */
+ NODE_UNLOCK(lock, locktype);
+ goto find_ns;
+ }
+
+ /*
+ * We found what we were looking for, or we found a CNAME.
+ */
+
+ if (nodep != NULL) {
+ new_reference(search.rbtdb, node);
+ INSIST(!ISC_LINK_LINKED(node, deadlink));
+ *nodep = node;
+ }
+
+ if (RBTDB_RDATATYPE_BASE(found->type) == 0) {
+ /*
+ * We found a negative cache entry.
+ */
+ if (NXDOMAIN(found))
+ result = DNS_R_NCACHENXDOMAIN;
+ else
+ result = DNS_R_NCACHENXRRSET;
+ } else if (type != found->type &&
+ type != dns_rdatatype_any &&
+ found->type == dns_rdatatype_cname) {
+ /*
+ * We weren't doing an ANY query and we found a CNAME instead
+ * of the type we were looking for, so we need to indicate
+ * that result to the caller.
+ */
+ result = DNS_R_CNAME;
+ } else {
+ /*
+ * An ordinary successful query!
+ */
+ result = ISC_R_SUCCESS;
+ }
+
+ if (type != dns_rdatatype_any || result == DNS_R_NCACHENXDOMAIN ||
+ result == DNS_R_NCACHENXRRSET) {
+ bind_rdataset(search.rbtdb, node, found, search.now,
+ rdataset);
+ if (need_headerupdate(found, search.now))
+ update = found;
+ if (foundsig != NULL) {
+ bind_rdataset(search.rbtdb, node, foundsig, search.now,
+ sigrdataset);
+ if (need_headerupdate(foundsig, search.now))
+ updatesig = foundsig;
+ }
+ }
+
+ node_exit:
+ if ((update != NULL || updatesig != NULL) &&
+ locktype != isc_rwlocktype_write) {
+ NODE_UNLOCK(lock, locktype);
+ NODE_LOCK(lock, isc_rwlocktype_write);
+ locktype = isc_rwlocktype_write;
+ }
+ if (update != NULL && need_headerupdate(update, search.now))
+ update_header(search.rbtdb, update, search.now);
+ if (updatesig != NULL && need_headerupdate(updatesig, search.now))
+ update_header(search.rbtdb, updatesig, search.now);
+
+ NODE_UNLOCK(lock, locktype);
+
+ tree_exit:
+ RWUNLOCK(&search.rbtdb->tree_lock, isc_rwlocktype_read);
+
+ /*
+ * If we found a zonecut but aren't going to use it, we have to
+ * let go of it.
+ */
+ if (search.need_cleanup) {
+ node = search.zonecut;
+ lock = &(search.rbtdb->node_locks[node->locknum].lock);
+
+ NODE_LOCK(lock, isc_rwlocktype_read);
+ decrement_reference(search.rbtdb, node, 0,
+ isc_rwlocktype_read, isc_rwlocktype_none);
+ NODE_UNLOCK(lock, isc_rwlocktype_read);
+ }
+
+ dns_rbtnodechain_reset(&search.chain);
+
+ return (result);
+}
+
+static isc_result_t
+cache_findzonecut(dns_db_t *db, dns_name_t *name, unsigned int options,
+ isc_stdtime_t now, dns_dbnode_t **nodep,
+ dns_name_t *foundname,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+ dns_rbtnode_t *node = NULL;
+ nodelock_t *lock;
+ isc_result_t result;
+ rbtdb_search_t search;
+ rdatasetheader_t *header, *header_prev, *header_next;
+ rdatasetheader_t *found, *foundsig;
+ unsigned int rbtoptions = DNS_RBTFIND_EMPTYDATA;
+ isc_rwlocktype_t locktype;
+
+ search.rbtdb = (dns_rbtdb_t *)db;
+
+ REQUIRE(VALID_RBTDB(search.rbtdb));
+
+ if (now == 0)
+ isc_stdtime_get(&now);
+
+ search.rbtversion = NULL;
+ search.serial = 1;
+ search.options = options;
+ search.copy_name = ISC_FALSE;
+ search.need_cleanup = ISC_FALSE;
+ search.wild = ISC_FALSE;
+ search.zonecut = NULL;
+ dns_fixedname_init(&search.zonecut_name);
+ dns_rbtnodechain_init(&search.chain, search.rbtdb->common.mctx);
+ search.now = now;
+
+ if ((options & DNS_DBFIND_NOEXACT) != 0)
+ rbtoptions |= DNS_RBTFIND_NOEXACT;
+
+ RWLOCK(&search.rbtdb->tree_lock, isc_rwlocktype_read);
+
+ /*
+ * Search down from the root of the tree.
+ */
+ result = dns_rbt_findnode(search.rbtdb->tree, name, foundname, &node,
+ &search.chain, rbtoptions, NULL, &search);
+
+ if (result == DNS_R_PARTIALMATCH) {
+ find_ns:
+ result = find_deepest_zonecut(&search, node, nodep, foundname,
+ rdataset, sigrdataset);
+ goto tree_exit;
+ } else if (result != ISC_R_SUCCESS)
+ goto tree_exit;
+
+ /*
+ * We now go looking for an NS rdataset at the node.
+ */
+
+ lock = &(search.rbtdb->node_locks[node->locknum].lock);
+ locktype = isc_rwlocktype_read;
+ NODE_LOCK(lock, locktype);
+
+ found = NULL;
+ foundsig = NULL;
+ header_prev = NULL;
+ for (header = node->data; header != NULL; header = header_next) {
+ header_next = header->next;
+ if (header->rdh_ttl <= now) {
+ /*
+ * This rdataset is stale. If no one else is using the
+ * node, we can clean it up right now, otherwise we
+ * mark it as stale, and the node as dirty, so it will
+ * get cleaned up later.
+ */
+ if ((header->rdh_ttl <= now - RBTDB_VIRTUAL) &&
+ (locktype == isc_rwlocktype_write ||
+ NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) {
+ /*
+ * We update the node's status only when we
+ * can get write access.
+ */
+ locktype = isc_rwlocktype_write;
+
+ if (dns_rbtnode_refcurrent(node) == 0) {
+ isc_mem_t *mctx;
+
+ mctx = search.rbtdb->common.mctx;
+ clean_stale_headers(search.rbtdb, mctx,
+ header);
+ if (header_prev != NULL)
+ header_prev->next =
+ header->next;
+ else
+ node->data = header->next;
+ free_rdataset(search.rbtdb, mctx,
+ header);
+ } else {
+ header->attributes |=
+ RDATASET_ATTR_STALE;
+ node->dirty = 1;
+ header_prev = header;
+ }
+ } else
+ header_prev = header;
+ } else if (EXISTS(header)) {
+ /*
+ * If we found a type we were looking for, remember
+ * it.
+ */
+ if (header->type == dns_rdatatype_ns) {
+ /*
+ * Remember a NS rdataset even if we're
+ * not specifically looking for it, because
+ * we might need it later.
+ */
+ found = header;
+ } else if (header->type == RBTDB_RDATATYPE_SIGNS) {
+ /*
+ * If we need the NS rdataset, we'll also
+ * need its signature.
+ */
+ foundsig = header;
+ }
+ header_prev = header;
+ } else
+ header_prev = header;
+ }
+
+ if (found == NULL) {
+ /*
+ * No NS records here.
+ */
+ NODE_UNLOCK(lock, locktype);
+ goto find_ns;
+ }
+
+ if (nodep != NULL) {
+ new_reference(search.rbtdb, node);
+ INSIST(!ISC_LINK_LINKED(node, deadlink));
+ *nodep = node;
+ }
+
+ bind_rdataset(search.rbtdb, node, found, search.now, rdataset);
+ if (foundsig != NULL)
+ bind_rdataset(search.rbtdb, node, foundsig, search.now,
+ sigrdataset);
+
+ if (need_headerupdate(found, search.now) ||
+ (foundsig != NULL && need_headerupdate(foundsig, search.now))) {
+ if (locktype != isc_rwlocktype_write) {
+ NODE_UNLOCK(lock, locktype);
+ NODE_LOCK(lock, isc_rwlocktype_write);
+ locktype = isc_rwlocktype_write;
+ }
+ if (need_headerupdate(found, search.now))
+ update_header(search.rbtdb, found, search.now);
+ if (foundsig != NULL &&
+ need_headerupdate(foundsig, search.now)) {
+ update_header(search.rbtdb, foundsig, search.now);
+ }
+ }
+
+ NODE_UNLOCK(lock, locktype);
+
+ tree_exit:
+ RWUNLOCK(&search.rbtdb->tree_lock, isc_rwlocktype_read);
+
+ INSIST(!search.need_cleanup);
+
+ dns_rbtnodechain_reset(&search.chain);
+
+ if (result == DNS_R_DELEGATION)
+ result = ISC_R_SUCCESS;
+
+ return (result);
+}
+
+static void
+attachnode(dns_db_t *db, dns_dbnode_t *source, dns_dbnode_t **targetp) {
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ dns_rbtnode_t *node = (dns_rbtnode_t *)source;
+ unsigned int refs;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ NODE_STRONGLOCK(&rbtdb->node_locks[node->locknum].lock);
+ dns_rbtnode_refincrement(node, &refs);
+ INSIST(refs != 0);
+ NODE_STRONGUNLOCK(&rbtdb->node_locks[node->locknum].lock);
+
+ *targetp = source;
+}
+
+static void
+detachnode(dns_db_t *db, dns_dbnode_t **targetp) {
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ dns_rbtnode_t *node;
+ isc_boolean_t want_free = ISC_FALSE;
+ isc_boolean_t inactive = ISC_FALSE;
+ rbtdb_nodelock_t *nodelock;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+ REQUIRE(targetp != NULL && *targetp != NULL);
+
+ node = (dns_rbtnode_t *)(*targetp);
+ nodelock = &rbtdb->node_locks[node->locknum];
+
+ NODE_LOCK(&nodelock->lock, isc_rwlocktype_read);
+
+ if (decrement_reference(rbtdb, node, 0, isc_rwlocktype_read,
+ isc_rwlocktype_none)) {
+ if (isc_refcount_current(&nodelock->references) == 0 &&
+ nodelock->exiting) {
+ inactive = ISC_TRUE;
+ }
+ }
+
+ NODE_UNLOCK(&nodelock->lock, isc_rwlocktype_read);
+
+ *targetp = NULL;
+
+ if (inactive) {
+ RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write);
+ rbtdb->active--;
+ if (rbtdb->active == 0)
+ want_free = ISC_TRUE;
+ RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write);
+ if (want_free) {
+ char buf[DNS_NAME_FORMATSIZE];
+ if (dns_name_dynamic(&rbtdb->common.origin))
+ dns_name_format(&rbtdb->common.origin, buf,
+ sizeof(buf));
+ else
+ strcpy(buf, "<UNKNOWN>");
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1),
+ "calling free_rbtdb(%s)", buf);
+ free_rbtdb(rbtdb, ISC_TRUE, NULL);
+ }
+ }
+}
+
+static isc_result_t
+expirenode(dns_db_t *db, dns_dbnode_t *node, isc_stdtime_t now) {
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ dns_rbtnode_t *rbtnode = node;
+ rdatasetheader_t *header;
+ isc_boolean_t force_expire = ISC_FALSE;
+ /*
+ * These are the category and module used by the cache cleaner.
+ */
+ isc_boolean_t log = ISC_FALSE;
+ isc_logcategory_t *category = DNS_LOGCATEGORY_DATABASE;
+ isc_logmodule_t *module = DNS_LOGMODULE_CACHE;
+ int level = ISC_LOG_DEBUG(2);
+ char printname[DNS_NAME_FORMATSIZE];
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ /*
+ * Caller must hold a tree lock.
+ */
+
+ if (now == 0)
+ isc_stdtime_get(&now);
+
+ if (rbtdb->overmem) {
+ isc_uint32_t val;
+
+ isc_random_get(&val);
+ /*
+ * XXXDCL Could stand to have a better policy, like LRU.
+ */
+ force_expire = ISC_TF(rbtnode->down == NULL && val % 4 == 0);
+
+ /*
+ * Note that 'log' can be true IFF rbtdb->overmem is also true.
+ * rbtdb->ovemem can currently only be true for cache databases
+ * -- hence all of the "overmem cache" log strings.
+ */
+ log = ISC_TF(isc_log_wouldlog(dns_lctx, level));
+ if (log)
+ isc_log_write(dns_lctx, category, module, level,
+ "overmem cache: %s %s",
+ force_expire ? "FORCE" : "check",
+ dns_rbt_formatnodename(rbtnode,
+ printname,
+ sizeof(printname)));
+ }
+
+ /*
+ * We may not need write access, but this code path is not performance
+ * sensitive, so it should be okay to always lock as a writer.
+ */
+ NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_write);
+
+ for (header = rbtnode->data; header != NULL; header = header->next)
+ if (header->rdh_ttl <= now - RBTDB_VIRTUAL) {
+ /*
+ * We don't check if refcurrent(rbtnode) == 0 and try
+ * to free like we do in cache_find(), because
+ * refcurrent(rbtnode) must be non-zero. This is so
+ * because 'node' is an argument to the function.
+ */
+ header->attributes |= RDATASET_ATTR_STALE;
+ rbtnode->dirty = 1;
+ if (log)
+ isc_log_write(dns_lctx, category, module,
+ level, "overmem cache: stale %s",
+ printname);
+ } else if (force_expire) {
+ if (! RETAIN(header)) {
+ set_ttl(rbtdb, header, 0);
+ header->attributes |= RDATASET_ATTR_STALE;
+ rbtnode->dirty = 1;
+ } else if (log) {
+ isc_log_write(dns_lctx, category, module,
+ level, "overmem cache: "
+ "reprieve by RETAIN() %s",
+ printname);
+ }
+ } else if (rbtdb->overmem && log)
+ isc_log_write(dns_lctx, category, module, level,
+ "overmem cache: saved %s", printname);
+
+ NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_write);
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+overmem(dns_db_t *db, isc_boolean_t overmem) {
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+
+ if (IS_CACHE(rbtdb))
+ rbtdb->overmem = overmem;
+}
+
+static void
+printnode(dns_db_t *db, dns_dbnode_t *node, FILE *out) {
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ dns_rbtnode_t *rbtnode = node;
+ isc_boolean_t first;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_read);
+
+ fprintf(out, "node %p, %u references, locknum = %u\n",
+ rbtnode, dns_rbtnode_refcurrent(rbtnode),
+ rbtnode->locknum);
+ if (rbtnode->data != NULL) {
+ rdatasetheader_t *current, *top_next;
+
+ for (current = rbtnode->data; current != NULL;
+ current = top_next) {
+ top_next = current->next;
+ first = ISC_TRUE;
+ fprintf(out, "\ttype %u", current->type);
+ do {
+ if (!first)
+ fprintf(out, "\t");
+ first = ISC_FALSE;
+ fprintf(out,
+ "\tserial = %lu, ttl = %u, "
+ "trust = %u, attributes = %u, "
+ "resign = %u\n",
+ (unsigned long)current->serial,
+ current->rdh_ttl,
+ current->trust,
+ current->attributes,
+ current->resign);
+ current = current->down;
+ } while (current != NULL);
+ }
+ } else
+ fprintf(out, "(empty)\n");
+
+ NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_read);
+}
+
+static isc_result_t
+createiterator(dns_db_t *db, unsigned int options, dns_dbiterator_t **iteratorp)
+{
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ rbtdb_dbiterator_t *rbtdbiter;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ rbtdbiter = isc_mem_get(rbtdb->common.mctx, sizeof(*rbtdbiter));
+ if (rbtdbiter == NULL)
+ return (ISC_R_NOMEMORY);
+
+ rbtdbiter->common.methods = &dbiterator_methods;
+ rbtdbiter->common.db = NULL;
+ dns_db_attach(db, &rbtdbiter->common.db);
+ rbtdbiter->common.relative_names =
+ ISC_TF((options & DNS_DB_RELATIVENAMES) != 0);
+ rbtdbiter->common.magic = DNS_DBITERATOR_MAGIC;
+ rbtdbiter->common.cleaning = ISC_FALSE;
+ rbtdbiter->paused = ISC_TRUE;
+ rbtdbiter->tree_locked = isc_rwlocktype_none;
+ rbtdbiter->result = ISC_R_SUCCESS;
+ dns_fixedname_init(&rbtdbiter->name);
+ dns_fixedname_init(&rbtdbiter->origin);
+ rbtdbiter->node = NULL;
+ rbtdbiter->delete = 0;
+ rbtdbiter->nsec3only = ISC_TF((options & DNS_DB_NSEC3ONLY) != 0);
+ rbtdbiter->nonsec3 = ISC_TF((options & DNS_DB_NONSEC3) != 0);
+ memset(rbtdbiter->deletions, 0, sizeof(rbtdbiter->deletions));
+ dns_rbtnodechain_init(&rbtdbiter->chain, db->mctx);
+ dns_rbtnodechain_init(&rbtdbiter->nsec3chain, db->mctx);
+ if (rbtdbiter->nsec3only)
+ rbtdbiter->current = &rbtdbiter->nsec3chain;
+ else
+ rbtdbiter->current = &rbtdbiter->chain;
+
+ *iteratorp = (dns_dbiterator_t *)rbtdbiter;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+zone_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ dns_rdatatype_t type, dns_rdatatype_t covers,
+ isc_stdtime_t now, dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset)
+{
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node;
+ rdatasetheader_t *header, *header_next, *found, *foundsig;
+ rbtdb_serial_t serial;
+ rbtdb_version_t *rbtversion = version;
+ isc_boolean_t close_version = ISC_FALSE;
+ rbtdb_rdatatype_t matchtype, sigmatchtype;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+ REQUIRE(type != dns_rdatatype_any);
+
+ if (rbtversion == NULL) {
+ currentversion(db, (dns_dbversion_t **) (void *)(&rbtversion));
+ close_version = ISC_TRUE;
+ }
+ serial = rbtversion->serial;
+ now = 0;
+
+ NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_read);
+
+ found = NULL;
+ foundsig = NULL;
+ matchtype = RBTDB_RDATATYPE_VALUE(type, covers);
+ if (covers == 0)
+ sigmatchtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, type);
+ else
+ sigmatchtype = 0;
+
+ for (header = rbtnode->data; header != NULL; header = header_next) {
+ header_next = header->next;
+ do {
+ if (header->serial <= serial &&
+ !IGNORE(header)) {
+ /*
+ * Is this a "this rdataset doesn't
+ * exist" record?
+ */
+ if (NONEXISTENT(header))
+ header = NULL;
+ break;
+ } else
+ header = header->down;
+ } while (header != NULL);
+ if (header != NULL) {
+ /*
+ * We have an active, extant rdataset. If it's a
+ * type we're looking for, remember it.
+ */
+ if (header->type == matchtype) {
+ found = header;
+ if (foundsig != NULL)
+ break;
+ } else if (header->type == sigmatchtype) {
+ foundsig = header;
+ if (found != NULL)
+ break;
+ }
+ }
+ }
+ if (found != NULL) {
+ bind_rdataset(rbtdb, rbtnode, found, now, rdataset);
+ if (foundsig != NULL)
+ bind_rdataset(rbtdb, rbtnode, foundsig, now,
+ sigrdataset);
+ }
+
+ NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_read);
+
+ if (close_version)
+ closeversion(db, (dns_dbversion_t **) (void *)(&rbtversion),
+ ISC_FALSE);
+
+ if (found == NULL)
+ return (ISC_R_NOTFOUND);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+cache_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ dns_rdatatype_t type, dns_rdatatype_t covers,
+ isc_stdtime_t now, dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset)
+{
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node;
+ rdatasetheader_t *header, *header_next, *found, *foundsig;
+ rbtdb_rdatatype_t matchtype, sigmatchtype, negtype;
+ isc_result_t result;
+ nodelock_t *lock;
+ isc_rwlocktype_t locktype;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+ REQUIRE(type != dns_rdatatype_any);
+
+ UNUSED(version);
+
+ result = ISC_R_SUCCESS;
+
+ if (now == 0)
+ isc_stdtime_get(&now);
+
+ lock = &rbtdb->node_locks[rbtnode->locknum].lock;
+ locktype = isc_rwlocktype_read;
+ NODE_LOCK(lock, locktype);
+
+ found = NULL;
+ foundsig = NULL;
+ matchtype = RBTDB_RDATATYPE_VALUE(type, covers);
+ negtype = RBTDB_RDATATYPE_VALUE(0, type);
+ if (covers == 0)
+ sigmatchtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, type);
+ else
+ sigmatchtype = 0;
+
+ for (header = rbtnode->data; header != NULL; header = header_next) {
+ header_next = header->next;
+ if (header->rdh_ttl <= now) {
+ if ((header->rdh_ttl <= now - RBTDB_VIRTUAL) &&
+ (locktype == isc_rwlocktype_write ||
+ NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) {
+ /*
+ * We update the node's status only when we
+ * can get write access.
+ */
+ locktype = isc_rwlocktype_write;
+
+ /*
+ * We don't check if refcurrent(rbtnode) == 0
+ * and try to free like we do in cache_find(),
+ * because refcurrent(rbtnode) must be
+ * non-zero. This is so because 'node' is an
+ * argument to the function.
+ */
+ header->attributes |= RDATASET_ATTR_STALE;
+ rbtnode->dirty = 1;
+ }
+ } else if (EXISTS(header)) {
+ if (header->type == matchtype)
+ found = header;
+ else if (header->type == RBTDB_RDATATYPE_NCACHEANY ||
+ header->type == negtype)
+ found = header;
+ else if (header->type == sigmatchtype)
+ foundsig = header;
+ }
+ }
+ if (found != NULL) {
+ bind_rdataset(rbtdb, rbtnode, found, now, rdataset);
+ if (foundsig != NULL)
+ bind_rdataset(rbtdb, rbtnode, foundsig, now,
+ sigrdataset);
+ }
+
+ NODE_UNLOCK(lock, locktype);
+
+ if (found == NULL)
+ return (ISC_R_NOTFOUND);
+
+ if (RBTDB_RDATATYPE_BASE(found->type) == 0) {
+ /*
+ * We found a negative cache entry.
+ */
+ if (NXDOMAIN(found))
+ result = DNS_R_NCACHENXDOMAIN;
+ else
+ result = DNS_R_NCACHENXRRSET;
+ }
+
+ return (result);
+}
+
+static isc_result_t
+allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ isc_stdtime_t now, dns_rdatasetiter_t **iteratorp)
+{
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node;
+ rbtdb_version_t *rbtversion = version;
+ rbtdb_rdatasetiter_t *iterator;
+ unsigned int refs;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ iterator = isc_mem_get(rbtdb->common.mctx, sizeof(*iterator));
+ if (iterator == NULL)
+ return (ISC_R_NOMEMORY);
+
+ if ((db->attributes & DNS_DBATTR_CACHE) == 0) {
+ now = 0;
+ if (rbtversion == NULL)
+ currentversion(db,
+ (dns_dbversion_t **) (void *)(&rbtversion));
+ else {
+ unsigned int refs;
+
+ isc_refcount_increment(&rbtversion->references,
+ &refs);
+ INSIST(refs > 1);
+ }
+ } else {
+ if (now == 0)
+ isc_stdtime_get(&now);
+ rbtversion = NULL;
+ }
+
+ iterator->common.magic = DNS_RDATASETITER_MAGIC;
+ iterator->common.methods = &rdatasetiter_methods;
+ iterator->common.db = db;
+ iterator->common.node = node;
+ iterator->common.version = (dns_dbversion_t *)rbtversion;
+ iterator->common.now = now;
+
+ NODE_STRONGLOCK(&rbtdb->node_locks[rbtnode->locknum].lock);
+
+ dns_rbtnode_refincrement(rbtnode, &refs);
+ INSIST(refs != 0);
+
+ iterator->current = NULL;
+
+ NODE_STRONGUNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock);
+
+ *iteratorp = (dns_rdatasetiter_t *)iterator;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_boolean_t
+cname_and_other_data(dns_rbtnode_t *node, rbtdb_serial_t serial) {
+ rdatasetheader_t *header, *header_next;
+ isc_boolean_t cname, other_data;
+ dns_rdatatype_t rdtype;
+
+ /*
+ * The caller must hold the node lock.
+ */
+
+ /*
+ * Look for CNAME and "other data" rdatasets active in our version.
+ */
+ cname = ISC_FALSE;
+ other_data = ISC_FALSE;
+ for (header = node->data; header != NULL; header = header_next) {
+ header_next = header->next;
+ if (header->type == dns_rdatatype_cname) {
+ /*
+ * Look for an active extant CNAME.
+ */
+ do {
+ if (header->serial <= serial &&
+ !IGNORE(header)) {
+ /*
+ * Is this a "this rdataset doesn't
+ * exist" record?
+ */
+ if (NONEXISTENT(header))
+ header = NULL;
+ break;
+ } else
+ header = header->down;
+ } while (header != NULL);
+ if (header != NULL)
+ cname = ISC_TRUE;
+ } else {
+ /*
+ * Look for active extant "other data".
+ *
+ * "Other data" is any rdataset whose type is not
+ * KEY, RRSIG KEY, NSEC, RRSIG NSEC or RRSIG CNAME.
+ */
+ rdtype = RBTDB_RDATATYPE_BASE(header->type);
+ if (rdtype == dns_rdatatype_rrsig ||
+ rdtype == dns_rdatatype_sig)
+ rdtype = RBTDB_RDATATYPE_EXT(header->type);
+ if (rdtype != dns_rdatatype_nsec &&
+ rdtype != dns_rdatatype_key &&
+ rdtype != dns_rdatatype_cname) {
+ /*
+ * We've found a type that isn't
+ * NSEC, KEY, CNAME, or one of their
+ * signatures. Is it active and extant?
+ */
+ do {
+ if (header->serial <= serial &&
+ !IGNORE(header)) {
+ /*
+ * Is this a "this rdataset
+ * doesn't exist" record?
+ */
+ if (NONEXISTENT(header))
+ header = NULL;
+ break;
+ } else
+ header = header->down;
+ } while (header != NULL);
+ if (header != NULL)
+ other_data = ISC_TRUE;
+ }
+ }
+ }
+
+ if (cname && other_data)
+ return (ISC_TRUE);
+
+ return (ISC_FALSE);
+}
+
+static isc_result_t
+resign_insert(dns_rbtdb_t *rbtdb, int idx, rdatasetheader_t *newheader) {
+ isc_result_t result;
+
+ INSIST(newheader->heap_index == 0);
+ INSIST(!ISC_LINK_LINKED(newheader, lru_link));
+ result = isc_heap_insert(rbtdb->heaps[idx], newheader);
+ return (result);
+}
+
+static isc_result_t
+add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
+ rdatasetheader_t *newheader, unsigned int options, isc_boolean_t loading,
+ dns_rdataset_t *addedrdataset, isc_stdtime_t now)
+{
+ rbtdb_changed_t *changed = NULL;
+ rdatasetheader_t *topheader, *topheader_prev, *header;
+ unsigned char *merged;
+ isc_result_t result;
+ isc_boolean_t header_nx;
+ isc_boolean_t newheader_nx;
+ isc_boolean_t merge;
+ dns_rdatatype_t rdtype, covers;
+ rbtdb_rdatatype_t negtype;
+ dns_trust_t trust;
+ int idx;
+
+ /*
+ * Add an rdatasetheader_t to a node.
+ */
+
+ /*
+ * Caller must be holding the node lock.
+ */
+
+ if ((options & DNS_DBADD_MERGE) != 0) {
+ REQUIRE(rbtversion != NULL);
+ merge = ISC_TRUE;
+ } else
+ merge = ISC_FALSE;
+
+ if ((options & DNS_DBADD_FORCE) != 0)
+ trust = dns_trust_ultimate;
+ else
+ trust = newheader->trust;
+
+ if (rbtversion != NULL && !loading) {
+ /*
+ * We always add a changed record, even if no changes end up
+ * being made to this node, because it's harmless and
+ * simplifies the code.
+ */
+ changed = add_changed(rbtdb, rbtversion, rbtnode);
+ if (changed == NULL) {
+ free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
+ return (ISC_R_NOMEMORY);
+ }
+ }
+
+ newheader_nx = NONEXISTENT(newheader) ? ISC_TRUE : ISC_FALSE;
+ topheader_prev = NULL;
+
+ negtype = 0;
+ if (rbtversion == NULL && !newheader_nx) {
+ rdtype = RBTDB_RDATATYPE_BASE(newheader->type);
+ if (rdtype == 0) {
+ /*
+ * We're adding a negative cache entry.
+ */
+ covers = RBTDB_RDATATYPE_EXT(newheader->type);
+ if (covers == dns_rdatatype_any) {
+ /*
+ * We're adding an negative cache entry
+ * which covers all types (NXDOMAIN,
+ * NODATA(QTYPE=ANY)).
+ *
+ * We make all other data stale so that the
+ * only rdataset that can be found at this
+ * node is the negative cache entry.
+ */
+ for (topheader = rbtnode->data;
+ topheader != NULL;
+ topheader = topheader->next) {
+ set_ttl(rbtdb, topheader, 0);
+ topheader->attributes |=
+ RDATASET_ATTR_STALE;
+ }
+ rbtnode->dirty = 1;
+ goto find_header;
+ }
+ negtype = RBTDB_RDATATYPE_VALUE(covers, 0);
+ } else {
+ /*
+ * We're adding something that isn't a
+ * negative cache entry. Look for an extant
+ * non-stale NXDOMAIN/NODATA(QTYPE=ANY) negative
+ * cache entry.
+ */
+ for (topheader = rbtnode->data;
+ topheader != NULL;
+ topheader = topheader->next) {
+ if (topheader->type ==
+ RBTDB_RDATATYPE_NCACHEANY)
+ break;
+ }
+ if (topheader != NULL && EXISTS(topheader) &&
+ topheader->rdh_ttl > now) {
+ /*
+ * Found one.
+ */
+ if (trust < topheader->trust) {
+ /*
+ * The NXDOMAIN/NODATA(QTYPE=ANY)
+ * is more trusted.
+ */
+ free_rdataset(rbtdb,
+ rbtdb->common.mctx,
+ newheader);
+ if (addedrdataset != NULL)
+ bind_rdataset(rbtdb, rbtnode,
+ topheader, now,
+ addedrdataset);
+ return (DNS_R_UNCHANGED);
+ }
+ /*
+ * The new rdataset is better. Expire the
+ * NXDOMAIN/NODATA(QTYPE=ANY).
+ */
+ set_ttl(rbtdb, topheader, 0);
+ topheader->attributes |= RDATASET_ATTR_STALE;
+ rbtnode->dirty = 1;
+ topheader = NULL;
+ goto find_header;
+ }
+ negtype = RBTDB_RDATATYPE_VALUE(0, rdtype);
+ }
+ }
+
+ for (topheader = rbtnode->data;
+ topheader != NULL;
+ topheader = topheader->next) {
+ if (topheader->type == newheader->type ||
+ topheader->type == negtype)
+ break;
+ topheader_prev = topheader;
+ }
+
+ find_header:
+ /*
+ * If header isn't NULL, we've found the right type. There may be
+ * IGNORE rdatasets between the top of the chain and the first real
+ * data. We skip over them.
+ */
+ header = topheader;
+ while (header != NULL && IGNORE(header))
+ header = header->down;
+ if (header != NULL) {
+ header_nx = NONEXISTENT(header) ? ISC_TRUE : ISC_FALSE;
+
+ /*
+ * Deleting an already non-existent rdataset has no effect.
+ */
+ if (header_nx && newheader_nx) {
+ free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
+ return (DNS_R_UNCHANGED);
+ }
+
+ /*
+ * Trying to add an rdataset with lower trust to a cache DB
+ * has no effect, provided that the cache data isn't stale.
+ */
+ if (rbtversion == NULL && trust < header->trust &&
+ (header->rdh_ttl > now || header_nx)) {
+ free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
+ if (addedrdataset != NULL)
+ bind_rdataset(rbtdb, rbtnode, header, now,
+ addedrdataset);
+ return (DNS_R_UNCHANGED);
+ }
+
+ /*
+ * Don't merge if a nonexistent rdataset is involved.
+ */
+ if (merge && (header_nx || newheader_nx))
+ merge = ISC_FALSE;
+
+ /*
+ * If 'merge' is ISC_TRUE, we'll try to create a new rdataset
+ * that is the union of 'newheader' and 'header'.
+ */
+ if (merge) {
+ unsigned int flags = 0;
+ INSIST(rbtversion->serial >= header->serial);
+ merged = NULL;
+ result = ISC_R_SUCCESS;
+
+ if ((options & DNS_DBADD_EXACT) != 0)
+ flags |= DNS_RDATASLAB_EXACT;
+ if ((options & DNS_DBADD_EXACTTTL) != 0 &&
+ newheader->rdh_ttl != header->rdh_ttl)
+ result = DNS_R_NOTEXACT;
+ else if (newheader->rdh_ttl != header->rdh_ttl)
+ flags |= DNS_RDATASLAB_FORCE;
+ if (result == ISC_R_SUCCESS)
+ result = dns_rdataslab_merge(
+ (unsigned char *)header,
+ (unsigned char *)newheader,
+ (unsigned int)(sizeof(*newheader)),
+ rbtdb->common.mctx,
+ rbtdb->common.rdclass,
+ (dns_rdatatype_t)header->type,
+ flags, &merged);
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * If 'header' has the same serial number as
+ * we do, we could clean it up now if we knew
+ * that our caller had no references to it.
+ * We don't know this, however, so we leave it
+ * alone. It will get cleaned up when
+ * clean_zone_node() runs.
+ */
+ free_rdataset(rbtdb, rbtdb->common.mctx,
+ newheader);
+ newheader = (rdatasetheader_t *)merged;
+ if (loading && RESIGN(newheader) &&
+ RESIGN(header) &&
+ header->resign < newheader->resign)
+ newheader->resign = header->resign;
+ } else {
+ free_rdataset(rbtdb, rbtdb->common.mctx,
+ newheader);
+ return (result);
+ }
+ }
+ /*
+ * Don't replace existing NS, A and AAAA RRsets
+ * in the cache if they are already exist. This
+ * prevents named being locked to old servers.
+ * Don't lower trust of existing record if the
+ * update is forced.
+ */
+ if (IS_CACHE(rbtdb) && header->rdh_ttl > now &&
+ header->type == dns_rdatatype_ns &&
+ !header_nx && !newheader_nx &&
+ header->trust >= newheader->trust &&
+ dns_rdataslab_equalx((unsigned char *)header,
+ (unsigned char *)newheader,
+ (unsigned int)(sizeof(*newheader)),
+ rbtdb->common.rdclass,
+ (dns_rdatatype_t)header->type)) {
+ /*
+ * Honour the new ttl if it is less than the
+ * older one.
+ */
+ if (header->rdh_ttl > newheader->rdh_ttl)
+ set_ttl(rbtdb, header, newheader->rdh_ttl);
+ if (header->noqname == NULL &&
+ newheader->noqname != NULL) {
+ header->noqname = newheader->noqname;
+ newheader->noqname = NULL;
+ }
+ if (header->closest == NULL &&
+ newheader->closest != NULL) {
+ header->closest = newheader->closest;
+ newheader->closest = NULL;
+ }
+ free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
+ if (addedrdataset != NULL)
+ bind_rdataset(rbtdb, rbtnode, header, now,
+ addedrdataset);
+ return (ISC_R_SUCCESS);
+ }
+ if (IS_CACHE(rbtdb) && header->rdh_ttl > now &&
+ (header->type == dns_rdatatype_a ||
+ header->type == dns_rdatatype_aaaa) &&
+ !header_nx && !newheader_nx &&
+ header->trust >= newheader->trust &&
+ dns_rdataslab_equal((unsigned char *)header,
+ (unsigned char *)newheader,
+ (unsigned int)(sizeof(*newheader)))) {
+ /*
+ * Honour the new ttl if it is less than the
+ * older one.
+ */
+ if (header->rdh_ttl > newheader->rdh_ttl)
+ set_ttl(rbtdb, header, newheader->rdh_ttl);
+ if (header->noqname == NULL &&
+ newheader->noqname != NULL) {
+ header->noqname = newheader->noqname;
+ newheader->noqname = NULL;
+ }
+ if (header->closest == NULL &&
+ newheader->closest != NULL) {
+ header->closest = newheader->closest;
+ newheader->closest = NULL;
+ }
+ free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
+ if (addedrdataset != NULL)
+ bind_rdataset(rbtdb, rbtnode, header, now,
+ addedrdataset);
+ return (ISC_R_SUCCESS);
+ }
+ INSIST(rbtversion == NULL ||
+ rbtversion->serial >= topheader->serial);
+ if (topheader_prev != NULL)
+ topheader_prev->next = newheader;
+ else
+ rbtnode->data = newheader;
+ newheader->next = topheader->next;
+ if (loading) {
+ /*
+ * There are no other references to 'header' when
+ * loading, so we MAY clean up 'header' now.
+ * Since we don't generate changed records when
+ * loading, we MUST clean up 'header' now.
+ */
+ newheader->down = NULL;
+ free_rdataset(rbtdb, rbtdb->common.mctx, header);
+ } else {
+ newheader->down = topheader;
+ topheader->next = newheader;
+ rbtnode->dirty = 1;
+ if (changed != NULL)
+ changed->dirty = ISC_TRUE;
+ if (rbtversion == NULL) {
+ set_ttl(rbtdb, header, 0);
+ header->attributes |= RDATASET_ATTR_STALE;
+ }
+ idx = newheader->node->locknum;
+ if (IS_CACHE(rbtdb)) {
+ ISC_LIST_PREPEND(rbtdb->rdatasets[idx],
+ newheader, lru_link);
+ /*
+ * XXXMLG We don't check the return value
+ * here. If it fails, we will not do TTL
+ * based expiry on this node. However, we
+ * will do it on the LRU side, so memory
+ * will not leak... for long.
+ */
+ isc_heap_insert(rbtdb->heaps[idx], newheader);
+ } else if (RESIGN(newheader))
+ resign_insert(rbtdb, idx, newheader);
+ }
+ } else {
+ /*
+ * No non-IGNORED rdatasets of the given type exist at
+ * this node.
+ */
+
+ /*
+ * If we're trying to delete the type, don't bother.
+ */
+ if (newheader_nx) {
+ free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
+ return (DNS_R_UNCHANGED);
+ }
+
+ if (topheader != NULL) {
+ /*
+ * We have an list of rdatasets of the given type,
+ * but they're all marked IGNORE. We simply insert
+ * the new rdataset at the head of the list.
+ *
+ * Ignored rdatasets cannot occur during loading, so
+ * we INSIST on it.
+ */
+ INSIST(!loading);
+ INSIST(rbtversion == NULL ||
+ rbtversion->serial >= topheader->serial);
+ if (topheader_prev != NULL)
+ topheader_prev->next = newheader;
+ else
+ rbtnode->data = newheader;
+ newheader->next = topheader->next;
+ newheader->down = topheader;
+ topheader->next = newheader;
+ rbtnode->dirty = 1;
+ if (changed != NULL)
+ changed->dirty = ISC_TRUE;
+ } else {
+ /*
+ * No rdatasets of the given type exist at the node.
+ */
+ newheader->next = rbtnode->data;
+ newheader->down = NULL;
+ rbtnode->data = newheader;
+ }
+ idx = newheader->node->locknum;
+ if (IS_CACHE(rbtdb)) {
+ ISC_LIST_PREPEND(rbtdb->rdatasets[idx],
+ newheader, lru_link);
+ isc_heap_insert(rbtdb->heaps[idx], newheader);
+ } else if (RESIGN(newheader)) {
+ resign_insert(rbtdb, idx, newheader);
+ }
+ }
+
+ /*
+ * Check if the node now contains CNAME and other data.
+ */
+ if (rbtversion != NULL &&
+ cname_and_other_data(rbtnode, rbtversion->serial))
+ return (DNS_R_CNAMEANDOTHER);
+
+ if (addedrdataset != NULL)
+ bind_rdataset(rbtdb, rbtnode, newheader, now, addedrdataset);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_boolean_t
+delegating_type(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
+ rbtdb_rdatatype_t type)
+{
+ if (IS_CACHE(rbtdb)) {
+ if (type == dns_rdatatype_dname)
+ return (ISC_TRUE);
+ else
+ return (ISC_FALSE);
+ } else if (type == dns_rdatatype_dname ||
+ (type == dns_rdatatype_ns &&
+ (node != rbtdb->origin_node || IS_STUB(rbtdb))))
+ return (ISC_TRUE);
+ return (ISC_FALSE);
+}
+
+static inline isc_result_t
+addnoqname(dns_rbtdb_t *rbtdb, rdatasetheader_t *newheader,
+ dns_rdataset_t *rdataset)
+{
+ struct noqname *noqname;
+ isc_mem_t *mctx = rbtdb->common.mctx;
+ dns_name_t name;
+ dns_rdataset_t neg, negsig;
+ isc_result_t result;
+ isc_region_t r;
+
+ dns_name_init(&name, NULL);
+ dns_rdataset_init(&neg);
+ dns_rdataset_init(&negsig);
+
+ result = dns_rdataset_getnoqname(rdataset, &name, &neg, &negsig);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ noqname = isc_mem_get(mctx, sizeof(*noqname));
+ if (noqname == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ dns_name_init(&noqname->name, NULL);
+ noqname->neg = NULL;
+ noqname->negsig = NULL;
+ noqname->type = neg.type;
+ result = dns_name_dup(&name, mctx, &noqname->name);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_rdataslab_fromrdataset(&neg, mctx, &r, 0);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ noqname->neg = r.base;
+ result = dns_rdataslab_fromrdataset(&negsig, mctx, &r, 0);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ noqname->negsig = r.base;
+ dns_rdataset_disassociate(&neg);
+ dns_rdataset_disassociate(&negsig);
+ newheader->noqname = noqname;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ dns_rdataset_disassociate(&neg);
+ dns_rdataset_disassociate(&negsig);
+ free_noqname(mctx, &noqname);
+ return(result);
+}
+
+static inline isc_result_t
+addclosest(dns_rbtdb_t *rbtdb, rdatasetheader_t *newheader,
+ dns_rdataset_t *rdataset)
+{
+ struct noqname *closest;
+ isc_mem_t *mctx = rbtdb->common.mctx;
+ dns_name_t name;
+ dns_rdataset_t neg, negsig;
+ isc_result_t result;
+ isc_region_t r;
+
+ dns_name_init(&name, NULL);
+ dns_rdataset_init(&neg);
+ dns_rdataset_init(&negsig);
+
+ result = dns_rdataset_getclosest(rdataset, &name, &neg, &negsig);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ closest = isc_mem_get(mctx, sizeof(*closest));
+ if (closest == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ dns_name_init(&closest->name, NULL);
+ closest->neg = NULL;
+ closest->negsig = NULL;
+ closest->type = neg.type;
+ result = dns_name_dup(&name, mctx, &closest->name);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_rdataslab_fromrdataset(&neg, mctx, &r, 0);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ closest->neg = r.base;
+ result = dns_rdataslab_fromrdataset(&negsig, mctx, &r, 0);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ closest->negsig = r.base;
+ dns_rdataset_disassociate(&neg);
+ dns_rdataset_disassociate(&negsig);
+ newheader->closest = closest;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ dns_rdataset_disassociate(&neg);
+ dns_rdataset_disassociate(&negsig);
+ free_noqname(mctx, &closest);
+ return(result);
+}
+
+static dns_dbmethods_t zone_methods;
+
+static isc_result_t
+addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ isc_stdtime_t now, dns_rdataset_t *rdataset, unsigned int options,
+ dns_rdataset_t *addedrdataset)
+{
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node;
+ rbtdb_version_t *rbtversion = version;
+ isc_region_t region;
+ rdatasetheader_t *newheader;
+ rdatasetheader_t *header;
+ isc_result_t result;
+ isc_boolean_t delegating;
+ isc_boolean_t tree_locked = ISC_FALSE;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ if (rbtdb->common.methods == &zone_methods)
+ REQUIRE(((rbtnode->nsec3 &&
+ (rdataset->type == dns_rdatatype_nsec3 ||
+ rdataset->covers == dns_rdatatype_nsec3)) ||
+ (!rbtnode->nsec3 &&
+ rdataset->type != dns_rdatatype_nsec3 &&
+ rdataset->covers != dns_rdatatype_nsec3)));
+
+ if (rbtversion == NULL) {
+ if (now == 0)
+ isc_stdtime_get(&now);
+ } else
+ now = 0;
+
+ result = dns_rdataslab_fromrdataset(rdataset, rbtdb->common.mctx,
+ &region,
+ sizeof(rdatasetheader_t));
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ newheader = (rdatasetheader_t *)region.base;
+ init_rdataset(rbtdb, newheader);
+ set_ttl(rbtdb, newheader, rdataset->ttl + now);
+ newheader->type = RBTDB_RDATATYPE_VALUE(rdataset->type,
+ rdataset->covers);
+ newheader->attributes = 0;
+ newheader->noqname = NULL;
+ newheader->closest = NULL;
+ newheader->count = init_count++;
+ newheader->trust = rdataset->trust;
+ newheader->additional_auth = NULL;
+ newheader->additional_glue = NULL;
+ newheader->last_used = now;
+ newheader->node = rbtnode;
+ if (rbtversion != NULL) {
+ newheader->serial = rbtversion->serial;
+ now = 0;
+
+ if ((rdataset->attributes & DNS_RDATASETATTR_RESIGN) != 0) {
+ newheader->attributes |= RDATASET_ATTR_RESIGN;
+ newheader->resign = rdataset->resign;
+ } else
+ newheader->resign = 0;
+ } else {
+ newheader->serial = 1;
+ newheader->resign = 0;
+ if ((rdataset->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0)
+ newheader->attributes |= RDATASET_ATTR_NXDOMAIN;
+ if ((rdataset->attributes & DNS_RDATASETATTR_OPTOUT) != 0)
+ newheader->attributes |= RDATASET_ATTR_OPTOUT;
+ if ((rdataset->attributes & DNS_RDATASETATTR_NOQNAME) != 0) {
+ result = addnoqname(rbtdb, newheader, rdataset);
+ if (result != ISC_R_SUCCESS) {
+ free_rdataset(rbtdb, rbtdb->common.mctx,
+ newheader);
+ return (result);
+ }
+ }
+ if ((rdataset->attributes & DNS_RDATASETATTR_CLOSEST) != 0) {
+ result = addclosest(rbtdb, newheader, rdataset);
+ if (result != ISC_R_SUCCESS) {
+ free_rdataset(rbtdb, rbtdb->common.mctx,
+ newheader);
+ return (result);
+ }
+ }
+ }
+
+ /*
+ * If we're adding a delegation type (e.g. NS or DNAME for a zone,
+ * just DNAME for the cache), then we need to set the callback bit
+ * on the node.
+ */
+ if (delegating_type(rbtdb, rbtnode, rdataset->type))
+ delegating = ISC_TRUE;
+ else
+ delegating = ISC_FALSE;
+
+ /*
+ * If we're adding a delegation type or the DB is a cache in an overmem
+ * state, hold an exclusive lock on the tree. In the latter case
+ * the lock does not necessarily have to be acquired but it will help
+ * purge stale entries more effectively.
+ */
+ if (delegating || (IS_CACHE(rbtdb) && rbtdb->overmem)) {
+ tree_locked = ISC_TRUE;
+ RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+ }
+
+ if (IS_CACHE(rbtdb) && rbtdb->overmem)
+ overmem_purge(rbtdb, rbtnode->locknum, now, tree_locked);
+
+ NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_write);
+
+ if (rbtdb->rrsetstats != NULL) {
+ newheader->attributes |= RDATASET_ATTR_STATCOUNT;
+ update_rrsetstats(rbtdb, newheader, ISC_TRUE);
+ }
+
+ if (IS_CACHE(rbtdb)) {
+ if (tree_locked)
+ cleanup_dead_nodes(rbtdb, rbtnode->locknum);
+
+ header = isc_heap_element(rbtdb->heaps[rbtnode->locknum], 1);
+ if (header && header->rdh_ttl <= now - RBTDB_VIRTUAL)
+ expire_header(rbtdb, header, tree_locked);
+
+ /*
+ * If we've been holding a write lock on the tree just for
+ * cleaning, we can release it now. However, we still need the
+ * node lock.
+ */
+ if (tree_locked && !delegating) {
+ RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+ tree_locked = ISC_FALSE;
+ }
+ }
+
+ result = add(rbtdb, rbtnode, rbtversion, newheader, options, ISC_FALSE,
+ addedrdataset, now);
+ if (result == ISC_R_SUCCESS && delegating)
+ rbtnode->find_callback = 1;
+
+ NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_write);
+
+ if (tree_locked)
+ RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+
+ /*
+ * Update the zone's secure status. If version is non-NULL
+ * this is defered until closeversion() is called.
+ */
+ if (result == ISC_R_SUCCESS && version == NULL && !IS_CACHE(rbtdb))
+ iszonesecure(db, version, rbtdb->origin_node);
+
+ return (result);
+}
+
+static isc_result_t
+subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ dns_rdataset_t *rdataset, unsigned int options,
+ dns_rdataset_t *newrdataset)
+{
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node;
+ rbtdb_version_t *rbtversion = version;
+ rdatasetheader_t *topheader, *topheader_prev, *header, *newheader;
+ unsigned char *subresult;
+ isc_region_t region;
+ isc_result_t result;
+ rbtdb_changed_t *changed;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ if (rbtdb->common.methods == &zone_methods)
+ REQUIRE(((rbtnode->nsec3 &&
+ (rdataset->type == dns_rdatatype_nsec3 ||
+ rdataset->covers == dns_rdatatype_nsec3)) ||
+ (!rbtnode->nsec3 &&
+ rdataset->type != dns_rdatatype_nsec3 &&
+ rdataset->covers != dns_rdatatype_nsec3)));
+
+ result = dns_rdataslab_fromrdataset(rdataset, rbtdb->common.mctx,
+ &region,
+ sizeof(rdatasetheader_t));
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ newheader = (rdatasetheader_t *)region.base;
+ init_rdataset(rbtdb, newheader);
+ set_ttl(rbtdb, newheader, rdataset->ttl);
+ newheader->type = RBTDB_RDATATYPE_VALUE(rdataset->type,
+ rdataset->covers);
+ newheader->attributes = 0;
+ newheader->serial = rbtversion->serial;
+ newheader->trust = 0;
+ newheader->noqname = NULL;
+ newheader->closest = NULL;
+ newheader->count = init_count++;
+ newheader->additional_auth = NULL;
+ newheader->additional_glue = NULL;
+ newheader->last_used = 0;
+ newheader->node = rbtnode;
+ if ((rdataset->attributes & DNS_RDATASETATTR_RESIGN) != 0) {
+ newheader->attributes |= RDATASET_ATTR_RESIGN;
+ newheader->resign = rdataset->resign;
+ } else
+ newheader->resign = 0;
+
+ NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_write);
+
+ changed = add_changed(rbtdb, rbtversion, rbtnode);
+ if (changed == NULL) {
+ free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
+ NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_write);
+ return (ISC_R_NOMEMORY);
+ }
+
+ topheader_prev = NULL;
+ for (topheader = rbtnode->data;
+ topheader != NULL;
+ topheader = topheader->next) {
+ if (topheader->type == newheader->type)
+ break;
+ topheader_prev = topheader;
+ }
+ /*
+ * If header isn't NULL, we've found the right type. There may be
+ * IGNORE rdatasets between the top of the chain and the first real
+ * data. We skip over them.
+ */
+ header = topheader;
+ while (header != NULL && IGNORE(header))
+ header = header->down;
+ if (header != NULL && EXISTS(header)) {
+ unsigned int flags = 0;
+ subresult = NULL;
+ result = ISC_R_SUCCESS;
+ if ((options & DNS_DBSUB_EXACT) != 0) {
+ flags |= DNS_RDATASLAB_EXACT;
+ if (newheader->rdh_ttl != header->rdh_ttl)
+ result = DNS_R_NOTEXACT;
+ }
+ if (result == ISC_R_SUCCESS)
+ result = dns_rdataslab_subtract(
+ (unsigned char *)header,
+ (unsigned char *)newheader,
+ (unsigned int)(sizeof(*newheader)),
+ rbtdb->common.mctx,
+ rbtdb->common.rdclass,
+ (dns_rdatatype_t)header->type,
+ flags, &subresult);
+ if (result == ISC_R_SUCCESS) {
+ free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
+ newheader = (rdatasetheader_t *)subresult;
+ init_rdataset(rbtdb, newheader);
+ /*
+ * We have to set the serial since the rdataslab
+ * subtraction routine copies the reserved portion of
+ * header, not newheader.
+ */
+ newheader->serial = rbtversion->serial;
+ /*
+ * XXXJT: dns_rdataslab_subtract() copied the pointers
+ * to additional info. We need to clear these fields
+ * to avoid having duplicated references.
+ */
+ newheader->additional_auth = NULL;
+ newheader->additional_glue = NULL;
+ } else if (result == DNS_R_NXRRSET) {
+ /*
+ * This subtraction would remove all of the rdata;
+ * add a nonexistent header instead.
+ */
+ free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
+ newheader = new_rdataset(rbtdb, rbtdb->common.mctx);
+ if (newheader == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto unlock;
+ }
+ set_ttl(rbtdb, newheader, 0);
+ newheader->type = topheader->type;
+ newheader->attributes = RDATASET_ATTR_NONEXISTENT;
+ newheader->trust = 0;
+ newheader->serial = rbtversion->serial;
+ newheader->noqname = NULL;
+ newheader->closest = NULL;
+ newheader->count = 0;
+ newheader->additional_auth = NULL;
+ newheader->additional_glue = NULL;
+ newheader->node = rbtnode;
+ newheader->resign = 0;
+ newheader->last_used = 0;
+ } else {
+ free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
+ goto unlock;
+ }
+
+ /*
+ * If we're here, we want to link newheader in front of
+ * topheader.
+ */
+ INSIST(rbtversion->serial >= topheader->serial);
+ if (topheader_prev != NULL)
+ topheader_prev->next = newheader;
+ else
+ rbtnode->data = newheader;
+ newheader->next = topheader->next;
+ newheader->down = topheader;
+ topheader->next = newheader;
+ rbtnode->dirty = 1;
+ changed->dirty = ISC_TRUE;
+ } else {
+ /*
+ * The rdataset doesn't exist, so we don't need to do anything
+ * to satisfy the deletion request.
+ */
+ free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
+ if ((options & DNS_DBSUB_EXACT) != 0)
+ result = DNS_R_NOTEXACT;
+ else
+ result = DNS_R_UNCHANGED;
+ }
+
+ if (result == ISC_R_SUCCESS && newrdataset != NULL)
+ bind_rdataset(rbtdb, rbtnode, newheader, 0, newrdataset);
+
+ unlock:
+ NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_write);
+
+ /*
+ * Update the zone's secure status. If version is non-NULL
+ * this is defered until closeversion() is called.
+ */
+ if (result == ISC_R_SUCCESS && version == NULL && !IS_CACHE(rbtdb))
+ iszonesecure(db, rbtdb->current_version, rbtdb->origin_node);
+
+ return (result);
+}
+
+static isc_result_t
+deleterdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ dns_rdatatype_t type, dns_rdatatype_t covers)
+{
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node;
+ rbtdb_version_t *rbtversion = version;
+ isc_result_t result;
+ rdatasetheader_t *newheader;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ if (type == dns_rdatatype_any)
+ return (ISC_R_NOTIMPLEMENTED);
+ if (type == dns_rdatatype_rrsig && covers == 0)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ newheader = new_rdataset(rbtdb, rbtdb->common.mctx);
+ if (newheader == NULL)
+ return (ISC_R_NOMEMORY);
+ set_ttl(rbtdb, newheader, 0);
+ newheader->type = RBTDB_RDATATYPE_VALUE(type, covers);
+ newheader->attributes = RDATASET_ATTR_NONEXISTENT;
+ newheader->trust = 0;
+ newheader->noqname = NULL;
+ newheader->closest = NULL;
+ newheader->additional_auth = NULL;
+ newheader->additional_glue = NULL;
+ if (rbtversion != NULL)
+ newheader->serial = rbtversion->serial;
+ else
+ newheader->serial = 0;
+ newheader->count = 0;
+ newheader->last_used = 0;
+ newheader->node = rbtnode;
+
+ NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_write);
+
+ result = add(rbtdb, rbtnode, rbtversion, newheader, DNS_DBADD_FORCE,
+ ISC_FALSE, NULL, 0);
+
+ NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_write);
+
+ /*
+ * Update the zone's secure status. If version is non-NULL
+ * this is defered until closeversion() is called.
+ */
+ if (result == ISC_R_SUCCESS && version == NULL && !IS_CACHE(rbtdb))
+ iszonesecure(db, rbtdb->current_version, rbtdb->origin_node);
+
+ return (result);
+}
+
+static isc_result_t
+loading_addrdataset(void *arg, dns_name_t *name, dns_rdataset_t *rdataset) {
+ rbtdb_load_t *loadctx = arg;
+ dns_rbtdb_t *rbtdb = loadctx->rbtdb;
+ dns_rbtnode_t *node;
+ isc_result_t result;
+ isc_region_t region;
+ rdatasetheader_t *newheader;
+
+ /*
+ * This routine does no node locking. See comments in
+ * 'load' below for more information on loading and
+ * locking.
+ */
+
+
+ /*
+ * SOA records are only allowed at top of zone.
+ */
+ if (rdataset->type == dns_rdatatype_soa &&
+ !IS_CACHE(rbtdb) && !dns_name_equal(name, &rbtdb->common.origin))
+ return (DNS_R_NOTZONETOP);
+
+ if (rdataset->type != dns_rdatatype_nsec3 &&
+ rdataset->covers != dns_rdatatype_nsec3)
+ add_empty_wildcards(rbtdb, name);
+
+ if (dns_name_iswildcard(name)) {
+ /*
+ * NS record owners cannot legally be wild cards.
+ */
+ if (rdataset->type == dns_rdatatype_ns)
+ return (DNS_R_INVALIDNS);
+ /*
+ * NSEC3 record owners cannot legally be wild cards.
+ */
+ if (rdataset->type == dns_rdatatype_nsec3)
+ return (DNS_R_INVALIDNSEC3);
+ result = add_wildcard_magic(rbtdb, name);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ node = NULL;
+ if (rdataset->type == dns_rdatatype_nsec3 ||
+ rdataset->covers == dns_rdatatype_nsec3) {
+ result = dns_rbt_addnode(rbtdb->nsec3, name, &node);
+ if (result == ISC_R_SUCCESS)
+ node->nsec3 = 1;
+ } else {
+ result = dns_rbt_addnode(rbtdb->tree, name, &node);
+ if (result == ISC_R_SUCCESS)
+ node->nsec3 = 0;
+ }
+ if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS)
+ return (result);
+ if (result != ISC_R_EXISTS) {
+ dns_name_t foundname;
+ dns_name_init(&foundname, NULL);
+ dns_rbt_namefromnode(node, &foundname);
+#ifdef DNS_RBT_USEHASH
+ node->locknum = node->hashval % rbtdb->node_lock_count;
+#else
+ node->locknum = dns_name_hash(&foundname, ISC_TRUE) %
+ rbtdb->node_lock_count;
+#endif
+ }
+
+ result = dns_rdataslab_fromrdataset(rdataset, rbtdb->common.mctx,
+ &region,
+ sizeof(rdatasetheader_t));
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ newheader = (rdatasetheader_t *)region.base;
+ init_rdataset(rbtdb, newheader);
+ set_ttl(rbtdb, newheader,
+ rdataset->ttl + loadctx->now); /* XXX overflow check */
+ newheader->type = RBTDB_RDATATYPE_VALUE(rdataset->type,
+ rdataset->covers);
+ newheader->attributes = 0;
+ newheader->trust = rdataset->trust;
+ newheader->serial = 1;
+ newheader->noqname = NULL;
+ newheader->closest = NULL;
+ newheader->count = init_count++;
+ newheader->additional_auth = NULL;
+ newheader->additional_glue = NULL;
+ newheader->last_used = 0;
+ newheader->node = node;
+ if ((rdataset->attributes & DNS_RDATASETATTR_RESIGN) != 0) {
+ newheader->attributes |= RDATASET_ATTR_RESIGN;
+ newheader->resign = rdataset->resign;
+ } else
+ newheader->resign = 0;
+
+ result = add(rbtdb, node, rbtdb->current_version, newheader,
+ DNS_DBADD_MERGE, ISC_TRUE, NULL, 0);
+ if (result == ISC_R_SUCCESS &&
+ delegating_type(rbtdb, node, rdataset->type))
+ node->find_callback = 1;
+ else if (result == DNS_R_UNCHANGED)
+ result = ISC_R_SUCCESS;
+
+ return (result);
+}
+
+static isc_result_t
+beginload(dns_db_t *db, dns_addrdatasetfunc_t *addp, dns_dbload_t **dbloadp) {
+ rbtdb_load_t *loadctx;
+ dns_rbtdb_t *rbtdb;
+
+ rbtdb = (dns_rbtdb_t *)db;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ loadctx = isc_mem_get(rbtdb->common.mctx, sizeof(*loadctx));
+ if (loadctx == NULL)
+ return (ISC_R_NOMEMORY);
+
+ loadctx->rbtdb = rbtdb;
+ if (IS_CACHE(rbtdb))
+ isc_stdtime_get(&loadctx->now);
+ else
+ loadctx->now = 0;
+
+ RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write);
+
+ REQUIRE((rbtdb->attributes & (RBTDB_ATTR_LOADED|RBTDB_ATTR_LOADING))
+ == 0);
+ rbtdb->attributes |= RBTDB_ATTR_LOADING;
+
+ RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write);
+
+ *addp = loading_addrdataset;
+ *dbloadp = loadctx;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+endload(dns_db_t *db, dns_dbload_t **dbloadp) {
+ rbtdb_load_t *loadctx;
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+ REQUIRE(dbloadp != NULL);
+ loadctx = *dbloadp;
+ REQUIRE(loadctx->rbtdb == rbtdb);
+
+ RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write);
+
+ REQUIRE((rbtdb->attributes & RBTDB_ATTR_LOADING) != 0);
+ REQUIRE((rbtdb->attributes & RBTDB_ATTR_LOADED) == 0);
+
+ rbtdb->attributes &= ~RBTDB_ATTR_LOADING;
+ rbtdb->attributes |= RBTDB_ATTR_LOADED;
+
+ RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write);
+
+ /*
+ * If there's a KEY rdataset at the zone origin containing a
+ * zone key, we consider the zone secure.
+ */
+ if (! IS_CACHE(rbtdb))
+ iszonesecure(db, rbtdb->current_version, rbtdb->origin_node);
+
+ *dbloadp = NULL;
+
+ isc_mem_put(rbtdb->common.mctx, loadctx, sizeof(*loadctx));
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+dump(dns_db_t *db, dns_dbversion_t *version, const char *filename,
+ dns_masterformat_t masterformat) {
+ dns_rbtdb_t *rbtdb;
+
+ rbtdb = (dns_rbtdb_t *)db;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ return (dns_master_dump2(rbtdb->common.mctx, db, version,
+ &dns_master_style_default,
+ filename, masterformat));
+}
+
+static void
+delete_callback(void *data, void *arg) {
+ dns_rbtdb_t *rbtdb = arg;
+ rdatasetheader_t *current, *next;
+
+ for (current = data; current != NULL; current = next) {
+ next = current->next;
+ free_rdataset(rbtdb, rbtdb->common.mctx, current);
+ }
+}
+
+static isc_boolean_t
+issecure(dns_db_t *db) {
+ dns_rbtdb_t *rbtdb;
+ isc_boolean_t secure;
+
+ rbtdb = (dns_rbtdb_t *)db;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+ secure = ISC_TF(rbtdb->current_version->secure == dns_db_secure);
+ RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+
+ return (secure);
+}
+
+static isc_boolean_t
+isdnssec(dns_db_t *db) {
+ dns_rbtdb_t *rbtdb;
+ isc_boolean_t dnssec;
+
+ rbtdb = (dns_rbtdb_t *)db;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+ dnssec = ISC_TF(rbtdb->current_version->secure != dns_db_insecure);
+ RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+
+ return (dnssec);
+}
+
+static unsigned int
+nodecount(dns_db_t *db) {
+ dns_rbtdb_t *rbtdb;
+ unsigned int count;
+
+ rbtdb = (dns_rbtdb_t *)db;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+ count = dns_rbt_nodecount(rbtdb->tree);
+ RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+
+ return (count);
+}
+
+static void
+settask(dns_db_t *db, isc_task_t *task) {
+ dns_rbtdb_t *rbtdb;
+
+ rbtdb = (dns_rbtdb_t *)db;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write);
+ if (rbtdb->task != NULL)
+ isc_task_detach(&rbtdb->task);
+ if (task != NULL)
+ isc_task_attach(task, &rbtdb->task);
+ RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write);
+}
+
+static isc_boolean_t
+ispersistent(dns_db_t *db) {
+ UNUSED(db);
+ return (ISC_FALSE);
+}
+
+static isc_result_t
+getoriginnode(dns_db_t *db, dns_dbnode_t **nodep) {
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ dns_rbtnode_t *onode;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+ REQUIRE(nodep != NULL && *nodep == NULL);
+
+ /* Note that the access to origin_node doesn't require a DB lock */
+ onode = (dns_rbtnode_t *)rbtdb->origin_node;
+ if (onode != NULL) {
+ NODE_STRONGLOCK(&rbtdb->node_locks[onode->locknum].lock);
+ new_reference(rbtdb, onode);
+ NODE_STRONGUNLOCK(&rbtdb->node_locks[onode->locknum].lock);
+
+ *nodep = rbtdb->origin_node;
+ } else {
+ INSIST(IS_CACHE(rbtdb));
+ result = ISC_R_NOTFOUND;
+ }
+
+ return (result);
+}
+
+static isc_result_t
+getnsec3parameters(dns_db_t *db, dns_dbversion_t *version, dns_hash_t *hash,
+ isc_uint8_t *flags, isc_uint16_t *iterations,
+ unsigned char *salt, size_t *salt_length)
+{
+ dns_rbtdb_t *rbtdb;
+ isc_result_t result = ISC_R_NOTFOUND;
+ rbtdb_version_t *rbtversion = version;
+
+ rbtdb = (dns_rbtdb_t *)db;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+
+ if (rbtversion == NULL)
+ rbtversion = rbtdb->current_version;
+
+ if (rbtversion->havensec3) {
+ if (hash != NULL)
+ *hash = rbtversion->hash;
+ if (salt != NULL && salt_length != 0) {
+ REQUIRE(*salt_length > rbtversion->salt_length);
+ memcpy(salt, rbtversion->salt, rbtversion->salt_length);
+ }
+ if (salt_length != NULL)
+ *salt_length = rbtversion->salt_length;
+ if (iterations != NULL)
+ *iterations = rbtversion->iterations;
+ if (flags != NULL)
+ *flags = rbtversion->flags;
+ result = ISC_R_SUCCESS;
+ }
+ RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+
+ return (result);
+}
+
+static isc_result_t
+setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, isc_stdtime_t resign) {
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ isc_stdtime_t oldresign;
+ isc_result_t result = ISC_R_SUCCESS;
+ rdatasetheader_t *header;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+ REQUIRE(!IS_CACHE(rbtdb));
+ REQUIRE(rdataset != NULL);
+
+ header = rdataset->private3;
+ header--;
+
+ NODE_LOCK(&rbtdb->node_locks[header->node->locknum].lock,
+ isc_rwlocktype_write);
+
+ oldresign = header->resign;
+ header->resign = resign;
+ if (header->heap_index != 0) {
+ INSIST(RESIGN(header));
+ if (resign == 0) {
+ isc_heap_delete(rbtdb->heaps[header->node->locknum],
+ header->heap_index);
+ header->heap_index = 0;
+ } else if (resign < oldresign)
+ isc_heap_increased(rbtdb->heaps[header->node->locknum],
+ header->heap_index);
+ else
+ isc_heap_decreased(rbtdb->heaps[header->node->locknum],
+ header->heap_index);
+ } else if (resign && header->heap_index == 0) {
+ header->attributes |= RDATASET_ATTR_RESIGN;
+ result = resign_insert(rbtdb, header->node->locknum, header);
+ }
+ NODE_UNLOCK(&rbtdb->node_locks[header->node->locknum].lock,
+ isc_rwlocktype_write);
+ return (result);
+}
+
+static isc_result_t
+getsigningtime(dns_db_t *db, dns_rdataset_t *rdataset,
+ dns_name_t *foundname)
+{
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ rdatasetheader_t *header = NULL, *this;
+ unsigned int i;
+ isc_result_t result = ISC_R_NOTFOUND;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+
+ RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_read);
+
+ for (i = 0; i < rbtdb->node_lock_count; i++) {
+ this = isc_heap_element(rbtdb->heaps[i], 1);
+ if (this == NULL)
+ continue;
+ if (header == NULL)
+ header = this;
+ else if (isc_serial_lt(this->resign, header->resign))
+ header = this;
+ }
+
+ if (header == NULL)
+ goto unlock;
+
+ NODE_LOCK(&rbtdb->node_locks[header->node->locknum].lock,
+ isc_rwlocktype_read);
+
+ bind_rdataset(rbtdb, header->node, header, 0, rdataset);
+
+ if (foundname != NULL)
+ dns_rbt_fullnamefromnode(header->node, foundname);
+
+ NODE_UNLOCK(&rbtdb->node_locks[header->node->locknum].lock,
+ isc_rwlocktype_read);
+
+ result = ISC_R_SUCCESS;
+
+ unlock:
+ RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_read);
+
+ return (result);
+}
+
+static void
+resigned(dns_db_t *db, dns_rdataset_t *rdataset, dns_dbversion_t *version)
+{
+ rbtdb_version_t *rbtversion = (rbtdb_version_t *)version;
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ dns_rbtnode_t *node;
+ rdatasetheader_t *header;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+ REQUIRE(rdataset != NULL);
+ REQUIRE(rbtdb->future_version == rbtversion);
+ REQUIRE(rbtversion->writer);
+
+ node = rdataset->private2;
+ header = rdataset->private3;
+ header--;
+
+ RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_read);
+ NODE_LOCK(&rbtdb->node_locks[node->locknum].lock,
+ isc_rwlocktype_write);
+ /*
+ * Delete from heap and save to re-signed list so that it can
+ * be restored if we backout of this change.
+ */
+ new_reference(rbtdb, node);
+ isc_heap_delete(rbtdb->heaps[node->locknum], header->heap_index);
+ header->heap_index = 0;
+ ISC_LIST_APPEND(rbtversion->resigned_list, header, lru_link);
+
+ NODE_UNLOCK(&rbtdb->node_locks[node->locknum].lock,
+ isc_rwlocktype_write);
+ RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_read);
+}
+
+static dns_stats_t *
+getrrsetstats(dns_db_t *db) {
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+
+ REQUIRE(VALID_RBTDB(rbtdb));
+ REQUIRE(IS_CACHE(rbtdb)); /* current restriction */
+
+ return (rbtdb->rrsetstats);
+}
+
+static dns_dbmethods_t zone_methods = {
+ attach,
+ detach,
+ beginload,
+ endload,
+ dump,
+ currentversion,
+ newversion,
+ attachversion,
+ closeversion,
+ findnode,
+ zone_find,
+ zone_findzonecut,
+ attachnode,
+ detachnode,
+ expirenode,
+ printnode,
+ createiterator,
+ zone_findrdataset,
+ allrdatasets,
+ addrdataset,
+ subtractrdataset,
+ deleterdataset,
+ issecure,
+ nodecount,
+ ispersistent,
+ overmem,
+ settask,
+ getoriginnode,
+ NULL,
+ getnsec3parameters,
+ findnsec3node,
+ setsigningtime,
+ getsigningtime,
+ resigned,
+ isdnssec,
+ NULL
+};
+
+static dns_dbmethods_t cache_methods = {
+ attach,
+ detach,
+ beginload,
+ endload,
+ dump,
+ currentversion,
+ newversion,
+ attachversion,
+ closeversion,
+ findnode,
+ cache_find,
+ cache_findzonecut,
+ attachnode,
+ detachnode,
+ expirenode,
+ printnode,
+ createiterator,
+ cache_findrdataset,
+ allrdatasets,
+ addrdataset,
+ subtractrdataset,
+ deleterdataset,
+ issecure,
+ nodecount,
+ ispersistent,
+ overmem,
+ settask,
+ getoriginnode,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ isdnssec,
+ getrrsetstats
+};
+
+isc_result_t
+#ifdef DNS_RBTDB_VERSION64
+dns_rbtdb64_create
+#else
+dns_rbtdb_create
+#endif
+ (isc_mem_t *mctx, dns_name_t *origin, dns_dbtype_t type,
+ dns_rdataclass_t rdclass, unsigned int argc, char *argv[],
+ void *driverarg, dns_db_t **dbp)
+{
+ dns_rbtdb_t *rbtdb;
+ isc_result_t result;
+ int i;
+ dns_name_t name;
+ isc_boolean_t (*sooner)(void *, void *);
+
+ /* Keep the compiler happy. */
+ UNUSED(argc);
+ UNUSED(argv);
+ UNUSED(driverarg);
+
+ rbtdb = isc_mem_get(mctx, sizeof(*rbtdb));
+ if (rbtdb == NULL)
+ return (ISC_R_NOMEMORY);
+
+ memset(rbtdb, '\0', sizeof(*rbtdb));
+ dns_name_init(&rbtdb->common.origin, NULL);
+ rbtdb->common.attributes = 0;
+ if (type == dns_dbtype_cache) {
+ rbtdb->common.methods = &cache_methods;
+ rbtdb->common.attributes |= DNS_DBATTR_CACHE;
+ } else if (type == dns_dbtype_stub) {
+ rbtdb->common.methods = &zone_methods;
+ rbtdb->common.attributes |= DNS_DBATTR_STUB;
+ } else
+ rbtdb->common.methods = &zone_methods;
+ rbtdb->common.rdclass = rdclass;
+ rbtdb->common.mctx = NULL;
+
+ result = RBTDB_INITLOCK(&rbtdb->lock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_rbtdb;
+
+ result = isc_rwlock_init(&rbtdb->tree_lock, 0, 0);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_lock;
+
+ /*
+ * Initialize node_lock_count in a generic way to support future
+ * extension which allows the user to specify this value on creation.
+ * Note that when specified for a cache DB it must be larger than 1
+ * as commented with the definition of DEFAULT_CACHE_NODE_LOCK_COUNT.
+ */
+ if (rbtdb->node_lock_count == 0) {
+ if (IS_CACHE(rbtdb))
+ rbtdb->node_lock_count = DEFAULT_CACHE_NODE_LOCK_COUNT;
+ else
+ rbtdb->node_lock_count = DEFAULT_NODE_LOCK_COUNT;
+ } else if (rbtdb->node_lock_count < 2 && IS_CACHE(rbtdb)) {
+ result = ISC_R_RANGE;
+ goto cleanup_tree_lock;
+ }
+ INSIST(rbtdb->node_lock_count < (1 << DNS_RBT_LOCKLENGTH));
+ rbtdb->node_locks = isc_mem_get(mctx, rbtdb->node_lock_count *
+ sizeof(rbtdb_nodelock_t));
+ if (rbtdb->node_locks == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_tree_lock;
+ }
+
+ rbtdb->rrsetstats = NULL;
+ if (IS_CACHE(rbtdb)) {
+ result = dns_rdatasetstats_create(mctx, &rbtdb->rrsetstats);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_node_locks;
+ rbtdb->rdatasets = isc_mem_get(mctx, rbtdb->node_lock_count *
+ sizeof(rdatasetheaderlist_t));
+ if (rbtdb->rdatasets == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_rrsetstats;
+ }
+ for (i = 0; i < (int)rbtdb->node_lock_count; i++)
+ ISC_LIST_INIT(rbtdb->rdatasets[i]);
+ } else
+ rbtdb->rdatasets = NULL;
+
+ /*
+ * Create the heaps.
+ */
+ rbtdb->heaps = isc_mem_get(mctx, rbtdb->node_lock_count *
+ sizeof(isc_heap_t *));
+ if (rbtdb->heaps == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_rdatasets;
+ }
+ for (i = 0; i < (int)rbtdb->node_lock_count; i++)
+ rbtdb->heaps[i] = NULL;
+ sooner = IS_CACHE(rbtdb) ? ttl_sooner : resign_sooner;
+ for (i = 0; i < (int)rbtdb->node_lock_count; i++) {
+ result = isc_heap_create(mctx, sooner, set_index, 0,
+ &rbtdb->heaps[i]);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_heaps;
+ }
+
+ /*
+ * Create deadnode lists.
+ */
+ rbtdb->deadnodes = isc_mem_get(mctx, rbtdb->node_lock_count *
+ sizeof(rbtnodelist_t));
+ if (rbtdb->deadnodes == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_heaps;
+ }
+ for (i = 0; i < (int)rbtdb->node_lock_count; i++)
+ ISC_LIST_INIT(rbtdb->deadnodes[i]);
+
+ rbtdb->active = rbtdb->node_lock_count;
+
+ for (i = 0; i < (int)(rbtdb->node_lock_count); i++) {
+ result = NODE_INITLOCK(&rbtdb->node_locks[i].lock);
+ if (result == ISC_R_SUCCESS) {
+ result = isc_refcount_init(&rbtdb->node_locks[i].references, 0);
+ if (result != ISC_R_SUCCESS)
+ NODE_DESTROYLOCK(&rbtdb->node_locks[i].lock);
+ }
+ if (result != ISC_R_SUCCESS) {
+ while (i-- > 0) {
+ NODE_DESTROYLOCK(&rbtdb->node_locks[i].lock);
+ isc_refcount_decrement(&rbtdb->node_locks[i].references, NULL);
+ isc_refcount_destroy(&rbtdb->node_locks[i].references);
+ }
+ goto cleanup_deadnodes;
+ }
+ rbtdb->node_locks[i].exiting = ISC_FALSE;
+ }
+
+ /*
+ * Attach to the mctx. The database will persist so long as there
+ * are references to it, and attaching to the mctx ensures that our
+ * mctx won't disappear out from under us.
+ */
+ isc_mem_attach(mctx, &rbtdb->common.mctx);
+
+ /*
+ * Must be initalized before free_rbtdb() is called.
+ */
+ isc_ondestroy_init(&rbtdb->common.ondest);
+
+ /*
+ * Make a copy of the origin name.
+ */
+ result = dns_name_dupwithoffsets(origin, mctx, &rbtdb->common.origin);
+ if (result != ISC_R_SUCCESS) {
+ free_rbtdb(rbtdb, ISC_FALSE, NULL);
+ return (result);
+ }
+
+ /*
+ * Make the Red-Black Trees.
+ */
+ result = dns_rbt_create(mctx, delete_callback, rbtdb, &rbtdb->tree);
+ if (result != ISC_R_SUCCESS) {
+ free_rbtdb(rbtdb, ISC_FALSE, NULL);
+ return (result);
+ }
+
+ result = dns_rbt_create(mctx, delete_callback, rbtdb, &rbtdb->nsec3);
+ if (result != ISC_R_SUCCESS) {
+ free_rbtdb(rbtdb, ISC_FALSE, NULL);
+ return (result);
+ }
+
+ /*
+ * In order to set the node callback bit correctly in zone databases,
+ * we need to know if the node has the origin name of the zone.
+ * In loading_addrdataset() we could simply compare the new name
+ * to the origin name, but this is expensive. Also, we don't know the
+ * node name in addrdataset(), so we need another way of knowing the
+ * zone's top.
+ *
+ * We now explicitly create a node for the zone's origin, and then
+ * we simply remember the node's address. This is safe, because
+ * the top-of-zone node can never be deleted, nor can its address
+ * change.
+ */
+ if (!IS_CACHE(rbtdb)) {
+ rbtdb->origin_node = NULL;
+ result = dns_rbt_addnode(rbtdb->tree, &rbtdb->common.origin,
+ &rbtdb->origin_node);
+ if (result != ISC_R_SUCCESS) {
+ INSIST(result != ISC_R_EXISTS);
+ free_rbtdb(rbtdb, ISC_FALSE, NULL);
+ return (result);
+ }
+ rbtdb->origin_node->nsec3 = 0;
+ /*
+ * We need to give the origin node the right locknum.
+ */
+ dns_name_init(&name, NULL);
+ dns_rbt_namefromnode(rbtdb->origin_node, &name);
+#ifdef DNS_RBT_USEHASH
+ rbtdb->origin_node->locknum =
+ rbtdb->origin_node->hashval %
+ rbtdb->node_lock_count;
+#else
+ rbtdb->origin_node->locknum =
+ dns_name_hash(&name, ISC_TRUE) %
+ rbtdb->node_lock_count;
+#endif
+ }
+
+ /*
+ * Misc. Initialization.
+ */
+ result = isc_refcount_init(&rbtdb->references, 1);
+ if (result != ISC_R_SUCCESS) {
+ free_rbtdb(rbtdb, ISC_FALSE, NULL);
+ return (result);
+ }
+ rbtdb->attributes = 0;
+ rbtdb->overmem = ISC_FALSE;
+ rbtdb->task = NULL;
+
+ /*
+ * Version Initialization.
+ */
+ rbtdb->current_serial = 1;
+ rbtdb->least_serial = 1;
+ rbtdb->next_serial = 2;
+ rbtdb->current_version = allocate_version(mctx, 1, 1, ISC_FALSE);
+ if (rbtdb->current_version == NULL) {
+ isc_refcount_decrement(&rbtdb->references, NULL);
+ isc_refcount_destroy(&rbtdb->references);
+ free_rbtdb(rbtdb, ISC_FALSE, NULL);
+ return (ISC_R_NOMEMORY);
+ }
+ rbtdb->current_version->secure = dns_db_insecure;
+ rbtdb->current_version->havensec3 = ISC_FALSE;
+ rbtdb->current_version->flags = 0;
+ rbtdb->current_version->iterations = 0;
+ rbtdb->current_version->hash = 0;
+ rbtdb->current_version->salt_length = 0;
+ memset(rbtdb->current_version->salt, 0,
+ sizeof(rbtdb->current_version->salt));
+ rbtdb->future_version = NULL;
+ ISC_LIST_INIT(rbtdb->open_versions);
+ /*
+ * Keep the current version in the open list so that list operation
+ * won't happen in normal lookup operations.
+ */
+ PREPEND(rbtdb->open_versions, rbtdb->current_version, link);
+
+ rbtdb->common.magic = DNS_DB_MAGIC;
+ rbtdb->common.impmagic = RBTDB_MAGIC;
+
+ *dbp = (dns_db_t *)rbtdb;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup_deadnodes:
+ isc_mem_put(mctx, rbtdb->deadnodes,
+ rbtdb->node_lock_count * sizeof(rbtnodelist_t));
+
+ cleanup_heaps:
+ if (rbtdb->heaps != NULL) {
+ for (i = 0 ; i < (int)rbtdb->node_lock_count ; i++)
+ if (rbtdb->heaps[i] != NULL)
+ isc_heap_destroy(&rbtdb->heaps[i]);
+ isc_mem_put(mctx, rbtdb->heaps,
+ rbtdb->node_lock_count * sizeof(isc_heap_t *));
+ }
+
+ cleanup_rdatasets:
+ if (rbtdb->rdatasets != NULL)
+ isc_mem_put(mctx, rbtdb->rdatasets, rbtdb->node_lock_count *
+ sizeof(rdatasetheaderlist_t));
+ cleanup_rrsetstats:
+ if (rbtdb->rrsetstats != NULL)
+ dns_stats_detach(&rbtdb->rrsetstats);
+
+ cleanup_node_locks:
+ isc_mem_put(mctx, rbtdb->node_locks,
+ rbtdb->node_lock_count * sizeof(rbtdb_nodelock_t));
+
+ cleanup_tree_lock:
+ isc_rwlock_destroy(&rbtdb->tree_lock);
+
+ cleanup_lock:
+ RBTDB_DESTROYLOCK(&rbtdb->lock);
+
+ cleanup_rbtdb:
+ isc_mem_put(mctx, rbtdb, sizeof(*rbtdb));
+ return (result);
+}
+
+
+/*
+ * Slabbed Rdataset Methods
+ */
+
+static void
+rdataset_disassociate(dns_rdataset_t *rdataset) {
+ dns_db_t *db = rdataset->private1;
+ dns_dbnode_t *node = rdataset->private2;
+
+ detachnode(db, &node);
+}
+
+static isc_result_t
+rdataset_first(dns_rdataset_t *rdataset) {
+ unsigned char *raw = rdataset->private3; /* RDATASLAB */
+ unsigned int count;
+
+ count = raw[0] * 256 + raw[1];
+ if (count == 0) {
+ rdataset->private5 = NULL;
+ return (ISC_R_NOMORE);
+ }
+
+#if DNS_RDATASET_FIXED
+ if ((rdataset->attributes & DNS_RDATASETATTR_LOADORDER) == 0)
+ raw += 2 + (4 * count);
+ else
+#endif
+ raw += 2;
+
+ /*
+ * The privateuint4 field is the number of rdata beyond the
+ * cursor position, so we decrement the total count by one
+ * before storing it.
+ *
+ * If DNS_RDATASETATTR_LOADORDER is not set 'raw' points to the
+ * first record. If DNS_RDATASETATTR_LOADORDER is set 'raw' points
+ * to the first entry in the offset table.
+ */
+ count--;
+ rdataset->privateuint4 = count;
+ rdataset->private5 = raw;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+rdataset_next(dns_rdataset_t *rdataset) {
+ unsigned int count;
+ unsigned int length;
+ unsigned char *raw; /* RDATASLAB */
+
+ count = rdataset->privateuint4;
+ if (count == 0)
+ return (ISC_R_NOMORE);
+ count--;
+ rdataset->privateuint4 = count;
+
+ /*
+ * Skip forward one record (length + 4) or one offset (4).
+ */
+ raw = rdataset->private5;
+#if DNS_RDATASET_FIXED
+ if ((rdataset->attributes & DNS_RDATASETATTR_LOADORDER) == 0) {
+#endif
+ length = raw[0] * 256 + raw[1];
+ raw += length;
+#if DNS_RDATASET_FIXED
+ }
+ rdataset->private5 = raw + 4; /* length(2) + order(2) */
+#else
+ rdataset->private5 = raw + 2; /* length(2) */
+#endif
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) {
+ unsigned char *raw = rdataset->private5; /* RDATASLAB */
+#if DNS_RDATASET_FIXED
+ unsigned int offset;
+#endif
+ unsigned int length;
+ isc_region_t r;
+ unsigned int flags = 0;
+
+ REQUIRE(raw != NULL);
+
+ /*
+ * Find the start of the record if not already in private5
+ * then skip the length and order fields.
+ */
+#if DNS_RDATASET_FIXED
+ if ((rdataset->attributes & DNS_RDATASETATTR_LOADORDER) != 0) {
+ offset = (raw[0] << 24) + (raw[1] << 16) +
+ (raw[2] << 8) + raw[3];
+ raw = rdataset->private3;
+ raw += offset;
+ }
+#endif
+ length = raw[0] * 256 + raw[1];
+#if DNS_RDATASET_FIXED
+ raw += 4;
+#else
+ raw += 2;
+#endif
+ if (rdataset->type == dns_rdatatype_rrsig) {
+ if (*raw & DNS_RDATASLAB_OFFLINE)
+ flags |= DNS_RDATA_OFFLINE;
+ length--;
+ raw++;
+ }
+ r.length = length;
+ r.base = raw;
+ dns_rdata_fromregion(rdata, rdataset->rdclass, rdataset->type, &r);
+ rdata->flags |= flags;
+}
+
+static void
+rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target) {
+ dns_db_t *db = source->private1;
+ dns_dbnode_t *node = source->private2;
+ dns_dbnode_t *cloned_node = NULL;
+
+ attachnode(db, node, &cloned_node);
+ *target = *source;
+
+ /*
+ * Reset iterator state.
+ */
+ target->privateuint4 = 0;
+ target->private5 = NULL;
+}
+
+static unsigned int
+rdataset_count(dns_rdataset_t *rdataset) {
+ unsigned char *raw = rdataset->private3; /* RDATASLAB */
+ unsigned int count;
+
+ count = raw[0] * 256 + raw[1];
+
+ return (count);
+}
+
+static isc_result_t
+rdataset_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name,
+ dns_rdataset_t *nsec, dns_rdataset_t *nsecsig)
+{
+ dns_db_t *db = rdataset->private1;
+ dns_dbnode_t *node = rdataset->private2;
+ dns_dbnode_t *cloned_node;
+ struct noqname *noqname = rdataset->private6;
+
+ cloned_node = NULL;
+ attachnode(db, node, &cloned_node);
+ nsec->methods = &rdataset_methods;
+ nsec->rdclass = db->rdclass;
+ nsec->type = noqname->type;
+ nsec->covers = 0;
+ nsec->ttl = rdataset->ttl;
+ nsec->trust = rdataset->trust;
+ nsec->private1 = rdataset->private1;
+ nsec->private2 = rdataset->private2;
+ nsec->private3 = noqname->neg;
+ nsec->privateuint4 = 0;
+ nsec->private5 = NULL;
+ nsec->private6 = NULL;
+ nsec->private7 = NULL;
+
+ cloned_node = NULL;
+ attachnode(db, node, &cloned_node);
+ nsecsig->methods = &rdataset_methods;
+ nsecsig->rdclass = db->rdclass;
+ nsecsig->type = dns_rdatatype_rrsig;
+ nsecsig->covers = noqname->type;
+ nsecsig->ttl = rdataset->ttl;
+ nsecsig->trust = rdataset->trust;
+ nsecsig->private1 = rdataset->private1;
+ nsecsig->private2 = rdataset->private2;
+ nsecsig->private3 = noqname->negsig;
+ nsecsig->privateuint4 = 0;
+ nsecsig->private5 = NULL;
+ nsec->private6 = NULL;
+ nsec->private7 = NULL;
+
+ dns_name_clone(&noqname->name, name);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+rdataset_getclosest(dns_rdataset_t *rdataset, dns_name_t *name,
+ dns_rdataset_t *nsec, dns_rdataset_t *nsecsig)
+{
+ dns_db_t *db = rdataset->private1;
+ dns_dbnode_t *node = rdataset->private2;
+ dns_dbnode_t *cloned_node;
+ struct noqname *closest = rdataset->private7;
+
+ cloned_node = NULL;
+ attachnode(db, node, &cloned_node);
+ nsec->methods = &rdataset_methods;
+ nsec->rdclass = db->rdclass;
+ nsec->type = closest->type;
+ nsec->covers = 0;
+ nsec->ttl = rdataset->ttl;
+ nsec->trust = rdataset->trust;
+ nsec->private1 = rdataset->private1;
+ nsec->private2 = rdataset->private2;
+ nsec->private3 = closest->neg;
+ nsec->privateuint4 = 0;
+ nsec->private5 = NULL;
+ nsec->private6 = NULL;
+ nsec->private7 = NULL;
+
+ cloned_node = NULL;
+ attachnode(db, node, &cloned_node);
+ nsecsig->methods = &rdataset_methods;
+ nsecsig->rdclass = db->rdclass;
+ nsecsig->type = dns_rdatatype_rrsig;
+ nsecsig->covers = closest->type;
+ nsecsig->ttl = rdataset->ttl;
+ nsecsig->trust = rdataset->trust;
+ nsecsig->private1 = rdataset->private1;
+ nsecsig->private2 = rdataset->private2;
+ nsecsig->private3 = closest->negsig;
+ nsecsig->privateuint4 = 0;
+ nsecsig->private5 = NULL;
+ nsec->private6 = NULL;
+ nsec->private7 = NULL;
+
+ dns_name_clone(&closest->name, name);
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Rdataset Iterator Methods
+ */
+
+static void
+rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp) {
+ rbtdb_rdatasetiter_t *rbtiterator;
+
+ rbtiterator = (rbtdb_rdatasetiter_t *)(*iteratorp);
+
+ if (rbtiterator->common.version != NULL)
+ closeversion(rbtiterator->common.db,
+ &rbtiterator->common.version, ISC_FALSE);
+ detachnode(rbtiterator->common.db, &rbtiterator->common.node);
+ isc_mem_put(rbtiterator->common.db->mctx, rbtiterator,
+ sizeof(*rbtiterator));
+
+ *iteratorp = NULL;
+}
+
+static isc_result_t
+rdatasetiter_first(dns_rdatasetiter_t *iterator) {
+ rbtdb_rdatasetiter_t *rbtiterator = (rbtdb_rdatasetiter_t *)iterator;
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)(rbtiterator->common.db);
+ dns_rbtnode_t *rbtnode = rbtiterator->common.node;
+ rbtdb_version_t *rbtversion = rbtiterator->common.version;
+ rdatasetheader_t *header, *top_next;
+ rbtdb_serial_t serial;
+ isc_stdtime_t now;
+
+ if (IS_CACHE(rbtdb)) {
+ serial = 1;
+ now = rbtiterator->common.now;
+ } else {
+ serial = rbtversion->serial;
+ now = 0;
+ }
+
+ NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_read);
+
+ for (header = rbtnode->data; header != NULL; header = top_next) {
+ top_next = header->next;
+ do {
+ if (header->serial <= serial && !IGNORE(header)) {
+ /*
+ * Is this a "this rdataset doesn't exist"
+ * record? Or is it too old in the cache?
+ *
+ * Note: unlike everywhere else, we
+ * check for now > header->rdh_ttl instead
+ * of now >= header->rdh_ttl. This allows
+ * ANY and RRSIG queries for 0 TTL
+ * rdatasets to work.
+ */
+ if (NONEXISTENT(header) ||
+ (now != 0 && now > header->rdh_ttl))
+ header = NULL;
+ break;
+ } else
+ header = header->down;
+ } while (header != NULL);
+ if (header != NULL)
+ break;
+ }
+
+ NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_read);
+
+ rbtiterator->current = header;
+
+ if (header == NULL)
+ return (ISC_R_NOMORE);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+rdatasetiter_next(dns_rdatasetiter_t *iterator) {
+ rbtdb_rdatasetiter_t *rbtiterator = (rbtdb_rdatasetiter_t *)iterator;
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)(rbtiterator->common.db);
+ dns_rbtnode_t *rbtnode = rbtiterator->common.node;
+ rbtdb_version_t *rbtversion = rbtiterator->common.version;
+ rdatasetheader_t *header, *top_next;
+ rbtdb_serial_t serial;
+ isc_stdtime_t now;
+ rbtdb_rdatatype_t type, negtype;
+ dns_rdatatype_t rdtype, covers;
+
+ header = rbtiterator->current;
+ if (header == NULL)
+ return (ISC_R_NOMORE);
+
+ if (IS_CACHE(rbtdb)) {
+ serial = 1;
+ now = rbtiterator->common.now;
+ } else {
+ serial = rbtversion->serial;
+ now = 0;
+ }
+
+ NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_read);
+
+ type = header->type;
+ rdtype = RBTDB_RDATATYPE_BASE(header->type);
+ if (rdtype == 0) {
+ covers = RBTDB_RDATATYPE_EXT(header->type);
+ negtype = RBTDB_RDATATYPE_VALUE(covers, 0);
+ } else
+ negtype = RBTDB_RDATATYPE_VALUE(0, rdtype);
+ for (header = header->next; header != NULL; header = top_next) {
+ top_next = header->next;
+ /*
+ * If not walking back up the down list.
+ */
+ if (header->type != type && header->type != negtype) {
+ do {
+ if (header->serial <= serial &&
+ !IGNORE(header)) {
+ /*
+ * Is this a "this rdataset doesn't
+ * exist" record?
+ *
+ * Note: unlike everywhere else, we
+ * check for now > header->ttl instead
+ * of now >= header->ttl. This allows
+ * ANY and RRSIG queries for 0 TTL
+ * rdatasets to work.
+ */
+ if ((header->attributes &
+ RDATASET_ATTR_NONEXISTENT) != 0 ||
+ (now != 0 && now > header->rdh_ttl))
+ header = NULL;
+ break;
+ } else
+ header = header->down;
+ } while (header != NULL);
+ if (header != NULL)
+ break;
+ }
+ }
+
+ NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_read);
+
+ rbtiterator->current = header;
+
+ if (header == NULL)
+ return (ISC_R_NOMORE);
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+rdatasetiter_current(dns_rdatasetiter_t *iterator, dns_rdataset_t *rdataset) {
+ rbtdb_rdatasetiter_t *rbtiterator = (rbtdb_rdatasetiter_t *)iterator;
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)(rbtiterator->common.db);
+ dns_rbtnode_t *rbtnode = rbtiterator->common.node;
+ rdatasetheader_t *header;
+
+ header = rbtiterator->current;
+ REQUIRE(header != NULL);
+
+ NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_read);
+
+ bind_rdataset(rbtdb, rbtnode, header, rbtiterator->common.now,
+ rdataset);
+
+ NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_read);
+}
+
+
+/*
+ * Database Iterator Methods
+ */
+
+static inline void
+reference_iter_node(rbtdb_dbiterator_t *rbtdbiter) {
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)rbtdbiter->common.db;
+ dns_rbtnode_t *node = rbtdbiter->node;
+
+ if (node == NULL)
+ return;
+
+ INSIST(rbtdbiter->tree_locked != isc_rwlocktype_none);
+ reactivate_node(rbtdb, node, rbtdbiter->tree_locked);
+}
+
+static inline void
+dereference_iter_node(rbtdb_dbiterator_t *rbtdbiter) {
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)rbtdbiter->common.db;
+ dns_rbtnode_t *node = rbtdbiter->node;
+ nodelock_t *lock;
+
+ if (node == NULL)
+ return;
+
+ lock = &rbtdb->node_locks[node->locknum].lock;
+ NODE_LOCK(lock, isc_rwlocktype_read);
+ decrement_reference(rbtdb, node, 0, isc_rwlocktype_read,
+ rbtdbiter->tree_locked);
+ NODE_UNLOCK(lock, isc_rwlocktype_read);
+
+ rbtdbiter->node = NULL;
+}
+
+static void
+flush_deletions(rbtdb_dbiterator_t *rbtdbiter) {
+ dns_rbtnode_t *node;
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)rbtdbiter->common.db;
+ isc_boolean_t was_read_locked = ISC_FALSE;
+ nodelock_t *lock;
+ int i;
+
+ if (rbtdbiter->delete != 0) {
+ /*
+ * Note that "%d node of %d in tree" can report things like
+ * "flush_deletions: 59 nodes of 41 in tree". This means
+ * That some nodes appear on the deletions list more than
+ * once. Only the last occurence will actually be deleted.
+ */
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1),
+ "flush_deletions: %d nodes of %d in tree",
+ rbtdbiter->delete,
+ dns_rbt_nodecount(rbtdb->tree));
+
+ if (rbtdbiter->tree_locked == isc_rwlocktype_read) {
+ RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+ was_read_locked = ISC_TRUE;
+ }
+ RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+ rbtdbiter->tree_locked = isc_rwlocktype_write;
+
+ for (i = 0; i < rbtdbiter->delete; i++) {
+ node = rbtdbiter->deletions[i];
+ lock = &rbtdb->node_locks[node->locknum].lock;
+
+ NODE_LOCK(lock, isc_rwlocktype_read);
+ decrement_reference(rbtdb, node, 0,
+ isc_rwlocktype_read,
+ rbtdbiter->tree_locked);
+ NODE_UNLOCK(lock, isc_rwlocktype_read);
+ }
+
+ rbtdbiter->delete = 0;
+
+ RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+ if (was_read_locked) {
+ RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+ rbtdbiter->tree_locked = isc_rwlocktype_read;
+
+ } else {
+ rbtdbiter->tree_locked = isc_rwlocktype_none;
+ }
+ }
+}
+
+static inline void
+resume_iteration(rbtdb_dbiterator_t *rbtdbiter) {
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)rbtdbiter->common.db;
+
+ REQUIRE(rbtdbiter->paused);
+ REQUIRE(rbtdbiter->tree_locked == isc_rwlocktype_none);
+
+ RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+ rbtdbiter->tree_locked = isc_rwlocktype_read;
+
+ rbtdbiter->paused = ISC_FALSE;
+}
+
+static void
+dbiterator_destroy(dns_dbiterator_t **iteratorp) {
+ rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)(*iteratorp);
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)rbtdbiter->common.db;
+ dns_db_t *db = NULL;
+
+ if (rbtdbiter->tree_locked == isc_rwlocktype_read) {
+ RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+ rbtdbiter->tree_locked = isc_rwlocktype_none;
+ } else
+ INSIST(rbtdbiter->tree_locked == isc_rwlocktype_none);
+
+ dereference_iter_node(rbtdbiter);
+
+ flush_deletions(rbtdbiter);
+
+ dns_db_attach(rbtdbiter->common.db, &db);
+ dns_db_detach(&rbtdbiter->common.db);
+
+ dns_rbtnodechain_reset(&rbtdbiter->chain);
+ dns_rbtnodechain_reset(&rbtdbiter->nsec3chain);
+ isc_mem_put(db->mctx, rbtdbiter, sizeof(*rbtdbiter));
+ dns_db_detach(&db);
+
+ *iteratorp = NULL;
+}
+
+static isc_result_t
+dbiterator_first(dns_dbiterator_t *iterator) {
+ isc_result_t result;
+ rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator;
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db;
+ dns_name_t *name, *origin;
+
+ if (rbtdbiter->result != ISC_R_SUCCESS &&
+ rbtdbiter->result != ISC_R_NOMORE)
+ return (rbtdbiter->result);
+
+ if (rbtdbiter->paused)
+ resume_iteration(rbtdbiter);
+
+ dereference_iter_node(rbtdbiter);
+
+ name = dns_fixedname_name(&rbtdbiter->name);
+ origin = dns_fixedname_name(&rbtdbiter->origin);
+ dns_rbtnodechain_reset(&rbtdbiter->chain);
+ dns_rbtnodechain_reset(&rbtdbiter->nsec3chain);
+
+ if (rbtdbiter->nsec3only) {
+ rbtdbiter->current = &rbtdbiter->nsec3chain;
+ result = dns_rbtnodechain_first(rbtdbiter->current,
+ rbtdb->nsec3, name, origin);
+ } else {
+ rbtdbiter->current = &rbtdbiter->chain;
+ result = dns_rbtnodechain_first(rbtdbiter->current,
+ rbtdb->tree, name, origin);
+ if (!rbtdbiter->nonsec3 && result == ISC_R_NOTFOUND) {
+ rbtdbiter->current = &rbtdbiter->nsec3chain;
+ result = dns_rbtnodechain_first(rbtdbiter->current,
+ rbtdb->nsec3, name,
+ origin);
+ }
+ }
+ if (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) {
+ result = dns_rbtnodechain_current(rbtdbiter->current, NULL,
+ NULL, &rbtdbiter->node);
+ if (result == ISC_R_SUCCESS) {
+ rbtdbiter->new_origin = ISC_TRUE;
+ reference_iter_node(rbtdbiter);
+ }
+ } else {
+ INSIST(result == ISC_R_NOTFOUND);
+ result = ISC_R_NOMORE; /* The tree is empty. */
+ }
+
+ rbtdbiter->result = result;
+
+ return (result);
+}
+
+static isc_result_t
+dbiterator_last(dns_dbiterator_t *iterator) {
+ isc_result_t result;
+ rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator;
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db;
+ dns_name_t *name, *origin;
+
+ if (rbtdbiter->result != ISC_R_SUCCESS &&
+ rbtdbiter->result != ISC_R_NOMORE)
+ return (rbtdbiter->result);
+
+ if (rbtdbiter->paused)
+ resume_iteration(rbtdbiter);
+
+ dereference_iter_node(rbtdbiter);
+
+ name = dns_fixedname_name(&rbtdbiter->name);
+ origin = dns_fixedname_name(&rbtdbiter->origin);
+ dns_rbtnodechain_reset(&rbtdbiter->chain);
+ dns_rbtnodechain_reset(&rbtdbiter->nsec3chain);
+
+ result = ISC_R_NOTFOUND;
+ if (rbtdbiter->nsec3only && !rbtdbiter->nonsec3) {
+ rbtdbiter->current = &rbtdbiter->nsec3chain;
+ result = dns_rbtnodechain_last(rbtdbiter->current,
+ rbtdb->nsec3, name, origin);
+ }
+ if (!rbtdbiter->nsec3only && result == ISC_R_NOTFOUND) {
+ rbtdbiter->current = &rbtdbiter->chain;
+ result = dns_rbtnodechain_last(rbtdbiter->current, rbtdb->tree,
+ name, origin);
+ }
+ if (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) {
+ result = dns_rbtnodechain_current(rbtdbiter->current, NULL,
+ NULL, &rbtdbiter->node);
+ if (result == ISC_R_SUCCESS) {
+ rbtdbiter->new_origin = ISC_TRUE;
+ reference_iter_node(rbtdbiter);
+ }
+ } else {
+ INSIST(result == ISC_R_NOTFOUND);
+ result = ISC_R_NOMORE; /* The tree is empty. */
+ }
+
+ rbtdbiter->result = result;
+
+ return (result);
+}
+
+static isc_result_t
+dbiterator_seek(dns_dbiterator_t *iterator, dns_name_t *name) {
+ isc_result_t result;
+ rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator;
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db;
+ dns_name_t *iname, *origin;
+
+ if (rbtdbiter->result != ISC_R_SUCCESS &&
+ rbtdbiter->result != ISC_R_NOTFOUND &&
+ rbtdbiter->result != ISC_R_NOMORE)
+ return (rbtdbiter->result);
+
+ if (rbtdbiter->paused)
+ resume_iteration(rbtdbiter);
+
+ dereference_iter_node(rbtdbiter);
+
+ iname = dns_fixedname_name(&rbtdbiter->name);
+ origin = dns_fixedname_name(&rbtdbiter->origin);
+ dns_rbtnodechain_reset(&rbtdbiter->chain);
+ dns_rbtnodechain_reset(&rbtdbiter->nsec3chain);
+
+ if (rbtdbiter->nsec3only) {
+ rbtdbiter->current = &rbtdbiter->nsec3chain;
+ result = dns_rbt_findnode(rbtdb->nsec3, name, NULL,
+ &rbtdbiter->node,
+ rbtdbiter->current,
+ DNS_RBTFIND_EMPTYDATA, NULL, NULL);
+ } else if (rbtdbiter->nonsec3) {
+ rbtdbiter->current = &rbtdbiter->chain;
+ result = dns_rbt_findnode(rbtdb->tree, name, NULL,
+ &rbtdbiter->node,
+ rbtdbiter->current,
+ DNS_RBTFIND_EMPTYDATA, NULL, NULL);
+ } else {
+ /*
+ * Stay on main chain if not found on either chain.
+ */
+ rbtdbiter->current = &rbtdbiter->chain;
+ result = dns_rbt_findnode(rbtdb->tree, name, NULL,
+ &rbtdbiter->node,
+ rbtdbiter->current,
+ DNS_RBTFIND_EMPTYDATA, NULL, NULL);
+ if (result == DNS_R_PARTIALMATCH) {
+ dns_rbtnode_t *node = NULL;
+ result = dns_rbt_findnode(rbtdb->nsec3, name, NULL,
+ &node, &rbtdbiter->nsec3chain,
+ DNS_RBTFIND_EMPTYDATA,
+ NULL, NULL);
+ if (result == ISC_R_SUCCESS) {
+ rbtdbiter->node = node;
+ rbtdbiter->current = &rbtdbiter->nsec3chain;
+ }
+ }
+ }
+
+#if 1
+ if (result == ISC_R_SUCCESS) {
+ result = dns_rbtnodechain_current(rbtdbiter->current, iname,
+ origin, NULL);
+ if (result == ISC_R_SUCCESS) {
+ rbtdbiter->new_origin = ISC_TRUE;
+ reference_iter_node(rbtdbiter);
+ }
+ } else if (result == DNS_R_PARTIALMATCH) {
+ result = ISC_R_NOTFOUND;
+ rbtdbiter->node = NULL;
+ }
+
+ rbtdbiter->result = result;
+#else
+ if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
+ isc_result_t tresult;
+ tresult = dns_rbtnodechain_current(rbtdbiter->current, iname,
+ origin, NULL);
+ if (tresult == ISC_R_SUCCESS) {
+ rbtdbiter->new_origin = ISC_TRUE;
+ reference_iter_node(rbtdbiter);
+ } else {
+ result = tresult;
+ rbtdbiter->node = NULL;
+ }
+ } else
+ rbtdbiter->node = NULL;
+
+ rbtdbiter->result = (result == DNS_R_PARTIALMATCH) ?
+ ISC_R_SUCCESS : result;
+#endif
+
+ return (result);
+}
+
+static isc_result_t
+dbiterator_prev(dns_dbiterator_t *iterator) {
+ isc_result_t result;
+ rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator;
+ dns_name_t *name, *origin;
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db;
+
+ REQUIRE(rbtdbiter->node != NULL);
+
+ if (rbtdbiter->result != ISC_R_SUCCESS)
+ return (rbtdbiter->result);
+
+ if (rbtdbiter->paused)
+ resume_iteration(rbtdbiter);
+
+ name = dns_fixedname_name(&rbtdbiter->name);
+ origin = dns_fixedname_name(&rbtdbiter->origin);
+ result = dns_rbtnodechain_prev(rbtdbiter->current, name, origin);
+ if (result == ISC_R_NOMORE && !rbtdbiter->nsec3only &&
+ !rbtdbiter->nonsec3 &&
+ &rbtdbiter->nsec3chain == rbtdbiter->current) {
+ rbtdbiter->current = &rbtdbiter->chain;
+ dns_rbtnodechain_reset(rbtdbiter->current);
+ result = dns_rbtnodechain_last(rbtdbiter->current, rbtdb->tree,
+ name, origin);
+ if (result == ISC_R_NOTFOUND)
+ result = ISC_R_NOMORE;
+ }
+
+ dereference_iter_node(rbtdbiter);
+
+ if (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
+ rbtdbiter->new_origin = ISC_TF(result == DNS_R_NEWORIGIN);
+ result = dns_rbtnodechain_current(rbtdbiter->current, NULL,
+ NULL, &rbtdbiter->node);
+ }
+
+ if (result == ISC_R_SUCCESS)
+ reference_iter_node(rbtdbiter);
+
+ rbtdbiter->result = result;
+
+ return (result);
+}
+
+static isc_result_t
+dbiterator_next(dns_dbiterator_t *iterator) {
+ isc_result_t result;
+ rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator;
+ dns_name_t *name, *origin;
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db;
+
+ REQUIRE(rbtdbiter->node != NULL);
+
+ if (rbtdbiter->result != ISC_R_SUCCESS)
+ return (rbtdbiter->result);
+
+ if (rbtdbiter->paused)
+ resume_iteration(rbtdbiter);
+
+ name = dns_fixedname_name(&rbtdbiter->name);
+ origin = dns_fixedname_name(&rbtdbiter->origin);
+ result = dns_rbtnodechain_next(rbtdbiter->current, name, origin);
+ if (result == ISC_R_NOMORE && !rbtdbiter->nsec3only &&
+ !rbtdbiter->nonsec3 && &rbtdbiter->chain == rbtdbiter->current) {
+ rbtdbiter->current = &rbtdbiter->nsec3chain;
+ dns_rbtnodechain_reset(rbtdbiter->current);
+ result = dns_rbtnodechain_first(rbtdbiter->current,
+ rbtdb->nsec3, name, origin);
+ if (result == ISC_R_NOTFOUND)
+ result = ISC_R_NOMORE;
+ }
+
+ dereference_iter_node(rbtdbiter);
+
+ if (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
+ rbtdbiter->new_origin = ISC_TF(result == DNS_R_NEWORIGIN);
+ result = dns_rbtnodechain_current(rbtdbiter->current, NULL,
+ NULL, &rbtdbiter->node);
+ }
+ if (result == ISC_R_SUCCESS)
+ reference_iter_node(rbtdbiter);
+
+ rbtdbiter->result = result;
+
+ return (result);
+}
+
+static isc_result_t
+dbiterator_current(dns_dbiterator_t *iterator, dns_dbnode_t **nodep,
+ dns_name_t *name)
+{
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db;
+ rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator;
+ dns_rbtnode_t *node = rbtdbiter->node;
+ isc_result_t result;
+ dns_name_t *nodename = dns_fixedname_name(&rbtdbiter->name);
+ dns_name_t *origin = dns_fixedname_name(&rbtdbiter->origin);
+
+ REQUIRE(rbtdbiter->result == ISC_R_SUCCESS);
+ REQUIRE(rbtdbiter->node != NULL);
+
+ if (rbtdbiter->paused)
+ resume_iteration(rbtdbiter);
+
+ if (name != NULL) {
+ if (rbtdbiter->common.relative_names)
+ origin = NULL;
+ result = dns_name_concatenate(nodename, origin, name, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (rbtdbiter->common.relative_names && rbtdbiter->new_origin)
+ result = DNS_R_NEWORIGIN;
+ } else
+ result = ISC_R_SUCCESS;
+
+ NODE_STRONGLOCK(&rbtdb->node_locks[node->locknum].lock);
+ new_reference(rbtdb, node);
+ NODE_STRONGUNLOCK(&rbtdb->node_locks[node->locknum].lock);
+
+ *nodep = rbtdbiter->node;
+
+ if (iterator->cleaning && result == ISC_R_SUCCESS) {
+ isc_result_t expire_result;
+
+ /*
+ * If the deletion array is full, flush it before trying
+ * to expire the current node. The current node can't
+ * fully deleted while the iteration cursor is still on it.
+ */
+ if (rbtdbiter->delete == DELETION_BATCH_MAX)
+ flush_deletions(rbtdbiter);
+
+ expire_result = expirenode(iterator->db, *nodep, 0);
+
+ /*
+ * expirenode() currently always returns success.
+ */
+ if (expire_result == ISC_R_SUCCESS && node->down == NULL) {
+ unsigned int refs;
+
+ rbtdbiter->deletions[rbtdbiter->delete++] = node;
+ NODE_STRONGLOCK(&rbtdb->node_locks[node->locknum].lock);
+ dns_rbtnode_refincrement(node, &refs);
+ INSIST(refs != 0);
+ NODE_STRONGUNLOCK(&rbtdb->node_locks[node->locknum].lock);
+ }
+ }
+
+ return (result);
+}
+
+static isc_result_t
+dbiterator_pause(dns_dbiterator_t *iterator) {
+ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)iterator->db;
+ rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator;
+
+ if (rbtdbiter->result != ISC_R_SUCCESS &&
+ rbtdbiter->result != ISC_R_NOMORE)
+ return (rbtdbiter->result);
+
+ if (rbtdbiter->paused)
+ return (ISC_R_SUCCESS);
+
+ rbtdbiter->paused = ISC_TRUE;
+
+ if (rbtdbiter->tree_locked != isc_rwlocktype_none) {
+ INSIST(rbtdbiter->tree_locked == isc_rwlocktype_read);
+ RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+ rbtdbiter->tree_locked = isc_rwlocktype_none;
+ }
+
+ flush_deletions(rbtdbiter);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name) {
+ rbtdb_dbiterator_t *rbtdbiter = (rbtdb_dbiterator_t *)iterator;
+ dns_name_t *origin = dns_fixedname_name(&rbtdbiter->origin);
+
+ if (rbtdbiter->result != ISC_R_SUCCESS)
+ return (rbtdbiter->result);
+
+ return (dns_name_copy(origin, name, NULL));
+}
+
+/*%
+ * Additional cache routines.
+ */
+static isc_result_t
+rdataset_getadditional(dns_rdataset_t *rdataset, dns_rdatasetadditional_t type,
+ dns_rdatatype_t qtype, dns_acache_t *acache,
+ dns_zone_t **zonep, dns_db_t **dbp,
+ dns_dbversion_t **versionp, dns_dbnode_t **nodep,
+ dns_name_t *fname, dns_message_t *msg,
+ isc_stdtime_t now)
+{
+ dns_rbtdb_t *rbtdb = rdataset->private1;
+ dns_rbtnode_t *rbtnode = rdataset->private2;
+ unsigned char *raw = rdataset->private3; /* RDATASLAB */
+ unsigned int current_count = rdataset->privateuint4;
+ unsigned int count;
+ rdatasetheader_t *header;
+ nodelock_t *nodelock;
+ unsigned int total_count;
+ acachectl_t *acarray;
+ dns_acacheentry_t *entry;
+ isc_result_t result;
+
+ UNUSED(qtype); /* we do not use this value at least for now */
+ UNUSED(acache);
+
+ header = (struct rdatasetheader *)(raw - sizeof(*header));
+
+ total_count = raw[0] * 256 + raw[1];
+ INSIST(total_count > current_count);
+ count = total_count - current_count - 1;
+
+ acarray = NULL;
+
+ nodelock = &rbtdb->node_locks[rbtnode->locknum].lock;
+ NODE_LOCK(nodelock, isc_rwlocktype_read);
+
+ switch (type) {
+ case dns_rdatasetadditional_fromauth:
+ acarray = header->additional_auth;
+ break;
+ case dns_rdatasetadditional_fromcache:
+ acarray = NULL;
+ break;
+ case dns_rdatasetadditional_fromglue:
+ acarray = header->additional_glue;
+ break;
+ default:
+ INSIST(0);
+ }
+
+ if (acarray == NULL) {
+ if (type != dns_rdatasetadditional_fromcache)
+ dns_acache_countquerymiss(acache);
+ NODE_UNLOCK(nodelock, isc_rwlocktype_read);
+ return (ISC_R_NOTFOUND);
+ }
+
+ if (acarray[count].entry == NULL) {
+ dns_acache_countquerymiss(acache);
+ NODE_UNLOCK(nodelock, isc_rwlocktype_read);
+ return (ISC_R_NOTFOUND);
+ }
+
+ entry = NULL;
+ dns_acache_attachentry(acarray[count].entry, &entry);
+
+ NODE_UNLOCK(nodelock, isc_rwlocktype_read);
+
+ result = dns_acache_getentry(entry, zonep, dbp, versionp,
+ nodep, fname, msg, now);
+
+ dns_acache_detachentry(&entry);
+
+ return (result);
+}
+
+static void
+acache_callback(dns_acacheentry_t *entry, void **arg) {
+ dns_rbtdb_t *rbtdb;
+ dns_rbtnode_t *rbtnode;
+ nodelock_t *nodelock;
+ acachectl_t *acarray = NULL;
+ acache_cbarg_t *cbarg;
+ unsigned int count;
+
+ REQUIRE(arg != NULL);
+ cbarg = *arg;
+
+ /*
+ * The caller must hold the entry lock.
+ */
+
+ rbtdb = (dns_rbtdb_t *)cbarg->db;
+ rbtnode = (dns_rbtnode_t *)cbarg->node;
+
+ nodelock = &rbtdb->node_locks[rbtnode->locknum].lock;
+ NODE_LOCK(nodelock, isc_rwlocktype_write);
+
+ switch (cbarg->type) {
+ case dns_rdatasetadditional_fromauth:
+ acarray = cbarg->header->additional_auth;
+ break;
+ case dns_rdatasetadditional_fromglue:
+ acarray = cbarg->header->additional_glue;
+ break;
+ default:
+ INSIST(0);
+ }
+
+ count = cbarg->count;
+ if (acarray != NULL && acarray[count].entry == entry) {
+ acarray[count].entry = NULL;
+ INSIST(acarray[count].cbarg == cbarg);
+ isc_mem_put(rbtdb->common.mctx, cbarg, sizeof(acache_cbarg_t));
+ acarray[count].cbarg = NULL;
+ } else
+ isc_mem_put(rbtdb->common.mctx, cbarg, sizeof(acache_cbarg_t));
+
+ dns_acache_detachentry(&entry);
+
+ NODE_UNLOCK(nodelock, isc_rwlocktype_write);
+
+ dns_db_detachnode((dns_db_t *)rbtdb, (dns_dbnode_t **)(void*)&rbtnode);
+ dns_db_detach((dns_db_t **)(void*)&rbtdb);
+
+ *arg = NULL;
+}
+
+static void
+acache_cancelentry(isc_mem_t *mctx, dns_acacheentry_t *entry,
+ acache_cbarg_t **cbargp)
+{
+ acache_cbarg_t *cbarg;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(entry != NULL);
+ REQUIRE(cbargp != NULL && *cbargp != NULL);
+
+ cbarg = *cbargp;
+
+ dns_acache_cancelentry(entry);
+ dns_db_detachnode(cbarg->db, &cbarg->node);
+ dns_db_detach(&cbarg->db);
+
+ isc_mem_put(mctx, cbarg, sizeof(acache_cbarg_t));
+
+ *cbargp = NULL;
+}
+
+static isc_result_t
+rdataset_setadditional(dns_rdataset_t *rdataset, dns_rdatasetadditional_t type,
+ dns_rdatatype_t qtype, dns_acache_t *acache,
+ dns_zone_t *zone, dns_db_t *db,
+ dns_dbversion_t *version, dns_dbnode_t *node,
+ dns_name_t *fname)
+{
+ dns_rbtdb_t *rbtdb = rdataset->private1;
+ dns_rbtnode_t *rbtnode = rdataset->private2;
+ unsigned char *raw = rdataset->private3; /* RDATASLAB */
+ unsigned int current_count = rdataset->privateuint4;
+ rdatasetheader_t *header;
+ unsigned int total_count, count;
+ nodelock_t *nodelock;
+ isc_result_t result;
+ acachectl_t *acarray;
+ dns_acacheentry_t *newentry, *oldentry = NULL;
+ acache_cbarg_t *newcbarg, *oldcbarg = NULL;
+
+ UNUSED(qtype);
+
+ if (type == dns_rdatasetadditional_fromcache)
+ return (ISC_R_SUCCESS);
+
+ header = (struct rdatasetheader *)(raw - sizeof(*header));
+
+ total_count = raw[0] * 256 + raw[1];
+ INSIST(total_count > current_count);
+ count = total_count - current_count - 1; /* should be private data */
+
+ newcbarg = isc_mem_get(rbtdb->common.mctx, sizeof(*newcbarg));
+ if (newcbarg == NULL)
+ return (ISC_R_NOMEMORY);
+ newcbarg->type = type;
+ newcbarg->count = count;
+ newcbarg->header = header;
+ newcbarg->db = NULL;
+ dns_db_attach((dns_db_t *)rbtdb, &newcbarg->db);
+ newcbarg->node = NULL;
+ dns_db_attachnode((dns_db_t *)rbtdb, (dns_dbnode_t *)rbtnode,
+ &newcbarg->node);
+ newentry = NULL;
+ result = dns_acache_createentry(acache, (dns_db_t *)rbtdb,
+ acache_callback, newcbarg, &newentry);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ /* Set cache data in the new entry. */
+ result = dns_acache_setentry(acache, newentry, zone, db,
+ version, node, fname);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+
+ nodelock = &rbtdb->node_locks[rbtnode->locknum].lock;
+ NODE_LOCK(nodelock, isc_rwlocktype_write);
+
+ acarray = NULL;
+ switch (type) {
+ case dns_rdatasetadditional_fromauth:
+ acarray = header->additional_auth;
+ break;
+ case dns_rdatasetadditional_fromglue:
+ acarray = header->additional_glue;
+ break;
+ default:
+ INSIST(0);
+ }
+
+ if (acarray == NULL) {
+ unsigned int i;
+
+ acarray = isc_mem_get(rbtdb->common.mctx, total_count *
+ sizeof(acachectl_t));
+
+ if (acarray == NULL) {
+ NODE_UNLOCK(nodelock, isc_rwlocktype_write);
+ goto fail;
+ }
+
+ for (i = 0; i < total_count; i++) {
+ acarray[i].entry = NULL;
+ acarray[i].cbarg = NULL;
+ }
+ }
+ switch (type) {
+ case dns_rdatasetadditional_fromauth:
+ header->additional_auth = acarray;
+ break;
+ case dns_rdatasetadditional_fromglue:
+ header->additional_glue = acarray;
+ break;
+ default:
+ INSIST(0);
+ }
+
+ if (acarray[count].entry != NULL) {
+ /*
+ * Swap the entry. Delay cleaning-up the old entry since
+ * it would require a node lock.
+ */
+ oldentry = acarray[count].entry;
+ INSIST(acarray[count].cbarg != NULL);
+ oldcbarg = acarray[count].cbarg;
+ }
+ acarray[count].entry = newentry;
+ acarray[count].cbarg = newcbarg;
+
+ NODE_UNLOCK(nodelock, isc_rwlocktype_write);
+
+ if (oldentry != NULL) {
+ acache_cancelentry(rbtdb->common.mctx, oldentry, &oldcbarg);
+ dns_acache_detachentry(&oldentry);
+ }
+
+ return (ISC_R_SUCCESS);
+
+ fail:
+ if (newcbarg != NULL) {
+ if (newentry != NULL) {
+ acache_cancelentry(rbtdb->common.mctx, newentry,
+ &newcbarg);
+ dns_acache_detachentry(&newentry);
+ } else {
+ dns_db_detachnode((dns_db_t *)rbtdb, &newcbarg->node);
+ dns_db_detach(&newcbarg->db);
+ isc_mem_put(rbtdb->common.mctx, newcbarg,
+ sizeof(*newcbarg));
+ }
+ }
+
+ return (result);
+}
+
+static isc_result_t
+rdataset_putadditional(dns_acache_t *acache, dns_rdataset_t *rdataset,
+ dns_rdatasetadditional_t type, dns_rdatatype_t qtype)
+{
+ dns_rbtdb_t *rbtdb = rdataset->private1;
+ dns_rbtnode_t *rbtnode = rdataset->private2;
+ unsigned char *raw = rdataset->private3; /* RDATASLAB */
+ unsigned int current_count = rdataset->privateuint4;
+ rdatasetheader_t *header;
+ nodelock_t *nodelock;
+ unsigned int total_count, count;
+ acachectl_t *acarray;
+ dns_acacheentry_t *entry;
+ acache_cbarg_t *cbarg;
+
+ UNUSED(qtype); /* we do not use this value at least for now */
+ UNUSED(acache);
+
+ if (type == dns_rdatasetadditional_fromcache)
+ return (ISC_R_SUCCESS);
+
+ header = (struct rdatasetheader *)(raw - sizeof(*header));
+
+ total_count = raw[0] * 256 + raw[1];
+ INSIST(total_count > current_count);
+ count = total_count - current_count - 1;
+
+ acarray = NULL;
+ entry = NULL;
+
+ nodelock = &rbtdb->node_locks[rbtnode->locknum].lock;
+ NODE_LOCK(nodelock, isc_rwlocktype_write);
+
+ switch (type) {
+ case dns_rdatasetadditional_fromauth:
+ acarray = header->additional_auth;
+ break;
+ case dns_rdatasetadditional_fromglue:
+ acarray = header->additional_glue;
+ break;
+ default:
+ INSIST(0);
+ }
+
+ if (acarray == NULL) {
+ NODE_UNLOCK(nodelock, isc_rwlocktype_write);
+ return (ISC_R_NOTFOUND);
+ }
+
+ entry = acarray[count].entry;
+ if (entry == NULL) {
+ NODE_UNLOCK(nodelock, isc_rwlocktype_write);
+ return (ISC_R_NOTFOUND);
+ }
+
+ acarray[count].entry = NULL;
+ cbarg = acarray[count].cbarg;
+ acarray[count].cbarg = NULL;
+
+ NODE_UNLOCK(nodelock, isc_rwlocktype_write);
+
+ if (entry != NULL) {
+ if (cbarg != NULL)
+ acache_cancelentry(rbtdb->common.mctx, entry, &cbarg);
+ dns_acache_detachentry(&entry);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Routines for LRU-based cache management.
+ */
+
+/*%
+ * See if a given cache entry that is being reused needs to be updated
+ * in the LRU-list. For the non-threaded case this is always true unless the
+ * entry has already been marked as stale; for the threaded case, updating
+ * the entry every time it is referenced might be expensive because it requires
+ * a node write lock. Thus this function returns true if the entry has not been
+ * updated for some period of time. We differentiate the NS or glue address
+ * case and the others since experiments have shown that the former tends to be
+ * accessed relatively infrequently and the cost of cache miss is higher
+ * (e.g., a missing NS records may cause external queries at a higher level
+ * zone, involving more transactions).
+ *
+ * Caller must hold the node (read or write) lock.
+ */
+static inline isc_boolean_t
+need_headerupdate(rdatasetheader_t *header, isc_stdtime_t now) {
+ if ((header->attributes &
+ (RDATASET_ATTR_NONEXISTENT|RDATASET_ATTR_STALE)) != 0)
+ return (ISC_FALSE);
+
+#ifdef ISC_PLATFORM_USETHREADS
+ if (header->type == dns_rdatatype_ns ||
+ (header->trust == dns_trust_glue &&
+ (header->type == dns_rdatatype_a ||
+ header->type == dns_rdatatype_aaaa))) {
+ /*
+ * Glue records are updated if at least 60 seconds have passed
+ * since the previous update time.
+ */
+ return (header->last_used + 60 <= now);
+ }
+
+ /* Other records are updated if 5 minutes have passed. */
+ return (header->last_used + 300 <= now);
+#else
+ UNUSED(now);
+
+ return (ISC_TRUE);
+#endif
+}
+
+/*%
+ * Update the timestamp of a given cache entry and move it to the head
+ * of the corresponding LRU list.
+ *
+ * Caller must hold the node (write) lock.
+ *
+ * Note that the we do NOT touch the heap here, as the TTL has not changed.
+ */
+static void
+update_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header,
+ isc_stdtime_t now)
+{
+ INSIST(IS_CACHE(rbtdb));
+
+ /* To be checked: can we really assume this? XXXMLG */
+ INSIST(ISC_LINK_LINKED(header, lru_link));
+
+ ISC_LIST_UNLINK(rbtdb->rdatasets[header->node->locknum],
+ header, lru_link);
+ header->last_used = now;
+ ISC_LIST_PREPEND(rbtdb->rdatasets[header->node->locknum],
+ header, lru_link);
+}
+
+/*%
+ * Purge some expired and/or stale (i.e. unused for some period) cache entries
+ * under an overmem condition. To recover from this condition quickly, up to
+ * 2 entries will be purged. This process is triggered while adding a new
+ * entry, and we specifically avoid purging entries in the same LRU bucket as
+ * the one to which the new entry will belong. Otherwise, we might purge
+ * entries of the same name of different RR types while adding RRsets from a
+ * single response (consider the case where we're adding A and AAAA glue records
+ * of the same NS name).
+ */
+static void
+overmem_purge(dns_rbtdb_t *rbtdb, unsigned int locknum_start,
+ isc_stdtime_t now, isc_boolean_t tree_locked)
+{
+ rdatasetheader_t *header, *header_prev;
+ unsigned int locknum;
+ int purgecount = 2;
+
+ for (locknum = (locknum_start + 1) % rbtdb->node_lock_count;
+ locknum != locknum_start && purgecount > 0;
+ locknum = (locknum + 1) % rbtdb->node_lock_count) {
+ NODE_LOCK(&rbtdb->node_locks[locknum].lock,
+ isc_rwlocktype_write);
+
+ header = isc_heap_element(rbtdb->heaps[locknum], 1);
+ if (header && header->rdh_ttl <= now - RBTDB_VIRTUAL) {
+ expire_header(rbtdb, header, tree_locked);
+ purgecount--;
+ }
+
+ for (header = ISC_LIST_TAIL(rbtdb->rdatasets[locknum]);
+ header != NULL && purgecount > 0;
+ header = header_prev) {
+ header_prev = ISC_LIST_PREV(header, lru_link);
+ expire_header(rbtdb, header, tree_locked);
+ purgecount--;
+ }
+
+ NODE_UNLOCK(&rbtdb->node_locks[locknum].lock,
+ isc_rwlocktype_write);
+ }
+}
+
+static void
+expire_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header,
+ isc_boolean_t tree_locked)
+{
+ set_ttl(rbtdb, header, 0);
+ header->attributes |= RDATASET_ATTR_STALE;
+ header->node->dirty = 1;
+
+ /*
+ * Caller must hold the node (write) lock.
+ */
+
+ if (dns_rbtnode_refcurrent(header->node) == 0) {
+ /*
+ * If no one else is using the node, we can clean it up now.
+ * We first need to gain a new reference to the node to meet a
+ * requirement of decrement_reference().
+ */
+ new_reference(rbtdb, header->node);
+ decrement_reference(rbtdb, header->node, 0,
+ isc_rwlocktype_write,
+ tree_locked ? isc_rwlocktype_write :
+ isc_rwlocktype_none);
+ }
+}
diff --git a/lib/dns/rbtdb.h b/lib/dns/rbtdb.h
new file mode 100644
index 0000000..b024d13
--- /dev/null
+++ b/lib/dns/rbtdb.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rbtdb.h,v 1.18 2007/06/19 23:47:16 tbox Exp $ */
+
+#ifndef DNS_RBTDB_H
+#define DNS_RBTDB_H 1
+
+#include <isc/lang.h>
+#include <dns/types.h>
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file
+ * \brief
+ * DNS Red-Black Tree DB Implementation
+ */
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_rbtdb_create(isc_mem_t *mctx, dns_name_t *base, dns_dbtype_t type,
+ dns_rdataclass_t rdclass, unsigned int argc, char *argv[],
+ void *driverarg, dns_db_t **dbp);
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_RBTDB_H */
diff --git a/lib/dns/rbtdb64.c b/lib/dns/rbtdb64.c
new file mode 100644
index 0000000..5e325fa
--- /dev/null
+++ b/lib/dns/rbtdb64.c
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rbtdb64.c,v 1.11 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file */
+
+#define DNS_RBTDB_VERSION64 1
+#include "rbtdb.c"
diff --git a/lib/dns/rbtdb64.h b/lib/dns/rbtdb64.h
new file mode 100644
index 0000000..fe11622
--- /dev/null
+++ b/lib/dns/rbtdb64.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rbtdb64.h,v 1.17 2007/06/19 23:47:16 tbox Exp $ */
+
+#ifndef DNS_RBTDB64_H
+#define DNS_RBTDB64_H 1
+
+#include <isc/lang.h>
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file
+ * \brief
+ * DNS Red-Black Tree DB Implementation with 64-bit version numbers
+ */
+
+#include <dns/db.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+dns_rbtdb64_create(isc_mem_t *mctx, dns_name_t *base, dns_dbtype_t type,
+ dns_rdataclass_t rdclass, unsigned int argc, char *argv[],
+ void *driverarg, dns_db_t **dbp);
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_RBTDB64_H */
diff --git a/lib/dns/rcode.c b/lib/dns/rcode.c
new file mode 100644
index 0000000..58ade85
--- /dev/null
+++ b/lib/dns/rcode.c
@@ -0,0 +1,489 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rcode.c,v 1.8 2008/09/25 04:02:38 tbox Exp $ */
+
+#include <config.h>
+#include <ctype.h>
+
+#include <isc/buffer.h>
+#include <isc/parseint.h>
+#include <isc/print.h>
+#include <isc/region.h>
+#include <isc/result.h>
+#include <isc/stdio.h>
+#include <isc/stdlib.h>
+#include <isc/string.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+#include <dns/cert.h>
+#include <dns/keyflags.h>
+#include <dns/keyvalues.h>
+#include <dns/rcode.h>
+#include <dns/rdataclass.h>
+#include <dns/result.h>
+#include <dns/secalg.h>
+#include <dns/secproto.h>
+
+#define RETERR(x) \
+ do { \
+ isc_result_t _r = (x); \
+ if (_r != ISC_R_SUCCESS) \
+ return (_r); \
+ } while (0)
+
+#define NUMBERSIZE sizeof("037777777777") /* 2^32-1 octal + NUL */
+
+#define RCODENAMES \
+ /* standard rcodes */ \
+ { dns_rcode_noerror, "NOERROR", 0}, \
+ { dns_rcode_formerr, "FORMERR", 0}, \
+ { dns_rcode_servfail, "SERVFAIL", 0}, \
+ { dns_rcode_nxdomain, "NXDOMAIN", 0}, \
+ { dns_rcode_notimp, "NOTIMP", 0}, \
+ { dns_rcode_refused, "REFUSED", 0}, \
+ { dns_rcode_yxdomain, "YXDOMAIN", 0}, \
+ { dns_rcode_yxrrset, "YXRRSET", 0}, \
+ { dns_rcode_nxrrset, "NXRRSET", 0}, \
+ { dns_rcode_notauth, "NOTAUTH", 0}, \
+ { dns_rcode_notzone, "NOTZONE", 0},
+
+#define ERCODENAMES \
+ /* extended rcodes */ \
+ { dns_rcode_badvers, "BADVERS", 0}, \
+ { 0, NULL, 0 }
+
+#define TSIGRCODENAMES \
+ /* extended rcodes */ \
+ { dns_tsigerror_badsig, "BADSIG", 0}, \
+ { dns_tsigerror_badkey, "BADKEY", 0}, \
+ { dns_tsigerror_badtime, "BADTIME", 0}, \
+ { dns_tsigerror_badmode, "BADMODE", 0}, \
+ { dns_tsigerror_badname, "BADNAME", 0}, \
+ { dns_tsigerror_badalg, "BADALG", 0}, \
+ { dns_tsigerror_badtrunc, "BADTRUNC", 0}, \
+ { 0, NULL, 0 }
+
+/* RFC2538 section 2.1 */
+
+#define CERTNAMES \
+ { 1, "PKIX", 0}, \
+ { 2, "SPKI", 0}, \
+ { 3, "PGP", 0}, \
+ { 253, "URI", 0}, \
+ { 254, "OID", 0}, \
+ { 0, NULL, 0}
+
+/* RFC2535 section 7, RFC3110 */
+
+#define SECALGNAMES \
+ { DNS_KEYALG_RSAMD5, "RSAMD5", 0 }, \
+ { DNS_KEYALG_RSAMD5, "RSA", 0 }, \
+ { DNS_KEYALG_DH, "DH", 0 }, \
+ { DNS_KEYALG_DSA, "DSA", 0 }, \
+ { DNS_KEYALG_NSEC3DSA, "NSEC3DSA", 0 }, \
+ { DNS_KEYALG_ECC, "ECC", 0 }, \
+ { DNS_KEYALG_RSASHA1, "RSASHA1", 0 }, \
+ { DNS_KEYALG_NSEC3RSASHA1, "NSEC3RSASHA1", 0 }, \
+ { DNS_KEYALG_INDIRECT, "INDIRECT", 0 }, \
+ { DNS_KEYALG_PRIVATEDNS, "PRIVATEDNS", 0 }, \
+ { DNS_KEYALG_PRIVATEOID, "PRIVATEOID", 0 }, \
+ { 0, NULL, 0}
+
+/* RFC2535 section 7.1 */
+
+#define SECPROTONAMES \
+ { 0, "NONE", 0 }, \
+ { 1, "TLS", 0 }, \
+ { 2, "EMAIL", 0 }, \
+ { 3, "DNSSEC", 0 }, \
+ { 4, "IPSEC", 0 }, \
+ { 255, "ALL", 0 }, \
+ { 0, NULL, 0}
+
+#define HASHALGNAMES \
+ { 1, "SHA-1", 0 }, \
+ { 0, NULL, 0 }
+
+struct tbl {
+ unsigned int value;
+ const char *name;
+ int flags;
+};
+
+static struct tbl rcodes[] = { RCODENAMES ERCODENAMES };
+static struct tbl tsigrcodes[] = { RCODENAMES TSIGRCODENAMES };
+static struct tbl certs[] = { CERTNAMES };
+static struct tbl secalgs[] = { SECALGNAMES };
+static struct tbl secprotos[] = { SECPROTONAMES };
+static struct tbl hashalgs[] = { HASHALGNAMES };
+
+static struct keyflag {
+ const char *name;
+ unsigned int value;
+ unsigned int mask;
+} keyflags[] = {
+ { "NOCONF", 0x4000, 0xC000 },
+ { "NOAUTH", 0x8000, 0xC000 },
+ { "NOKEY", 0xC000, 0xC000 },
+ { "FLAG2", 0x2000, 0x2000 },
+ { "EXTEND", 0x1000, 0x1000 },
+ { "FLAG4", 0x0800, 0x0800 },
+ { "FLAG5", 0x0400, 0x0400 },
+ { "USER", 0x0000, 0x0300 },
+ { "ZONE", 0x0100, 0x0300 },
+ { "HOST", 0x0200, 0x0300 },
+ { "NTYP3", 0x0300, 0x0300 },
+ { "FLAG8", 0x0080, 0x0080 },
+ { "FLAG9", 0x0040, 0x0040 },
+ { "FLAG10", 0x0020, 0x0020 },
+ { "FLAG11", 0x0010, 0x0010 },
+ { "SIG0", 0x0000, 0x000F },
+ { "SIG1", 0x0001, 0x000F },
+ { "SIG2", 0x0002, 0x000F },
+ { "SIG3", 0x0003, 0x000F },
+ { "SIG4", 0x0004, 0x000F },
+ { "SIG5", 0x0005, 0x000F },
+ { "SIG6", 0x0006, 0x000F },
+ { "SIG7", 0x0007, 0x000F },
+ { "SIG8", 0x0008, 0x000F },
+ { "SIG9", 0x0009, 0x000F },
+ { "SIG10", 0x000A, 0x000F },
+ { "SIG11", 0x000B, 0x000F },
+ { "SIG12", 0x000C, 0x000F },
+ { "SIG13", 0x000D, 0x000F },
+ { "SIG14", 0x000E, 0x000F },
+ { "SIG15", 0x000F, 0x000F },
+ { "KSK", DNS_KEYFLAG_KSK, DNS_KEYFLAG_KSK },
+ { NULL, 0, 0 }
+};
+
+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);
+
+ memcpy(region.base, source, l);
+ isc_buffer_add(target, l);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+maybe_numeric(unsigned int *valuep, isc_textregion_t *source,
+ unsigned int max, isc_boolean_t hex_allowed)
+{
+ isc_result_t result;
+ isc_uint32_t n;
+ char buffer[NUMBERSIZE];
+
+ if (! isdigit(source->base[0] & 0xff) ||
+ source->length > NUMBERSIZE - 1)
+ return (ISC_R_BADNUMBER);
+
+ /*
+ * We have a potential number. Try to parse it with
+ * isc_parse_uint32(). isc_parse_uint32() requires
+ * null termination, so we must make a copy.
+ */
+ strncpy(buffer, source->base, NUMBERSIZE);
+ INSIST(buffer[source->length] == '\0');
+
+ result = isc_parse_uint32(&n, buffer, 10);
+ if (result == ISC_R_BADNUMBER && hex_allowed)
+ result = isc_parse_uint32(&n, buffer, 16);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (n > max)
+ return (ISC_R_RANGE);
+ *valuep = n;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+dns_mnemonic_fromtext(unsigned int *valuep, isc_textregion_t *source,
+ struct tbl *table, unsigned int max)
+{
+ isc_result_t result;
+ int i;
+
+ result = maybe_numeric(valuep, source, max, ISC_FALSE);
+ if (result != ISC_R_BADNUMBER)
+ return (result);
+
+ for (i = 0; table[i].name != NULL; i++) {
+ unsigned int n;
+ n = strlen(table[i].name);
+ if (n == source->length &&
+ strncasecmp(source->base, table[i].name, n) == 0) {
+ *valuep = table[i].value;
+ return (ISC_R_SUCCESS);
+ }
+ }
+ return (DNS_R_UNKNOWN);
+}
+
+static isc_result_t
+dns_mnemonic_totext(unsigned int value, isc_buffer_t *target,
+ struct tbl *table)
+{
+ int i = 0;
+ char buf[sizeof("4294967296")];
+ while (table[i].name != NULL) {
+ if (table[i].value == value) {
+ return (str_totext(table[i].name, target));
+ }
+ i++;
+ }
+ snprintf(buf, sizeof(buf), "%u", value);
+ return (str_totext(buf, target));
+}
+
+isc_result_t
+dns_rcode_fromtext(dns_rcode_t *rcodep, isc_textregion_t *source) {
+ unsigned int value;
+ RETERR(dns_mnemonic_fromtext(&value, source, rcodes, 0xffff));
+ *rcodep = value;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_rcode_totext(dns_rcode_t rcode, isc_buffer_t *target) {
+ return (dns_mnemonic_totext(rcode, target, rcodes));
+}
+
+isc_result_t
+dns_tsigrcode_fromtext(dns_rcode_t *rcodep, isc_textregion_t *source) {
+ unsigned int value;
+ RETERR(dns_mnemonic_fromtext(&value, source, tsigrcodes, 0xffff));
+ *rcodep = value;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_tsigrcode_totext(dns_rcode_t rcode, isc_buffer_t *target) {
+ return (dns_mnemonic_totext(rcode, target, tsigrcodes));
+}
+
+isc_result_t
+dns_cert_fromtext(dns_cert_t *certp, isc_textregion_t *source) {
+ unsigned int value;
+ RETERR(dns_mnemonic_fromtext(&value, source, certs, 0xffff));
+ *certp = value;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_cert_totext(dns_cert_t cert, isc_buffer_t *target) {
+ return (dns_mnemonic_totext(cert, target, certs));
+}
+
+isc_result_t
+dns_secalg_fromtext(dns_secalg_t *secalgp, isc_textregion_t *source) {
+ unsigned int value;
+ RETERR(dns_mnemonic_fromtext(&value, source, secalgs, 0xff));
+ *secalgp = value;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_secalg_totext(dns_secalg_t secalg, isc_buffer_t *target) {
+ return (dns_mnemonic_totext(secalg, target, secalgs));
+}
+
+isc_result_t
+dns_secproto_fromtext(dns_secproto_t *secprotop, isc_textregion_t *source) {
+ unsigned int value;
+ RETERR(dns_mnemonic_fromtext(&value, source, secprotos, 0xff));
+ *secprotop = value;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_secproto_totext(dns_secproto_t secproto, isc_buffer_t *target) {
+ return (dns_mnemonic_totext(secproto, target, secprotos));
+}
+
+isc_result_t
+dns_hashalg_fromtext(unsigned char *hashalg, isc_textregion_t *source) {
+ unsigned int value;
+ RETERR(dns_mnemonic_fromtext(&value, source, hashalgs, 0xff));
+ *hashalg = value;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_keyflags_fromtext(dns_keyflags_t *flagsp, isc_textregion_t *source)
+{
+ isc_result_t result;
+ char *text, *end;
+ unsigned int value, mask;
+
+ result = maybe_numeric(&value, source, 0xffff, ISC_TRUE);
+ if (result == ISC_R_SUCCESS) {
+ *flagsp = value;
+ return (ISC_R_SUCCESS);
+ }
+ if (result != ISC_R_BADNUMBER)
+ return (result);
+
+ text = source->base;
+ end = source->base + source->length;
+ value = mask = 0;
+
+ while (text < end) {
+ struct keyflag *p;
+ unsigned int len;
+ char *delim = memchr(text, '|', end - text);
+ if (delim != NULL)
+ len = delim - text;
+ else
+ len = end - text;
+ for (p = keyflags; p->name != NULL; p++) {
+ if (strncasecmp(p->name, text, len) == 0)
+ break;
+ }
+ if (p->name == NULL)
+ return (DNS_R_UNKNOWNFLAG);
+ value |= p->value;
+#ifdef notyet
+ if ((mask & p->mask) != 0)
+ warn("overlapping key flags");
+#endif
+ mask |= p->mask;
+ text += len;
+ if (delim != NULL)
+ text++; /* Skip "|" */
+ }
+ *flagsp = value;
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * This uses lots of hard coded values, but how often do we actually
+ * add classes?
+ */
+isc_result_t
+dns_rdataclass_fromtext(dns_rdataclass_t *classp, isc_textregion_t *source) {
+#define COMPARE(string, rdclass) \
+ if (((sizeof(string) - 1) == source->length) \
+ && (strncasecmp(source->base, string, source->length) == 0)) { \
+ *classp = rdclass; \
+ return (ISC_R_SUCCESS); \
+ }
+
+ switch (tolower((unsigned char)source->base[0])) {
+ case 'a':
+ COMPARE("any", dns_rdataclass_any);
+ break;
+ case 'c':
+ /*
+ * RFC1035 says the mnemonic for the CHAOS class is CH,
+ * but historical BIND practice is to call it CHAOS.
+ * We will accept both forms, but only generate CH.
+ */
+ COMPARE("ch", dns_rdataclass_chaos);
+ COMPARE("chaos", dns_rdataclass_chaos);
+
+ if (source->length > 5 &&
+ source->length < (5 + sizeof("65000")) &&
+ strncasecmp("class", source->base, 5) == 0) {
+ char buf[sizeof("65000")];
+ char *endp;
+ unsigned int val;
+
+ strncpy(buf, source->base + 5, source->length - 5);
+ buf[source->length - 5] = '\0';
+ val = strtoul(buf, &endp, 10);
+ if (*endp == '\0' && val <= 0xffff) {
+ *classp = (dns_rdataclass_t)val;
+ return (ISC_R_SUCCESS);
+ }
+ }
+ break;
+ case 'h':
+ COMPARE("hs", dns_rdataclass_hs);
+ COMPARE("hesiod", dns_rdataclass_hs);
+ break;
+ case 'i':
+ COMPARE("in", dns_rdataclass_in);
+ break;
+ case 'n':
+ COMPARE("none", dns_rdataclass_none);
+ break;
+ case 'r':
+ COMPARE("reserved0", dns_rdataclass_reserved0);
+ break;
+ }
+
+#undef COMPARE
+
+ return (DNS_R_UNKNOWN);
+}
+
+isc_result_t
+dns_rdataclass_totext(dns_rdataclass_t rdclass, isc_buffer_t *target) {
+ char buf[sizeof("CLASS65535")];
+
+ switch (rdclass) {
+ case dns_rdataclass_any:
+ return (str_totext("ANY", target));
+ case dns_rdataclass_chaos:
+ return (str_totext("CH", target));
+ case dns_rdataclass_hs:
+ return (str_totext("HS", target));
+ case dns_rdataclass_in:
+ return (str_totext("IN", target));
+ case dns_rdataclass_none:
+ return (str_totext("NONE", target));
+ case dns_rdataclass_reserved0:
+ return (str_totext("RESERVED0", target));
+ default:
+ snprintf(buf, sizeof(buf), "CLASS%u", rdclass);
+ return (str_totext(buf, target));
+ }
+}
+
+void
+dns_rdataclass_format(dns_rdataclass_t rdclass,
+ char *array, unsigned int size)
+{
+ isc_result_t result;
+ isc_buffer_t buf;
+
+ isc_buffer_init(&buf, array, size);
+ result = dns_rdataclass_totext(rdclass, &buf);
+ /*
+ * 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>");
+ array[size - 1] = '\0';
+ }
+}
diff --git a/lib/dns/rdata.c b/lib/dns/rdata.c
new file mode 100644
index 0000000..d35900b
--- /dev/null
+++ b/lib/dns/rdata.c
@@ -0,0 +1,1769 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rdata.c,v 1.199 2008/09/24 02:46:22 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+#include <ctype.h>
+
+#include <isc/base64.h>
+#include <isc/hex.h>
+#include <isc/lex.h>
+#include <isc/mem.h>
+#include <isc/parseint.h>
+#include <isc/print.h>
+#include <isc/string.h>
+#include <isc/stdlib.h>
+#include <isc/util.h>
+
+#include <dns/callbacks.h>
+#include <dns/cert.h>
+#include <dns/compress.h>
+#include <dns/enumtype.h>
+#include <dns/keyflags.h>
+#include <dns/keyvalues.h>
+#include <dns/rcode.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatastruct.h>
+#include <dns/rdatatype.h>
+#include <dns/result.h>
+#include <dns/secalg.h>
+#include <dns/secproto.h>
+#include <dns/time.h>
+#include <dns/ttl.h>
+
+#define RETERR(x) \
+ do { \
+ isc_result_t _r = (x); \
+ if (_r != ISC_R_SUCCESS) \
+ return (_r); \
+ } while (0)
+
+#define RETTOK(x) \
+ do { \
+ isc_result_t _r = (x); \
+ if (_r != ISC_R_SUCCESS) { \
+ isc_lex_ungettoken(lexer, &token); \
+ return (_r); \
+ } \
+ } while (0)
+
+#define DNS_AS_STR(t) ((t).value.as_textregion.base)
+
+#define ARGS_FROMTEXT int rdclass, dns_rdatatype_t type, \
+ isc_lex_t *lexer, dns_name_t *origin, \
+ unsigned int options, isc_buffer_t *target, \
+ dns_rdatacallbacks_t *callbacks
+
+#define ARGS_TOTEXT dns_rdata_t *rdata, dns_rdata_textctx_t *tctx, \
+ isc_buffer_t *target
+
+#define ARGS_FROMWIRE int rdclass, dns_rdatatype_t type, \
+ isc_buffer_t *source, dns_decompress_t *dctx, \
+ unsigned int options, isc_buffer_t *target
+
+#define ARGS_TOWIRE dns_rdata_t *rdata, dns_compress_t *cctx, \
+ isc_buffer_t *target
+
+#define ARGS_COMPARE const dns_rdata_t *rdata1, const dns_rdata_t *rdata2
+
+#define ARGS_FROMSTRUCT int rdclass, dns_rdatatype_t type, \
+ void *source, isc_buffer_t *target
+
+#define ARGS_TOSTRUCT dns_rdata_t *rdata, void *target, isc_mem_t *mctx
+
+#define ARGS_FREESTRUCT void *source
+
+#define ARGS_ADDLDATA dns_rdata_t *rdata, dns_additionaldatafunc_t add, \
+ void *arg
+
+#define ARGS_DIGEST dns_rdata_t *rdata, dns_digestfunc_t digest, void *arg
+
+#define ARGS_CHECKOWNER dns_name_t *name, dns_rdataclass_t rdclass, \
+ dns_rdatatype_t type, isc_boolean_t wildcard
+
+#define ARGS_CHECKNAMES dns_rdata_t *rdata, dns_name_t *owner, dns_name_t *bad
+
+
+/*%
+ * Context structure for the totext_ functions.
+ * Contains formatting options for rdata-to-text
+ * conversion.
+ */
+typedef struct dns_rdata_textctx {
+ dns_name_t *origin; /*%< Current origin, or NULL. */
+ unsigned int flags; /*%< DNS_STYLEFLAG_* */
+ unsigned int width; /*%< Width of rdata column. */
+ const char *linebreak; /*%< Line break string. */
+} dns_rdata_textctx_t;
+
+static isc_result_t
+txt_totext(isc_region_t *source, isc_buffer_t *target);
+
+static isc_result_t
+txt_fromtext(isc_textregion_t *source, isc_buffer_t *target);
+
+static isc_result_t
+txt_fromwire(isc_buffer_t *source, isc_buffer_t *target);
+
+static isc_boolean_t
+name_prefix(dns_name_t *name, dns_name_t *origin, dns_name_t *target);
+
+static unsigned int
+name_length(dns_name_t *name);
+
+static isc_result_t
+str_totext(const char *source, isc_buffer_t *target);
+
+static isc_result_t
+inet_totext(int af, isc_region_t *src, isc_buffer_t *target);
+
+static isc_boolean_t
+buffer_empty(isc_buffer_t *source);
+
+static void
+buffer_fromregion(isc_buffer_t *buffer, isc_region_t *region);
+
+static isc_result_t
+uint32_tobuffer(isc_uint32_t, isc_buffer_t *target);
+
+static isc_result_t
+uint16_tobuffer(isc_uint32_t, isc_buffer_t *target);
+
+static isc_result_t
+uint8_tobuffer(isc_uint32_t, isc_buffer_t *target);
+
+static isc_result_t
+name_tobuffer(dns_name_t *name, isc_buffer_t *target);
+
+static isc_uint32_t
+uint32_fromregion(isc_region_t *region);
+
+static isc_uint16_t
+uint16_fromregion(isc_region_t *region);
+
+static isc_uint8_t
+uint8_fromregion(isc_region_t *region);
+
+static isc_uint8_t
+uint8_consume_fromregion(isc_region_t *region);
+
+static isc_result_t
+mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length);
+
+static int
+hexvalue(char value);
+
+static int
+decvalue(char value);
+
+static isc_result_t
+btoa_totext(unsigned char *inbuf, int inbuflen, isc_buffer_t *target);
+
+static isc_result_t
+atob_tobuffer(isc_lex_t *lexer, isc_buffer_t *target);
+
+static void
+default_fromtext_callback(dns_rdatacallbacks_t *callbacks, const char *, ...)
+ ISC_FORMAT_PRINTF(2, 3);
+
+static void
+fromtext_error(void (*callback)(dns_rdatacallbacks_t *, const char *, ...),
+ dns_rdatacallbacks_t *callbacks, const char *name,
+ unsigned long line, isc_token_t *token, isc_result_t result);
+
+static void
+fromtext_warneof(isc_lex_t *lexer, dns_rdatacallbacks_t *callbacks);
+
+static isc_result_t
+rdata_totext(dns_rdata_t *rdata, dns_rdata_textctx_t *tctx,
+ isc_buffer_t *target);
+
+static void
+warn_badname(dns_name_t *name, isc_lex_t *lexer,
+ dns_rdatacallbacks_t *callbacks);
+
+static void
+warn_badmx(isc_token_t *token, isc_lex_t *lexer,
+ dns_rdatacallbacks_t *callbacks);
+
+static isc_uint16_t
+uint16_consume_fromregion(isc_region_t *region);
+
+static inline int
+getquad(const void *src, struct in_addr *dst,
+ isc_lex_t *lexer, dns_rdatacallbacks_t *callbacks)
+{
+ int result;
+ struct in_addr *tmp;
+
+ result = inet_aton(src, dst);
+ if (result == 1 && callbacks != NULL &&
+ inet_pton(AF_INET, src, &tmp) != 1) {
+ const char *name = isc_lex_getsourcename(lexer);
+ if (name == NULL)
+ name = "UNKNOWN";
+ (*callbacks->warn)(callbacks, "%s:%lu: \"%s\" "
+ "is not a decimal dotted quad", name,
+ isc_lex_getsourceline(lexer), src);
+ }
+ return (result);
+}
+
+static inline isc_result_t
+name_duporclone(dns_name_t *source, isc_mem_t *mctx, dns_name_t *target) {
+
+ if (mctx != NULL)
+ return (dns_name_dup(source, mctx, target));
+ dns_name_clone(source, target);
+ return (ISC_R_SUCCESS);
+}
+
+static inline void *
+mem_maybedup(isc_mem_t *mctx, void *source, size_t length) {
+ void *new;
+
+ if (mctx == NULL)
+ return (source);
+ new = isc_mem_allocate(mctx, length);
+ if (new != NULL)
+ memcpy(new, source, length);
+
+ return (new);
+}
+
+static const char hexdigits[] = "0123456789abcdef";
+static const char decdigits[] = "0123456789";
+
+#include "code.h"
+
+#define META 0x0001
+#define RESERVED 0x0002
+
+/***
+ *** Initialization
+ ***/
+
+void
+dns_rdata_init(dns_rdata_t *rdata) {
+
+ REQUIRE(rdata != NULL);
+
+ rdata->data = NULL;
+ rdata->length = 0;
+ rdata->rdclass = 0;
+ rdata->type = 0;
+ rdata->flags = 0;
+ ISC_LINK_INIT(rdata, link);
+ /* ISC_LIST_INIT(rdata->list); */
+}
+
+#if 1
+#define DNS_RDATA_INITIALIZED(rdata) \
+ ((rdata)->data == NULL && (rdata)->length == 0 && \
+ (rdata)->rdclass == 0 && (rdata)->type == 0 && (rdata)->flags == 0 && \
+ !ISC_LINK_LINKED((rdata), link))
+#else
+#ifdef ISC_LIST_CHECKINIT
+#define DNS_RDATA_INITIALIZED(rdata) \
+ (!ISC_LINK_LINKED((rdata), link))
+#else
+#define DNS_RDATA_INITIALIZED(rdata) ISC_TRUE
+#endif
+#endif
+
+#define DNS_RDATA_VALIDFLAGS(rdata) \
+ (((rdata)->flags & ~(DNS_RDATA_UPDATE|DNS_RDATA_OFFLINE)) == 0)
+
+void
+dns_rdata_reset(dns_rdata_t *rdata) {
+
+ REQUIRE(rdata != NULL);
+
+ REQUIRE(!ISC_LINK_LINKED(rdata, link));
+ REQUIRE(DNS_RDATA_VALIDFLAGS(rdata));
+
+ rdata->data = NULL;
+ rdata->length = 0;
+ rdata->rdclass = 0;
+ rdata->type = 0;
+ rdata->flags = 0;
+}
+
+/***
+ ***
+ ***/
+
+void
+dns_rdata_clone(const dns_rdata_t *src, dns_rdata_t *target) {
+
+ REQUIRE(src != NULL);
+ REQUIRE(target != NULL);
+
+ REQUIRE(DNS_RDATA_INITIALIZED(target));
+
+ REQUIRE(DNS_RDATA_VALIDFLAGS(src));
+ REQUIRE(DNS_RDATA_VALIDFLAGS(target));
+
+ target->data = src->data;
+ target->length = src->length;
+ target->rdclass = src->rdclass;
+ target->type = src->type;
+ target->flags = src->flags;
+}
+
+
+/***
+ *** Comparisons
+ ***/
+
+int
+dns_rdata_compare(const dns_rdata_t *rdata1, const dns_rdata_t *rdata2) {
+ int result = 0;
+ isc_boolean_t use_default = ISC_FALSE;
+
+ REQUIRE(rdata1 != NULL);
+ REQUIRE(rdata2 != NULL);
+ REQUIRE(rdata1->data != NULL);
+ REQUIRE(rdata2->data != NULL);
+ REQUIRE(DNS_RDATA_VALIDFLAGS(rdata1));
+ REQUIRE(DNS_RDATA_VALIDFLAGS(rdata2));
+
+ if (rdata1->rdclass != rdata2->rdclass)
+ return (rdata1->rdclass < rdata2->rdclass ? -1 : 1);
+
+ if (rdata1->type != rdata2->type)
+ return (rdata1->type < rdata2->type ? -1 : 1);
+
+ COMPARESWITCH
+
+ if (use_default) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ result = isc_region_compare(&r1, &r2);
+ }
+ return (result);
+}
+
+/***
+ *** Conversions
+ ***/
+
+void
+dns_rdata_fromregion(dns_rdata_t *rdata, dns_rdataclass_t rdclass,
+ dns_rdatatype_t type, isc_region_t *r)
+{
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(DNS_RDATA_INITIALIZED(rdata));
+ REQUIRE(r != NULL);
+
+ REQUIRE(DNS_RDATA_VALIDFLAGS(rdata));
+
+ rdata->data = r->base;
+ rdata->length = r->length;
+ rdata->rdclass = rdclass;
+ rdata->type = type;
+ rdata->flags = 0;
+}
+
+void
+dns_rdata_toregion(const dns_rdata_t *rdata, isc_region_t *r) {
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(r != NULL);
+ REQUIRE(DNS_RDATA_VALIDFLAGS(rdata));
+
+ r->base = rdata->data;
+ r->length = rdata->length;
+}
+
+isc_result_t
+dns_rdata_fromwire(dns_rdata_t *rdata, dns_rdataclass_t rdclass,
+ dns_rdatatype_t type, isc_buffer_t *source,
+ dns_decompress_t *dctx, unsigned int options,
+ isc_buffer_t *target)
+{
+ isc_result_t result = ISC_R_NOTIMPLEMENTED;
+ isc_region_t region;
+ isc_buffer_t ss;
+ isc_buffer_t st;
+ isc_boolean_t use_default = ISC_FALSE;
+ isc_uint32_t activelength;
+
+ REQUIRE(dctx != NULL);
+ if (rdata != NULL) {
+ REQUIRE(DNS_RDATA_INITIALIZED(rdata));
+ REQUIRE(DNS_RDATA_VALIDFLAGS(rdata));
+ }
+
+ if (type == 0)
+ return (DNS_R_FORMERR);
+
+ ss = *source;
+ st = *target;
+
+ activelength = isc_buffer_activelength(source);
+ INSIST(activelength < 65536);
+
+ FROMWIRESWITCH
+
+ if (use_default) {
+ if (activelength > isc_buffer_availablelength(target))
+ result = ISC_R_NOSPACE;
+ else {
+ isc_buffer_putmem(target, isc_buffer_current(source),
+ activelength);
+ isc_buffer_forward(source, activelength);
+ result = ISC_R_SUCCESS;
+ }
+ }
+
+ /*
+ * We should have consumed all of our buffer.
+ */
+ if (result == ISC_R_SUCCESS && !buffer_empty(source))
+ result = DNS_R_EXTRADATA;
+
+ if (rdata != NULL && result == ISC_R_SUCCESS) {
+ region.base = isc_buffer_used(&st);
+ region.length = isc_buffer_usedlength(target) -
+ isc_buffer_usedlength(&st);
+ dns_rdata_fromregion(rdata, rdclass, type, &region);
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ *source = ss;
+ *target = st;
+ }
+ return (result);
+}
+
+isc_result_t
+dns_rdata_towire(dns_rdata_t *rdata, dns_compress_t *cctx,
+ isc_buffer_t *target)
+{
+ isc_result_t result = ISC_R_NOTIMPLEMENTED;
+ isc_boolean_t use_default = ISC_FALSE;
+ isc_region_t tr;
+ isc_buffer_t st;
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(DNS_RDATA_VALIDFLAGS(rdata));
+
+ /*
+ * Some DynDNS meta-RRs have empty rdata.
+ */
+ if ((rdata->flags & DNS_RDATA_UPDATE) != 0) {
+ INSIST(rdata->length == 0);
+ return (ISC_R_SUCCESS);
+ }
+
+ st = *target;
+
+ TOWIRESWITCH
+
+ if (use_default) {
+ isc_buffer_availableregion(target, &tr);
+ if (tr.length < rdata->length)
+ return (ISC_R_NOSPACE);
+ memcpy(tr.base, rdata->data, rdata->length);
+ isc_buffer_add(target, rdata->length);
+ return (ISC_R_SUCCESS);
+ }
+ if (result != ISC_R_SUCCESS) {
+ *target = st;
+ INSIST(target->used < 65536);
+ dns_compress_rollback(cctx, (isc_uint16_t)target->used);
+ }
+ return (result);
+}
+
+/*
+ * If the binary data in 'src' is valid uncompressed wire format
+ * rdata of class 'rdclass' and type 'type', return ISC_R_SUCCESS
+ * and copy the validated rdata to 'dest'. Otherwise return an error.
+ */
+static isc_result_t
+rdata_validate(isc_buffer_t *src, isc_buffer_t *dest, dns_rdataclass_t rdclass,
+ dns_rdatatype_t type)
+{
+ dns_decompress_t dctx;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_result_t result;
+
+ dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_NONE);
+ isc_buffer_setactive(src, isc_buffer_usedlength(src));
+ result = dns_rdata_fromwire(&rdata, rdclass, type, src,
+ &dctx, 0, dest);
+ dns_decompress_invalidate(&dctx);
+
+ return (result);
+}
+
+static isc_result_t
+unknown_fromtext(dns_rdataclass_t rdclass, dns_rdatatype_t type,
+ isc_lex_t *lexer, isc_mem_t *mctx, isc_buffer_t *target)
+{
+ isc_result_t result;
+ isc_buffer_t *buf = NULL;
+ isc_token_t token;
+
+ if (type == 0 || dns_rdatatype_ismeta(type))
+ return (DNS_R_METATYPE);
+
+ result = isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE);
+ if (result == ISC_R_SUCCESS && token.value.as_ulong > 65535U)
+ return (ISC_R_RANGE);
+ result = isc_buffer_allocate(mctx, &buf, token.value.as_ulong);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = isc_hex_tobuffer(lexer, buf,
+ (unsigned int)token.value.as_ulong);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ if (isc_buffer_usedlength(buf) != token.value.as_ulong) {
+ result = ISC_R_UNEXPECTEDEND;
+ goto failure;
+ }
+
+ if (dns_rdatatype_isknown(type)) {
+ result = rdata_validate(buf, target, rdclass, type);
+ } else {
+ isc_region_t r;
+ isc_buffer_usedregion(buf, &r);
+ result = isc_buffer_copyregion(target, &r);
+ }
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ isc_buffer_free(&buf);
+ return (ISC_R_SUCCESS);
+
+ failure:
+ isc_buffer_free(&buf);
+ return (result);
+}
+
+isc_result_t
+dns_rdata_fromtext(dns_rdata_t *rdata, dns_rdataclass_t rdclass,
+ dns_rdatatype_t type, isc_lex_t *lexer,
+ dns_name_t *origin, unsigned int options, isc_mem_t *mctx,
+ isc_buffer_t *target, dns_rdatacallbacks_t *callbacks)
+{
+ isc_result_t result = ISC_R_NOTIMPLEMENTED;
+ isc_region_t region;
+ isc_buffer_t st;
+ isc_token_t token;
+ unsigned int lexoptions = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF |
+ ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE;
+ char *name;
+ unsigned long line;
+ void (*callback)(dns_rdatacallbacks_t *, const char *, ...);
+ isc_result_t tresult;
+
+ REQUIRE(origin == NULL || dns_name_isabsolute(origin) == ISC_TRUE);
+ if (rdata != NULL) {
+ REQUIRE(DNS_RDATA_INITIALIZED(rdata));
+ REQUIRE(DNS_RDATA_VALIDFLAGS(rdata));
+ }
+ if (callbacks != NULL) {
+ REQUIRE(callbacks->warn != NULL);
+ REQUIRE(callbacks->error != NULL);
+ }
+
+ st = *target;
+
+ if (callbacks != NULL)
+ callback = callbacks->error;
+ else
+ callback = default_fromtext_callback;
+
+ result = isc_lex_getmastertoken(lexer, &token, isc_tokentype_qstring,
+ ISC_FALSE);
+ if (result != ISC_R_SUCCESS) {
+ name = isc_lex_getsourcename(lexer);
+ line = isc_lex_getsourceline(lexer);
+ fromtext_error(callback, callbacks, name, line,
+ &token, result);
+ return (result);
+ }
+
+ if (strcmp(DNS_AS_STR(token), "\\#") == 0)
+ result = unknown_fromtext(rdclass, type, lexer, mctx, target);
+ else {
+ isc_lex_ungettoken(lexer, &token);
+
+ FROMTEXTSWITCH
+ }
+
+ /*
+ * Consume to end of line / file.
+ * If not at end of line initially set error code.
+ * Call callback via fromtext_error once if there was an error.
+ */
+ do {
+ name = isc_lex_getsourcename(lexer);
+ line = isc_lex_getsourceline(lexer);
+ tresult = isc_lex_gettoken(lexer, lexoptions, &token);
+ if (tresult != ISC_R_SUCCESS) {
+ if (result == ISC_R_SUCCESS)
+ result = tresult;
+ if (callback != NULL)
+ fromtext_error(callback, callbacks, name,
+ line, NULL, result);
+ break;
+ } else if (token.type != isc_tokentype_eol &&
+ token.type != isc_tokentype_eof) {
+ if (result == ISC_R_SUCCESS)
+ result = DNS_R_EXTRATOKEN;
+ if (callback != NULL) {
+ fromtext_error(callback, callbacks, name,
+ line, &token, result);
+ callback = NULL;
+ }
+ } else if (result != ISC_R_SUCCESS && callback != NULL) {
+ fromtext_error(callback, callbacks, name, line,
+ &token, result);
+ break;
+ } else {
+ if (token.type == isc_tokentype_eof)
+ fromtext_warneof(lexer, callbacks);
+ break;
+ }
+ } while (1);
+
+ if (rdata != NULL && result == ISC_R_SUCCESS) {
+ region.base = isc_buffer_used(&st);
+ region.length = isc_buffer_usedlength(target) -
+ isc_buffer_usedlength(&st);
+ dns_rdata_fromregion(rdata, rdclass, type, &region);
+ }
+ if (result != ISC_R_SUCCESS) {
+ *target = st;
+ }
+ return (result);
+}
+
+static isc_result_t
+rdata_totext(dns_rdata_t *rdata, dns_rdata_textctx_t *tctx,
+ isc_buffer_t *target)
+{
+ isc_result_t result = ISC_R_NOTIMPLEMENTED;
+ isc_boolean_t use_default = ISC_FALSE;
+ char buf[sizeof("65535")];
+ isc_region_t sr;
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(tctx->origin == NULL ||
+ dns_name_isabsolute(tctx->origin) == ISC_TRUE);
+
+ /*
+ * Some DynDNS meta-RRs have empty rdata.
+ */
+ if ((rdata->flags & DNS_RDATA_UPDATE) != 0) {
+ INSIST(rdata->length == 0);
+ return (ISC_R_SUCCESS);
+ }
+
+ TOTEXTSWITCH
+
+ if (use_default) {
+ strlcpy(buf, "\\# ", sizeof(buf));
+ result = str_totext(buf, target);
+ dns_rdata_toregion(rdata, &sr);
+ INSIST(sr.length < 65536);
+ snprintf(buf, sizeof(buf), "%u", sr.length);
+ result = str_totext(buf, target);
+ if (sr.length != 0 && result == ISC_R_SUCCESS) {
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ result = str_totext(" ( ", target);
+ else
+ result = str_totext(" ", target);
+ if (result == ISC_R_SUCCESS)
+ result = isc_hex_totext(&sr, tctx->width - 2,
+ tctx->linebreak,
+ target);
+ if (result == ISC_R_SUCCESS &&
+ (tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ result = str_totext(" )", target);
+ }
+ }
+
+ return (result);
+}
+
+isc_result_t
+dns_rdata_totext(dns_rdata_t *rdata, dns_name_t *origin, isc_buffer_t *target)
+{
+ dns_rdata_textctx_t tctx;
+
+ REQUIRE(DNS_RDATA_VALIDFLAGS(rdata));
+
+ /*
+ * Set up formatting options for single-line output.
+ */
+ tctx.origin = origin;
+ tctx.flags = 0;
+ tctx.width = 60;
+ tctx.linebreak = " ";
+ return (rdata_totext(rdata, &tctx, target));
+}
+
+isc_result_t
+dns_rdata_tofmttext(dns_rdata_t *rdata, dns_name_t *origin,
+ unsigned int flags, unsigned int width,
+ char *linebreak, isc_buffer_t *target)
+{
+ dns_rdata_textctx_t tctx;
+
+ REQUIRE(DNS_RDATA_VALIDFLAGS(rdata));
+
+ /*
+ * Set up formatting options for formatted output.
+ */
+ tctx.origin = origin;
+ tctx.flags = flags;
+ if ((flags & DNS_STYLEFLAG_MULTILINE) != 0) {
+ tctx.width = width;
+ tctx.linebreak = linebreak;
+ } else {
+ tctx.width = 60; /* Used for hex word length only. */
+ tctx.linebreak = " ";
+ }
+ return (rdata_totext(rdata, &tctx, target));
+}
+
+isc_result_t
+dns_rdata_fromstruct(dns_rdata_t *rdata, dns_rdataclass_t rdclass,
+ dns_rdatatype_t type, void *source,
+ isc_buffer_t *target)
+{
+ isc_result_t result = ISC_R_NOTIMPLEMENTED;
+ isc_buffer_t st;
+ isc_region_t region;
+ isc_boolean_t use_default = ISC_FALSE;
+
+ REQUIRE(source != NULL);
+ if (rdata != NULL) {
+ REQUIRE(DNS_RDATA_INITIALIZED(rdata));
+ REQUIRE(DNS_RDATA_VALIDFLAGS(rdata));
+ }
+
+ st = *target;
+
+ FROMSTRUCTSWITCH
+
+ if (use_default)
+ (void)NULL;
+
+ if (rdata != NULL && result == ISC_R_SUCCESS) {
+ region.base = isc_buffer_used(&st);
+ region.length = isc_buffer_usedlength(target) -
+ isc_buffer_usedlength(&st);
+ dns_rdata_fromregion(rdata, rdclass, type, &region);
+ }
+ if (result != ISC_R_SUCCESS)
+ *target = st;
+ return (result);
+}
+
+isc_result_t
+dns_rdata_tostruct(dns_rdata_t *rdata, void *target, isc_mem_t *mctx) {
+ isc_result_t result = ISC_R_NOTIMPLEMENTED;
+ isc_boolean_t use_default = ISC_FALSE;
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(DNS_RDATA_VALIDFLAGS(rdata));
+
+ TOSTRUCTSWITCH
+
+ if (use_default)
+ (void)NULL;
+
+ return (result);
+}
+
+void
+dns_rdata_freestruct(void *source) {
+ dns_rdatacommon_t *common = source;
+ REQUIRE(source != NULL);
+
+ FREESTRUCTSWITCH
+}
+
+isc_result_t
+dns_rdata_additionaldata(dns_rdata_t *rdata, dns_additionaldatafunc_t add,
+ void *arg)
+{
+ isc_result_t result = ISC_R_NOTIMPLEMENTED;
+ isc_boolean_t use_default = ISC_FALSE;
+
+ /*
+ * Call 'add' for each name and type from 'rdata' which is subject to
+ * additional section processing.
+ */
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(add != NULL);
+ REQUIRE(DNS_RDATA_VALIDFLAGS(rdata));
+
+ ADDITIONALDATASWITCH
+
+ /* No additional processing for unknown types */
+ if (use_default)
+ result = ISC_R_SUCCESS;
+
+ return (result);
+}
+
+isc_result_t
+dns_rdata_digest(dns_rdata_t *rdata, dns_digestfunc_t digest, void *arg) {
+ isc_result_t result = ISC_R_NOTIMPLEMENTED;
+ isc_boolean_t use_default = ISC_FALSE;
+ isc_region_t r;
+
+ /*
+ * Send 'rdata' in DNSSEC canonical form to 'digest'.
+ */
+
+ REQUIRE(rdata != NULL);
+ REQUIRE(digest != NULL);
+ REQUIRE(DNS_RDATA_VALIDFLAGS(rdata));
+
+ DIGESTSWITCH
+
+ if (use_default) {
+ dns_rdata_toregion(rdata, &r);
+ result = (digest)(arg, &r);
+ }
+
+ return (result);
+}
+
+isc_boolean_t
+dns_rdata_checkowner(dns_name_t *name, dns_rdataclass_t rdclass,
+ dns_rdatatype_t type, isc_boolean_t wildcard)
+{
+ isc_boolean_t result;
+
+ CHECKOWNERSWITCH
+ return (result);
+}
+
+isc_boolean_t
+dns_rdata_checknames(dns_rdata_t *rdata, dns_name_t *owner, dns_name_t *bad)
+{
+ isc_boolean_t result;
+
+ CHECKNAMESSWITCH
+ return (result);
+}
+
+unsigned int
+dns_rdatatype_attributes(dns_rdatatype_t type)
+{
+ RDATATYPE_ATTRIBUTE_SW
+ if (type >= (dns_rdatatype_t)128 && type < (dns_rdatatype_t)255)
+ return (DNS_RDATATYPEATTR_UNKNOWN | DNS_RDATATYPEATTR_META);
+ return (DNS_RDATATYPEATTR_UNKNOWN);
+}
+
+isc_result_t
+dns_rdatatype_fromtext(dns_rdatatype_t *typep, isc_textregion_t *source) {
+ unsigned int hash;
+ unsigned int n;
+ unsigned char a, b;
+
+ n = source->length;
+
+ if (n == 0)
+ return (DNS_R_UNKNOWN);
+
+ a = tolower((unsigned char)source->base[0]);
+ b = tolower((unsigned char)source->base[n - 1]);
+
+ hash = ((a + n) * b) % 256;
+
+ /*
+ * This switch block is inlined via \#define, and will use "return"
+ * to return a result to the caller if it is a valid (known)
+ * rdatatype name.
+ */
+ RDATATYPE_FROMTEXT_SW(hash, source->base, n, typep);
+
+ if (source->length > 4 && source->length < (4 + sizeof("65000")) &&
+ strncasecmp("type", source->base, 4) == 0) {
+ char buf[sizeof("65000")];
+ char *endp;
+ unsigned int val;
+
+ strncpy(buf, source->base + 4, source->length - 4);
+ buf[source->length - 4] = '\0';
+ val = strtoul(buf, &endp, 10);
+ if (*endp == '\0' && val <= 0xffff) {
+ *typep = (dns_rdatatype_t)val;
+ return (ISC_R_SUCCESS);
+ }
+ }
+
+ return (DNS_R_UNKNOWN);
+}
+
+isc_result_t
+dns_rdatatype_totext(dns_rdatatype_t type, isc_buffer_t *target) {
+ char buf[sizeof("TYPE65535")];
+
+ RDATATYPE_TOTEXT_SW
+ snprintf(buf, sizeof(buf), "TYPE%u", type);
+ return (str_totext(buf, target));
+}
+
+void
+dns_rdatatype_format(dns_rdatatype_t rdtype,
+ char *array, unsigned int size)
+{
+ isc_result_t result;
+ isc_buffer_t buf;
+
+ isc_buffer_init(&buf, array, size);
+ result = dns_rdatatype_totext(rdtype, &buf);
+ /*
+ * 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>");
+ array[size - 1] = '\0';
+ }
+}
+
+/*
+ * Private function.
+ */
+
+static unsigned int
+name_length(dns_name_t *name) {
+ return (name->length);
+}
+
+static isc_result_t
+txt_totext(isc_region_t *source, isc_buffer_t *target) {
+ unsigned int tl;
+ unsigned int n;
+ unsigned char *sp;
+ char *tp;
+ isc_region_t region;
+
+ isc_buffer_availableregion(target, &region);
+ sp = source->base;
+ tp = (char *)region.base;
+ tl = region.length;
+
+ n = *sp++;
+
+ REQUIRE(n + 1 <= source->length);
+
+ if (tl < 1)
+ return (ISC_R_NOSPACE);
+ *tp++ = '"';
+ tl--;
+ while (n--) {
+ if (*sp < 0x20 || *sp >= 0x7f) {
+ if (tl < 4)
+ return (ISC_R_NOSPACE);
+ *tp++ = 0x5c;
+ *tp++ = 0x30 + ((*sp / 100) % 10);
+ *tp++ = 0x30 + ((*sp / 10) % 10);
+ *tp++ = 0x30 + (*sp % 10);
+ sp++;
+ tl -= 4;
+ continue;
+ }
+ /* double quote, semi-colon, backslash */
+ if (*sp == 0x22 || *sp == 0x3b || *sp == 0x5c) {
+ if (tl < 2)
+ return (ISC_R_NOSPACE);
+ *tp++ = '\\';
+ tl--;
+ }
+ if (tl < 1)
+ return (ISC_R_NOSPACE);
+ *tp++ = *sp++;
+ tl--;
+ }
+ if (tl < 1)
+ return (ISC_R_NOSPACE);
+ *tp++ = '"';
+ tl--;
+ isc_buffer_add(target, tp - (char *)region.base);
+ isc_region_consume(source, *source->base + 1);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+txt_fromtext(isc_textregion_t *source, isc_buffer_t *target) {
+ isc_region_t tregion;
+ isc_boolean_t escape;
+ unsigned int n, nrem;
+ char *s;
+ unsigned char *t;
+ int d;
+ int c;
+
+ isc_buffer_availableregion(target, &tregion);
+ s = source->base;
+ n = source->length;
+ t = tregion.base;
+ nrem = tregion.length;
+ escape = ISC_FALSE;
+ if (nrem < 1)
+ return (ISC_R_NOSPACE);
+ /*
+ * Length byte.
+ */
+ nrem--;
+ t++;
+ /*
+ * Maximum text string length.
+ */
+ if (nrem > 255)
+ nrem = 255;
+ while (n-- != 0) {
+ c = (*s++) & 0xff;
+ if (escape && (d = decvalue((char)c)) != -1) {
+ c = d;
+ if (n == 0)
+ return (DNS_R_SYNTAX);
+ n--;
+ if ((d = decvalue(*s++)) != -1)
+ c = c * 10 + d;
+ else
+ return (DNS_R_SYNTAX);
+ if (n == 0)
+ return (DNS_R_SYNTAX);
+ n--;
+ if ((d = decvalue(*s++)) != -1)
+ c = c * 10 + d;
+ else
+ return (DNS_R_SYNTAX);
+ if (c > 255)
+ return (DNS_R_SYNTAX);
+ } else if (!escape && c == '\\') {
+ escape = ISC_TRUE;
+ continue;
+ }
+ escape = ISC_FALSE;
+ if (nrem == 0)
+ return (ISC_R_NOSPACE);
+ *t++ = c;
+ nrem--;
+ }
+ if (escape)
+ return (DNS_R_SYNTAX);
+ *tregion.base = t - tregion.base - 1;
+ isc_buffer_add(target, *tregion.base + 1);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+txt_fromwire(isc_buffer_t *source, isc_buffer_t *target) {
+ unsigned int n;
+ isc_region_t sregion;
+ isc_region_t tregion;
+
+ isc_buffer_activeregion(source, &sregion);
+ if (sregion.length == 0)
+ return(ISC_R_UNEXPECTEDEND);
+ n = *sregion.base + 1;
+ if (n > sregion.length)
+ return (ISC_R_UNEXPECTEDEND);
+
+ isc_buffer_availableregion(target, &tregion);
+ if (n > tregion.length)
+ return (ISC_R_NOSPACE);
+
+ memcpy(tregion.base, sregion.base, n);
+ isc_buffer_forward(source, n);
+ isc_buffer_add(target, n);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_boolean_t
+name_prefix(dns_name_t *name, dns_name_t *origin, dns_name_t *target) {
+ int l1, l2;
+
+ if (origin == NULL)
+ goto return_false;
+
+ if (dns_name_compare(origin, dns_rootname) == 0)
+ goto return_false;
+
+ if (!dns_name_issubdomain(name, origin))
+ goto return_false;
+
+ l1 = dns_name_countlabels(name);
+ l2 = dns_name_countlabels(origin);
+
+ if (l1 == l2)
+ goto return_false;
+
+ dns_name_getlabelsequence(name, 0, l1 - l2, target);
+ return (ISC_TRUE);
+
+return_false:
+ *target = *name;
+ return (ISC_FALSE);
+}
+
+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);
+
+ memcpy(region.base, source, l);
+ isc_buffer_add(target, l);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+inet_totext(int af, isc_region_t *src, isc_buffer_t *target) {
+ char tmpbuf[64];
+
+ /* Note - inet_ntop doesn't do size checking on its input. */
+ if (inet_ntop(af, src->base, tmpbuf, sizeof(tmpbuf)) == NULL)
+ return (ISC_R_NOSPACE);
+ if (strlen(tmpbuf) > isc_buffer_availablelength(target))
+ return (ISC_R_NOSPACE);
+ isc_buffer_putstr(target, tmpbuf);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_boolean_t
+buffer_empty(isc_buffer_t *source) {
+ return((source->current == source->active) ? ISC_TRUE : ISC_FALSE);
+}
+
+static void
+buffer_fromregion(isc_buffer_t *buffer, isc_region_t *region) {
+ isc_buffer_init(buffer, region->base, region->length);
+ isc_buffer_add(buffer, region->length);
+ isc_buffer_setactive(buffer, region->length);
+}
+
+static isc_result_t
+uint32_tobuffer(isc_uint32_t value, isc_buffer_t *target) {
+ isc_region_t region;
+
+ isc_buffer_availableregion(target, &region);
+ if (region.length < 4)
+ return (ISC_R_NOSPACE);
+ isc_buffer_putuint32(target, value);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+uint16_tobuffer(isc_uint32_t value, isc_buffer_t *target) {
+ isc_region_t region;
+
+ if (value > 0xffff)
+ return (ISC_R_RANGE);
+ isc_buffer_availableregion(target, &region);
+ if (region.length < 2)
+ return (ISC_R_NOSPACE);
+ isc_buffer_putuint16(target, (isc_uint16_t)value);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+uint8_tobuffer(isc_uint32_t value, isc_buffer_t *target) {
+ isc_region_t region;
+
+ if (value > 0xff)
+ return (ISC_R_RANGE);
+ isc_buffer_availableregion(target, &region);
+ if (region.length < 1)
+ return (ISC_R_NOSPACE);
+ isc_buffer_putuint8(target, (isc_uint8_t)value);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+name_tobuffer(dns_name_t *name, isc_buffer_t *target) {
+ isc_region_t r;
+ dns_name_toregion(name, &r);
+ return (isc_buffer_copyregion(target, &r));
+}
+
+static isc_uint32_t
+uint32_fromregion(isc_region_t *region) {
+ isc_uint32_t value;
+
+ REQUIRE(region->length >= 4);
+ value = region->base[0] << 24;
+ value |= region->base[1] << 16;
+ value |= region->base[2] << 8;
+ value |= region->base[3];
+ return(value);
+}
+
+static isc_uint16_t
+uint16_consume_fromregion(isc_region_t *region) {
+ isc_uint16_t r = uint16_fromregion(region);
+
+ isc_region_consume(region, 2);
+ return r;
+}
+
+static isc_uint16_t
+uint16_fromregion(isc_region_t *region) {
+
+ REQUIRE(region->length >= 2);
+
+ return ((region->base[0] << 8) | region->base[1]);
+}
+
+static isc_uint8_t
+uint8_fromregion(isc_region_t *region) {
+
+ REQUIRE(region->length >= 1);
+
+ return (region->base[0]);
+}
+
+static isc_uint8_t
+uint8_consume_fromregion(isc_region_t *region) {
+ isc_uint8_t r = uint8_fromregion(region);
+
+ isc_region_consume(region, 1);
+ return r;
+}
+
+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);
+ memcpy(tr.base, base, length);
+ isc_buffer_add(target, length);
+ return (ISC_R_SUCCESS);
+}
+
+static int
+hexvalue(char value) {
+ char *s;
+ unsigned char c;
+
+ c = (unsigned char)value;
+
+ if (!isascii(c))
+ return (-1);
+ if (isupper(c))
+ c = tolower(c);
+ if ((s = strchr(hexdigits, c)) == NULL)
+ return (-1);
+ return (s - hexdigits);
+}
+
+static int
+decvalue(char value) {
+ char *s;
+
+ /*
+ * isascii() is valid for full range of int values, no need to
+ * mask or cast.
+ */
+ if (!isascii(value))
+ return (-1);
+ if ((s = strchr(decdigits, value)) == NULL)
+ return (-1);
+ return (s - decdigits);
+}
+
+static const char atob_digits[86] =
+ "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`" \
+ "abcdefghijklmnopqrstu";
+/*
+ * Subroutines to convert between 8 bit binary bytes and printable ASCII.
+ * Computes the number of bytes, and three kinds of simple checksums.
+ * Incoming bytes are collected into 32-bit words, then printed in base 85:
+ * exp(85,5) > exp(2,32)
+ * The ASCII characters used are between '!' and 'u';
+ * 'z' encodes 32-bit zero; 'x' is used to mark the end of encoded data.
+ *
+ * Originally by Paul Rutter (philabs!per) and Joe Orost (petsd!joe) for
+ * the atob/btoa programs, released with the compress program, in mod.sources.
+ * Modified by Mike Schwartz 8/19/86 for use in BIND.
+ * Modified to be re-entrant 3/2/99.
+ */
+
+
+struct state {
+ isc_int32_t Ceor;
+ isc_int32_t Csum;
+ isc_int32_t Crot;
+ isc_int32_t word;
+ isc_int32_t bcount;
+};
+
+#define Ceor state->Ceor
+#define Csum state->Csum
+#define Crot state->Crot
+#define word state->word
+#define bcount state->bcount
+
+#define times85(x) ((((((x<<2)+x)<<2)+x)<<2)+x)
+
+static isc_result_t byte_atob(int c, isc_buffer_t *target,
+ struct state *state);
+static isc_result_t putbyte(int c, isc_buffer_t *, struct state *state);
+static isc_result_t byte_btoa(int c, isc_buffer_t *, struct state *state);
+
+/*
+ * Decode ASCII-encoded byte c into binary representation and
+ * place into *bufp, advancing bufp.
+ */
+static isc_result_t
+byte_atob(int c, isc_buffer_t *target, struct state *state) {
+ char *s;
+ if (c == 'z') {
+ if (bcount != 0)
+ return(DNS_R_SYNTAX);
+ else {
+ RETERR(putbyte(0, target, state));
+ RETERR(putbyte(0, target, state));
+ RETERR(putbyte(0, target, state));
+ RETERR(putbyte(0, target, state));
+ }
+ } else if ((s = strchr(atob_digits, c)) != NULL) {
+ if (bcount == 0) {
+ word = s - atob_digits;
+ ++bcount;
+ } else if (bcount < 4) {
+ word = times85(word);
+ word += s - atob_digits;
+ ++bcount;
+ } else {
+ word = times85(word);
+ word += s - atob_digits;
+ RETERR(putbyte((word >> 24) & 0xff, target, state));
+ RETERR(putbyte((word >> 16) & 0xff, target, state));
+ RETERR(putbyte((word >> 8) & 0xff, target, state));
+ RETERR(putbyte(word & 0xff, target, state));
+ word = 0;
+ bcount = 0;
+ }
+ } else
+ return(DNS_R_SYNTAX);
+ return(ISC_R_SUCCESS);
+}
+
+/*
+ * Compute checksum info and place c into target.
+ */
+static isc_result_t
+putbyte(int c, isc_buffer_t *target, struct state *state) {
+ isc_region_t tr;
+
+ Ceor ^= c;
+ Csum += c;
+ Csum += 1;
+ if ((Crot & 0x80000000)) {
+ Crot <<= 1;
+ Crot += 1;
+ } else {
+ Crot <<= 1;
+ }
+ Crot += c;
+ isc_buffer_availableregion(target, &tr);
+ if (tr.length < 1)
+ return (ISC_R_NOSPACE);
+ tr.base[0] = c;
+ isc_buffer_add(target, 1);
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Read the ASCII-encoded data from inbuf, of length inbuflen, and convert
+ * it into T_UNSPEC (binary data) in outbuf, not to exceed outbuflen bytes;
+ * outbuflen must be divisible by 4. (Note: this is because outbuf is filled
+ * in 4 bytes at a time. If the actual data doesn't end on an even 4-byte
+ * boundary, there will be no problem...it will be padded with 0 bytes, and
+ * numbytes will indicate the correct number of bytes. The main point is
+ * that since the buffer is filled in 4 bytes at a time, even if there is
+ * not a full 4 bytes of data at the end, there has to be room to 0-pad the
+ * data, so the buffer must be of size divisible by 4). Place the number of
+ * output bytes in numbytes, and return a failure/success status.
+ */
+
+static isc_result_t
+atob_tobuffer(isc_lex_t *lexer, isc_buffer_t *target) {
+ long oeor, osum, orot;
+ struct state statebuf, *state= &statebuf;
+ isc_token_t token;
+ char c;
+ char *e;
+
+ Ceor = Csum = Crot = word = bcount = 0;
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ while (token.value.as_textregion.length != 0) {
+ if ((c = token.value.as_textregion.base[0]) == 'x') {
+ break;
+ } else
+ RETERR(byte_atob(c, target, state));
+ isc_textregion_consume(&token.value.as_textregion, 1);
+ }
+
+ /*
+ * Number of bytes.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if ((token.value.as_ulong % 4) != 0U)
+ isc_buffer_subtract(target, 4 - (token.value.as_ulong % 4));
+
+ /*
+ * Checksum.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ oeor = strtol(DNS_AS_STR(token), &e, 16);
+ if (*e != 0)
+ return (DNS_R_SYNTAX);
+
+ /*
+ * Checksum.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ osum = strtol(DNS_AS_STR(token), &e, 16);
+ if (*e != 0)
+ return (DNS_R_SYNTAX);
+
+ /*
+ * Checksum.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ orot = strtol(DNS_AS_STR(token), &e, 16);
+ if (*e != 0)
+ return (DNS_R_SYNTAX);
+
+ if ((oeor != Ceor) || (osum != Csum) || (orot != Crot))
+ return(DNS_R_BADCKSUM);
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Encode binary byte c into ASCII representation and place into *bufp,
+ * advancing bufp.
+ */
+static isc_result_t
+byte_btoa(int c, isc_buffer_t *target, struct state *state) {
+ isc_region_t tr;
+
+ isc_buffer_availableregion(target, &tr);
+ Ceor ^= c;
+ Csum += c;
+ Csum += 1;
+ if ((Crot & 0x80000000)) {
+ Crot <<= 1;
+ Crot += 1;
+ } else {
+ Crot <<= 1;
+ }
+ Crot += c;
+
+ word <<= 8;
+ word |= c;
+ if (bcount == 3) {
+ if (word == 0) {
+ if (tr.length < 1)
+ return (ISC_R_NOSPACE);
+ tr.base[0] = 'z';
+ isc_buffer_add(target, 1);
+ } else {
+ register int tmp = 0;
+ register isc_int32_t tmpword = word;
+
+ if (tmpword < 0) {
+ /*
+ * Because some don't support u_long.
+ */
+ tmp = 32;
+ tmpword -= (isc_int32_t)(85 * 85 * 85 * 85 * 32);
+ }
+ if (tmpword < 0) {
+ tmp = 64;
+ tmpword -= (isc_int32_t)(85 * 85 * 85 * 85 * 32);
+ }
+ if (tr.length < 5)
+ return (ISC_R_NOSPACE);
+ tr.base[0] = atob_digits[(tmpword /
+ (isc_int32_t)(85 * 85 * 85 * 85))
+ + tmp];
+ tmpword %= (isc_int32_t)(85 * 85 * 85 * 85);
+ tr.base[1] = atob_digits[tmpword / (85 * 85 * 85)];
+ tmpword %= (85 * 85 * 85);
+ tr.base[2] = atob_digits[tmpword / (85 * 85)];
+ tmpword %= (85 * 85);
+ tr.base[3] = atob_digits[tmpword / 85];
+ tmpword %= 85;
+ tr.base[4] = atob_digits[tmpword];
+ isc_buffer_add(target, 5);
+ }
+ bcount = 0;
+ } else {
+ bcount += 1;
+ }
+ return (ISC_R_SUCCESS);
+}
+
+
+/*
+ * Encode the binary data from inbuf, of length inbuflen, into a
+ * target. Return success/failure status
+ */
+static isc_result_t
+btoa_totext(unsigned char *inbuf, int inbuflen, isc_buffer_t *target) {
+ int inc;
+ struct state statebuf, *state = &statebuf;
+ char buf[sizeof("x 2000000000 ffffffff ffffffff ffffffff")];
+
+ Ceor = Csum = Crot = word = bcount = 0;
+ for (inc = 0; inc < inbuflen; inbuf++, inc++)
+ RETERR(byte_btoa(*inbuf, target, state));
+
+ while (bcount != 0)
+ RETERR(byte_btoa(0, target, state));
+
+ /*
+ * Put byte count and checksum information at end of buffer,
+ * delimited by 'x'
+ */
+ snprintf(buf, sizeof(buf), "x %d %x %x %x", inbuflen, Ceor, Csum, Crot);
+ return (str_totext(buf, target));
+}
+
+
+static void
+default_fromtext_callback(dns_rdatacallbacks_t *callbacks, const char *fmt,
+ ...)
+{
+ va_list ap;
+
+ UNUSED(callbacks);
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+}
+
+static void
+fromtext_warneof(isc_lex_t *lexer, dns_rdatacallbacks_t *callbacks) {
+ if (isc_lex_isfile(lexer) && callbacks != NULL) {
+ const char *name = isc_lex_getsourcename(lexer);
+ if (name == NULL)
+ name = "UNKNOWN";
+ (*callbacks->warn)(callbacks,
+ "%s:%lu: file does not end with newline",
+ name, isc_lex_getsourceline(lexer));
+ }
+}
+
+static void
+warn_badmx(isc_token_t *token, isc_lex_t *lexer,
+ dns_rdatacallbacks_t *callbacks)
+{
+ const char *file;
+ unsigned long line;
+
+ if (lexer != NULL) {
+ file = isc_lex_getsourcename(lexer);
+ line = isc_lex_getsourceline(lexer);
+ (*callbacks->warn)(callbacks, "%s:%u: warning: '%s': %s",
+ file, line, DNS_AS_STR(*token),
+ dns_result_totext(DNS_R_MXISADDRESS));
+ }
+}
+
+static void
+warn_badname(dns_name_t *name, isc_lex_t *lexer,
+ dns_rdatacallbacks_t *callbacks)
+{
+ const char *file;
+ unsigned long line;
+ char namebuf[DNS_NAME_FORMATSIZE];
+
+ if (lexer != NULL) {
+ file = isc_lex_getsourcename(lexer);
+ line = isc_lex_getsourceline(lexer);
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ (*callbacks->warn)(callbacks, "%s:%u: warning: %s: %s",
+ file, line, namebuf,
+ dns_result_totext(DNS_R_BADNAME));
+ }
+}
+
+static void
+fromtext_error(void (*callback)(dns_rdatacallbacks_t *, const char *, ...),
+ dns_rdatacallbacks_t *callbacks, const char *name,
+ unsigned long line, isc_token_t *token, isc_result_t result)
+{
+ if (name == NULL)
+ name = "UNKNOWN";
+
+ if (token != NULL) {
+ switch (token->type) {
+ case isc_tokentype_eol:
+ (*callback)(callbacks, "%s: %s:%lu: near eol: %s",
+ "dns_rdata_fromtext", name, line,
+ dns_result_totext(result));
+ break;
+ case isc_tokentype_eof:
+ (*callback)(callbacks, "%s: %s:%lu: near eof: %s",
+ "dns_rdata_fromtext", name, line,
+ dns_result_totext(result));
+ break;
+ case isc_tokentype_number:
+ (*callback)(callbacks, "%s: %s:%lu: near %lu: %s",
+ "dns_rdata_fromtext", name, line,
+ token->value.as_ulong,
+ dns_result_totext(result));
+ break;
+ case isc_tokentype_string:
+ case isc_tokentype_qstring:
+ (*callback)(callbacks, "%s: %s:%lu: near '%s': %s",
+ "dns_rdata_fromtext", name, line,
+ DNS_AS_STR(*token),
+ dns_result_totext(result));
+ break;
+ default:
+ (*callback)(callbacks, "%s: %s:%lu: %s",
+ "dns_rdata_fromtext", name, line,
+ dns_result_totext(result));
+ break;
+ }
+ } else {
+ (*callback)(callbacks, "dns_rdata_fromtext: %s:%lu: %s",
+ name, line, dns_result_totext(result));
+ }
+}
+
+dns_rdatatype_t
+dns_rdata_covers(dns_rdata_t *rdata) {
+ if (rdata->type == 46)
+ return (covers_rrsig(rdata));
+ return (covers_sig(rdata));
+}
+
+isc_boolean_t
+dns_rdatatype_ismeta(dns_rdatatype_t type) {
+ if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_META) != 0)
+ return (ISC_TRUE);
+ return (ISC_FALSE);
+}
+
+isc_boolean_t
+dns_rdatatype_issingleton(dns_rdatatype_t type) {
+ if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_SINGLETON)
+ != 0)
+ return (ISC_TRUE);
+ return (ISC_FALSE);
+}
+
+isc_boolean_t
+dns_rdatatype_notquestion(dns_rdatatype_t type) {
+ if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_NOTQUESTION)
+ != 0)
+ return (ISC_TRUE);
+ return (ISC_FALSE);
+}
+
+isc_boolean_t
+dns_rdatatype_questiononly(dns_rdatatype_t type) {
+ if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_QUESTIONONLY)
+ != 0)
+ return (ISC_TRUE);
+ return (ISC_FALSE);
+}
+
+isc_boolean_t
+dns_rdatatype_atparent(dns_rdatatype_t type) {
+ if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_ATPARENT) != 0)
+ return (ISC_TRUE);
+ return (ISC_FALSE);
+}
+
+isc_boolean_t
+dns_rdataclass_ismeta(dns_rdataclass_t rdclass) {
+
+ if (rdclass == dns_rdataclass_reserved0
+ || rdclass == dns_rdataclass_none
+ || rdclass == dns_rdataclass_any)
+ return (ISC_TRUE);
+
+ return (ISC_FALSE); /* Assume it is not a meta class. */
+}
+
+isc_boolean_t
+dns_rdatatype_isdnssec(dns_rdatatype_t type) {
+ if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_DNSSEC) != 0)
+ return (ISC_TRUE);
+ return (ISC_FALSE);
+}
+
+isc_boolean_t
+dns_rdatatype_iszonecutauth(dns_rdatatype_t type) {
+ if ((dns_rdatatype_attributes(type)
+ & (DNS_RDATATYPEATTR_DNSSEC | DNS_RDATATYPEATTR_ZONECUTAUTH))
+ != 0)
+ return (ISC_TRUE);
+ return (ISC_FALSE);
+}
+
+isc_boolean_t
+dns_rdatatype_isknown(dns_rdatatype_t type) {
+ if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_UNKNOWN)
+ == 0)
+ return (ISC_TRUE);
+ return (ISC_FALSE);
+}
diff --git a/lib/dns/rdata/any_255/tsig_250.c b/lib/dns/rdata/any_255/tsig_250.c
new file mode 100644
index 0000000..3121f78
--- /dev/null
+++ b/lib/dns/rdata/any_255/tsig_250.c
@@ -0,0 +1,597 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: tsig_250.c,v 1.63 2007/06/19 23:47:17 tbox Exp $ */
+
+/* Reviewed: Thu Mar 16 13:39:43 PST 2000 by gson */
+
+#ifndef RDATA_ANY_255_TSIG_250_C
+#define RDATA_ANY_255_TSIG_250_C
+
+#define RRTYPE_TSIG_ATTRIBUTES \
+ (DNS_RDATATYPEATTR_META | DNS_RDATATYPEATTR_NOTQUESTION)
+
+static inline isc_result_t
+fromtext_any_tsig(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_uint64_t sigtime;
+ isc_buffer_t buffer;
+ dns_rcode_t rcode;
+ long i;
+ char *e;
+
+ REQUIRE(type == 250);
+ REQUIRE(rdclass == 255);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ /*
+ * Algorithm Name.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+
+ /*
+ * Time Signed: 48 bits.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ sigtime = isc_string_touint64(DNS_AS_STR(token), &e, 10);
+ if (*e != 0)
+ RETTOK(DNS_R_SYNTAX);
+ if ((sigtime >> 48) != 0)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint16_tobuffer((isc_uint16_t)(sigtime >> 32), target));
+ RETERR(uint32_tobuffer((isc_uint32_t)(sigtime & 0xffffffffU), target));
+
+ /*
+ * Fudge.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Signature Size.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Signature.
+ */
+ RETERR(isc_base64_tobuffer(lexer, target, (int)token.value.as_ulong));
+
+ /*
+ * Original ID.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Error.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ if (dns_tsigrcode_fromtext(&rcode, &token.value.as_textregion)
+ != ISC_R_SUCCESS)
+ {
+ i = strtol(DNS_AS_STR(token), &e, 10);
+ if (*e != 0)
+ RETTOK(DNS_R_UNKNOWN);
+ if (i < 0 || i > 0xffff)
+ RETTOK(ISC_R_RANGE);
+ rcode = (dns_rcode_t)i;
+ }
+ RETERR(uint16_tobuffer(rcode, target));
+
+ /*
+ * Other Len.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Other Data.
+ */
+ return (isc_base64_tobuffer(lexer, target, (int)token.value.as_ulong));
+}
+
+static inline isc_result_t
+totext_any_tsig(ARGS_TOTEXT) {
+ isc_region_t sr;
+ isc_region_t sigr;
+ char buf[sizeof("281474976710655 ")];
+ char *bufp;
+ dns_name_t name;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+ isc_uint64_t sigtime;
+ unsigned short n;
+
+ REQUIRE(rdata->type == 250);
+ REQUIRE(rdata->rdclass == 255);
+ REQUIRE(rdata->length != 0);
+
+ dns_rdata_toregion(rdata, &sr);
+ /*
+ * Algorithm Name.
+ */
+ dns_name_init(&name, NULL);
+ dns_name_init(&prefix, NULL);
+ dns_name_fromregion(&name, &sr);
+ sub = name_prefix(&name, tctx->origin, &prefix);
+ RETERR(dns_name_totext(&prefix, sub, target));
+ RETERR(str_totext(" ", target));
+ isc_region_consume(&sr, name_length(&name));
+
+ /*
+ * Time Signed.
+ */
+ sigtime = ((isc_uint64_t)sr.base[0] << 40) |
+ ((isc_uint64_t)sr.base[1] << 32) |
+ ((isc_uint64_t)sr.base[2] << 24) |
+ ((isc_uint64_t)sr.base[3] << 16) |
+ ((isc_uint64_t)sr.base[4] << 8) |
+ (isc_uint64_t)sr.base[5];
+ isc_region_consume(&sr, 6);
+ bufp = &buf[sizeof(buf) - 1];
+ *bufp-- = 0;
+ *bufp-- = ' ';
+ do {
+ *bufp-- = decdigits[sigtime % 10];
+ sigtime /= 10;
+ } while (sigtime != 0);
+ bufp++;
+ RETERR(str_totext(bufp, target));
+
+ /*
+ * Fudge.
+ */
+ n = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ sprintf(buf, "%u ", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Signature Size.
+ */
+ n = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ sprintf(buf, "%u", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Signature.
+ */
+ REQUIRE(n <= sr.length);
+ sigr = sr;
+ sigr.length = n;
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" (", target));
+ RETERR(str_totext(tctx->linebreak, target));
+ RETERR(isc_base64_totext(&sigr, tctx->width - 2,
+ tctx->linebreak, target));
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" ) ", target));
+ else
+ RETERR(str_totext(" ", target));
+ isc_region_consume(&sr, n);
+
+ /*
+ * Original ID.
+ */
+ n = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ sprintf(buf, "%u ", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Error.
+ */
+ n = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ if (dns_tsigrcode_totext((dns_rcode_t)n, target) == ISC_R_SUCCESS)
+ RETERR(str_totext(" ", target));
+ else {
+ sprintf(buf, "%u ", n);
+ RETERR(str_totext(buf, target));
+ }
+
+ /*
+ * Other Size.
+ */
+ n = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ sprintf(buf, "%u ", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Other.
+ */
+ return (isc_base64_totext(&sr, 60, " ", target));
+}
+
+static inline isc_result_t
+fromwire_any_tsig(ARGS_FROMWIRE) {
+ isc_region_t sr;
+ dns_name_t name;
+ unsigned long n;
+
+ REQUIRE(type == 250);
+ REQUIRE(rdclass == 255);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_NONE);
+
+ /*
+ * Algorithm Name.
+ */
+ dns_name_init(&name, NULL);
+ RETERR(dns_name_fromwire(&name, source, dctx, options, target));
+
+ isc_buffer_activeregion(source, &sr);
+ /*
+ * Time Signed + Fudge.
+ */
+ if (sr.length < 8)
+ return (ISC_R_UNEXPECTEDEND);
+ RETERR(mem_tobuffer(target, sr.base, 8));
+ isc_region_consume(&sr, 8);
+ isc_buffer_forward(source, 8);
+
+ /*
+ * Signature Length + Signature.
+ */
+ if (sr.length < 2)
+ return (ISC_R_UNEXPECTEDEND);
+ n = uint16_fromregion(&sr);
+ if (sr.length < n + 2)
+ return (ISC_R_UNEXPECTEDEND);
+ RETERR(mem_tobuffer(target, sr.base, n + 2));
+ isc_region_consume(&sr, n + 2);
+ isc_buffer_forward(source, n + 2);
+
+ /*
+ * Original ID + Error.
+ */
+ if (sr.length < 4)
+ return (ISC_R_UNEXPECTEDEND);
+ RETERR(mem_tobuffer(target, sr.base, 4));
+ isc_region_consume(&sr, 4);
+ isc_buffer_forward(source, 4);
+
+ /*
+ * Other Length + Other.
+ */
+ if (sr.length < 2)
+ return (ISC_R_UNEXPECTEDEND);
+ n = uint16_fromregion(&sr);
+ if (sr.length < n + 2)
+ return (ISC_R_UNEXPECTEDEND);
+ isc_buffer_forward(source, n + 2);
+ return (mem_tobuffer(target, sr.base, n + 2));
+}
+
+static inline isc_result_t
+towire_any_tsig(ARGS_TOWIRE) {
+ isc_region_t sr;
+ dns_name_t name;
+ dns_offsets_t offsets;
+
+ REQUIRE(rdata->type == 250);
+ REQUIRE(rdata->rdclass == 255);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_NONE);
+ dns_rdata_toregion(rdata, &sr);
+ dns_name_init(&name, offsets);
+ dns_name_fromregion(&name, &sr);
+ RETERR(dns_name_towire(&name, cctx, target));
+ isc_region_consume(&sr, name_length(&name));
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline int
+compare_any_tsig(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+ dns_name_t name1;
+ dns_name_t name2;
+ int order;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 250);
+ REQUIRE(rdata1->rdclass == 255);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+ dns_name_fromregion(&name1, &r1);
+ dns_name_fromregion(&name2, &r2);
+ order = dns_name_rdatacompare(&name1, &name2);
+ if (order != 0)
+ return (order);
+ isc_region_consume(&r1, name_length(&name1));
+ isc_region_consume(&r2, name_length(&name2));
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_any_tsig(ARGS_FROMSTRUCT) {
+ dns_rdata_any_tsig_t *tsig = source;
+ isc_region_t tr;
+
+ REQUIRE(type == 250);
+ REQUIRE(rdclass == 255);
+ REQUIRE(source != NULL);
+ REQUIRE(tsig->common.rdclass == rdclass);
+ REQUIRE(tsig->common.rdtype == type);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ /*
+ * Algorithm Name.
+ */
+ RETERR(name_tobuffer(&tsig->algorithm, target));
+
+ isc_buffer_availableregion(target, &tr);
+ if (tr.length < 6 + 2 + 2)
+ return (ISC_R_NOSPACE);
+
+ /*
+ * Time Signed: 48 bits.
+ */
+ RETERR(uint16_tobuffer((isc_uint16_t)(tsig->timesigned >> 32),
+ target));
+ RETERR(uint32_tobuffer((isc_uint32_t)(tsig->timesigned & 0xffffffffU),
+ target));
+
+ /*
+ * Fudge.
+ */
+ RETERR(uint16_tobuffer(tsig->fudge, target));
+
+ /*
+ * Signature Size.
+ */
+ RETERR(uint16_tobuffer(tsig->siglen, target));
+
+ /*
+ * Signature.
+ */
+ RETERR(mem_tobuffer(target, tsig->signature, tsig->siglen));
+
+ isc_buffer_availableregion(target, &tr);
+ if (tr.length < 2 + 2 + 2)
+ return (ISC_R_NOSPACE);
+
+ /*
+ * Original ID.
+ */
+ RETERR(uint16_tobuffer(tsig->originalid, target));
+
+ /*
+ * Error.
+ */
+ RETERR(uint16_tobuffer(tsig->error, target));
+
+ /*
+ * Other Len.
+ */
+ RETERR(uint16_tobuffer(tsig->otherlen, target));
+
+ /*
+ * Other Data.
+ */
+ return (mem_tobuffer(target, tsig->other, tsig->otherlen));
+}
+
+static inline isc_result_t
+tostruct_any_tsig(ARGS_TOSTRUCT) {
+ dns_rdata_any_tsig_t *tsig;
+ dns_name_t alg;
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == 250);
+ REQUIRE(rdata->rdclass == 255);
+ REQUIRE(rdata->length != 0);
+
+ tsig = (dns_rdata_any_tsig_t *) target;
+ tsig->common.rdclass = rdata->rdclass;
+ tsig->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&tsig->common, link);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /*
+ * Algorithm Name.
+ */
+ dns_name_init(&alg, NULL);
+ dns_name_fromregion(&alg, &sr);
+ dns_name_init(&tsig->algorithm, NULL);
+ RETERR(name_duporclone(&alg, mctx, &tsig->algorithm));
+
+ isc_region_consume(&sr, name_length(&tsig->algorithm));
+
+ /*
+ * Time Signed.
+ */
+ INSIST(sr.length >= 6);
+ tsig->timesigned = ((isc_uint64_t)sr.base[0] << 40) |
+ ((isc_uint64_t)sr.base[1] << 32) |
+ ((isc_uint64_t)sr.base[2] << 24) |
+ ((isc_uint64_t)sr.base[3] << 16) |
+ ((isc_uint64_t)sr.base[4] << 8) |
+ (isc_uint64_t)sr.base[5];
+ isc_region_consume(&sr, 6);
+
+ /*
+ * Fudge.
+ */
+ tsig->fudge = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+
+ /*
+ * Signature Size.
+ */
+ tsig->siglen = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+
+ /*
+ * Signature.
+ */
+ INSIST(sr.length >= tsig->siglen);
+ tsig->signature = mem_maybedup(mctx, sr.base, tsig->siglen);
+ if (tsig->signature == NULL)
+ goto cleanup;
+ isc_region_consume(&sr, tsig->siglen);
+
+ /*
+ * Original ID.
+ */
+ tsig->originalid = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+
+ /*
+ * Error.
+ */
+ tsig->error = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+
+ /*
+ * Other Size.
+ */
+ tsig->otherlen = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+
+ /*
+ * Other.
+ */
+ INSIST(sr.length == tsig->otherlen);
+ tsig->other = mem_maybedup(mctx, sr.base, tsig->otherlen);
+ if (tsig->other == NULL)
+ goto cleanup;
+
+ tsig->mctx = mctx;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (mctx != NULL)
+ dns_name_free(&tsig->algorithm, tsig->mctx);
+ if (mctx != NULL && tsig->signature != NULL)
+ isc_mem_free(mctx, tsig->signature);
+ return (ISC_R_NOMEMORY);
+}
+
+static inline void
+freestruct_any_tsig(ARGS_FREESTRUCT) {
+ dns_rdata_any_tsig_t *tsig = (dns_rdata_any_tsig_t *) source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(tsig->common.rdclass == 255);
+ REQUIRE(tsig->common.rdtype == 250);
+
+ if (tsig->mctx == NULL)
+ return;
+
+ dns_name_free(&tsig->algorithm, tsig->mctx);
+ if (tsig->signature != NULL)
+ isc_mem_free(tsig->mctx, tsig->signature);
+ if (tsig->other != NULL)
+ isc_mem_free(tsig->mctx, tsig->other);
+ tsig->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_any_tsig(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 250);
+ REQUIRE(rdata->rdclass == 255);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_any_tsig(ARGS_DIGEST) {
+
+ REQUIRE(rdata->type == 250);
+ REQUIRE(rdata->rdclass == 255);
+
+ UNUSED(rdata);
+ UNUSED(digest);
+ UNUSED(arg);
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static inline isc_boolean_t
+checkowner_any_tsig(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 250);
+ REQUIRE(rdclass == 255);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_any_tsig(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 250);
+ REQUIRE(rdata->rdclass == 250);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_ANY_255_TSIG_250_C */
diff --git a/lib/dns/rdata/any_255/tsig_250.h b/lib/dns/rdata/any_255/tsig_250.h
new file mode 100644
index 0000000..0c01667
--- /dev/null
+++ b/lib/dns/rdata/any_255/tsig_250.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: tsig_250.h,v 1.25 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef ANY_255_TSIG_250_H
+#define ANY_255_TSIG_250_H 1
+
+/*% RFC2845 */
+typedef struct dns_rdata_any_tsig {
+ dns_rdatacommon_t common;
+ isc_mem_t * mctx;
+ dns_name_t algorithm;
+ isc_uint64_t timesigned;
+ isc_uint16_t fudge;
+ isc_uint16_t siglen;
+ unsigned char * signature;
+ isc_uint16_t originalid;
+ isc_uint16_t error;
+ isc_uint16_t otherlen;
+ unsigned char * other;
+} dns_rdata_any_tsig_t;
+
+#endif /* ANY_255_TSIG_250_H */
diff --git a/lib/dns/rdata/ch_3/a_1.c b/lib/dns/rdata/ch_3/a_1.c
new file mode 100644
index 0000000..78d4ecd
--- /dev/null
+++ b/lib/dns/rdata/ch_3/a_1.c
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 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.
+ */
+
+/* $Id: a_1.c,v 1.6 2007/06/19 23:47:17 tbox Exp $ */
+
+/* by Bjorn.Victor@it.uu.se, 2005-05-07 */
+/* Based on generic/soa_6.c and generic/mx_15.c */
+
+#ifndef RDATA_CH_3_A_1_C
+#define RDATA_CH_3_A_1_C
+
+#include <isc/net.h>
+
+#define RRTYPE_A_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_ch_a(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+
+ REQUIRE(type == 1);
+ REQUIRE(rdclass == dns_rdataclass_ch); /* 3 */
+
+ UNUSED(type);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+
+ /* get domain name */
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+ if ((options & DNS_RDATA_CHECKNAMES) != 0 &&
+ (options & DNS_RDATA_CHECKREVERSE) != 0) {
+ isc_boolean_t ok;
+ ok = dns_name_ishostname(&name, ISC_FALSE);
+ if (!ok && (options & DNS_RDATA_CHECKNAMESFAIL) != 0)
+ RETTOK(DNS_R_BADNAME);
+ if (!ok && callbacks != NULL)
+ warn_badname(&name, lexer, callbacks);
+ }
+
+ /* 16-bit octal address */
+ RETERR(isc_lex_getoctaltoken(lexer, &token, ISC_FALSE));
+ if (token.value.as_ulong > 0xffffU)
+ RETTOK(ISC_R_RANGE);
+ return (uint16_tobuffer(token.value.as_ulong, target));
+}
+
+static inline isc_result_t
+totext_ch_a(ARGS_TOTEXT) {
+ isc_region_t region;
+ dns_name_t name;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+ char buf[sizeof("0177777")];
+ isc_uint16_t addr;
+
+ REQUIRE(rdata->type == 1);
+ REQUIRE(rdata->rdclass == dns_rdataclass_ch); /* 3 */
+ REQUIRE(rdata->length != 0);
+
+ dns_name_init(&name, NULL);
+ dns_name_init(&prefix, NULL);
+
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+ isc_region_consume(&region, name_length(&name));
+ addr = uint16_fromregion(&region);
+
+ sub = name_prefix(&name, tctx->origin, &prefix);
+ RETERR(dns_name_totext(&prefix, sub, target));
+
+ sprintf(buf, "%o", addr); /* note octal */
+ RETERR(str_totext(" ", target));
+ return (str_totext(buf, target));
+}
+
+static inline isc_result_t
+fromwire_ch_a(ARGS_FROMWIRE) {
+ isc_region_t sregion;
+ isc_region_t tregion;
+ dns_name_t name;
+
+ REQUIRE(type == 1);
+ REQUIRE(rdclass == dns_rdataclass_ch);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_GLOBAL14);
+
+ dns_name_init(&name, NULL);
+
+ RETERR(dns_name_fromwire(&name, source, dctx, options, target));
+
+ isc_buffer_activeregion(source, &sregion);
+ isc_buffer_availableregion(target, &tregion);
+ if (sregion.length < 2)
+ return (ISC_R_UNEXPECTEDEND);
+ if (tregion.length < 2)
+ return (ISC_R_NOSPACE);
+
+ memcpy(tregion.base, sregion.base, 2);
+ isc_buffer_forward(source, 2);
+ isc_buffer_add(target, 2);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+towire_ch_a(ARGS_TOWIRE) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t sregion;
+ isc_region_t tregion;
+
+ REQUIRE(rdata->type == 1);
+ REQUIRE(rdata->rdclass == dns_rdataclass_ch);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14);
+
+ dns_name_init(&name, offsets);
+
+ dns_rdata_toregion(rdata, &sregion);
+
+ dns_name_fromregion(&name, &sregion);
+ isc_region_consume(&sregion, name_length(&name));
+ RETERR(dns_name_towire(&name, cctx, target));
+
+ isc_buffer_availableregion(target, &tregion);
+ if (tregion.length < 2)
+ return (ISC_R_NOSPACE);
+
+ memcpy(tregion.base, sregion.base, 2);
+ isc_buffer_add(target, 2);
+ return (ISC_R_SUCCESS);
+}
+
+static inline int
+compare_ch_a(ARGS_COMPARE) {
+ dns_name_t name1;
+ dns_name_t name2;
+ isc_region_t region1;
+ isc_region_t region2;
+ int order;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 1);
+ REQUIRE(rdata1->rdclass == dns_rdataclass_ch);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+
+ dns_name_fromregion(&name1, &region1);
+ dns_name_fromregion(&name2, &region2);
+ isc_region_consume(&region1, name_length(&name1));
+ isc_region_consume(&region2, name_length(&name2));
+
+ order = dns_name_rdatacompare(&name1, &name2);
+ if (order != 0)
+ return (order);
+
+ order = memcmp(rdata1->data, rdata2->data, 2);
+ if (order != 0)
+ order = (order < 0) ? -1 : 1;
+ return (order);
+}
+
+static inline isc_result_t
+fromstruct_ch_a(ARGS_FROMSTRUCT) {
+ dns_rdata_ch_a_t *a = source;
+ isc_region_t region;
+
+ REQUIRE(type == 1);
+ REQUIRE(source != NULL);
+ REQUIRE(a->common.rdtype == type);
+ REQUIRE(a->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_name_toregion(&a->ch_addr_dom, &region);
+ RETERR(isc_buffer_copyregion(target, &region));
+
+ return (uint16_tobuffer(ntohs(a->ch_addr), target));
+}
+
+static inline isc_result_t
+tostruct_ch_a(ARGS_TOSTRUCT) {
+ dns_rdata_ch_a_t *a = target;
+ isc_region_t region;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 1);
+ REQUIRE(rdata->rdclass == dns_rdataclass_ch);
+ REQUIRE(rdata->length != 0);
+
+ a->common.rdclass = rdata->rdclass;
+ a->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&a->common, link);
+
+ dns_rdata_toregion(rdata, &region);
+
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &region);
+ isc_region_consume(&region, name_length(&name));
+
+ dns_name_init(&a->ch_addr_dom, NULL);
+ RETERR(name_duporclone(&name, mctx, &a->ch_addr_dom));
+ a->ch_addr = htons(uint16_fromregion(&region));
+ a->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_ch_a(ARGS_FREESTRUCT) {
+ dns_rdata_ch_a_t *a = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(a->common.rdtype == 1);
+
+ if (a->mctx == NULL)
+ return;
+
+ dns_name_free(&a->ch_addr_dom, a->mctx);
+ a->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_ch_a(ARGS_ADDLDATA) {
+
+ REQUIRE(rdata->type == 1);
+ REQUIRE(rdata->rdclass == dns_rdataclass_ch);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_ch_a(ARGS_DIGEST) {
+ isc_region_t r;
+
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 1);
+ REQUIRE(rdata->rdclass == dns_rdataclass_ch);
+
+ dns_rdata_toregion(rdata, &r);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+ isc_region_consume(&r, name_length(&name));
+ RETERR(dns_name_digest(&name, digest, arg));
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_ch_a(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 1);
+ REQUIRE(rdclass == dns_rdataclass_ch);
+
+ UNUSED(type);
+
+ return (dns_name_ishostname(name, wildcard));
+}
+
+static inline isc_boolean_t
+checknames_ch_a(ARGS_CHECKNAMES) {
+ isc_region_t region;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 1);
+ REQUIRE(rdata->rdclass == dns_rdataclass_ch);
+
+ UNUSED(owner);
+
+ dns_rdata_toregion(rdata, &region);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &region);
+ if (!dns_name_ishostname(&name, ISC_FALSE)) {
+ if (bad != NULL)
+ dns_name_clone(&name, bad);
+ return (ISC_FALSE);
+ }
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_CH_3_A_1_C */
diff --git a/lib/dns/rdata/ch_3/a_1.h b/lib/dns/rdata/ch_3/a_1.h
new file mode 100644
index 0000000..a279d0e
--- /dev/null
+++ b/lib/dns/rdata/ch_3/a_1.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 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.
+ */
+
+/* $Id: a_1.h,v 1.5 2007/06/19 23:47:17 tbox Exp $ */
+
+/* by Bjorn.Victor@it.uu.se, 2005-05-07 */
+/* Based on generic/mx_15.h */
+
+#ifndef CH_3_A_1_H
+#define CH_3_A_1_H 1
+
+typedef isc_uint16_t ch_addr_t;
+
+typedef struct dns_rdata_ch_a {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ dns_name_t ch_addr_dom; /* ch-addr domain for back mapping */
+ ch_addr_t ch_addr; /* chaos address (16 bit) network order */
+} dns_rdata_ch_a_t;
+
+#endif /* CH_3_A_1_H */
diff --git a/lib/dns/rdata/generic/afsdb_18.c b/lib/dns/rdata/generic/afsdb_18.c
new file mode 100644
index 0000000..2230efb
--- /dev/null
+++ b/lib/dns/rdata/generic/afsdb_18.c
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001, 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: afsdb_18.c,v 1.47 2007/06/19 23:47:17 tbox Exp $ */
+
+/* Reviewed: Wed Mar 15 14:59:00 PST 2000 by explorer */
+
+/* RFC1183 */
+
+#ifndef RDATA_GENERIC_AFSDB_18_C
+#define RDATA_GENERIC_AFSDB_18_C
+
+#define RRTYPE_AFSDB_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_afsdb(ARGS_FROMTEXT) {
+ isc_token_t token;
+ isc_buffer_t buffer;
+ dns_name_t name;
+ isc_boolean_t ok;
+
+ REQUIRE(type == 18);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ /*
+ * Subtype.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Hostname.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+ ok = ISC_TRUE;
+ if ((options & DNS_RDATA_CHECKNAMES) != 0)
+ ok = dns_name_ishostname(&name, ISC_FALSE);
+ if (!ok && (options & DNS_RDATA_CHECKNAMESFAIL) != 0)
+ RETTOK(DNS_R_BADNAME);
+ if (!ok && callbacks != NULL)
+ warn_badname(&name, lexer, callbacks);
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_afsdb(ARGS_TOTEXT) {
+ dns_name_t name;
+ dns_name_t prefix;
+ isc_region_t region;
+ char buf[sizeof("64000 ")];
+ isc_boolean_t sub;
+ unsigned int num;
+
+ REQUIRE(rdata->type == 18);
+ REQUIRE(rdata->length != 0);
+
+ dns_name_init(&name, NULL);
+ dns_name_init(&prefix, NULL);
+
+ dns_rdata_toregion(rdata, &region);
+ num = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ sprintf(buf, "%u ", num);
+ RETERR(str_totext(buf, target));
+ dns_name_fromregion(&name, &region);
+ sub = name_prefix(&name, tctx->origin, &prefix);
+ return (dns_name_totext(&prefix, sub, target));
+}
+
+static inline isc_result_t
+fromwire_afsdb(ARGS_FROMWIRE) {
+ dns_name_t name;
+ isc_region_t sr;
+ isc_region_t tr;
+
+ REQUIRE(type == 18);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_NONE);
+
+ dns_name_init(&name, NULL);
+
+ isc_buffer_activeregion(source, &sr);
+ isc_buffer_availableregion(target, &tr);
+ if (tr.length < 2)
+ return (ISC_R_NOSPACE);
+ if (sr.length < 2)
+ return (ISC_R_UNEXPECTEDEND);
+ memcpy(tr.base, sr.base, 2);
+ isc_buffer_forward(source, 2);
+ isc_buffer_add(target, 2);
+ return (dns_name_fromwire(&name, source, dctx, options, target));
+}
+
+static inline isc_result_t
+towire_afsdb(ARGS_TOWIRE) {
+ isc_region_t tr;
+ isc_region_t sr;
+ dns_name_t name;
+ dns_offsets_t offsets;
+
+ REQUIRE(rdata->type == 18);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_NONE);
+ isc_buffer_availableregion(target, &tr);
+ dns_rdata_toregion(rdata, &sr);
+ if (tr.length < 2)
+ return (ISC_R_NOSPACE);
+ memcpy(tr.base, sr.base, 2);
+ isc_region_consume(&sr, 2);
+ isc_buffer_add(target, 2);
+
+ dns_name_init(&name, offsets);
+ dns_name_fromregion(&name, &sr);
+
+ return (dns_name_towire(&name, cctx, target));
+}
+
+static inline int
+compare_afsdb(ARGS_COMPARE) {
+ int result;
+ dns_name_t name1;
+ dns_name_t name2;
+ isc_region_t region1;
+ isc_region_t region2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 18);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ result = memcmp(rdata1->data, rdata2->data, 2);
+ if (result != 0)
+ return (result < 0 ? -1 : 1);
+
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+
+ isc_region_consume(&region1, 2);
+ isc_region_consume(&region2, 2);
+
+ dns_name_fromregion(&name1, &region1);
+ dns_name_fromregion(&name2, &region2);
+
+ return (dns_name_rdatacompare(&name1, &name2));
+}
+
+static inline isc_result_t
+fromstruct_afsdb(ARGS_FROMSTRUCT) {
+ dns_rdata_afsdb_t *afsdb = source;
+ isc_region_t region;
+
+ REQUIRE(type == 18);
+ REQUIRE(source != NULL);
+ REQUIRE(afsdb->common.rdclass == rdclass);
+ REQUIRE(afsdb->common.rdtype == type);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ RETERR(uint16_tobuffer(afsdb->subtype, target));
+ dns_name_toregion(&afsdb->server, &region);
+ return (isc_buffer_copyregion(target, &region));
+}
+
+static inline isc_result_t
+tostruct_afsdb(ARGS_TOSTRUCT) {
+ isc_region_t region;
+ dns_rdata_afsdb_t *afsdb = target;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 18);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ afsdb->common.rdclass = rdata->rdclass;
+ afsdb->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&afsdb->common, link);
+
+ dns_name_init(&afsdb->server, NULL);
+
+ dns_rdata_toregion(rdata, &region);
+
+ afsdb->subtype = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &region);
+
+ RETERR(name_duporclone(&name, mctx, &afsdb->server));
+ afsdb->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_afsdb(ARGS_FREESTRUCT) {
+ dns_rdata_afsdb_t *afsdb = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(afsdb->common.rdtype == 18);
+
+ if (afsdb->mctx == NULL)
+ return;
+
+ dns_name_free(&afsdb->server, afsdb->mctx);
+ afsdb->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_afsdb(ARGS_ADDLDATA) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 18);
+
+ dns_name_init(&name, offsets);
+ dns_rdata_toregion(rdata, &region);
+ isc_region_consume(&region, 2);
+ dns_name_fromregion(&name, &region);
+
+ return ((add)(arg, &name, dns_rdatatype_a));
+}
+
+static inline isc_result_t
+digest_afsdb(ARGS_DIGEST) {
+ isc_region_t r1, r2;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 18);
+
+ dns_rdata_toregion(rdata, &r1);
+ r2 = r1;
+ isc_region_consume(&r2, 2);
+ r1.length = 2;
+ RETERR((digest)(arg, &r1));
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r2);
+
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline isc_boolean_t
+checkowner_afsdb(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 18);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_afsdb(ARGS_CHECKNAMES) {
+ isc_region_t region;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 18);
+
+ UNUSED(owner);
+
+ dns_rdata_toregion(rdata, &region);
+ isc_region_consume(&region, 2);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &region);
+ if (!dns_name_ishostname(&name, ISC_FALSE)) {
+ if (bad != NULL)
+ dns_name_clone(&name, bad);
+ return (ISC_FALSE);
+ }
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_AFSDB_18_C */
diff --git a/lib/dns/rdata/generic/afsdb_18.h b/lib/dns/rdata/generic/afsdb_18.h
new file mode 100644
index 0000000..ccccc11
--- /dev/null
+++ b/lib/dns/rdata/generic/afsdb_18.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+#ifndef GENERIC_AFSDB_18_H
+#define GENERIC_AFSDB_18_H 1
+
+/* $Id: afsdb_18.h,v 1.20 2007/06/19 23:47:17 tbox Exp $ */
+
+/*!
+ * \brief Per RFC1183 */
+
+typedef struct dns_rdata_afsdb {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ isc_uint16_t subtype;
+ dns_name_t server;
+} dns_rdata_afsdb_t;
+
+#endif /* GENERIC_AFSDB_18_H */
+
diff --git a/lib/dns/rdata/generic/cert_37.c b/lib/dns/rdata/generic/cert_37.c
new file mode 100644
index 0000000..2c45230
--- /dev/null
+++ b/lib/dns/rdata/generic/cert_37.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: cert_37.c,v 1.50 2007/06/19 23:47:17 tbox Exp $ */
+
+/* Reviewed: Wed Mar 15 21:14:32 EST 2000 by tale */
+
+/* RFC2538 */
+
+#ifndef RDATA_GENERIC_CERT_37_C
+#define RDATA_GENERIC_CERT_37_C
+
+#define RRTYPE_CERT_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_cert(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_secalg_t secalg;
+ dns_cert_t cert;
+
+ REQUIRE(type == 37);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ /*
+ * Cert type.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ RETTOK(dns_cert_fromtext(&cert, &token.value.as_textregion));
+ RETERR(uint16_tobuffer(cert, target));
+
+ /*
+ * Key tag.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Algorithm.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ RETTOK(dns_secalg_fromtext(&secalg, &token.value.as_textregion));
+ RETERR(mem_tobuffer(target, &secalg, 1));
+
+ return (isc_base64_tobuffer(lexer, target, -1));
+}
+
+static inline isc_result_t
+totext_cert(ARGS_TOTEXT) {
+ isc_region_t sr;
+ char buf[sizeof("64000 ")];
+ unsigned int n;
+
+ REQUIRE(rdata->type == 37);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(tctx);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /*
+ * Type.
+ */
+ n = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ RETERR(dns_cert_totext((dns_cert_t)n, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Key tag.
+ */
+ n = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ sprintf(buf, "%u ", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Algorithm.
+ */
+ RETERR(dns_secalg_totext(sr.base[0], target));
+ isc_region_consume(&sr, 1);
+
+ /*
+ * Cert.
+ */
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" (", target));
+ RETERR(str_totext(tctx->linebreak, target));
+ RETERR(isc_base64_totext(&sr, tctx->width - 2,
+ tctx->linebreak, target));
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" )", target));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_cert(ARGS_FROMWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(type == 37);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(dctx);
+ UNUSED(options);
+
+ isc_buffer_activeregion(source, &sr);
+ if (sr.length < 5)
+ return (ISC_R_UNEXPECTEDEND);
+
+ isc_buffer_forward(source, sr.length);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline isc_result_t
+towire_cert(ARGS_TOWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == 37);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(cctx);
+
+ dns_rdata_toregion(rdata, &sr);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline int
+compare_cert(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 37);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_cert(ARGS_FROMSTRUCT) {
+ dns_rdata_cert_t *cert = source;
+
+ REQUIRE(type == 37);
+ REQUIRE(source != NULL);
+ REQUIRE(cert->common.rdtype == type);
+ REQUIRE(cert->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ RETERR(uint16_tobuffer(cert->type, target));
+ RETERR(uint16_tobuffer(cert->key_tag, target));
+ RETERR(uint8_tobuffer(cert->algorithm, target));
+
+ return (mem_tobuffer(target, cert->certificate, cert->length));
+}
+
+static inline isc_result_t
+tostruct_cert(ARGS_TOSTRUCT) {
+ dns_rdata_cert_t *cert = target;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 37);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ cert->common.rdclass = rdata->rdclass;
+ cert->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&cert->common, link);
+
+ dns_rdata_toregion(rdata, &region);
+
+ cert->type = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ cert->key_tag = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ cert->algorithm = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+ cert->length = region.length;
+
+ cert->certificate = mem_maybedup(mctx, region.base, region.length);
+ if (cert->certificate == NULL)
+ return (ISC_R_NOMEMORY);
+
+ cert->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_cert(ARGS_FREESTRUCT) {
+ dns_rdata_cert_t *cert = source;
+
+ REQUIRE(cert != NULL);
+ REQUIRE(cert->common.rdtype == 37);
+
+ if (cert->mctx == NULL)
+ return;
+
+ if (cert->certificate != NULL)
+ isc_mem_free(cert->mctx, cert->certificate);
+ cert->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_cert(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 37);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_cert(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 37);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_cert(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 37);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_cert(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 37);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_CERT_37_C */
+
diff --git a/lib/dns/rdata/generic/cert_37.h b/lib/dns/rdata/generic/cert_37.h
new file mode 100644
index 0000000..ddfaa4f
--- /dev/null
+++ b/lib/dns/rdata/generic/cert_37.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: cert_37.h,v 1.20 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef GENERIC_CERT_37_H
+#define GENERIC_CERT_37_H 1
+
+/*% RFC2538 */
+typedef struct dns_rdata_cert {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ isc_uint16_t type;
+ isc_uint16_t key_tag;
+ isc_uint8_t algorithm;
+ isc_uint16_t length;
+ unsigned char *certificate;
+} dns_rdata_cert_t;
+
+#endif /* GENERIC_CERT_37_H */
diff --git a/lib/dns/rdata/generic/cname_5.c b/lib/dns/rdata/generic/cname_5.c
new file mode 100644
index 0000000..28c3d60
--- /dev/null
+++ b/lib/dns/rdata/generic/cname_5.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: cname_5.c,v 1.47 2007/06/19 23:47:17 tbox Exp $ */
+
+/* reviewed: Wed Mar 15 16:48:45 PST 2000 by brister */
+
+#ifndef RDATA_GENERIC_CNAME_5_C
+#define RDATA_GENERIC_CNAME_5_C
+
+#define RRTYPE_CNAME_ATTRIBUTES \
+ (DNS_RDATATYPEATTR_EXCLUSIVE | DNS_RDATATYPEATTR_SINGLETON)
+
+static inline isc_result_t
+fromtext_cname(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+
+ REQUIRE(type == 5);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_cname(ARGS_TOTEXT) {
+ isc_region_t region;
+ dns_name_t name;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+
+ REQUIRE(rdata->type == 5);
+ REQUIRE(rdata->length != 0);
+
+ dns_name_init(&name, NULL);
+ dns_name_init(&prefix, NULL);
+
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+
+ sub = name_prefix(&name, tctx->origin, &prefix);
+
+ return (dns_name_totext(&prefix, sub, target));
+}
+
+static inline isc_result_t
+fromwire_cname(ARGS_FROMWIRE) {
+ dns_name_t name;
+
+ REQUIRE(type == 5);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_GLOBAL14);
+
+ dns_name_init(&name, NULL);
+ return (dns_name_fromwire(&name, source, dctx, options, target));
+}
+
+static inline isc_result_t
+towire_cname(ARGS_TOWIRE) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 5);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14);
+
+ dns_name_init(&name, offsets);
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+
+ return (dns_name_towire(&name, cctx, target));
+}
+
+static inline int
+compare_cname(ARGS_COMPARE) {
+ dns_name_t name1;
+ dns_name_t name2;
+ isc_region_t region1;
+ isc_region_t region2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 5);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+
+ dns_name_fromregion(&name1, &region1);
+ dns_name_fromregion(&name2, &region2);
+
+ return (dns_name_rdatacompare(&name1, &name2));
+}
+
+static inline isc_result_t
+fromstruct_cname(ARGS_FROMSTRUCT) {
+ dns_rdata_cname_t *cname = source;
+ isc_region_t region;
+
+ REQUIRE(type == 5);
+ REQUIRE(source != NULL);
+ REQUIRE(cname->common.rdtype == type);
+ REQUIRE(cname->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_name_toregion(&cname->cname, &region);
+ return (isc_buffer_copyregion(target, &region));
+}
+
+static inline isc_result_t
+tostruct_cname(ARGS_TOSTRUCT) {
+ isc_region_t region;
+ dns_rdata_cname_t *cname = target;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 5);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ cname->common.rdclass = rdata->rdclass;
+ cname->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&cname->common, link);
+
+ dns_name_init(&name, NULL);
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+ dns_name_init(&cname->cname, NULL);
+ RETERR(name_duporclone(&name, mctx, &cname->cname));
+ cname->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_cname(ARGS_FREESTRUCT) {
+ dns_rdata_cname_t *cname = source;
+
+ REQUIRE(source != NULL);
+
+ if (cname->mctx == NULL)
+ return;
+
+ dns_name_free(&cname->cname, cname->mctx);
+ cname->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_cname(ARGS_ADDLDATA) {
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ REQUIRE(rdata->type == 5);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_cname(ARGS_DIGEST) {
+ isc_region_t r;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 5);
+
+ dns_rdata_toregion(rdata, &r);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline isc_boolean_t
+checkowner_cname(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 5);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_cname(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 5);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_CNAME_5_C */
diff --git a/lib/dns/rdata/generic/cname_5.h b/lib/dns/rdata/generic/cname_5.h
new file mode 100644
index 0000000..516f8d3
--- /dev/null
+++ b/lib/dns/rdata/generic/cname_5.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: cname_5.h,v 1.26 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef GENERIC_CNAME_5_H
+#define GENERIC_CNAME_5_H 1
+
+typedef struct dns_rdata_cname {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ dns_name_t cname;
+} dns_rdata_cname_t;
+
+#endif /* GENERIC_CNAME_5_H */
diff --git a/lib/dns/rdata/generic/dlv_32769.c b/lib/dns/rdata/generic/dlv_32769.c
new file mode 100644
index 0000000..957f038
--- /dev/null
+++ b/lib/dns/rdata/generic/dlv_32769.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2004, 2006, 2007 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 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.
+ */
+
+/* $Id: dlv_32769.c,v 1.6 2007/06/18 23:47:43 tbox Exp $ */
+
+/* draft-ietf-dnsext-delegation-signer-05.txt */
+
+#ifndef RDATA_GENERIC_DLV_32769_C
+#define RDATA_GENERIC_DLV_32769_C
+
+#define RRTYPE_DLV_ATTRIBUTES 0
+
+#include <isc/sha1.h>
+#include <isc/sha2.h>
+
+#include <dns/ds.h>
+
+
+static inline isc_result_t
+fromtext_dlv(ARGS_FROMTEXT) {
+ isc_token_t token;
+ unsigned char c;
+ int length;
+
+ REQUIRE(type == 32769);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ /*
+ * Key tag.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Algorithm.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint8_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Digest type.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint8_tobuffer(token.value.as_ulong, target));
+ c = (unsigned char) token.value.as_ulong;
+
+ /*
+ * Digest.
+ */
+ if (c == DNS_DSDIGEST_SHA1)
+ length = ISC_SHA1_DIGESTLENGTH;
+ else if (c == DNS_DSDIGEST_SHA256)
+ length = ISC_SHA256_DIGESTLENGTH;
+ else
+ length = -1;
+ return (isc_hex_tobuffer(lexer, target, -1));
+}
+
+static inline isc_result_t
+totext_dlv(ARGS_TOTEXT) {
+ isc_region_t sr;
+ char buf[sizeof("64000 ")];
+ unsigned int n;
+
+ REQUIRE(rdata->type == 32769);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(tctx);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /*
+ * Key tag.
+ */
+ n = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ sprintf(buf, "%u ", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Algorithm.
+ */
+ n = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+ sprintf(buf, "%u ", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Digest type.
+ */
+ n = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+ sprintf(buf, "%u", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Digest.
+ */
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" (", target));
+ RETERR(str_totext(tctx->linebreak, target));
+ RETERR(isc_hex_totext(&sr, tctx->width - 2, tctx->linebreak, target));
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" )", target));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_dlv(ARGS_FROMWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(type == 32769);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(dctx);
+ UNUSED(options);
+
+ isc_buffer_activeregion(source, &sr);
+
+ /*
+ * Check digest lengths if we know them.
+ */
+ if (sr.length < 4 ||
+ (sr.base[3] == DNS_DSDIGEST_SHA1 &&
+ sr.length < 4 + ISC_SHA1_DIGESTLENGTH) ||
+ (sr.base[3] == DNS_DSDIGEST_SHA256 &&
+ sr.length < 4 + ISC_SHA256_DIGESTLENGTH))
+ return (ISC_R_UNEXPECTEDEND);
+
+ /*
+ * Only copy digest lengths if we know them.
+ * If there is extra data dns_rdata_fromwire() will
+ * detect that.
+ */
+ if (sr.base[3] == DNS_DSDIGEST_SHA1)
+ sr.length = 4 + ISC_SHA1_DIGESTLENGTH;
+ else if (sr.base[3] == DNS_DSDIGEST_SHA256)
+ sr.length = 4 + ISC_SHA256_DIGESTLENGTH;
+
+ isc_buffer_forward(source, sr.length);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline isc_result_t
+towire_dlv(ARGS_TOWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == 32769);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(cctx);
+
+ dns_rdata_toregion(rdata, &sr);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline int
+compare_dlv(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 32769);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_dlv(ARGS_FROMSTRUCT) {
+ dns_rdata_dlv_t *dlv = source;
+
+ REQUIRE(type == 32769);
+ REQUIRE(source != NULL);
+ REQUIRE(dlv->common.rdtype == type);
+ REQUIRE(dlv->common.rdclass == rdclass);
+ switch (dlv->digest_type) {
+ case DNS_DSDIGEST_SHA1:
+ REQUIRE(dlv->length == ISC_SHA1_DIGESTLENGTH);
+ break;
+ case DNS_DSDIGEST_SHA256:
+ REQUIRE(dlv->length == ISC_SHA256_DIGESTLENGTH);
+ break;
+ }
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ RETERR(uint16_tobuffer(dlv->key_tag, target));
+ RETERR(uint8_tobuffer(dlv->algorithm, target));
+ RETERR(uint8_tobuffer(dlv->digest_type, target));
+
+ return (mem_tobuffer(target, dlv->digest, dlv->length));
+}
+
+static inline isc_result_t
+tostruct_dlv(ARGS_TOSTRUCT) {
+ dns_rdata_dlv_t *dlv = target;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 32769);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ dlv->common.rdclass = rdata->rdclass;
+ dlv->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&dlv->common, link);
+
+ dns_rdata_toregion(rdata, &region);
+
+ dlv->key_tag = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ dlv->algorithm = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+ dlv->digest_type = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+ dlv->length = region.length;
+
+ dlv->digest = mem_maybedup(mctx, region.base, region.length);
+ if (dlv->digest == NULL)
+ return (ISC_R_NOMEMORY);
+
+ dlv->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_dlv(ARGS_FREESTRUCT) {
+ dns_rdata_dlv_t *dlv = source;
+
+ REQUIRE(dlv != NULL);
+ REQUIRE(dlv->common.rdtype == 32769);
+
+ if (dlv->mctx == NULL)
+ return;
+
+ if (dlv->digest != NULL)
+ isc_mem_free(dlv->mctx, dlv->digest);
+ dlv->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_dlv(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 32769);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_dlv(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 32769);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_dlv(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 32769);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_dlv(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 32769);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_DLV_32769_C */
diff --git a/lib/dns/rdata/generic/dlv_32769.h b/lib/dns/rdata/generic/dlv_32769.h
new file mode 100644
index 0000000..2313c57
--- /dev/null
+++ b/lib/dns/rdata/generic/dlv_32769.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2004, 2006, 2007 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 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.
+ */
+
+/* $Id: dlv_32769.h,v 1.5 2007/06/19 23:47:17 tbox Exp $ */
+
+/* draft-ietf-dnsext-delegation-signer-05.txt */
+#ifndef GENERIC_DLV_32769_H
+#define GENERIC_DLV_32769_H 1
+
+typedef struct dns_rdata_dlv {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ isc_uint16_t key_tag;
+ isc_uint8_t algorithm;
+ isc_uint8_t digest_type;
+ isc_uint16_t length;
+ unsigned char *digest;
+} dns_rdata_dlv_t;
+
+#endif /* GENERIC_DLV_32769_H */
diff --git a/lib/dns/rdata/generic/dname_39.c b/lib/dns/rdata/generic/dname_39.c
new file mode 100644
index 0000000..c399f1e
--- /dev/null
+++ b/lib/dns/rdata/generic/dname_39.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: dname_39.c,v 1.38 2007/06/19 23:47:17 tbox Exp $ */
+
+/* Reviewed: Wed Mar 15 16:52:38 PST 2000 by explorer */
+
+/* RFC2672 */
+
+#ifndef RDATA_GENERIC_DNAME_39_C
+#define RDATA_GENERIC_DNAME_39_C
+
+#define RRTYPE_DNAME_ATTRIBUTES (DNS_RDATATYPEATTR_SINGLETON)
+
+static inline isc_result_t
+fromtext_dname(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+
+ REQUIRE(type == 39);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_dname(ARGS_TOTEXT) {
+ isc_region_t region;
+ dns_name_t name;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+
+ REQUIRE(rdata->type == 39);
+ REQUIRE(rdata->length != 0);
+
+ dns_name_init(&name, NULL);
+ dns_name_init(&prefix, NULL);
+
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+
+ sub = name_prefix(&name, tctx->origin, &prefix);
+
+ return (dns_name_totext(&prefix, sub, target));
+}
+
+static inline isc_result_t
+fromwire_dname(ARGS_FROMWIRE) {
+ dns_name_t name;
+
+ REQUIRE(type == 39);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_NONE);
+
+ dns_name_init(&name, NULL);
+ return(dns_name_fromwire(&name, source, dctx, options, target));
+}
+
+static inline isc_result_t
+towire_dname(ARGS_TOWIRE) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 39);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_NONE);
+ dns_name_init(&name, offsets);
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+
+ return (dns_name_towire(&name, cctx, target));
+}
+
+static inline int
+compare_dname(ARGS_COMPARE) {
+ dns_name_t name1;
+ dns_name_t name2;
+ isc_region_t region1;
+ isc_region_t region2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 39);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+
+ dns_name_fromregion(&name1, &region1);
+ dns_name_fromregion(&name2, &region2);
+
+ return (dns_name_rdatacompare(&name1, &name2));
+}
+
+static inline isc_result_t
+fromstruct_dname(ARGS_FROMSTRUCT) {
+ dns_rdata_dname_t *dname = source;
+ isc_region_t region;
+
+ REQUIRE(type == 39);
+ REQUIRE(source != NULL);
+ REQUIRE(dname->common.rdtype == type);
+ REQUIRE(dname->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_name_toregion(&dname->dname, &region);
+ return (isc_buffer_copyregion(target, &region));
+}
+
+static inline isc_result_t
+tostruct_dname(ARGS_TOSTRUCT) {
+ isc_region_t region;
+ dns_rdata_dname_t *dname = target;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 39);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ dname->common.rdclass = rdata->rdclass;
+ dname->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&dname->common, link);
+
+ dns_name_init(&name, NULL);
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+ dns_name_init(&dname->dname, NULL);
+ RETERR(name_duporclone(&name, mctx, &dname->dname));
+ dname->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_dname(ARGS_FREESTRUCT) {
+ dns_rdata_dname_t *dname = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(dname->common.rdtype == 39);
+
+ if (dname->mctx == NULL)
+ return;
+
+ dns_name_free(&dname->dname, dname->mctx);
+ dname->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_dname(ARGS_ADDLDATA) {
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ REQUIRE(rdata->type == 39);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_dname(ARGS_DIGEST) {
+ isc_region_t r;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 39);
+
+ dns_rdata_toregion(rdata, &r);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline isc_boolean_t
+checkowner_dname(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 39);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_dname(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 39);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_DNAME_39_C */
diff --git a/lib/dns/rdata/generic/dname_39.h b/lib/dns/rdata/generic/dname_39.h
new file mode 100644
index 0000000..f8aca27
--- /dev/null
+++ b/lib/dns/rdata/generic/dname_39.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+#ifndef GENERIC_DNAME_39_H
+#define GENERIC_DNAME_39_H 1
+
+/* $Id: dname_39.h,v 1.21 2007/06/19 23:47:17 tbox Exp $ */
+
+/*!
+ * \brief per RFC2672 */
+
+typedef struct dns_rdata_dname {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ dns_name_t dname;
+} dns_rdata_dname_t;
+
+#endif /* GENERIC_DNAME_39_H */
diff --git a/lib/dns/rdata/generic/dnskey_48.c b/lib/dns/rdata/generic/dnskey_48.c
new file mode 100644
index 0000000..2e11cba
--- /dev/null
+++ b/lib/dns/rdata/generic/dnskey_48.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: dnskey_48.c,v 1.8 2007/06/19 23:47:17 tbox Exp $ */
+
+/*
+ * Reviewed: Wed Mar 15 16:47:10 PST 2000 by halley.
+ */
+
+/* RFC2535 */
+
+#ifndef RDATA_GENERIC_DNSKEY_48_C
+#define RDATA_GENERIC_DNSKEY_48_C
+
+#include <dst/dst.h>
+
+#define RRTYPE_DNSKEY_ATTRIBUTES (DNS_RDATATYPEATTR_DNSSEC)
+
+static inline isc_result_t
+fromtext_dnskey(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_secalg_t alg;
+ dns_secproto_t proto;
+ dns_keyflags_t flags;
+
+ REQUIRE(type == 48);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ /* flags */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ RETTOK(dns_keyflags_fromtext(&flags, &token.value.as_textregion));
+ RETERR(uint16_tobuffer(flags, target));
+
+ /* protocol */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ RETTOK(dns_secproto_fromtext(&proto, &token.value.as_textregion));
+ RETERR(mem_tobuffer(target, &proto, 1));
+
+ /* algorithm */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ RETTOK(dns_secalg_fromtext(&alg, &token.value.as_textregion));
+ RETERR(mem_tobuffer(target, &alg, 1));
+
+ /* No Key? */
+ if ((flags & 0xc000) == 0xc000)
+ return (ISC_R_SUCCESS);
+
+ return (isc_base64_tobuffer(lexer, target, -1));
+}
+
+static inline isc_result_t
+totext_dnskey(ARGS_TOTEXT) {
+ isc_region_t sr;
+ char buf[sizeof("64000")];
+ unsigned int flags;
+ unsigned char algorithm;
+
+ REQUIRE(rdata->type == 48);
+ REQUIRE(rdata->length != 0);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /* flags */
+ flags = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ sprintf(buf, "%u", flags);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ /* protocol */
+ sprintf(buf, "%u", sr.base[0]);
+ isc_region_consume(&sr, 1);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ /* algorithm */
+ algorithm = sr.base[0];
+ sprintf(buf, "%u", algorithm);
+ isc_region_consume(&sr, 1);
+ RETERR(str_totext(buf, target));
+
+ /* No Key? */
+ if ((flags & 0xc000) == 0xc000)
+ return (ISC_R_SUCCESS);
+
+ /* key */
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" (", target));
+ RETERR(str_totext(tctx->linebreak, target));
+ RETERR(isc_base64_totext(&sr, tctx->width - 2,
+ tctx->linebreak, target));
+
+ if ((tctx->flags & DNS_STYLEFLAG_COMMENT) != 0)
+ RETERR(str_totext(tctx->linebreak, target));
+ else if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" ", target));
+
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(")", target));
+
+ if ((tctx->flags & DNS_STYLEFLAG_COMMENT) != 0) {
+ isc_region_t tmpr;
+
+ RETERR(str_totext(" ; key id = ", target));
+ dns_rdata_toregion(rdata, &tmpr);
+ sprintf(buf, "%u", dst_region_computeid(&tmpr, algorithm));
+ RETERR(str_totext(buf, target));
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_dnskey(ARGS_FROMWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(type == 48);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(dctx);
+ UNUSED(options);
+
+ isc_buffer_activeregion(source, &sr);
+ if (sr.length < 4)
+ return (ISC_R_UNEXPECTEDEND);
+
+ isc_buffer_forward(source, sr.length);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline isc_result_t
+towire_dnskey(ARGS_TOWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == 48);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(cctx);
+
+ dns_rdata_toregion(rdata, &sr);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline int
+compare_dnskey(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 48);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_dnskey(ARGS_FROMSTRUCT) {
+ dns_rdata_dnskey_t *dnskey = source;
+
+ REQUIRE(type == 48);
+ REQUIRE(source != NULL);
+ REQUIRE(dnskey->common.rdtype == type);
+ REQUIRE(dnskey->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ /* Flags */
+ RETERR(uint16_tobuffer(dnskey->flags, target));
+
+ /* Protocol */
+ RETERR(uint8_tobuffer(dnskey->protocol, target));
+
+ /* Algorithm */
+ RETERR(uint8_tobuffer(dnskey->algorithm, target));
+
+ /* Data */
+ return (mem_tobuffer(target, dnskey->data, dnskey->datalen));
+}
+
+static inline isc_result_t
+tostruct_dnskey(ARGS_TOSTRUCT) {
+ dns_rdata_dnskey_t *dnskey = target;
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == 48);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ dnskey->common.rdclass = rdata->rdclass;
+ dnskey->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&dnskey->common, link);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /* Flags */
+ if (sr.length < 2)
+ return (ISC_R_UNEXPECTEDEND);
+ dnskey->flags = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+
+ /* Protocol */
+ if (sr.length < 1)
+ return (ISC_R_UNEXPECTEDEND);
+ dnskey->protocol = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+
+ /* Algorithm */
+ if (sr.length < 1)
+ return (ISC_R_UNEXPECTEDEND);
+ dnskey->algorithm = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+
+ /* Data */
+ dnskey->datalen = sr.length;
+ dnskey->data = mem_maybedup(mctx, sr.base, dnskey->datalen);
+ if (dnskey->data == NULL)
+ return (ISC_R_NOMEMORY);
+
+ dnskey->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_dnskey(ARGS_FREESTRUCT) {
+ dns_rdata_dnskey_t *dnskey = (dns_rdata_dnskey_t *) source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(dnskey->common.rdtype == 48);
+
+ if (dnskey->mctx == NULL)
+ return;
+
+ if (dnskey->data != NULL)
+ isc_mem_free(dnskey->mctx, dnskey->data);
+ dnskey->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_dnskey(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 48);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_dnskey(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 48);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_dnskey(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 48);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_dnskey(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 48);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_DNSKEY_48_C */
diff --git a/lib/dns/rdata/generic/dnskey_48.h b/lib/dns/rdata/generic/dnskey_48.h
new file mode 100644
index 0000000..ce88cd1
--- /dev/null
+++ b/lib/dns/rdata/generic/dnskey_48.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+#ifndef GENERIC_DNSKEY_48_H
+#define GENERIC_DNSKEY_48_H 1
+
+/* $Id: dnskey_48.h,v 1.7 2007/06/19 23:47:17 tbox Exp $ */
+
+/*!
+ * \brief per RFC2535 */
+
+typedef struct dns_rdata_dnskey {
+ dns_rdatacommon_t common;
+ isc_mem_t * mctx;
+ isc_uint16_t flags;
+ isc_uint8_t protocol;
+ isc_uint8_t algorithm;
+ isc_uint16_t datalen;
+ unsigned char * data;
+} dns_rdata_dnskey_t;
+
+
+#endif /* GENERIC_DNSKEY_48_H */
diff --git a/lib/dns/rdata/generic/ds_43.c b/lib/dns/rdata/generic/ds_43.c
new file mode 100644
index 0000000..08e5d5f
--- /dev/null
+++ b/lib/dns/rdata/generic/ds_43.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: ds_43.c,v 1.12 2007/06/18 23:47:43 tbox Exp $ */
+
+/* draft-ietf-dnsext-delegation-signer-05.txt */
+
+#ifndef RDATA_GENERIC_DS_43_C
+#define RDATA_GENERIC_DS_43_C
+
+#define RRTYPE_DS_ATTRIBUTES \
+ (DNS_RDATATYPEATTR_DNSSEC|DNS_RDATATYPEATTR_ATPARENT)
+
+#include <isc/sha1.h>
+#include <isc/sha2.h>
+
+#include <dns/ds.h>
+
+static inline isc_result_t
+fromtext_ds(ARGS_FROMTEXT) {
+ isc_token_t token;
+ unsigned char c;
+ int length;
+
+ REQUIRE(type == 43);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ /*
+ * Key tag.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Algorithm.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ RETTOK(dns_secalg_fromtext(&c, &token.value.as_textregion));
+ RETERR(mem_tobuffer(target, &c, 1));
+
+ /*
+ * Digest type.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint8_tobuffer(token.value.as_ulong, target));
+ c = (unsigned char) token.value.as_ulong;
+
+ /*
+ * Digest.
+ */
+ if (c == DNS_DSDIGEST_SHA1)
+ length = ISC_SHA1_DIGESTLENGTH;
+ else if (c == DNS_DSDIGEST_SHA256)
+ length = ISC_SHA256_DIGESTLENGTH;
+ else
+ length = -1;
+ return (isc_hex_tobuffer(lexer, target, length));
+}
+
+static inline isc_result_t
+totext_ds(ARGS_TOTEXT) {
+ isc_region_t sr;
+ char buf[sizeof("64000 ")];
+ unsigned int n;
+
+ REQUIRE(rdata->type == 43);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(tctx);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /*
+ * Key tag.
+ */
+ n = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ sprintf(buf, "%u ", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Algorithm.
+ */
+ n = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+ sprintf(buf, "%u ", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Digest type.
+ */
+ n = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+ sprintf(buf, "%u", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Digest.
+ */
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" (", target));
+ RETERR(str_totext(tctx->linebreak, target));
+ RETERR(isc_hex_totext(&sr, tctx->width - 2, tctx->linebreak, target));
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" )", target));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_ds(ARGS_FROMWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(type == 43);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(dctx);
+ UNUSED(options);
+
+ isc_buffer_activeregion(source, &sr);
+
+ /*
+ * Check digest lengths if we know them.
+ */
+ if (sr.length < 4 ||
+ (sr.base[3] == DNS_DSDIGEST_SHA1 &&
+ sr.length < 4 + ISC_SHA1_DIGESTLENGTH) ||
+ (sr.base[3] == DNS_DSDIGEST_SHA256 &&
+ sr.length < 4 + ISC_SHA256_DIGESTLENGTH))
+ return (ISC_R_UNEXPECTEDEND);
+
+ /*
+ * Only copy digest lengths if we know them.
+ * If there is extra data dns_rdata_fromwire() will
+ * detect that.
+ */
+ if (sr.base[3] == DNS_DSDIGEST_SHA1)
+ sr.length = 4 + ISC_SHA1_DIGESTLENGTH;
+ else if (sr.base[3] == DNS_DSDIGEST_SHA256)
+ sr.length = 4 + ISC_SHA256_DIGESTLENGTH;
+
+ isc_buffer_forward(source, sr.length);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline isc_result_t
+towire_ds(ARGS_TOWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == 43);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(cctx);
+
+ dns_rdata_toregion(rdata, &sr);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline int
+compare_ds(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 43);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_ds(ARGS_FROMSTRUCT) {
+ dns_rdata_ds_t *ds = source;
+
+ REQUIRE(type == 43);
+ REQUIRE(source != NULL);
+ REQUIRE(ds->common.rdtype == type);
+ REQUIRE(ds->common.rdclass == rdclass);
+ switch (ds->digest_type) {
+ case DNS_DSDIGEST_SHA1:
+ REQUIRE(ds->length == ISC_SHA1_DIGESTLENGTH);
+ break;
+ case DNS_DSDIGEST_SHA256:
+ REQUIRE(ds->length == ISC_SHA256_DIGESTLENGTH);
+ break;
+ }
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ RETERR(uint16_tobuffer(ds->key_tag, target));
+ RETERR(uint8_tobuffer(ds->algorithm, target));
+ RETERR(uint8_tobuffer(ds->digest_type, target));
+
+ return (mem_tobuffer(target, ds->digest, ds->length));
+}
+
+static inline isc_result_t
+tostruct_ds(ARGS_TOSTRUCT) {
+ dns_rdata_ds_t *ds = target;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 43);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ ds->common.rdclass = rdata->rdclass;
+ ds->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&ds->common, link);
+
+ dns_rdata_toregion(rdata, &region);
+
+ ds->key_tag = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ ds->algorithm = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+ ds->digest_type = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+ ds->length = region.length;
+
+ ds->digest = mem_maybedup(mctx, region.base, region.length);
+ if (ds->digest == NULL)
+ return (ISC_R_NOMEMORY);
+
+ ds->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_ds(ARGS_FREESTRUCT) {
+ dns_rdata_ds_t *ds = source;
+
+ REQUIRE(ds != NULL);
+ REQUIRE(ds->common.rdtype == 43);
+
+ if (ds->mctx == NULL)
+ return;
+
+ if (ds->digest != NULL)
+ isc_mem_free(ds->mctx, ds->digest);
+ ds->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_ds(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 43);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_ds(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 43);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_ds(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 43);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_ds(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 43);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_DS_43_C */
diff --git a/lib/dns/rdata/generic/ds_43.h b/lib/dns/rdata/generic/ds_43.h
new file mode 100644
index 0000000..3a409a1
--- /dev/null
+++ b/lib/dns/rdata/generic/ds_43.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: ds_43.h,v 1.7 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef GENERIC_DS_43_H
+#define GENERIC_DS_43_H 1
+
+/*!
+ * \brief per draft-ietf-dnsext-delegation-signer-05.txt */
+typedef struct dns_rdata_ds {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ isc_uint16_t key_tag;
+ isc_uint8_t algorithm;
+ isc_uint8_t digest_type;
+ isc_uint16_t length;
+ unsigned char *digest;
+} dns_rdata_ds_t;
+
+#endif /* GENERIC_DS_43_H */
diff --git a/lib/dns/rdata/generic/gpos_27.c b/lib/dns/rdata/generic/gpos_27.c
new file mode 100644
index 0000000..18effb5
--- /dev/null
+++ b/lib/dns/rdata/generic/gpos_27.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: gpos_27.c,v 1.41 2007/06/19 23:47:17 tbox Exp $ */
+
+/* reviewed: Wed Mar 15 16:48:45 PST 2000 by brister */
+
+/* RFC1712 */
+
+#ifndef RDATA_GENERIC_GPOS_27_C
+#define RDATA_GENERIC_GPOS_27_C
+
+#define RRTYPE_GPOS_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_gpos(ARGS_FROMTEXT) {
+ isc_token_t token;
+ int i;
+
+ REQUIRE(type == 27);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ for (i = 0; i < 3; i++) {
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_qstring,
+ ISC_FALSE));
+ RETTOK(txt_fromtext(&token.value.as_textregion, target));
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_gpos(ARGS_TOTEXT) {
+ isc_region_t region;
+ int i;
+
+ REQUIRE(rdata->type == 27);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(tctx);
+
+ dns_rdata_toregion(rdata, &region);
+
+ for (i = 0; i < 3; i++) {
+ RETERR(txt_totext(&region, target));
+ if (i != 2)
+ RETERR(str_totext(" ", target));
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_gpos(ARGS_FROMWIRE) {
+ int i;
+
+ REQUIRE(type == 27);
+
+ UNUSED(type);
+ UNUSED(dctx);
+ UNUSED(rdclass);
+ UNUSED(options);
+
+ for (i = 0; i < 3; i++)
+ RETERR(txt_fromwire(source, target));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+towire_gpos(ARGS_TOWIRE) {
+
+ REQUIRE(rdata->type == 27);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(cctx);
+
+ return (mem_tobuffer(target, rdata->data, rdata->length));
+}
+
+static inline int
+compare_gpos(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 27);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_gpos(ARGS_FROMSTRUCT) {
+ dns_rdata_gpos_t *gpos = source;
+
+ REQUIRE(type == 27);
+ REQUIRE(source != NULL);
+ REQUIRE(gpos->common.rdtype == type);
+ REQUIRE(gpos->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ RETERR(uint8_tobuffer(gpos->long_len, target));
+ RETERR(mem_tobuffer(target, gpos->longitude, gpos->long_len));
+ RETERR(uint8_tobuffer(gpos->lat_len, target));
+ RETERR(mem_tobuffer(target, gpos->latitude, gpos->lat_len));
+ RETERR(uint8_tobuffer(gpos->alt_len, target));
+ return (mem_tobuffer(target, gpos->altitude, gpos->alt_len));
+}
+
+static inline isc_result_t
+tostruct_gpos(ARGS_TOSTRUCT) {
+ dns_rdata_gpos_t *gpos = target;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 27);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ gpos->common.rdclass = rdata->rdclass;
+ gpos->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&gpos->common, link);
+
+ dns_rdata_toregion(rdata, &region);
+ gpos->long_len = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+ gpos->longitude = mem_maybedup(mctx, region.base, gpos->long_len);
+ if (gpos->longitude == NULL)
+ return (ISC_R_NOMEMORY);
+ isc_region_consume(&region, gpos->long_len);
+
+ gpos->lat_len = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+ gpos->latitude = mem_maybedup(mctx, region.base, gpos->lat_len);
+ if (gpos->latitude == NULL)
+ goto cleanup_longitude;
+ isc_region_consume(&region, gpos->lat_len);
+
+ gpos->alt_len = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+ if (gpos->lat_len > 0) {
+ gpos->altitude =
+ mem_maybedup(mctx, region.base, gpos->alt_len);
+ if (gpos->altitude == NULL)
+ goto cleanup_latitude;
+ } else
+ gpos->altitude = NULL;
+
+ gpos->mctx = mctx;
+ return (ISC_R_SUCCESS);
+
+ cleanup_latitude:
+ if (mctx != NULL && gpos->longitude != NULL)
+ isc_mem_free(mctx, gpos->longitude);
+
+ cleanup_longitude:
+ if (mctx != NULL && gpos->latitude != NULL)
+ isc_mem_free(mctx, gpos->latitude);
+ return (ISC_R_NOMEMORY);
+}
+
+static inline void
+freestruct_gpos(ARGS_FREESTRUCT) {
+ dns_rdata_gpos_t *gpos = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(gpos->common.rdtype == 27);
+
+ if (gpos->mctx == NULL)
+ return;
+
+ if (gpos->longitude != NULL)
+ isc_mem_free(gpos->mctx, gpos->longitude);
+ if (gpos->latitude != NULL)
+ isc_mem_free(gpos->mctx, gpos->latitude);
+ if (gpos->altitude != NULL)
+ isc_mem_free(gpos->mctx, gpos->altitude);
+ gpos->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_gpos(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 27);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_gpos(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 27);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_gpos(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 27);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_gpos(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 27);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_GPOS_27_C */
diff --git a/lib/dns/rdata/generic/gpos_27.h b/lib/dns/rdata/generic/gpos_27.h
new file mode 100644
index 0000000..f5df4fa
--- /dev/null
+++ b/lib/dns/rdata/generic/gpos_27.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+#ifndef GENERIC_GPOS_27_H
+#define GENERIC_GPOS_27_H 1
+
+/* $Id: gpos_27.h,v 1.17 2007/06/19 23:47:17 tbox Exp $ */
+
+/*!
+ * \brief per RFC1712 */
+
+typedef struct dns_rdata_gpos {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ char *longitude;
+ char *latitude;
+ char *altitude;
+ isc_uint8_t long_len;
+ isc_uint8_t lat_len;
+ isc_uint8_t alt_len;
+} dns_rdata_gpos_t;
+
+#endif /* GENERIC_GPOS_27_H */
diff --git a/lib/dns/rdata/generic/hinfo_13.c b/lib/dns/rdata/generic/hinfo_13.c
new file mode 100644
index 0000000..5321357
--- /dev/null
+++ b/lib/dns/rdata/generic/hinfo_13.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: hinfo_13.c,v 1.44 2007/06/19 23:47:17 tbox Exp $ */
+
+/*
+ * Reviewed: Wed Mar 15 16:47:10 PST 2000 by halley.
+ */
+
+#ifndef RDATA_GENERIC_HINFO_13_C
+#define RDATA_GENERIC_HINFO_13_C
+
+#define RRTYPE_HINFO_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_hinfo(ARGS_FROMTEXT) {
+ isc_token_t token;
+ int i;
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ REQUIRE(type == 13);
+
+ for (i = 0; i < 2; i++) {
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_qstring,
+ ISC_FALSE));
+ RETTOK(txt_fromtext(&token.value.as_textregion, target));
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_hinfo(ARGS_TOTEXT) {
+ isc_region_t region;
+
+ UNUSED(tctx);
+
+ REQUIRE(rdata->type == 13);
+ REQUIRE(rdata->length != 0);
+
+ dns_rdata_toregion(rdata, &region);
+ RETERR(txt_totext(&region, target));
+ RETERR(str_totext(" ", target));
+ return (txt_totext(&region, target));
+}
+
+static inline isc_result_t
+fromwire_hinfo(ARGS_FROMWIRE) {
+
+ REQUIRE(type == 13);
+
+ UNUSED(type);
+ UNUSED(dctx);
+ UNUSED(rdclass);
+ UNUSED(options);
+
+ RETERR(txt_fromwire(source, target));
+ return (txt_fromwire(source, target));
+}
+
+static inline isc_result_t
+towire_hinfo(ARGS_TOWIRE) {
+
+ UNUSED(cctx);
+
+ REQUIRE(rdata->type == 13);
+ REQUIRE(rdata->length != 0);
+
+ return (mem_tobuffer(target, rdata->data, rdata->length));
+}
+
+static inline int
+compare_hinfo(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 13);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_hinfo(ARGS_FROMSTRUCT) {
+ dns_rdata_hinfo_t *hinfo = source;
+
+ REQUIRE(type == 13);
+ REQUIRE(source != NULL);
+ REQUIRE(hinfo->common.rdtype == type);
+ REQUIRE(hinfo->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ RETERR(uint8_tobuffer(hinfo->cpu_len, target));
+ RETERR(mem_tobuffer(target, hinfo->cpu, hinfo->cpu_len));
+ RETERR(uint8_tobuffer(hinfo->os_len, target));
+ return (mem_tobuffer(target, hinfo->os, hinfo->os_len));
+}
+
+static inline isc_result_t
+tostruct_hinfo(ARGS_TOSTRUCT) {
+ dns_rdata_hinfo_t *hinfo = target;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 13);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ hinfo->common.rdclass = rdata->rdclass;
+ hinfo->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&hinfo->common, link);
+
+ dns_rdata_toregion(rdata, &region);
+ hinfo->cpu_len = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+ hinfo->cpu = mem_maybedup(mctx, region.base, hinfo->cpu_len);
+ if (hinfo->cpu == NULL)
+ return (ISC_R_NOMEMORY);
+ isc_region_consume(&region, hinfo->cpu_len);
+
+ hinfo->os_len = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+ hinfo->os = mem_maybedup(mctx, region.base, hinfo->os_len);
+ if (hinfo->os == NULL)
+ goto cleanup;
+
+ hinfo->mctx = mctx;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (mctx != NULL && hinfo->cpu != NULL)
+ isc_mem_free(mctx, hinfo->cpu);
+ return (ISC_R_NOMEMORY);
+}
+
+static inline void
+freestruct_hinfo(ARGS_FREESTRUCT) {
+ dns_rdata_hinfo_t *hinfo = source;
+
+ REQUIRE(source != NULL);
+
+ if (hinfo->mctx == NULL)
+ return;
+
+ if (hinfo->cpu != NULL)
+ isc_mem_free(hinfo->mctx, hinfo->cpu);
+ if (hinfo->os != NULL)
+ isc_mem_free(hinfo->mctx, hinfo->os);
+ hinfo->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_hinfo(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 13);
+
+ UNUSED(add);
+ UNUSED(arg);
+ UNUSED(rdata);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_hinfo(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 13);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_hinfo(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 13);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_hinfo(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 13);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_HINFO_13_C */
diff --git a/lib/dns/rdata/generic/hinfo_13.h b/lib/dns/rdata/generic/hinfo_13.h
new file mode 100644
index 0000000..66766df
--- /dev/null
+++ b/lib/dns/rdata/generic/hinfo_13.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+#ifndef GENERIC_HINFO_13_H
+#define GENERIC_HINFO_13_H 1
+
+/* $Id: hinfo_13.h,v 1.25 2007/06/19 23:47:17 tbox Exp $ */
+
+typedef struct dns_rdata_hinfo {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ char *cpu;
+ char *os;
+ isc_uint8_t cpu_len;
+ isc_uint8_t os_len;
+} dns_rdata_hinfo_t;
+
+#endif /* GENERIC_HINFO_13_H */
diff --git a/lib/dns/rdata/generic/ipseckey_45.c b/lib/dns/rdata/generic/ipseckey_45.c
new file mode 100644
index 0000000..92f5e21
--- /dev/null
+++ b/lib/dns/rdata/generic/ipseckey_45.c
@@ -0,0 +1,462 @@
+/*
+ * Copyright (C) 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 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.
+ */
+
+/* $Id: ipseckey_45.c,v 1.4 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef RDATA_GENERIC_IPSECKEY_45_C
+#define RDATA_GENERIC_IPSECKEY_45_C
+
+#include <string.h>
+
+#include <isc/net.h>
+
+#define RRTYPE_IPSECKEY_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_ipseckey(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+ unsigned int gateway;
+ struct in_addr addr;
+ unsigned char addr6[16];
+ isc_region_t region;
+
+ REQUIRE(type == 45);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ /*
+ * Precedence.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint8_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Gateway type.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0x3U)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint8_tobuffer(token.value.as_ulong, target));
+ gateway = token.value.as_ulong;
+
+ /*
+ * Algorithm.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint8_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Gateway.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+
+ switch (gateway) {
+ case 0:
+ if (strcmp(DNS_AS_STR(token), ".") != 0)
+ RETTOK(DNS_R_SYNTAX);
+ break;
+
+ case 1:
+ if (getquad(DNS_AS_STR(token), &addr, lexer, callbacks) != 1)
+ RETTOK(DNS_R_BADDOTTEDQUAD);
+ isc_buffer_availableregion(target, &region);
+ if (region.length < 4)
+ return (ISC_R_NOSPACE);
+ memcpy(region.base, &addr, 4);
+ isc_buffer_add(target, 4);
+ break;
+
+ case 2:
+ if (inet_pton(AF_INET6, DNS_AS_STR(token), addr6) != 1)
+ RETTOK(DNS_R_BADAAAA);
+ isc_buffer_availableregion(target, &region);
+ if (region.length < 16)
+ return (ISC_R_NOSPACE);
+ memcpy(region.base, addr6, 16);
+ isc_buffer_add(target, 16);
+ break;
+
+ case 3:
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin,
+ options, target));
+ break;
+ }
+
+ /*
+ * Public key.
+ */
+ return (isc_base64_tobuffer(lexer, target, -1));
+}
+
+static inline isc_result_t
+totext_ipseckey(ARGS_TOTEXT) {
+ isc_region_t region;
+ dns_name_t name;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+ char buf[sizeof("255 ")];
+ unsigned short num;
+ unsigned short gateway;
+
+ REQUIRE(rdata->type == 45);
+ REQUIRE(rdata->length >= 3);
+
+ dns_name_init(&name, NULL);
+ dns_name_init(&prefix, NULL);
+
+ if (rdata->data[1] > 3U)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext("( ", target));
+
+ /*
+ * Precendence.
+ */
+ dns_rdata_toregion(rdata, &region);
+ num = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+ sprintf(buf, "%u ", num);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Gateway type.
+ */
+ gateway = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+ sprintf(buf, "%u ", gateway);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Algorithm.
+ */
+ num = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+ sprintf(buf, "%u ", num);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Gateway.
+ */
+ switch (gateway) {
+ case 0:
+ RETERR(str_totext(".", target));
+ break;
+
+ case 1:
+ RETERR(inet_totext(AF_INET, &region, target));
+ isc_region_consume(&region, 4);
+ break;
+
+ case 2:
+ RETERR(inet_totext(AF_INET6, &region, target));
+ isc_region_consume(&region, 16);
+ break;
+
+ case 3:
+ dns_name_fromregion(&name, &region);
+ sub = name_prefix(&name, tctx->origin, &prefix);
+ RETERR(dns_name_totext(&prefix, sub, target));
+ isc_region_consume(&region, name_length(&name));
+ break;
+ }
+
+ /*
+ * Key.
+ */
+ if (region.length > 0U) {
+ RETERR(str_totext(tctx->linebreak, target));
+ RETERR(isc_base64_totext(&region, tctx->width - 2,
+ tctx->linebreak, target));
+ }
+
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" )", target));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_ipseckey(ARGS_FROMWIRE) {
+ dns_name_t name;
+ isc_region_t region;
+
+ REQUIRE(type == 45);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_NONE);
+
+ dns_name_init(&name, NULL);
+
+ isc_buffer_activeregion(source, &region);
+ if (region.length < 3)
+ return (ISC_R_UNEXPECTEDEND);
+
+ switch (region.base[1]) {
+ case 0:
+ isc_buffer_forward(source, region.length);
+ return (mem_tobuffer(target, region.base, region.length));
+
+ case 1:
+ if (region.length < 7)
+ return (ISC_R_UNEXPECTEDEND);
+ isc_buffer_forward(source, region.length);
+ return (mem_tobuffer(target, region.base, region.length));
+
+ case 2:
+ if (region.length < 19)
+ return (ISC_R_UNEXPECTEDEND);
+ isc_buffer_forward(source, region.length);
+ return (mem_tobuffer(target, region.base, region.length));
+
+ case 3:
+ RETERR(mem_tobuffer(target, region.base, 3));
+ isc_buffer_forward(source, 3);
+ RETERR(dns_name_fromwire(&name, source, dctx, options, target));
+ isc_buffer_activeregion(source, &region);
+ return(mem_tobuffer(target, region.base, region.length));
+
+ default:
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+}
+
+static inline isc_result_t
+towire_ipseckey(ARGS_TOWIRE) {
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 45);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(cctx);
+
+ dns_rdata_toregion(rdata, &region);
+ return (mem_tobuffer(target, region.base, region.length));
+}
+
+static inline int
+compare_ipseckey(ARGS_COMPARE) {
+ isc_region_t region1;
+ isc_region_t region2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 45);
+ REQUIRE(rdata1->length >= 3);
+ REQUIRE(rdata2->length >= 3);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+
+ return (isc_region_compare(&region1, &region2));
+}
+
+static inline isc_result_t
+fromstruct_ipseckey(ARGS_FROMSTRUCT) {
+ dns_rdata_ipseckey_t *ipseckey = source;
+ isc_region_t region;
+ isc_uint32_t n;
+
+ REQUIRE(type == 45);
+ REQUIRE(source != NULL);
+ REQUIRE(ipseckey->common.rdtype == type);
+ REQUIRE(ipseckey->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ if (ipseckey->gateway_type > 3U)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ RETERR(uint8_tobuffer(ipseckey->precedence, target));
+ RETERR(uint8_tobuffer(ipseckey->gateway_type, target));
+ RETERR(uint8_tobuffer(ipseckey->algorithm, target));
+
+ switch (ipseckey->gateway_type) {
+ case 0:
+ break;
+
+ case 1:
+ n = ntohl(ipseckey->in_addr.s_addr);
+ RETERR(uint32_tobuffer(n, target));
+ break;
+
+ case 2:
+ RETERR(mem_tobuffer(target, ipseckey->in6_addr.s6_addr, 16));
+ break;
+
+ case 3:
+ dns_name_toregion(&ipseckey->gateway, &region);
+ RETERR(isc_buffer_copyregion(target, &region));
+ break;
+ }
+
+ return (mem_tobuffer(target, ipseckey->key, ipseckey->keylength));
+}
+
+static inline isc_result_t
+tostruct_ipseckey(ARGS_TOSTRUCT) {
+ isc_region_t region;
+ dns_rdata_ipseckey_t *ipseckey = target;
+ dns_name_t name;
+ isc_uint32_t n;
+
+ REQUIRE(rdata->type == 45);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length >= 3);
+
+ if (rdata->data[1] > 3U)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ ipseckey->common.rdclass = rdata->rdclass;
+ ipseckey->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&ipseckey->common, link);
+
+ dns_name_init(&name, NULL);
+ dns_rdata_toregion(rdata, &region);
+
+ ipseckey->precedence = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+
+ ipseckey->gateway_type = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+
+ ipseckey->algorithm = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+
+ switch (ipseckey->gateway_type) {
+ case 0:
+ break;
+
+ case 1:
+ n = uint32_fromregion(&region);
+ ipseckey->in_addr.s_addr = htonl(n);
+ isc_region_consume(&region, 4);
+ break;
+
+ case 2:
+ memcpy(ipseckey->in6_addr.s6_addr, region.base, 16);
+ isc_region_consume(&region, 16);
+ break;
+
+ case 3:
+ dns_name_init(&ipseckey->gateway, NULL);
+ dns_name_fromregion(&name, &region);
+ RETERR(name_duporclone(&name, mctx, &ipseckey->gateway));
+ isc_region_consume(&region, name_length(&name));
+ break;
+ }
+
+ ipseckey->keylength = region.length;
+ if (ipseckey->keylength != 0U) {
+ ipseckey->key = mem_maybedup(mctx, region.base,
+ ipseckey->keylength);
+ if (ipseckey->key == NULL) {
+ if (ipseckey->gateway_type == 3)
+ dns_name_free(&ipseckey->gateway,
+ ipseckey->mctx);
+ return (ISC_R_NOMEMORY);
+ }
+ } else
+ ipseckey->key = NULL;
+
+ ipseckey->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_ipseckey(ARGS_FREESTRUCT) {
+ dns_rdata_ipseckey_t *ipseckey = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(ipseckey->common.rdtype == 45);
+
+ if (ipseckey->mctx == NULL)
+ return;
+
+ if (ipseckey->gateway_type == 3)
+ dns_name_free(&ipseckey->gateway, ipseckey->mctx);
+
+ if (ipseckey->key != NULL)
+ isc_mem_free(ipseckey->mctx, ipseckey->key);
+
+ ipseckey->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_ipseckey(ARGS_ADDLDATA) {
+
+ REQUIRE(rdata->type == 45);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_ipseckey(ARGS_DIGEST) {
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 45);
+
+ dns_rdata_toregion(rdata, &region);
+ return ((digest)(arg, &region));
+}
+
+static inline isc_boolean_t
+checkowner_ipseckey(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 45);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_ipseckey(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 45);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_IPSECKEY_45_C */
diff --git a/lib/dns/rdata/generic/ipseckey_45.h b/lib/dns/rdata/generic/ipseckey_45.h
new file mode 100644
index 0000000..2a6201f
--- /dev/null
+++ b/lib/dns/rdata/generic/ipseckey_45.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 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.
+ */
+
+/* $Id: ipseckey_45.h,v 1.4 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef GENERIC_IPSECKEY_45_H
+#define GENERIC_IPSECKEY_45_H 1
+
+typedef struct dns_rdata_ipseckey {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ isc_uint8_t precedence;
+ isc_uint8_t gateway_type;
+ isc_uint8_t algorithm;
+ struct in_addr in_addr; /* gateway type 1 */
+ struct in6_addr in6_addr; /* gateway type 2 */
+ dns_name_t gateway; /* gateway type 3 */
+ unsigned char *key;
+ isc_uint16_t keylength;
+} dns_rdata_ipseckey_t;
+
+#endif /* GENERIC_IPSECKEY_45_H */
diff --git a/lib/dns/rdata/generic/isdn_20.c b/lib/dns/rdata/generic/isdn_20.c
new file mode 100644
index 0000000..d7333d1
--- /dev/null
+++ b/lib/dns/rdata/generic/isdn_20.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: isdn_20.c,v 1.38 2007/06/19 23:47:17 tbox Exp $ */
+
+/* Reviewed: Wed Mar 15 16:53:11 PST 2000 by bwelling */
+
+/* RFC1183 */
+
+#ifndef RDATA_GENERIC_ISDN_20_C
+#define RDATA_GENERIC_ISDN_20_C
+
+#define RRTYPE_ISDN_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_isdn(ARGS_FROMTEXT) {
+ isc_token_t token;
+
+ REQUIRE(type == 20);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ /* ISDN-address */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_qstring,
+ ISC_FALSE));
+ RETTOK(txt_fromtext(&token.value.as_textregion, target));
+
+ /* sa: optional */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_qstring,
+ ISC_TRUE));
+ if (token.type != isc_tokentype_string &&
+ token.type != isc_tokentype_qstring) {
+ isc_lex_ungettoken(lexer, &token);
+ return (ISC_R_SUCCESS);
+ }
+ RETTOK(txt_fromtext(&token.value.as_textregion, target));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_isdn(ARGS_TOTEXT) {
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 20);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(tctx);
+
+ dns_rdata_toregion(rdata, &region);
+ RETERR(txt_totext(&region, target));
+ if (region.length == 0)
+ return (ISC_R_SUCCESS);
+ RETERR(str_totext(" ", target));
+ return (txt_totext(&region, target));
+}
+
+static inline isc_result_t
+fromwire_isdn(ARGS_FROMWIRE) {
+ REQUIRE(type == 20);
+
+ UNUSED(type);
+ UNUSED(dctx);
+ UNUSED(rdclass);
+ UNUSED(options);
+
+ RETERR(txt_fromwire(source, target));
+ if (buffer_empty(source))
+ return (ISC_R_SUCCESS);
+ return (txt_fromwire(source, target));
+}
+
+static inline isc_result_t
+towire_isdn(ARGS_TOWIRE) {
+ UNUSED(cctx);
+
+ REQUIRE(rdata->type == 20);
+ REQUIRE(rdata->length != 0);
+
+ return (mem_tobuffer(target, rdata->data, rdata->length));
+}
+
+static inline int
+compare_isdn(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 20);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_isdn(ARGS_FROMSTRUCT) {
+ dns_rdata_isdn_t *isdn = source;
+
+ REQUIRE(type == 20);
+ REQUIRE(source != NULL);
+ REQUIRE(isdn->common.rdtype == type);
+ REQUIRE(isdn->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ RETERR(uint8_tobuffer(isdn->isdn_len, target));
+ RETERR(mem_tobuffer(target, isdn->isdn, isdn->isdn_len));
+ RETERR(uint8_tobuffer(isdn->subaddress_len, target));
+ return (mem_tobuffer(target, isdn->subaddress, isdn->subaddress_len));
+}
+
+static inline isc_result_t
+tostruct_isdn(ARGS_TOSTRUCT) {
+ dns_rdata_isdn_t *isdn = target;
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 20);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ isdn->common.rdclass = rdata->rdclass;
+ isdn->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&isdn->common, link);
+
+ dns_rdata_toregion(rdata, &r);
+
+ isdn->isdn_len = uint8_fromregion(&r);
+ isc_region_consume(&r, 1);
+ isdn->isdn = mem_maybedup(mctx, r.base, isdn->isdn_len);
+ if (isdn->isdn == NULL)
+ return (ISC_R_NOMEMORY);
+ isc_region_consume(&r, isdn->isdn_len);
+
+ isdn->subaddress_len = uint8_fromregion(&r);
+ isc_region_consume(&r, 1);
+ isdn->subaddress = mem_maybedup(mctx, r.base, isdn->subaddress_len);
+ if (isdn->subaddress == NULL)
+ goto cleanup;
+
+ isdn->mctx = mctx;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (mctx != NULL && isdn->isdn != NULL)
+ isc_mem_free(mctx, isdn->isdn);
+ return (ISC_R_NOMEMORY);
+}
+
+static inline void
+freestruct_isdn(ARGS_FREESTRUCT) {
+ dns_rdata_isdn_t *isdn = source;
+
+ REQUIRE(source != NULL);
+
+ if (isdn->mctx == NULL)
+ return;
+
+ if (isdn->isdn != NULL)
+ isc_mem_free(isdn->mctx, isdn->isdn);
+ if (isdn->subaddress != NULL)
+ isc_mem_free(isdn->mctx, isdn->subaddress);
+ isdn->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_isdn(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 20);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_isdn(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 20);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_isdn(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 20);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_isdn(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 20);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_ISDN_20_C */
diff --git a/lib/dns/rdata/generic/isdn_20.h b/lib/dns/rdata/generic/isdn_20.h
new file mode 100644
index 0000000..a1f65ca
--- /dev/null
+++ b/lib/dns/rdata/generic/isdn_20.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+#ifndef GENERIC_ISDN_20_H
+#define GENERIC_ISDN_20_H 1
+
+/* $Id: isdn_20.h,v 1.18 2007/06/19 23:47:17 tbox Exp $ */
+
+/*!
+ * \brief Per RFC1183 */
+
+typedef struct dns_rdata_isdn {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ char *isdn;
+ char *subaddress;
+ isc_uint8_t isdn_len;
+ isc_uint8_t subaddress_len;
+} dns_rdata_isdn_t;
+
+#endif /* GENERIC_ISDN_20_H */
diff --git a/lib/dns/rdata/generic/key_25.c b/lib/dns/rdata/generic/key_25.c
new file mode 100644
index 0000000..9acfe95
--- /dev/null
+++ b/lib/dns/rdata/generic/key_25.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: key_25.c,v 1.51 2007/06/19 23:47:17 tbox Exp $ */
+
+/*
+ * Reviewed: Wed Mar 15 16:47:10 PST 2000 by halley.
+ */
+
+/* RFC2535 */
+
+#ifndef RDATA_GENERIC_KEY_25_C
+#define RDATA_GENERIC_KEY_25_C
+
+#include <dst/dst.h>
+
+#define RRTYPE_KEY_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_key(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_secalg_t alg;
+ dns_secproto_t proto;
+ dns_keyflags_t flags;
+
+ REQUIRE(type == 25);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ /* flags */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ RETTOK(dns_keyflags_fromtext(&flags, &token.value.as_textregion));
+ RETERR(uint16_tobuffer(flags, target));
+
+ /* protocol */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ RETTOK(dns_secproto_fromtext(&proto, &token.value.as_textregion));
+ RETERR(mem_tobuffer(target, &proto, 1));
+
+ /* algorithm */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ RETTOK(dns_secalg_fromtext(&alg, &token.value.as_textregion));
+ RETERR(mem_tobuffer(target, &alg, 1));
+
+ /* No Key? */
+ if ((flags & 0xc000) == 0xc000)
+ return (ISC_R_SUCCESS);
+
+ return (isc_base64_tobuffer(lexer, target, -1));
+}
+
+static inline isc_result_t
+totext_key(ARGS_TOTEXT) {
+ isc_region_t sr;
+ char buf[sizeof("64000")];
+ unsigned int flags;
+ unsigned char algorithm;
+
+ REQUIRE(rdata->type == 25);
+ REQUIRE(rdata->length != 0);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /* flags */
+ flags = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ sprintf(buf, "%u", flags);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ /* protocol */
+ sprintf(buf, "%u", sr.base[0]);
+ isc_region_consume(&sr, 1);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ /* algorithm */
+ algorithm = sr.base[0];
+ sprintf(buf, "%u", algorithm);
+ isc_region_consume(&sr, 1);
+ RETERR(str_totext(buf, target));
+
+ /* No Key? */
+ if ((flags & 0xc000) == 0xc000)
+ return (ISC_R_SUCCESS);
+
+ /* key */
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" (", target));
+ RETERR(str_totext(tctx->linebreak, target));
+ RETERR(isc_base64_totext(&sr, tctx->width - 2,
+ tctx->linebreak, target));
+
+ if ((tctx->flags & DNS_STYLEFLAG_COMMENT) != 0)
+ RETERR(str_totext(tctx->linebreak, target));
+ else if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" ", target));
+
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(")", target));
+
+ if ((tctx->flags & DNS_STYLEFLAG_COMMENT) != 0) {
+ isc_region_t tmpr;
+
+ RETERR(str_totext(" ; key id = ", target));
+ dns_rdata_toregion(rdata, &tmpr);
+ sprintf(buf, "%u", dst_region_computeid(&tmpr, algorithm));
+ RETERR(str_totext(buf, target));
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_key(ARGS_FROMWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(type == 25);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(dctx);
+ UNUSED(options);
+
+ isc_buffer_activeregion(source, &sr);
+ if (sr.length < 4)
+ return (ISC_R_UNEXPECTEDEND);
+
+ isc_buffer_forward(source, sr.length);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline isc_result_t
+towire_key(ARGS_TOWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == 25);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(cctx);
+
+ dns_rdata_toregion(rdata, &sr);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline int
+compare_key(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 25);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_key(ARGS_FROMSTRUCT) {
+ dns_rdata_key_t *key = source;
+
+ REQUIRE(type == 25);
+ REQUIRE(source != NULL);
+ REQUIRE(key->common.rdtype == type);
+ REQUIRE(key->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ /* Flags */
+ RETERR(uint16_tobuffer(key->flags, target));
+
+ /* Protocol */
+ RETERR(uint8_tobuffer(key->protocol, target));
+
+ /* Algorithm */
+ RETERR(uint8_tobuffer(key->algorithm, target));
+
+ /* Data */
+ return (mem_tobuffer(target, key->data, key->datalen));
+}
+
+static inline isc_result_t
+tostruct_key(ARGS_TOSTRUCT) {
+ dns_rdata_key_t *key = target;
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == 25);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ key->common.rdclass = rdata->rdclass;
+ key->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&key->common, link);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /* Flags */
+ if (sr.length < 2)
+ return (ISC_R_UNEXPECTEDEND);
+ key->flags = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+
+ /* Protocol */
+ if (sr.length < 1)
+ return (ISC_R_UNEXPECTEDEND);
+ key->protocol = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+
+ /* Algorithm */
+ if (sr.length < 1)
+ return (ISC_R_UNEXPECTEDEND);
+ key->algorithm = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+
+ /* Data */
+ key->datalen = sr.length;
+ key->data = mem_maybedup(mctx, sr.base, key->datalen);
+ if (key->data == NULL)
+ return (ISC_R_NOMEMORY);
+
+ key->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_key(ARGS_FREESTRUCT) {
+ dns_rdata_key_t *key = (dns_rdata_key_t *) source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(key->common.rdtype == 25);
+
+ if (key->mctx == NULL)
+ return;
+
+ if (key->data != NULL)
+ isc_mem_free(key->mctx, key->data);
+ key->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_key(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 25);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_key(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 25);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_key(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 25);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_key(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 25);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_KEY_25_C */
diff --git a/lib/dns/rdata/generic/key_25.h b/lib/dns/rdata/generic/key_25.h
new file mode 100644
index 0000000..bcf9cb6
--- /dev/null
+++ b/lib/dns/rdata/generic/key_25.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+#ifndef GENERIC_KEY_25_H
+#define GENERIC_KEY_25_H 1
+
+/* $Id: key_25.h,v 1.19 2007/06/19 23:47:17 tbox Exp $ */
+
+/*!
+ * \brief Per RFC2535 */
+
+typedef struct dns_rdata_key_t {
+ dns_rdatacommon_t common;
+ isc_mem_t * mctx;
+ isc_uint16_t flags;
+ isc_uint8_t protocol;
+ isc_uint8_t algorithm;
+ isc_uint16_t datalen;
+ unsigned char * data;
+} dns_rdata_key_t;
+
+
+#endif /* GENERIC_KEY_25_H */
diff --git a/lib/dns/rdata/generic/loc_29.c b/lib/dns/rdata/generic/loc_29.c
new file mode 100644
index 0000000..54b55a3
--- /dev/null
+++ b/lib/dns/rdata/generic/loc_29.c
@@ -0,0 +1,794 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: loc_29.c,v 1.45 2007/06/19 23:47:17 tbox Exp $ */
+
+/* Reviewed: Wed Mar 15 18:13:09 PST 2000 by explorer */
+
+/* RFC1876 */
+
+#ifndef RDATA_GENERIC_LOC_29_C
+#define RDATA_GENERIC_LOC_29_C
+
+#define RRTYPE_LOC_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_loc(ARGS_FROMTEXT) {
+ isc_token_t token;
+ int d1, m1, s1;
+ int d2, m2, s2;
+ unsigned char size;
+ unsigned char hp;
+ unsigned char vp;
+ unsigned char version;
+ isc_boolean_t east = ISC_FALSE;
+ isc_boolean_t north = ISC_FALSE;
+ long tmp;
+ long m;
+ long cm;
+ long poweroften[8] = { 1, 10, 100, 1000,
+ 10000, 100000, 1000000, 10000000 };
+ int man;
+ int exp;
+ char *e;
+ int i;
+ unsigned long latitude;
+ unsigned long longitude;
+ unsigned long altitude;
+
+ REQUIRE(type == 29);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+
+ /*
+ * Defaults.
+ */
+ m1 = s1 = 0;
+ m2 = s2 = 0;
+ size = 0x12; /* 1.00m */
+ hp = 0x16; /* 10000.00 m */
+ vp = 0x13; /* 10.00 m */
+ version = 0;
+
+ /*
+ * Degrees.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 90U)
+ RETTOK(ISC_R_RANGE);
+ d1 = (int)token.value.as_ulong;
+ /*
+ * Minutes.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ if (strcasecmp(DNS_AS_STR(token), "N") == 0)
+ north = ISC_TRUE;
+ if (north || strcasecmp(DNS_AS_STR(token), "S") == 0)
+ goto getlong;
+ m1 = strtol(DNS_AS_STR(token), &e, 10);
+ if (*e != 0)
+ RETTOK(DNS_R_SYNTAX);
+ if (m1 < 0 || m1 > 59)
+ RETTOK(ISC_R_RANGE);
+ if (d1 == 90 && m1 != 0)
+ RETTOK(ISC_R_RANGE);
+
+ /*
+ * Seconds.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ if (strcasecmp(DNS_AS_STR(token), "N") == 0)
+ north = ISC_TRUE;
+ if (north || strcasecmp(DNS_AS_STR(token), "S") == 0)
+ goto getlong;
+ s1 = strtol(DNS_AS_STR(token), &e, 10);
+ if (*e != 0 && *e != '.')
+ RETTOK(DNS_R_SYNTAX);
+ if (s1 < 0 || s1 > 59)
+ RETTOK(ISC_R_RANGE);
+ if (*e == '.') {
+ const char *l;
+ e++;
+ for (i = 0; i < 3; i++) {
+ if (*e == 0)
+ break;
+ if ((tmp = decvalue(*e++)) < 0)
+ RETTOK(DNS_R_SYNTAX);
+ s1 *= 10;
+ s1 += tmp;
+ }
+ for (; i < 3; i++)
+ s1 *= 10;
+ l = e;
+ while (*e != 0) {
+ if (decvalue(*e++) < 0)
+ RETTOK(DNS_R_SYNTAX);
+ }
+ if (*l != '\0' && callbacks != NULL) {
+ const char *file = isc_lex_getsourcename(lexer);
+ unsigned long line = isc_lex_getsourceline(lexer);
+
+ if (file == NULL)
+ file = "UNKNOWN";
+ (*callbacks->warn)(callbacks, "%s: %s:%u: '%s' extra "
+ "precision digits ignored",
+ "dns_rdata_fromtext", file, line,
+ DNS_AS_STR(token));
+ }
+ } else
+ s1 *= 1000;
+ if (d1 == 90 && s1 != 0)
+ RETTOK(ISC_R_RANGE);
+
+ /*
+ * Direction.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ if (strcasecmp(DNS_AS_STR(token), "N") == 0)
+ north = ISC_TRUE;
+ if (!north && strcasecmp(DNS_AS_STR(token), "S") != 0)
+ RETTOK(DNS_R_SYNTAX);
+
+ getlong:
+ /*
+ * Degrees.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 180U)
+ RETTOK(ISC_R_RANGE);
+ d2 = (int)token.value.as_ulong;
+
+ /*
+ * Minutes.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ if (strcasecmp(DNS_AS_STR(token), "E") == 0)
+ east = ISC_TRUE;
+ if (east || strcasecmp(DNS_AS_STR(token), "W") == 0)
+ goto getalt;
+ m2 = strtol(DNS_AS_STR(token), &e, 10);
+ if (*e != 0)
+ RETTOK(DNS_R_SYNTAX);
+ if (m2 < 0 || m2 > 59)
+ RETTOK(ISC_R_RANGE);
+ if (d2 == 180 && m2 != 0)
+ RETTOK(ISC_R_RANGE);
+
+ /*
+ * Seconds.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ if (strcasecmp(DNS_AS_STR(token), "E") == 0)
+ east = ISC_TRUE;
+ if (east || strcasecmp(DNS_AS_STR(token), "W") == 0)
+ goto getalt;
+ s2 = strtol(DNS_AS_STR(token), &e, 10);
+ if (*e != 0 && *e != '.')
+ RETTOK(DNS_R_SYNTAX);
+ if (s2 < 0 || s2 > 59)
+ RETTOK(ISC_R_RANGE);
+ if (*e == '.') {
+ const char *l;
+ e++;
+ for (i = 0; i < 3; i++) {
+ if (*e == 0)
+ break;
+ if ((tmp = decvalue(*e++)) < 0)
+ RETTOK(DNS_R_SYNTAX);
+ s2 *= 10;
+ s2 += tmp;
+ }
+ for (; i < 3; i++)
+ s2 *= 10;
+ l = e;
+ while (*e != 0) {
+ if (decvalue(*e++) < 0)
+ RETTOK(DNS_R_SYNTAX);
+ }
+ if (*l != '\0' && callbacks != NULL) {
+ const char *file = isc_lex_getsourcename(lexer);
+ unsigned long line = isc_lex_getsourceline(lexer);
+
+ if (file == NULL)
+ file = "UNKNOWN";
+ (*callbacks->warn)(callbacks, "%s: %s:%u: '%s' extra "
+ "precision digits ignored",
+ "dns_rdata_fromtext",
+ file, line, DNS_AS_STR(token));
+ }
+ } else
+ s2 *= 1000;
+ if (d2 == 180 && s2 != 0)
+ RETTOK(ISC_R_RANGE);
+
+ /*
+ * Direction.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ if (strcasecmp(DNS_AS_STR(token), "E") == 0)
+ east = ISC_TRUE;
+ if (!east && strcasecmp(DNS_AS_STR(token), "W") != 0)
+ RETTOK(DNS_R_SYNTAX);
+
+ getalt:
+ /*
+ * Altitude.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ m = strtol(DNS_AS_STR(token), &e, 10);
+ if (*e != 0 && *e != '.' && *e != 'm')
+ RETTOK(DNS_R_SYNTAX);
+ if (m < -100000 || m > 42849672)
+ RETTOK(ISC_R_RANGE);
+ cm = 0;
+ if (*e == '.') {
+ e++;
+ for (i = 0; i < 2; i++) {
+ if (*e == 0 || *e == 'm')
+ break;
+ if ((tmp = decvalue(*e++)) < 0)
+ return (DNS_R_SYNTAX);
+ cm *= 10;
+ if (m < 0)
+ cm -= tmp;
+ else
+ cm += tmp;
+ }
+ for (; i < 2; i++)
+ cm *= 10;
+ }
+ if (*e == 'm')
+ e++;
+ if (*e != 0)
+ RETTOK(DNS_R_SYNTAX);
+ if (m == -100000 && cm != 0)
+ RETTOK(ISC_R_RANGE);
+ if (m == 42849672 && cm > 95)
+ RETTOK(ISC_R_RANGE);
+ /*
+ * Adjust base.
+ */
+ altitude = m + 100000;
+ altitude *= 100;
+ altitude += cm;
+
+ /*
+ * Size: optional.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_TRUE));
+ if (token.type == isc_tokentype_eol ||
+ token.type == isc_tokentype_eof) {
+ isc_lex_ungettoken(lexer, &token);
+ goto encode;
+ }
+ m = strtol(DNS_AS_STR(token), &e, 10);
+ if (*e != 0 && *e != '.' && *e != 'm')
+ RETTOK(DNS_R_SYNTAX);
+ if (m < 0 || m > 90000000)
+ RETTOK(ISC_R_RANGE);
+ cm = 0;
+ if (*e == '.') {
+ e++;
+ for (i = 0; i < 2; i++) {
+ if (*e == 0 || *e == 'm')
+ break;
+ if ((tmp = decvalue(*e++)) < 0)
+ RETTOK(DNS_R_SYNTAX);
+ cm *= 10;
+ cm += tmp;
+ }
+ for (; i < 2; i++)
+ cm *= 10;
+ }
+ if (*e == 'm')
+ e++;
+ if (*e != 0)
+ RETTOK(DNS_R_SYNTAX);
+ /*
+ * We don't just multiply out as we will overflow.
+ */
+ if (m > 0) {
+ for (exp = 0; exp < 7; exp++)
+ if (m < poweroften[exp+1])
+ break;
+ man = m / poweroften[exp];
+ exp += 2;
+ } else {
+ if (cm >= 10) {
+ man = cm / 10;
+ exp = 1;
+ } else {
+ man = cm;
+ exp = 0;
+ }
+ }
+ size = (man << 4) + exp;
+
+ /*
+ * Horizontal precision: optional.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_TRUE));
+ if (token.type == isc_tokentype_eol ||
+ token.type == isc_tokentype_eof) {
+ isc_lex_ungettoken(lexer, &token);
+ goto encode;
+ }
+ m = strtol(DNS_AS_STR(token), &e, 10);
+ if (*e != 0 && *e != '.' && *e != 'm')
+ RETTOK(DNS_R_SYNTAX);
+ if (m < 0 || m > 90000000)
+ RETTOK(ISC_R_RANGE);
+ cm = 0;
+ if (*e == '.') {
+ e++;
+ for (i = 0; i < 2; i++) {
+ if (*e == 0 || *e == 'm')
+ break;
+ if ((tmp = decvalue(*e++)) < 0)
+ RETTOK(DNS_R_SYNTAX);
+ cm *= 10;
+ cm += tmp;
+ }
+ for (; i < 2; i++)
+ cm *= 10;
+ }
+ if (*e == 'm')
+ e++;
+ if (*e != 0)
+ RETTOK(DNS_R_SYNTAX);
+ /*
+ * We don't just multiply out as we will overflow.
+ */
+ if (m > 0) {
+ for (exp = 0; exp < 7; exp++)
+ if (m < poweroften[exp+1])
+ break;
+ man = m / poweroften[exp];
+ exp += 2;
+ } else if (cm >= 10) {
+ man = cm / 10;
+ exp = 1;
+ } else {
+ man = cm;
+ exp = 0;
+ }
+ hp = (man << 4) + exp;
+
+ /*
+ * Vertical precision: optional.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_TRUE));
+ if (token.type == isc_tokentype_eol ||
+ token.type == isc_tokentype_eof) {
+ isc_lex_ungettoken(lexer, &token);
+ goto encode;
+ }
+ m = strtol(DNS_AS_STR(token), &e, 10);
+ if (*e != 0 && *e != '.' && *e != 'm')
+ RETTOK(DNS_R_SYNTAX);
+ if (m < 0 || m > 90000000)
+ RETTOK(ISC_R_RANGE);
+ cm = 0;
+ if (*e == '.') {
+ e++;
+ for (i = 0; i < 2; i++) {
+ if (*e == 0 || *e == 'm')
+ break;
+ if ((tmp = decvalue(*e++)) < 0)
+ RETTOK(DNS_R_SYNTAX);
+ cm *= 10;
+ cm += tmp;
+ }
+ for (; i < 2; i++)
+ cm *= 10;
+ }
+ if (*e == 'm')
+ e++;
+ if (*e != 0)
+ RETTOK(DNS_R_SYNTAX);
+ /*
+ * We don't just multiply out as we will overflow.
+ */
+ if (m > 0) {
+ for (exp = 0; exp < 7; exp++)
+ if (m < poweroften[exp+1])
+ break;
+ man = m / poweroften[exp];
+ exp += 2;
+ } else if (cm >= 10) {
+ man = cm / 10;
+ exp = 1;
+ } else {
+ man = cm;
+ exp = 0;
+ }
+ vp = (man << 4) + exp;
+
+ encode:
+ RETERR(mem_tobuffer(target, &version, 1));
+ RETERR(mem_tobuffer(target, &size, 1));
+ RETERR(mem_tobuffer(target, &hp, 1));
+ RETERR(mem_tobuffer(target, &vp, 1));
+ if (north)
+ latitude = 0x80000000 + ( d1 * 3600 + m1 * 60 ) * 1000 + s1;
+ else
+ latitude = 0x80000000 - ( d1 * 3600 + m1 * 60 ) * 1000 - s1;
+ RETERR(uint32_tobuffer(latitude, target));
+
+ if (east)
+ longitude = 0x80000000 + ( d2 * 3600 + m2 * 60 ) * 1000 + s2;
+ else
+ longitude = 0x80000000 - ( d2 * 3600 + m2 * 60 ) * 1000 - s2;
+ RETERR(uint32_tobuffer(longitude, target));
+
+ return (uint32_tobuffer(altitude, target));
+}
+
+static inline isc_result_t
+totext_loc(ARGS_TOTEXT) {
+ int d1, m1, s1, fs1;
+ int d2, m2, s2, fs2;
+ unsigned long latitude;
+ unsigned long longitude;
+ unsigned long altitude;
+ isc_boolean_t north;
+ isc_boolean_t east;
+ isc_boolean_t below;
+ isc_region_t sr;
+ char buf[sizeof("89 59 59.999 N 179 59 59.999 E "
+ "42849672.95m 90000000m 90000000m 90000000m")];
+ char sbuf[sizeof("90000000m")];
+ char hbuf[sizeof("90000000m")];
+ char vbuf[sizeof("90000000m")];
+ unsigned char size, hp, vp;
+ unsigned long poweroften[8] = { 1, 10, 100, 1000,
+ 10000, 100000, 1000000, 10000000 };
+
+ UNUSED(tctx);
+
+ REQUIRE(rdata->type == 29);
+ REQUIRE(rdata->length != 0);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /* version = sr.base[0]; */
+ size = sr.base[1];
+ if ((size&0x0f)> 1)
+ sprintf(sbuf, "%lum", (size>>4) * poweroften[(size&0x0f)-2]);
+ else
+ sprintf(sbuf, "0.%02lum", (size>>4) * poweroften[(size&0x0f)]);
+ hp = sr.base[2];
+ if ((hp&0x0f)> 1)
+ sprintf(hbuf, "%lum", (hp>>4) * poweroften[(hp&0x0f)-2]);
+ else
+ sprintf(hbuf, "0.%02lum", (hp>>4) * poweroften[(hp&0x0f)]);
+ vp = sr.base[3];
+ if ((vp&0x0f)> 1)
+ sprintf(vbuf, "%lum", (vp>>4) * poweroften[(vp&0x0f)-2]);
+ else
+ sprintf(vbuf, "0.%02lum", (vp>>4) * poweroften[(vp&0x0f)]);
+ isc_region_consume(&sr, 4);
+
+ latitude = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+ if (latitude >= 0x80000000) {
+ north = ISC_TRUE;
+ latitude -= 0x80000000;
+ } else {
+ north = ISC_FALSE;
+ latitude = 0x80000000 - latitude;
+ }
+ fs1 = (int)(latitude % 1000);
+ latitude /= 1000;
+ s1 = (int)(latitude % 60);
+ latitude /= 60;
+ m1 = (int)(latitude % 60);
+ latitude /= 60;
+ d1 = (int)latitude;
+
+ longitude = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+ if (longitude >= 0x80000000) {
+ east = ISC_TRUE;
+ longitude -= 0x80000000;
+ } else {
+ east = ISC_FALSE;
+ longitude = 0x80000000 - longitude;
+ }
+ fs2 = (int)(longitude % 1000);
+ longitude /= 1000;
+ s2 = (int)(longitude % 60);
+ longitude /= 60;
+ m2 = (int)(longitude % 60);
+ longitude /= 60;
+ d2 = (int)longitude;
+
+ altitude = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+ if (altitude < 10000000U) {
+ below = ISC_TRUE;
+ altitude = 10000000 - altitude;
+ } else {
+ below =ISC_FALSE;
+ altitude -= 10000000;
+ }
+
+ sprintf(buf, "%d %d %d.%03d %s %d %d %d.%03d %s %s%ld.%02ldm %s %s %s",
+ d1, m1, s1, fs1, north ? "N" : "S",
+ d2, m2, s2, fs2, east ? "E" : "W",
+ below ? "-" : "", altitude/100, altitude % 100,
+ sbuf, hbuf, vbuf);
+
+ return (str_totext(buf, target));
+}
+
+static inline isc_result_t
+fromwire_loc(ARGS_FROMWIRE) {
+ isc_region_t sr;
+ unsigned char c;
+ unsigned long latitude;
+ unsigned long longitude;
+
+ REQUIRE(type == 29);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(dctx);
+ UNUSED(options);
+
+ isc_buffer_activeregion(source, &sr);
+ if (sr.length < 1)
+ return (ISC_R_UNEXPECTEDEND);
+ if (sr.base[0] != 0)
+ return (ISC_R_NOTIMPLEMENTED);
+ if (sr.length < 16)
+ return (ISC_R_UNEXPECTEDEND);
+
+ /*
+ * Size.
+ */
+ c = sr.base[1];
+ if (c != 0)
+ if ((c&0xf) > 9 || ((c>>4)&0xf) > 9 || ((c>>4)&0xf) == 0)
+ return (ISC_R_RANGE);
+
+ /*
+ * Horizontal precision.
+ */
+ c = sr.base[2];
+ if (c != 0)
+ if ((c&0xf) > 9 || ((c>>4)&0xf) > 9 || ((c>>4)&0xf) == 0)
+ return (ISC_R_RANGE);
+
+ /*
+ * Vertical precision.
+ */
+ c = sr.base[3];
+ if (c != 0)
+ if ((c&0xf) > 9 || ((c>>4)&0xf) > 9 || ((c>>4)&0xf) == 0)
+ return (ISC_R_RANGE);
+ isc_region_consume(&sr, 4);
+
+ /*
+ * Latitude.
+ */
+ latitude = uint32_fromregion(&sr);
+ if (latitude < (0x80000000UL - 90 * 3600000) ||
+ latitude > (0x80000000UL + 90 * 3600000))
+ return (ISC_R_RANGE);
+ isc_region_consume(&sr, 4);
+
+ /*
+ * Longitude.
+ */
+ longitude = uint32_fromregion(&sr);
+ if (longitude < (0x80000000UL - 180 * 3600000) ||
+ longitude > (0x80000000UL + 180 * 3600000))
+ return (ISC_R_RANGE);
+
+ /*
+ * Altitiude.
+ * All values possible.
+ */
+
+ isc_buffer_activeregion(source, &sr);
+ isc_buffer_forward(source, 16);
+ return (mem_tobuffer(target, sr.base, 16));
+}
+
+static inline isc_result_t
+towire_loc(ARGS_TOWIRE) {
+ UNUSED(cctx);
+
+ REQUIRE(rdata->type == 29);
+ REQUIRE(rdata->length != 0);
+
+ return (mem_tobuffer(target, rdata->data, rdata->length));
+}
+
+static inline int
+compare_loc(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 29);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_loc(ARGS_FROMSTRUCT) {
+ dns_rdata_loc_t *loc = source;
+ isc_uint8_t c;
+
+ REQUIRE(type == 29);
+ REQUIRE(source != NULL);
+ REQUIRE(loc->common.rdtype == type);
+ REQUIRE(loc->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ if (loc->v.v0.version != 0)
+ return (ISC_R_NOTIMPLEMENTED);
+ RETERR(uint8_tobuffer(loc->v.v0.version, target));
+
+ c = loc->v.v0.size;
+ if ((c&0xf) > 9 || ((c>>4)&0xf) > 9 || ((c>>4)&0xf) == 0)
+ return (ISC_R_RANGE);
+ RETERR(uint8_tobuffer(loc->v.v0.size, target));
+
+ c = loc->v.v0.horizontal;
+ if ((c&0xf) > 9 || ((c>>4)&0xf) > 9 || ((c>>4)&0xf) == 0)
+ return (ISC_R_RANGE);
+ RETERR(uint8_tobuffer(loc->v.v0.horizontal, target));
+
+ c = loc->v.v0.vertical;
+ if ((c&0xf) > 9 || ((c>>4)&0xf) > 9 || ((c>>4)&0xf) == 0)
+ return (ISC_R_RANGE);
+ RETERR(uint8_tobuffer(loc->v.v0.vertical, target));
+
+ if (loc->v.v0.latitude < (0x80000000UL - 90 * 3600000) ||
+ loc->v.v0.latitude > (0x80000000UL + 90 * 3600000))
+ return (ISC_R_RANGE);
+ RETERR(uint32_tobuffer(loc->v.v0.latitude, target));
+
+ if (loc->v.v0.longitude < (0x80000000UL - 180 * 3600000) ||
+ loc->v.v0.longitude > (0x80000000UL + 180 * 3600000))
+ return (ISC_R_RANGE);
+ RETERR(uint32_tobuffer(loc->v.v0.longitude, target));
+ return (uint32_tobuffer(loc->v.v0.altitude, target));
+}
+
+static inline isc_result_t
+tostruct_loc(ARGS_TOSTRUCT) {
+ dns_rdata_loc_t *loc = target;
+ isc_region_t r;
+ isc_uint8_t version;
+
+ REQUIRE(rdata->type == 29);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(mctx);
+
+ dns_rdata_toregion(rdata, &r);
+ version = uint8_fromregion(&r);
+ if (version != 0)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ loc->common.rdclass = rdata->rdclass;
+ loc->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&loc->common, link);
+
+ loc->v.v0.version = version;
+ isc_region_consume(&r, 1);
+ loc->v.v0.size = uint8_fromregion(&r);
+ isc_region_consume(&r, 1);
+ loc->v.v0.horizontal = uint8_fromregion(&r);
+ isc_region_consume(&r, 1);
+ loc->v.v0.vertical = uint8_fromregion(&r);
+ isc_region_consume(&r, 1);
+ loc->v.v0.latitude = uint32_fromregion(&r);
+ isc_region_consume(&r, 4);
+ loc->v.v0.longitude = uint32_fromregion(&r);
+ isc_region_consume(&r, 4);
+ loc->v.v0.altitude = uint32_fromregion(&r);
+ isc_region_consume(&r, 4);
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_loc(ARGS_FREESTRUCT) {
+ dns_rdata_loc_t *loc = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(loc->common.rdtype == 29);
+
+ UNUSED(source);
+ UNUSED(loc);
+}
+
+static inline isc_result_t
+additionaldata_loc(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 29);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_loc(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 29);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_loc(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 29);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_loc(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 29);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_LOC_29_C */
diff --git a/lib/dns/rdata/generic/loc_29.h b/lib/dns/rdata/generic/loc_29.h
new file mode 100644
index 0000000..f053c60
--- /dev/null
+++ b/lib/dns/rdata/generic/loc_29.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+#ifndef GENERIC_LOC_29_H
+#define GENERIC_LOC_29_H 1
+
+/* $Id: loc_29.h,v 1.19 2007/06/19 23:47:17 tbox Exp $ */
+
+/*!
+ * \brief Per RFC1876 */
+
+typedef struct dns_rdata_loc_0 {
+ isc_uint8_t version; /* must be first and zero */
+ isc_uint8_t size;
+ isc_uint8_t horizontal;
+ isc_uint8_t vertical;
+ isc_uint32_t latitude;
+ isc_uint32_t longitude;
+ isc_uint32_t altitude;
+} dns_rdata_loc_0_t;
+
+typedef struct dns_rdata_loc {
+ dns_rdatacommon_t common;
+ union {
+ dns_rdata_loc_0_t v0;
+ } v;
+} dns_rdata_loc_t;
+
+#endif /* GENERIC_LOC_29_H */
diff --git a/lib/dns/rdata/generic/mb_7.c b/lib/dns/rdata/generic/mb_7.c
new file mode 100644
index 0000000..fc3a7b6
--- /dev/null
+++ b/lib/dns/rdata/generic/mb_7.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: mb_7.c,v 1.45 2007/06/19 23:47:17 tbox Exp $ */
+
+/* Reviewed: Wed Mar 15 17:31:26 PST 2000 by bwelling */
+
+#ifndef RDATA_GENERIC_MB_7_C
+#define RDATA_GENERIC_MB_7_C
+
+#define RRTYPE_MB_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_mb(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+
+ REQUIRE(type == 7);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_mb(ARGS_TOTEXT) {
+ isc_region_t region;
+ dns_name_t name;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+
+ REQUIRE(rdata->type == 7);
+ REQUIRE(rdata->length != 0);
+
+ dns_name_init(&name, NULL);
+ dns_name_init(&prefix, NULL);
+
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+
+ sub = name_prefix(&name, tctx->origin, &prefix);
+
+ return (dns_name_totext(&prefix, sub, target));
+}
+
+static inline isc_result_t
+fromwire_mb(ARGS_FROMWIRE) {
+ dns_name_t name;
+
+ REQUIRE(type == 7);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_GLOBAL14);
+
+ dns_name_init(&name, NULL);
+ return (dns_name_fromwire(&name, source, dctx, options, target));
+}
+
+static inline isc_result_t
+towire_mb(ARGS_TOWIRE) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 7);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14);
+
+ dns_name_init(&name, offsets);
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+
+ return (dns_name_towire(&name, cctx, target));
+}
+
+static inline int
+compare_mb(ARGS_COMPARE) {
+ dns_name_t name1;
+ dns_name_t name2;
+ isc_region_t region1;
+ isc_region_t region2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 7);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+
+ dns_name_fromregion(&name1, &region1);
+ dns_name_fromregion(&name2, &region2);
+
+ return (dns_name_rdatacompare(&name1, &name2));
+}
+
+static inline isc_result_t
+fromstruct_mb(ARGS_FROMSTRUCT) {
+ dns_rdata_mb_t *mb = source;
+ isc_region_t region;
+
+ REQUIRE(type == 7);
+ REQUIRE(source != NULL);
+ REQUIRE(mb->common.rdtype == type);
+ REQUIRE(mb->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_name_toregion(&mb->mb, &region);
+ return (isc_buffer_copyregion(target, &region));
+}
+
+static inline isc_result_t
+tostruct_mb(ARGS_TOSTRUCT) {
+ isc_region_t region;
+ dns_rdata_mb_t *mb = target;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 7);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ mb->common.rdclass = rdata->rdclass;
+ mb->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&mb->common, link);
+
+ dns_name_init(&name, NULL);
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+ dns_name_init(&mb->mb, NULL);
+ RETERR(name_duporclone(&name, mctx, &mb->mb));
+ mb->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_mb(ARGS_FREESTRUCT) {
+ dns_rdata_mb_t *mb = source;
+
+ REQUIRE(source != NULL);
+
+ if (mb->mctx == NULL)
+ return;
+
+ dns_name_free(&mb->mb, mb->mctx);
+ mb->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_mb(ARGS_ADDLDATA) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 7);
+
+ dns_name_init(&name, offsets);
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+
+ return ((add)(arg, &name, dns_rdatatype_a));
+}
+
+static inline isc_result_t
+digest_mb(ARGS_DIGEST) {
+ isc_region_t r;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 7);
+
+ dns_rdata_toregion(rdata, &r);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline isc_boolean_t
+checkowner_mb(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 7);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (dns_name_ismailbox(name));
+}
+
+static inline isc_boolean_t
+checknames_mb(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 7);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_MB_7_C */
diff --git a/lib/dns/rdata/generic/mb_7.h b/lib/dns/rdata/generic/mb_7.h
new file mode 100644
index 0000000..b427ee9
--- /dev/null
+++ b/lib/dns/rdata/generic/mb_7.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* */
+#ifndef GENERIC_MB_7_H
+#define GENERIC_MB_7_H 1
+
+/* $Id: mb_7.h,v 1.27 2007/06/19 23:47:17 tbox Exp $ */
+
+typedef struct dns_rdata_mb {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ dns_name_t mb;
+} dns_rdata_mb_t;
+
+#endif /* GENERIC_MB_7_H */
diff --git a/lib/dns/rdata/generic/md_3.c b/lib/dns/rdata/generic/md_3.c
new file mode 100644
index 0000000..0f8560f
--- /dev/null
+++ b/lib/dns/rdata/generic/md_3.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: md_3.c,v 1.47 2007/06/19 23:47:17 tbox Exp $ */
+
+/* Reviewed: Wed Mar 15 17:48:20 PST 2000 by bwelling */
+
+#ifndef RDATA_GENERIC_MD_3_C
+#define RDATA_GENERIC_MD_3_C
+
+#define RRTYPE_MD_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_md(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+
+ REQUIRE(type == 3);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_md(ARGS_TOTEXT) {
+ isc_region_t region;
+ dns_name_t name;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+
+ REQUIRE(rdata->type == 3);
+ REQUIRE(rdata->length != 0);
+
+ dns_name_init(&name, NULL);
+ dns_name_init(&prefix, NULL);
+
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+
+ sub = name_prefix(&name, tctx->origin, &prefix);
+
+ return (dns_name_totext(&prefix, sub, target));
+}
+
+static inline isc_result_t
+fromwire_md(ARGS_FROMWIRE) {
+ dns_name_t name;
+
+ REQUIRE(type == 3);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_GLOBAL14);
+
+ dns_name_init(&name, NULL);
+ return (dns_name_fromwire(&name, source, dctx, options, target));
+}
+
+static inline isc_result_t
+towire_md(ARGS_TOWIRE) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 3);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14);
+
+ dns_name_init(&name, offsets);
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+
+ return (dns_name_towire(&name, cctx, target));
+}
+
+static inline int
+compare_md(ARGS_COMPARE) {
+ dns_name_t name1;
+ dns_name_t name2;
+ isc_region_t region1;
+ isc_region_t region2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 3);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+
+ dns_name_fromregion(&name1, &region1);
+ dns_name_fromregion(&name2, &region2);
+
+ return (dns_name_rdatacompare(&name1, &name2));
+}
+
+static inline isc_result_t
+fromstruct_md(ARGS_FROMSTRUCT) {
+ dns_rdata_md_t *md = source;
+ isc_region_t region;
+
+ REQUIRE(type == 3);
+ REQUIRE(source != NULL);
+ REQUIRE(md->common.rdtype == type);
+ REQUIRE(md->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_name_toregion(&md->md, &region);
+ return (isc_buffer_copyregion(target, &region));
+}
+
+static inline isc_result_t
+tostruct_md(ARGS_TOSTRUCT) {
+ dns_rdata_md_t *md = target;
+ isc_region_t r;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 3);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ md->common.rdclass = rdata->rdclass;
+ md->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&md->common, link);
+
+ dns_name_init(&name, NULL);
+ dns_rdata_toregion(rdata, &r);
+ dns_name_fromregion(&name, &r);
+ dns_name_init(&md->md, NULL);
+ RETERR(name_duporclone(&name, mctx, &md->md));
+ md->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_md(ARGS_FREESTRUCT) {
+ dns_rdata_md_t *md = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(md->common.rdtype == 3);
+
+ if (md->mctx == NULL)
+ return;
+
+ dns_name_free(&md->md, md->mctx);
+ md->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_md(ARGS_ADDLDATA) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 3);
+
+ dns_name_init(&name, offsets);
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+
+ return ((add)(arg, &name, dns_rdatatype_a));
+}
+
+static inline isc_result_t
+digest_md(ARGS_DIGEST) {
+ isc_region_t r;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 3);
+
+ dns_rdata_toregion(rdata, &r);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline isc_boolean_t
+checkowner_md(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 3);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_md(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 3);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_MD_3_C */
diff --git a/lib/dns/rdata/generic/md_3.h b/lib/dns/rdata/generic/md_3.h
new file mode 100644
index 0000000..ba70d18
--- /dev/null
+++ b/lib/dns/rdata/generic/md_3.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* */
+#ifndef GENERIC_MD_3_H
+#define GENERIC_MD_3_H 1
+
+/* $Id: md_3.h,v 1.28 2007/06/19 23:47:17 tbox Exp $ */
+
+typedef struct dns_rdata_md {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ dns_name_t md;
+} dns_rdata_md_t;
+
+
+#endif /* GENERIC_MD_3_H */
diff --git a/lib/dns/rdata/generic/mf_4.c b/lib/dns/rdata/generic/mf_4.c
new file mode 100644
index 0000000..dffcec2
--- /dev/null
+++ b/lib/dns/rdata/generic/mf_4.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: mf_4.c,v 1.45 2007/06/19 23:47:17 tbox Exp $ */
+
+/* reviewed: Wed Mar 15 17:47:33 PST 2000 by brister */
+
+#ifndef RDATA_GENERIC_MF_4_C
+#define RDATA_GENERIC_MF_4_C
+
+#define RRTYPE_MF_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_mf(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+
+ REQUIRE(type == 4);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_mf(ARGS_TOTEXT) {
+ isc_region_t region;
+ dns_name_t name;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+
+ REQUIRE(rdata->type == 4);
+ REQUIRE(rdata->length != 0);
+
+ dns_name_init(&name, NULL);
+ dns_name_init(&prefix, NULL);
+
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+
+ sub = name_prefix(&name, tctx->origin, &prefix);
+
+ return (dns_name_totext(&prefix, sub, target));
+}
+
+static inline isc_result_t
+fromwire_mf(ARGS_FROMWIRE) {
+ dns_name_t name;
+
+ REQUIRE(type == 4);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_GLOBAL14);
+
+ dns_name_init(&name, NULL);
+ return (dns_name_fromwire(&name, source, dctx, options, target));
+}
+
+static inline isc_result_t
+towire_mf(ARGS_TOWIRE) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 4);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14);
+
+ dns_name_init(&name, offsets);
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+
+ return (dns_name_towire(&name, cctx, target));
+}
+
+static inline int
+compare_mf(ARGS_COMPARE) {
+ dns_name_t name1;
+ dns_name_t name2;
+ isc_region_t region1;
+ isc_region_t region2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 4);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+
+ dns_name_fromregion(&name1, &region1);
+ dns_name_fromregion(&name2, &region2);
+
+ return (dns_name_rdatacompare(&name1, &name2));
+}
+
+static inline isc_result_t
+fromstruct_mf(ARGS_FROMSTRUCT) {
+ dns_rdata_mf_t *mf = source;
+ isc_region_t region;
+
+ REQUIRE(type == 4);
+ REQUIRE(source != NULL);
+ REQUIRE(mf->common.rdtype == type);
+ REQUIRE(mf->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_name_toregion(&mf->mf, &region);
+ return (isc_buffer_copyregion(target, &region));
+}
+
+static inline isc_result_t
+tostruct_mf(ARGS_TOSTRUCT) {
+ dns_rdata_mf_t *mf = target;
+ isc_region_t r;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 4);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ mf->common.rdclass = rdata->rdclass;
+ mf->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&mf->common, link);
+
+ dns_name_init(&name, NULL);
+ dns_rdata_toregion(rdata, &r);
+ dns_name_fromregion(&name, &r);
+ dns_name_init(&mf->mf, NULL);
+ RETERR(name_duporclone(&name, mctx, &mf->mf));
+ mf->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_mf(ARGS_FREESTRUCT) {
+ dns_rdata_mf_t *mf = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(mf->common.rdtype == 4);
+
+ if (mf->mctx == NULL)
+ return;
+ dns_name_free(&mf->mf, mf->mctx);
+ mf->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_mf(ARGS_ADDLDATA) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 4);
+
+ dns_name_init(&name, offsets);
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+
+ return ((add)(arg, &name, dns_rdatatype_a));
+}
+
+static inline isc_result_t
+digest_mf(ARGS_DIGEST) {
+ isc_region_t r;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 4);
+
+ dns_rdata_toregion(rdata, &r);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline isc_boolean_t
+checkowner_mf(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 4);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_mf(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 4);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_MF_4_C */
diff --git a/lib/dns/rdata/generic/mf_4.h b/lib/dns/rdata/generic/mf_4.h
new file mode 100644
index 0000000..32d2493
--- /dev/null
+++ b/lib/dns/rdata/generic/mf_4.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* */
+#ifndef GENERIC_MF_4_H
+#define GENERIC_MF_4_H 1
+
+/* $Id: mf_4.h,v 1.26 2007/06/19 23:47:17 tbox Exp $ */
+
+typedef struct dns_rdata_mf {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ dns_name_t mf;
+} dns_rdata_mf_t;
+
+#endif /* GENERIC_MF_4_H */
diff --git a/lib/dns/rdata/generic/mg_8.c b/lib/dns/rdata/generic/mg_8.c
new file mode 100644
index 0000000..e4dca1d
--- /dev/null
+++ b/lib/dns/rdata/generic/mg_8.c
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: mg_8.c,v 1.43 2007/06/19 23:47:17 tbox Exp $ */
+
+/* reviewed: Wed Mar 15 17:49:21 PST 2000 by brister */
+
+#ifndef RDATA_GENERIC_MG_8_C
+#define RDATA_GENERIC_MG_8_C
+
+#define RRTYPE_MG_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_mg(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+
+ REQUIRE(type == 8);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_mg(ARGS_TOTEXT) {
+ isc_region_t region;
+ dns_name_t name;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+
+ REQUIRE(rdata->type == 8);
+ REQUIRE(rdata->length != 0);
+
+ dns_name_init(&name, NULL);
+ dns_name_init(&prefix, NULL);
+
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+
+ sub = name_prefix(&name, tctx->origin, &prefix);
+
+ return (dns_name_totext(&prefix, sub, target));
+}
+
+static inline isc_result_t
+fromwire_mg(ARGS_FROMWIRE) {
+ dns_name_t name;
+
+ REQUIRE(type == 8);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_GLOBAL14);
+
+ dns_name_init(&name, NULL);
+ return (dns_name_fromwire(&name, source, dctx, options, target));
+}
+
+static inline isc_result_t
+towire_mg(ARGS_TOWIRE) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 8);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14);
+
+ dns_name_init(&name, offsets);
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+
+ return (dns_name_towire(&name, cctx, target));
+}
+
+static inline int
+compare_mg(ARGS_COMPARE) {
+ dns_name_t name1;
+ dns_name_t name2;
+ isc_region_t region1;
+ isc_region_t region2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 8);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+
+ dns_name_fromregion(&name1, &region1);
+ dns_name_fromregion(&name2, &region2);
+
+ return (dns_name_rdatacompare(&name1, &name2));
+}
+
+static inline isc_result_t
+fromstruct_mg(ARGS_FROMSTRUCT) {
+ dns_rdata_mg_t *mg = source;
+ isc_region_t region;
+
+ REQUIRE(type == 8);
+ REQUIRE(source != NULL);
+ REQUIRE(mg->common.rdtype == type);
+ REQUIRE(mg->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_name_toregion(&mg->mg, &region);
+ return (isc_buffer_copyregion(target, &region));
+}
+
+static inline isc_result_t
+tostruct_mg(ARGS_TOSTRUCT) {
+ isc_region_t region;
+ dns_rdata_mg_t *mg = target;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 8);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ mg->common.rdclass = rdata->rdclass;
+ mg->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&mg->common, link);
+
+ dns_name_init(&name, NULL);
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+ dns_name_init(&mg->mg, NULL);
+ RETERR(name_duporclone(&name, mctx, &mg->mg));
+ mg->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_mg(ARGS_FREESTRUCT) {
+ dns_rdata_mg_t *mg = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(mg->common.rdtype == 8);
+
+ if (mg->mctx == NULL)
+ return;
+ dns_name_free(&mg->mg, mg->mctx);
+ mg->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_mg(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 8);
+
+ UNUSED(add);
+ UNUSED(arg);
+ UNUSED(rdata);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_mg(ARGS_DIGEST) {
+ isc_region_t r;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 8);
+
+ dns_rdata_toregion(rdata, &r);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline isc_boolean_t
+checkowner_mg(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 8);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (dns_name_ismailbox(name));
+}
+
+static inline isc_boolean_t
+checknames_mg(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 8);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_MG_8_C */
diff --git a/lib/dns/rdata/generic/mg_8.h b/lib/dns/rdata/generic/mg_8.h
new file mode 100644
index 0000000..8fa143a
--- /dev/null
+++ b/lib/dns/rdata/generic/mg_8.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* */
+#ifndef GENERIC_MG_8_H
+#define GENERIC_MG_8_H 1
+
+/* $Id: mg_8.h,v 1.26 2007/06/19 23:47:17 tbox Exp $ */
+
+typedef struct dns_rdata_mg {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ dns_name_t mg;
+} dns_rdata_mg_t;
+
+#endif /* GENERIC_MG_8_H */
diff --git a/lib/dns/rdata/generic/minfo_14.c b/lib/dns/rdata/generic/minfo_14.c
new file mode 100644
index 0000000..6645bbc
--- /dev/null
+++ b/lib/dns/rdata/generic/minfo_14.c
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: minfo_14.c,v 1.45 2007/06/19 23:47:17 tbox Exp $ */
+
+/* reviewed: Wed Mar 15 17:45:32 PST 2000 by brister */
+
+#ifndef RDATA_GENERIC_MINFO_14_C
+#define RDATA_GENERIC_MINFO_14_C
+
+#define RRTYPE_MINFO_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_minfo(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+ int i;
+ isc_boolean_t ok;
+
+ REQUIRE(type == 14);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ for (i = 0; i < 2; i++) {
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_string,
+ ISC_FALSE));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin,
+ options, target));
+ ok = ISC_TRUE;
+ if ((options & DNS_RDATA_CHECKNAMES) != 0)
+ ok = dns_name_ismailbox(&name);
+ if (!ok && (options & DNS_RDATA_CHECKNAMESFAIL) != 0)
+ RETTOK(DNS_R_BADNAME);
+ if (!ok && callbacks != NULL)
+ warn_badname(&name, lexer, callbacks);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_minfo(ARGS_TOTEXT) {
+ isc_region_t region;
+ dns_name_t rmail;
+ dns_name_t email;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+
+ REQUIRE(rdata->type == 14);
+ REQUIRE(rdata->length != 0);
+
+ dns_name_init(&rmail, NULL);
+ dns_name_init(&email, NULL);
+ dns_name_init(&prefix, NULL);
+
+ dns_rdata_toregion(rdata, &region);
+
+ dns_name_fromregion(&rmail, &region);
+ isc_region_consume(&region, rmail.length);
+
+ dns_name_fromregion(&email, &region);
+ isc_region_consume(&region, email.length);
+
+ sub = name_prefix(&rmail, tctx->origin, &prefix);
+
+ RETERR(dns_name_totext(&prefix, sub, target));
+
+ RETERR(str_totext(" ", target));
+
+ sub = name_prefix(&email, tctx->origin, &prefix);
+ return (dns_name_totext(&prefix, sub, target));
+}
+
+static inline isc_result_t
+fromwire_minfo(ARGS_FROMWIRE) {
+ dns_name_t rmail;
+ dns_name_t email;
+
+ REQUIRE(type == 14);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_GLOBAL14);
+
+ dns_name_init(&rmail, NULL);
+ dns_name_init(&email, NULL);
+
+ RETERR(dns_name_fromwire(&rmail, source, dctx, options, target));
+ return (dns_name_fromwire(&email, source, dctx, options, target));
+}
+
+static inline isc_result_t
+towire_minfo(ARGS_TOWIRE) {
+ isc_region_t region;
+ dns_name_t rmail;
+ dns_name_t email;
+ dns_offsets_t roffsets;
+ dns_offsets_t eoffsets;
+
+ REQUIRE(rdata->type == 14);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14);
+
+ dns_name_init(&rmail, roffsets);
+ dns_name_init(&email, eoffsets);
+
+ dns_rdata_toregion(rdata, &region);
+
+ dns_name_fromregion(&rmail, &region);
+ isc_region_consume(&region, name_length(&rmail));
+
+ RETERR(dns_name_towire(&rmail, cctx, target));
+
+ dns_name_fromregion(&rmail, &region);
+ isc_region_consume(&region, rmail.length);
+
+ return (dns_name_towire(&rmail, cctx, target));
+}
+
+static inline int
+compare_minfo(ARGS_COMPARE) {
+ isc_region_t region1;
+ isc_region_t region2;
+ dns_name_t name1;
+ dns_name_t name2;
+ int order;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 14);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+
+ dns_name_fromregion(&name1, &region1);
+ dns_name_fromregion(&name2, &region2);
+
+ order = dns_name_rdatacompare(&name1, &name2);
+ if (order != 0)
+ return (order);
+
+ isc_region_consume(&region1, name_length(&name1));
+ isc_region_consume(&region2, name_length(&name2));
+
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+
+ dns_name_fromregion(&name1, &region1);
+ dns_name_fromregion(&name2, &region2);
+
+ order = dns_name_rdatacompare(&name1, &name2);
+ return (order);
+}
+
+static inline isc_result_t
+fromstruct_minfo(ARGS_FROMSTRUCT) {
+ dns_rdata_minfo_t *minfo = source;
+ isc_region_t region;
+
+ REQUIRE(type == 14);
+ REQUIRE(source != NULL);
+ REQUIRE(minfo->common.rdtype == type);
+ REQUIRE(minfo->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_name_toregion(&minfo->rmailbox, &region);
+ RETERR(isc_buffer_copyregion(target, &region));
+ dns_name_toregion(&minfo->emailbox, &region);
+ return (isc_buffer_copyregion(target, &region));
+}
+
+static inline isc_result_t
+tostruct_minfo(ARGS_TOSTRUCT) {
+ dns_rdata_minfo_t *minfo = target;
+ isc_region_t region;
+ dns_name_t name;
+ isc_result_t result;
+
+ REQUIRE(rdata->type == 14);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ minfo->common.rdclass = rdata->rdclass;
+ minfo->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&minfo->common, link);
+
+ dns_name_init(&name, NULL);
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+ dns_name_init(&minfo->rmailbox, NULL);
+ RETERR(name_duporclone(&name, mctx, &minfo->rmailbox));
+ isc_region_consume(&region, name_length(&name));
+
+ dns_name_fromregion(&name, &region);
+ dns_name_init(&minfo->emailbox, NULL);
+ result = name_duporclone(&name, mctx, &minfo->emailbox);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ minfo->mctx = mctx;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (mctx != NULL)
+ dns_name_free(&minfo->rmailbox, mctx);
+ return (ISC_R_NOMEMORY);
+}
+
+static inline void
+freestruct_minfo(ARGS_FREESTRUCT) {
+ dns_rdata_minfo_t *minfo = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(minfo->common.rdtype == 14);
+
+ if (minfo->mctx == NULL)
+ return;
+
+ dns_name_free(&minfo->rmailbox, minfo->mctx);
+ dns_name_free(&minfo->emailbox, minfo->mctx);
+ minfo->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_minfo(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 14);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_minfo(ARGS_DIGEST) {
+ isc_region_t r;
+ dns_name_t name;
+ isc_result_t result;
+
+ REQUIRE(rdata->type == 14);
+
+ dns_rdata_toregion(rdata, &r);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+ result = dns_name_digest(&name, digest, arg);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ isc_region_consume(&r, name_length(&name));
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline isc_boolean_t
+checkowner_minfo(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 14);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_minfo(ARGS_CHECKNAMES) {
+ isc_region_t region;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 14);
+
+ UNUSED(owner);
+
+ dns_rdata_toregion(rdata, &region);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &region);
+ if (!dns_name_ismailbox(&name)) {
+ if (bad != NULL)
+ dns_name_clone(&name, bad);
+ return (ISC_FALSE);
+ }
+ isc_region_consume(&region, name_length(&name));
+ dns_name_fromregion(&name, &region);
+ if (!dns_name_ismailbox(&name)) {
+ if (bad != NULL)
+ dns_name_clone(&name, bad);
+ return (ISC_FALSE);
+ }
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_MINFO_14_C */
diff --git a/lib/dns/rdata/generic/minfo_14.h b/lib/dns/rdata/generic/minfo_14.h
new file mode 100644
index 0000000..76195c5
--- /dev/null
+++ b/lib/dns/rdata/generic/minfo_14.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* */
+#ifndef GENERIC_MINFO_14_H
+#define GENERIC_MINFO_14_H 1
+
+/* $Id: minfo_14.h,v 1.27 2007/06/19 23:47:17 tbox Exp $ */
+
+typedef struct dns_rdata_minfo {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ dns_name_t rmailbox;
+ dns_name_t emailbox;
+} dns_rdata_minfo_t;
+
+#endif /* GENERIC_MINFO_14_H */
diff --git a/lib/dns/rdata/generic/mr_9.c b/lib/dns/rdata/generic/mr_9.c
new file mode 100644
index 0000000..289d739
--- /dev/null
+++ b/lib/dns/rdata/generic/mr_9.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: mr_9.c,v 1.42 2007/06/19 23:47:17 tbox Exp $ */
+
+/* Reviewed: Wed Mar 15 21:30:35 EST 2000 by tale */
+
+#ifndef RDATA_GENERIC_MR_9_C
+#define RDATA_GENERIC_MR_9_C
+
+#define RRTYPE_MR_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_mr(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+
+ REQUIRE(type == 9);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_mr(ARGS_TOTEXT) {
+ isc_region_t region;
+ dns_name_t name;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+
+ REQUIRE(rdata->type == 9);
+ REQUIRE(rdata->length != 0);
+
+ dns_name_init(&name, NULL);
+ dns_name_init(&prefix, NULL);
+
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+
+ sub = name_prefix(&name, tctx->origin, &prefix);
+
+ return (dns_name_totext(&prefix, sub, target));
+}
+
+static inline isc_result_t
+fromwire_mr(ARGS_FROMWIRE) {
+ dns_name_t name;
+
+ REQUIRE(type == 9);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_GLOBAL14);
+
+ dns_name_init(&name, NULL);
+ return (dns_name_fromwire(&name, source, dctx, options, target));
+}
+
+static inline isc_result_t
+towire_mr(ARGS_TOWIRE) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 9);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14);
+
+ dns_name_init(&name, offsets);
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+
+ return (dns_name_towire(&name, cctx, target));
+}
+
+static inline int
+compare_mr(ARGS_COMPARE) {
+ dns_name_t name1;
+ dns_name_t name2;
+ isc_region_t region1;
+ isc_region_t region2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 9);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+
+ dns_name_fromregion(&name1, &region1);
+ dns_name_fromregion(&name2, &region2);
+
+ return (dns_name_rdatacompare(&name1, &name2));
+}
+
+static inline isc_result_t
+fromstruct_mr(ARGS_FROMSTRUCT) {
+ dns_rdata_mr_t *mr = source;
+ isc_region_t region;
+
+ REQUIRE(type == 9);
+ REQUIRE(source != NULL);
+ REQUIRE(mr->common.rdtype == type);
+ REQUIRE(mr->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_name_toregion(&mr->mr, &region);
+ return (isc_buffer_copyregion(target, &region));
+}
+
+static inline isc_result_t
+tostruct_mr(ARGS_TOSTRUCT) {
+ isc_region_t region;
+ dns_rdata_mr_t *mr = target;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 9);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ mr->common.rdclass = rdata->rdclass;
+ mr->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&mr->common, link);
+
+ dns_name_init(&name, NULL);
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+ dns_name_init(&mr->mr, NULL);
+ RETERR(name_duporclone(&name, mctx, &mr->mr));
+ mr->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_mr(ARGS_FREESTRUCT) {
+ dns_rdata_mr_t *mr = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(mr->common.rdtype == 9);
+
+ if (mr->mctx == NULL)
+ return;
+ dns_name_free(&mr->mr, mr->mctx);
+ mr->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_mr(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 9);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_mr(ARGS_DIGEST) {
+ isc_region_t r;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 9);
+
+ dns_rdata_toregion(rdata, &r);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline isc_boolean_t
+checkowner_mr(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 9);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_mr(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 9);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_MR_9_C */
diff --git a/lib/dns/rdata/generic/mr_9.h b/lib/dns/rdata/generic/mr_9.h
new file mode 100644
index 0000000..3d81bdd
--- /dev/null
+++ b/lib/dns/rdata/generic/mr_9.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* */
+#ifndef GENERIC_MR_9_H
+#define GENERIC_MR_9_H 1
+
+/* $Id: mr_9.h,v 1.26 2007/06/19 23:47:17 tbox Exp $ */
+
+typedef struct dns_rdata_mr {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ dns_name_t mr;
+} dns_rdata_mr_t;
+
+#endif /* GENERIC_MR_9_H */
diff --git a/lib/dns/rdata/generic/mx_15.c b/lib/dns/rdata/generic/mx_15.c
new file mode 100644
index 0000000..086c043
--- /dev/null
+++ b/lib/dns/rdata/generic/mx_15.c
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001, 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: mx_15.c,v 1.56 2007/06/19 23:47:17 tbox Exp $ */
+
+/* reviewed: Wed Mar 15 18:05:46 PST 2000 by brister */
+
+#ifndef RDATA_GENERIC_MX_15_C
+#define RDATA_GENERIC_MX_15_C
+
+#include <string.h>
+
+#include <isc/net.h>
+
+#define RRTYPE_MX_ATTRIBUTES (0)
+
+static isc_boolean_t
+check_mx(isc_token_t *token) {
+ char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:123.123.123.123.")];
+ struct in_addr addr;
+ struct in6_addr addr6;
+
+ if (strlcpy(tmp, DNS_AS_STR(*token), sizeof(tmp)) >= sizeof(tmp))
+ return (ISC_TRUE);
+
+ if (tmp[strlen(tmp) - 1] == '.')
+ tmp[strlen(tmp) - 1] = '\0';
+ if (inet_aton(tmp, &addr) == 1 ||
+ inet_pton(AF_INET6, tmp, &addr6) == 1)
+ return (ISC_FALSE);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_result_t
+fromtext_mx(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+ isc_boolean_t ok;
+
+ REQUIRE(type == 15);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+
+ ok = ISC_TRUE;
+ if ((options & DNS_RDATA_CHECKMX) != 0)
+ ok = check_mx(&token);
+ if (!ok && (options & DNS_RDATA_CHECKMXFAIL) != 0)
+ RETTOK(DNS_R_MXISADDRESS);
+ if (!ok && callbacks != NULL)
+ warn_badmx(&token, lexer, callbacks);
+
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+ ok = ISC_TRUE;
+ if ((options & DNS_RDATA_CHECKNAMES) != 0)
+ ok = dns_name_ishostname(&name, ISC_FALSE);
+ if (!ok && (options & DNS_RDATA_CHECKNAMESFAIL) != 0)
+ RETTOK(DNS_R_BADNAME);
+ if (!ok && callbacks != NULL)
+ warn_badname(&name, lexer, callbacks);
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_mx(ARGS_TOTEXT) {
+ isc_region_t region;
+ dns_name_t name;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+ char buf[sizeof("64000")];
+ unsigned short num;
+
+ REQUIRE(rdata->type == 15);
+ REQUIRE(rdata->length != 0);
+
+ dns_name_init(&name, NULL);
+ dns_name_init(&prefix, NULL);
+
+ dns_rdata_toregion(rdata, &region);
+ num = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ sprintf(buf, "%u", num);
+ RETERR(str_totext(buf, target));
+
+ RETERR(str_totext(" ", target));
+
+ dns_name_fromregion(&name, &region);
+ sub = name_prefix(&name, tctx->origin, &prefix);
+ return (dns_name_totext(&prefix, sub, target));
+}
+
+static inline isc_result_t
+fromwire_mx(ARGS_FROMWIRE) {
+ dns_name_t name;
+ isc_region_t sregion;
+
+ REQUIRE(type == 15);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_GLOBAL14);
+
+ dns_name_init(&name, NULL);
+
+ isc_buffer_activeregion(source, &sregion);
+ if (sregion.length < 2)
+ return (ISC_R_UNEXPECTEDEND);
+ RETERR(mem_tobuffer(target, sregion.base, 2));
+ isc_buffer_forward(source, 2);
+ return (dns_name_fromwire(&name, source, dctx, options, target));
+}
+
+static inline isc_result_t
+towire_mx(ARGS_TOWIRE) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 15);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14);
+
+ dns_rdata_toregion(rdata, &region);
+ RETERR(mem_tobuffer(target, region.base, 2));
+ isc_region_consume(&region, 2);
+
+ dns_name_init(&name, offsets);
+ dns_name_fromregion(&name, &region);
+
+ return (dns_name_towire(&name, cctx, target));
+}
+
+static inline int
+compare_mx(ARGS_COMPARE) {
+ dns_name_t name1;
+ dns_name_t name2;
+ isc_region_t region1;
+ isc_region_t region2;
+ int order;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 15);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ order = memcmp(rdata1->data, rdata2->data, 2);
+ if (order != 0)
+ return (order < 0 ? -1 : 1);
+
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+
+ isc_region_consume(&region1, 2);
+ isc_region_consume(&region2, 2);
+
+ dns_name_fromregion(&name1, &region1);
+ dns_name_fromregion(&name2, &region2);
+
+ return (dns_name_rdatacompare(&name1, &name2));
+}
+
+static inline isc_result_t
+fromstruct_mx(ARGS_FROMSTRUCT) {
+ dns_rdata_mx_t *mx = source;
+ isc_region_t region;
+
+ REQUIRE(type == 15);
+ REQUIRE(source != NULL);
+ REQUIRE(mx->common.rdtype == type);
+ REQUIRE(mx->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ RETERR(uint16_tobuffer(mx->pref, target));
+ dns_name_toregion(&mx->mx, &region);
+ return (isc_buffer_copyregion(target, &region));
+}
+
+static inline isc_result_t
+tostruct_mx(ARGS_TOSTRUCT) {
+ isc_region_t region;
+ dns_rdata_mx_t *mx = target;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 15);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ mx->common.rdclass = rdata->rdclass;
+ mx->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&mx->common, link);
+
+ dns_name_init(&name, NULL);
+ dns_rdata_toregion(rdata, &region);
+ mx->pref = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ dns_name_fromregion(&name, &region);
+ dns_name_init(&mx->mx, NULL);
+ RETERR(name_duporclone(&name, mctx, &mx->mx));
+ mx->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_mx(ARGS_FREESTRUCT) {
+ dns_rdata_mx_t *mx = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(mx->common.rdtype == 15);
+
+ if (mx->mctx == NULL)
+ return;
+
+ dns_name_free(&mx->mx, mx->mctx);
+ mx->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_mx(ARGS_ADDLDATA) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 15);
+
+ dns_name_init(&name, offsets);
+ dns_rdata_toregion(rdata, &region);
+ isc_region_consume(&region, 2);
+ dns_name_fromregion(&name, &region);
+
+ return ((add)(arg, &name, dns_rdatatype_a));
+}
+
+static inline isc_result_t
+digest_mx(ARGS_DIGEST) {
+ isc_region_t r1, r2;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 15);
+
+ dns_rdata_toregion(rdata, &r1);
+ r2 = r1;
+ isc_region_consume(&r2, 2);
+ r1.length = 2;
+ RETERR((digest)(arg, &r1));
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r2);
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline isc_boolean_t
+checkowner_mx(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 15);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ return (dns_name_ishostname(name, wildcard));
+}
+
+static inline isc_boolean_t
+checknames_mx(ARGS_CHECKNAMES) {
+ isc_region_t region;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 15);
+
+ UNUSED(owner);
+
+ dns_rdata_toregion(rdata, &region);
+ isc_region_consume(&region, 2);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &region);
+ if (!dns_name_ishostname(&name, ISC_FALSE)) {
+ if (bad != NULL)
+ dns_name_clone(&name, bad);
+ return (ISC_FALSE);
+ }
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_MX_15_C */
diff --git a/lib/dns/rdata/generic/mx_15.h b/lib/dns/rdata/generic/mx_15.h
new file mode 100644
index 0000000..25d5ac5
--- /dev/null
+++ b/lib/dns/rdata/generic/mx_15.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* */
+#ifndef GENERIC_MX_15_H
+#define GENERIC_MX_15_H 1
+
+/* $Id: mx_15.h,v 1.29 2007/06/19 23:47:17 tbox Exp $ */
+
+typedef struct dns_rdata_mx {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ isc_uint16_t pref;
+ dns_name_t mx;
+} dns_rdata_mx_t;
+
+#endif /* GENERIC_MX_15_H */
diff --git a/lib/dns/rdata/generic/ns_2.c b/lib/dns/rdata/generic/ns_2.c
new file mode 100644
index 0000000..9a2ee8c
--- /dev/null
+++ b/lib/dns/rdata/generic/ns_2.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: ns_2.c,v 1.46 2007/06/19 23:47:17 tbox Exp $ */
+
+/* Reviewed: Wed Mar 15 18:15:00 PST 2000 by bwelling */
+
+#ifndef RDATA_GENERIC_NS_2_C
+#define RDATA_GENERIC_NS_2_C
+
+#define RRTYPE_NS_ATTRIBUTES (DNS_RDATATYPEATTR_ZONECUTAUTH)
+
+static inline isc_result_t
+fromtext_ns(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+ isc_boolean_t ok;
+
+ REQUIRE(type == 2);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token,isc_tokentype_string,
+ ISC_FALSE));
+
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+ ok = ISC_TRUE;
+ if ((options & DNS_RDATA_CHECKNAMES) != 0)
+ ok = dns_name_ishostname(&name, ISC_FALSE);
+ if (!ok && (options & DNS_RDATA_CHECKNAMESFAIL) != 0)
+ RETTOK(DNS_R_BADNAME);
+ if (!ok && callbacks != NULL)
+ warn_badname(&name, lexer, callbacks);
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_ns(ARGS_TOTEXT) {
+ isc_region_t region;
+ dns_name_t name;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+
+ REQUIRE(rdata->type == 2);
+ REQUIRE(rdata->length != 0);
+
+ dns_name_init(&name, NULL);
+ dns_name_init(&prefix, NULL);
+
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+
+ sub = name_prefix(&name, tctx->origin, &prefix);
+
+ return (dns_name_totext(&prefix, sub, target));
+}
+
+static inline isc_result_t
+fromwire_ns(ARGS_FROMWIRE) {
+ dns_name_t name;
+
+ REQUIRE(type == 2);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_GLOBAL14);
+
+ dns_name_init(&name, NULL);
+ return (dns_name_fromwire(&name, source, dctx, options, target));
+}
+
+static inline isc_result_t
+towire_ns(ARGS_TOWIRE) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 2);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14);
+
+ dns_name_init(&name, offsets);
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+
+ return (dns_name_towire(&name, cctx, target));
+}
+
+static inline int
+compare_ns(ARGS_COMPARE) {
+ dns_name_t name1;
+ dns_name_t name2;
+ isc_region_t region1;
+ isc_region_t region2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 2);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+
+ dns_name_fromregion(&name1, &region1);
+ dns_name_fromregion(&name2, &region2);
+
+ return (dns_name_rdatacompare(&name1, &name2));
+}
+
+static inline isc_result_t
+fromstruct_ns(ARGS_FROMSTRUCT) {
+ dns_rdata_ns_t *ns = source;
+ isc_region_t region;
+
+ REQUIRE(type == 2);
+ REQUIRE(source != NULL);
+ REQUIRE(ns->common.rdtype == type);
+ REQUIRE(ns->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_name_toregion(&ns->name, &region);
+ return (isc_buffer_copyregion(target, &region));
+}
+
+static inline isc_result_t
+tostruct_ns(ARGS_TOSTRUCT) {
+ isc_region_t region;
+ dns_rdata_ns_t *ns = target;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 2);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ ns->common.rdclass = rdata->rdclass;
+ ns->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&ns->common, link);
+
+ dns_name_init(&name, NULL);
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+ dns_name_init(&ns->name, NULL);
+ RETERR(name_duporclone(&name, mctx, &ns->name));
+ ns->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_ns(ARGS_FREESTRUCT) {
+ dns_rdata_ns_t *ns = source;
+
+ REQUIRE(source != NULL);
+
+ if (ns->mctx == NULL)
+ return;
+
+ dns_name_free(&ns->name, ns->mctx);
+ ns->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_ns(ARGS_ADDLDATA) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 2);
+
+ dns_name_init(&name, offsets);
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+
+ return ((add)(arg, &name, dns_rdatatype_a));
+}
+
+static inline isc_result_t
+digest_ns(ARGS_DIGEST) {
+ isc_region_t r;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 2);
+
+ dns_rdata_toregion(rdata, &r);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline isc_boolean_t
+checkowner_ns(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 2);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_ns(ARGS_CHECKNAMES) {
+ isc_region_t region;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 2);
+
+ UNUSED(owner);
+
+ dns_rdata_toregion(rdata, &region);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &region);
+ if (!dns_name_ishostname(&name, ISC_FALSE)) {
+ if (bad != NULL)
+ dns_name_clone(&name, bad);
+ return (ISC_FALSE);
+ }
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_NS_2_C */
diff --git a/lib/dns/rdata/generic/ns_2.h b/lib/dns/rdata/generic/ns_2.h
new file mode 100644
index 0000000..546e71a
--- /dev/null
+++ b/lib/dns/rdata/generic/ns_2.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* */
+#ifndef GENERIC_NS_2_H
+#define GENERIC_NS_2_H 1
+
+/* $Id: ns_2.h,v 1.27 2007/06/19 23:47:17 tbox Exp $ */
+
+typedef struct dns_rdata_ns {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ dns_name_t name;
+} dns_rdata_ns_t;
+
+
+#endif /* GENERIC_NS_2_H */
diff --git a/lib/dns/rdata/generic/nsec3_50.c b/lib/dns/rdata/generic/nsec3_50.c
new file mode 100644
index 0000000..3d79451
--- /dev/null
+++ b/lib/dns/rdata/generic/nsec3_50.c
@@ -0,0 +1,481 @@
+/*
+ * Copyright (C) 2008 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 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.
+ */
+
+/* $Id: nsec3_50.c,v 1.4 2008/09/25 04:02:39 tbox Exp $ */
+
+/*
+ * Copyright (C) 2004 Nominet, Ltd.
+ *
+ * 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 NOMINET 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.
+ */
+
+/* RFC 5155 */
+
+#ifndef RDATA_GENERIC_NSEC3_50_C
+#define RDATA_GENERIC_NSEC3_50_C
+
+#include <isc/iterated_hash.h>
+#include <isc/base32.h>
+
+#define RRTYPE_NSEC3_ATTRIBUTES DNS_RDATATYPEATTR_DNSSEC
+
+static inline isc_result_t
+fromtext_nsec3(ARGS_FROMTEXT) {
+ isc_token_t token;
+ unsigned char bm[8*1024]; /* 64k bits */
+ dns_rdatatype_t covered;
+ int octet;
+ int window;
+ unsigned int flags;
+ unsigned char hashalg;
+ isc_buffer_t b;
+
+ REQUIRE(type == 50);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+ UNUSED(origin);
+ UNUSED(options);
+
+ /* Hash. */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ RETTOK(dns_hashalg_fromtext(&hashalg, &token.value.as_textregion));
+ RETERR(uint8_tobuffer(hashalg, target));
+
+ /* Flags. */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ flags = token.value.as_ulong;
+ if (flags > 255U)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint8_tobuffer(flags, target));
+
+ /* Iterations. */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ /* salt */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ if (token.value.as_textregion.length > (255*2))
+ RETTOK(DNS_R_TEXTTOOLONG);
+ if (strcmp(DNS_AS_STR(token), "-") == 0) {
+ RETERR(uint8_tobuffer(0, target));
+ } else {
+ RETERR(uint8_tobuffer(strlen(DNS_AS_STR(token)) / 2, target));
+ RETERR(isc_hex_decodestring(DNS_AS_STR(token), target));
+ }
+
+ /*
+ * Next hash a single base32hex word.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ isc_buffer_init(&b, bm, sizeof(bm));
+ RETTOK(isc_base32hex_decodestring(DNS_AS_STR(token), &b));
+ if (isc_buffer_usedlength(&b) > 0xffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint8_tobuffer(isc_buffer_usedlength(&b), target));
+ RETERR(mem_tobuffer(target, &bm, isc_buffer_usedlength(&b)));
+
+ memset(bm, 0, sizeof(bm));
+ do {
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_string, ISC_TRUE));
+ if (token.type != isc_tokentype_string)
+ break;
+ RETTOK(dns_rdatatype_fromtext(&covered,
+ &token.value.as_textregion));
+ bm[covered/8] |= (0x80>>(covered%8));
+ } while (1);
+ isc_lex_ungettoken(lexer, &token);
+ for (window = 0; window < 256 ; window++) {
+ /*
+ * Find if we have a type in this window.
+ */
+ for (octet = 31; octet >= 0; octet--)
+ if (bm[window * 32 + octet] != 0)
+ break;
+ if (octet < 0)
+ continue;
+ RETERR(uint8_tobuffer(window, target));
+ RETERR(uint8_tobuffer(octet + 1, target));
+ RETERR(mem_tobuffer(target, &bm[window * 32], octet + 1));
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_nsec3(ARGS_TOTEXT) {
+ isc_region_t sr;
+ unsigned int i, j, k;
+ unsigned int window, len;
+ unsigned char hash;
+ unsigned char flags;
+ char buf[sizeof("65535 ")];
+ isc_uint32_t iterations;
+
+ REQUIRE(rdata->type == 50);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(tctx);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ hash = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+
+ flags = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+
+ iterations = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+
+ sprintf(buf, "%u ", hash);
+ RETERR(str_totext(buf, target));
+
+ sprintf(buf, "%u ", flags);
+ RETERR(str_totext(buf, target));
+
+ sprintf(buf, "%u ", iterations);
+ RETERR(str_totext(buf, target));
+
+ j = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+ INSIST(j <= sr.length);
+
+ if (j != 0) {
+ i = sr.length;
+ sr.length = j;
+ RETERR(isc_hex_totext(&sr, 1, "", target));
+ sr.length = i - j;
+ RETERR(str_totext(" ", target));
+ } else
+ RETERR(str_totext("- ", target));
+
+ j = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+ INSIST(j <= sr.length);
+
+ i = sr.length;
+ sr.length = j;
+ RETERR(isc_base32hex_totext(&sr, 1, "", target));
+ sr.length = i - j;
+
+ for (i = 0; i < sr.length; i += len) {
+ INSIST(i + 2 <= sr.length);
+ window = sr.base[i];
+ len = sr.base[i + 1];
+ INSIST(len > 0 && len <= 32);
+ i += 2;
+ INSIST(i + len <= sr.length);
+ for (j = 0; j < len; j++) {
+ dns_rdatatype_t t;
+ if (sr.base[i + j] == 0)
+ continue;
+ for (k = 0; k < 8; k++) {
+ if ((sr.base[i + j] & (0x80 >> k)) == 0)
+ continue;
+ t = window * 256 + j * 8 + k;
+ RETERR(str_totext(" ", target));
+ if (dns_rdatatype_isknown(t)) {
+ RETERR(dns_rdatatype_totext(t, target));
+ } else {
+ char buf[sizeof("TYPE65535")];
+ sprintf(buf, "TYPE%u", t);
+ RETERR(str_totext(buf, target));
+ }
+ }
+ }
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_nsec3(ARGS_FROMWIRE) {
+ isc_region_t sr, rr;
+ unsigned int window, lastwindow = 0;
+ unsigned int len;
+ unsigned int saltlen, hashlen;
+ isc_boolean_t first = ISC_TRUE;
+ unsigned int i;
+
+ REQUIRE(type == 50);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(options);
+ UNUSED(dctx);
+
+ isc_buffer_activeregion(source, &sr);
+ rr = sr;
+
+ /* hash(1), flags(1), interation(2), saltlen(1) */
+ if (sr.length < 5U)
+ RETERR(DNS_R_FORMERR);
+ saltlen = sr.base[4];
+ isc_region_consume(&sr, 5);
+
+ if (sr.length < saltlen)
+ RETERR(DNS_R_FORMERR);
+ isc_region_consume(&sr, saltlen);
+
+ if (sr.length < 1U)
+ RETERR(DNS_R_FORMERR);
+ hashlen = sr.base[0];
+ isc_region_consume(&sr, 1);
+
+ if (sr.length < hashlen)
+ RETERR(DNS_R_FORMERR);
+ isc_region_consume(&sr, hashlen);
+
+ for (i = 0; i < sr.length; i += len) {
+ /*
+ * Check for overflow.
+ */
+ if (i + 2 > sr.length)
+ RETERR(DNS_R_FORMERR);
+ window = sr.base[i];
+ len = sr.base[i + 1];
+ i += 2;
+ /*
+ * Check that bitmap windows are in the correct order.
+ */
+ if (!first && window <= lastwindow)
+ RETERR(DNS_R_FORMERR);
+ /*
+ * Check for legal lengths.
+ */
+ if (len < 1 || len > 32)
+ RETERR(DNS_R_FORMERR);
+ /*
+ * Check for overflow.
+ */
+ if (i + len > sr.length)
+ RETERR(DNS_R_FORMERR);
+ /*
+ * The last octet of the bitmap must be non zero.
+ */
+ if (sr.base[i + len - 1] == 0)
+ RETERR(DNS_R_FORMERR);
+ lastwindow = window;
+ first = ISC_FALSE;
+ }
+ if (i != sr.length)
+ return (DNS_R_EXTRADATA);
+ RETERR(mem_tobuffer(target, rr.base, rr.length));
+ isc_buffer_forward(source, rr.length);
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+towire_nsec3(ARGS_TOWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == 50);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(cctx);
+
+ dns_rdata_toregion(rdata, &sr);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline int
+compare_nsec3(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 50);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_nsec3(ARGS_FROMSTRUCT) {
+ dns_rdata_nsec3_t *nsec3 = source;
+ unsigned int i, len, window, lastwindow = 0;
+ isc_boolean_t first = ISC_TRUE;
+
+ REQUIRE(type == 50);
+ REQUIRE(source != NULL);
+ REQUIRE(nsec3->common.rdtype == type);
+ REQUIRE(nsec3->common.rdclass == rdclass);
+ REQUIRE(nsec3->typebits != NULL || nsec3->len == 0);
+ REQUIRE(nsec3->hash == dns_hash_sha1);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ RETERR(uint8_tobuffer(nsec3->hash, target));
+ RETERR(uint8_tobuffer(nsec3->flags, target));
+ RETERR(uint16_tobuffer(nsec3->iterations, target));
+ RETERR(uint8_tobuffer(nsec3->salt_length, target));
+ RETERR(mem_tobuffer(target, nsec3->salt, nsec3->salt_length));
+ RETERR(uint8_tobuffer(nsec3->next_length, target));
+ RETERR(mem_tobuffer(target, nsec3->next, nsec3->next_length));
+
+ /*
+ * Perform sanity check.
+ */
+ for (i = 0; i < nsec3->len ; i += len) {
+ INSIST(i + 2 <= nsec3->len);
+ window = nsec3->typebits[i];
+ len = nsec3->typebits[i+1];
+ i += 2;
+ INSIST(first || window > lastwindow);
+ INSIST(len > 0 && len <= 32);
+ INSIST(i + len <= nsec3->len);
+ INSIST(nsec3->typebits[i + len - 1] != 0);
+ lastwindow = window;
+ first = ISC_FALSE;
+ }
+ return (mem_tobuffer(target, nsec3->typebits, nsec3->len));
+}
+
+static inline isc_result_t
+tostruct_nsec3(ARGS_TOSTRUCT) {
+ isc_region_t region;
+ dns_rdata_nsec3_t *nsec3 = target;
+
+ REQUIRE(rdata->type == 50);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ nsec3->common.rdclass = rdata->rdclass;
+ nsec3->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&nsec3->common, link);
+
+ region.base = rdata->data;
+ region.length = rdata->length;
+ nsec3->hash = uint8_consume_fromregion(&region);
+ nsec3->flags = uint8_consume_fromregion(&region);
+ nsec3->iterations = uint16_consume_fromregion(&region);
+
+ nsec3->salt_length = uint8_consume_fromregion(&region);
+ nsec3->salt = mem_maybedup(mctx, region.base, nsec3->salt_length);
+ if (nsec3->salt == NULL)
+ return (ISC_R_NOMEMORY);
+ isc_region_consume(&region, nsec3->salt_length);
+
+ nsec3->next_length = uint8_consume_fromregion(&region);
+ nsec3->next = mem_maybedup(mctx, region.base, nsec3->next_length);
+ if (nsec3->next == NULL)
+ goto cleanup;
+ isc_region_consume(&region, nsec3->next_length);
+
+ nsec3->len = region.length;
+ nsec3->typebits = mem_maybedup(mctx, region.base, region.length);
+ if (nsec3->typebits == NULL)
+ goto cleanup;
+
+ nsec3->mctx = mctx;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (nsec3->next != NULL)
+ isc_mem_free(mctx, nsec3->next);
+ isc_mem_free(mctx, nsec3->salt);
+ return (ISC_R_NOMEMORY);
+}
+
+static inline void
+freestruct_nsec3(ARGS_FREESTRUCT) {
+ dns_rdata_nsec3_t *nsec3 = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(nsec3->common.rdtype == 50);
+
+ if (nsec3->mctx == NULL)
+ return;
+
+ if (nsec3->salt != NULL)
+ isc_mem_free(nsec3->mctx, nsec3->salt);
+ if (nsec3->next != NULL)
+ isc_mem_free(nsec3->mctx, nsec3->next);
+ if (nsec3->typebits != NULL)
+ isc_mem_free(nsec3->mctx, nsec3->typebits);
+ nsec3->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_nsec3(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 50);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_nsec3(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 50);
+
+ dns_rdata_toregion(rdata, &r);
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_nsec3(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 50);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_nsec3(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 50);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_NSEC3_50_C */
diff --git a/lib/dns/rdata/generic/nsec3_50.h b/lib/dns/rdata/generic/nsec3_50.h
new file mode 100644
index 0000000..658dd9d
--- /dev/null
+++ b/lib/dns/rdata/generic/nsec3_50.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2008 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 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.
+ */
+
+
+#ifndef GENERIC_NSEC3_50_H
+#define GENERIC_NSEC3_50_H 1
+
+/* $Id: nsec3_50.h,v 1.4 2008/09/25 04:02:39 tbox Exp $ */
+
+/*!
+ * \brief Per RFC 5155 */
+
+#include <isc/iterated_hash.h>
+
+typedef struct dns_rdata_nsec3 {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ dns_hash_t hash;
+ unsigned char flags;
+ dns_iterations_t iterations;
+ unsigned char salt_length;
+ unsigned char next_length;
+ isc_uint16_t len;
+ unsigned char *salt;
+ unsigned char *next;
+ unsigned char *typebits;
+} dns_rdata_nsec3_t;
+
+/*
+ * The corresponding NSEC3 interval is OPTOUT indicating possible
+ * insecure delegations.
+ */
+#define DNS_NSEC3FLAG_OPTOUT 0x01U
+
+/*%
+ * Non-standard, NSEC3PARAM only.
+ *
+ * Create a corresponding NSEC3 chain.
+ * Once the NSEC3 chain is complete this flag will be removed to signal
+ * that there is a complete chain.
+ *
+ * This flag is automatically set when a NSEC3PARAM record is added to
+ * the zone via UPDATE.
+ *
+ * NSEC3PARAM records with this flag set are supposed to be ignored by
+ * RFC 5155 compliant nameservers.
+ */
+#define DNS_NSEC3FLAG_CREATE 0x80U
+
+/*%
+ * Non-standard, NSEC3PARAM only.
+ *
+ * The corresponding NSEC3 set is to be removed once the NSEC chain
+ * has been generated.
+ *
+ * This flag is automatically set when the last active NSEC3PARAM record
+ * is removed from the zone via UPDATE.
+ *
+ * NSEC3PARAM records with this flag set are supposed to be ignored by
+ * RFC 5155 compliant nameservers.
+ */
+#define DNS_NSEC3FLAG_REMOVE 0x40U
+
+/*%
+ * Non-standard, NSEC3PARAM only.
+ *
+ * Used to identify NSEC3PARAM records added in this UPDATE request.
+ */
+#define DNS_NSEC3FLAG_UPDATE 0x20U
+
+/*%
+ * Non-standard, NSEC3PARAM only.
+ *
+ * Prevent the creation of a NSEC chain before the last NSEC3 chain
+ * is removed. This will normally only be set when the zone is
+ * transitioning from secure with NSEC3 chains to insecure.
+ */
+#define DNS_NSEC3FLAG_NONSEC 0x10U
+
+#endif /* GENERIC_NSEC3_50_H */
diff --git a/lib/dns/rdata/generic/nsec3param_51.c b/lib/dns/rdata/generic/nsec3param_51.c
new file mode 100644
index 0000000..90de724
--- /dev/null
+++ b/lib/dns/rdata/generic/nsec3param_51.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2008 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 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.
+ */
+
+/* $Id: nsec3param_51.c,v 1.4 2008/09/25 04:02:39 tbox Exp $ */
+
+/*
+ * Copyright (C) 2004 Nominet, Ltd.
+ *
+ * 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 NOMINET 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.
+ */
+
+/* RFC 5155 */
+
+#ifndef RDATA_GENERIC_NSEC3PARAM_51_C
+#define RDATA_GENERIC_NSEC3PARAM_51_C
+
+#include <isc/iterated_hash.h>
+#include <isc/base32.h>
+
+#define RRTYPE_NSEC3PARAM_ATTRIBUTES (DNS_RDATATYPEATTR_DNSSEC)
+
+static inline isc_result_t
+fromtext_nsec3param(ARGS_FROMTEXT) {
+ isc_token_t token;
+ unsigned int flags = 0;
+ unsigned char hashalg;
+
+ REQUIRE(type == 51);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+ UNUSED(origin);
+ UNUSED(options);
+
+ /* Hash. */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ RETTOK(dns_hashalg_fromtext(&hashalg, &token.value.as_textregion));
+ RETERR(uint8_tobuffer(hashalg, target));
+
+ /* Flags. */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ flags = token.value.as_ulong;
+ if (flags > 255U)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint8_tobuffer(flags, target));
+
+ /* Iterations. */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ /* Salt. */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ if (token.value.as_textregion.length > (255*2))
+ RETTOK(DNS_R_TEXTTOOLONG);
+ if (strcmp(DNS_AS_STR(token), "-") == 0) {
+ RETERR(uint8_tobuffer(0, target));
+ } else {
+ RETERR(uint8_tobuffer(strlen(DNS_AS_STR(token)) / 2, target));
+ RETERR(isc_hex_decodestring(DNS_AS_STR(token), target));
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_nsec3param(ARGS_TOTEXT) {
+ isc_region_t sr;
+ unsigned int i, j;
+ unsigned char hash;
+ unsigned char flags;
+ char buf[sizeof("65535 ")];
+ isc_uint32_t iterations;
+
+ REQUIRE(rdata->type == 51);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(tctx);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ hash = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+
+ flags = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+
+ iterations = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+
+ sprintf(buf, "%u ", hash);
+ RETERR(str_totext(buf, target));
+
+ sprintf(buf, "%u ", flags);
+ RETERR(str_totext(buf, target));
+
+ sprintf(buf, "%u ", iterations);
+ RETERR(str_totext(buf, target));
+
+ j = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+ INSIST(j <= sr.length);
+
+ if (j != 0) {
+ i = sr.length;
+ sr.length = j;
+ RETERR(isc_hex_totext(&sr, 1, "", target));
+ sr.length = i - j;
+ } else
+ RETERR(str_totext("-", target));
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_nsec3param(ARGS_FROMWIRE) {
+ isc_region_t sr, rr;
+ unsigned int saltlen;
+
+ REQUIRE(type == 51);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(options);
+ UNUSED(dctx);
+
+ isc_buffer_activeregion(source, &sr);
+ rr = sr;
+
+ /* hash(1), flags(1), interations(2), saltlen(1) */
+ if (sr.length < 5U)
+ RETERR(DNS_R_FORMERR);
+ saltlen = sr.base[4];
+ isc_region_consume(&sr, 5);
+
+ if (sr.length < saltlen)
+ RETERR(DNS_R_FORMERR);
+ isc_region_consume(&sr, saltlen);
+ RETERR(mem_tobuffer(target, rr.base, rr.length));
+ isc_buffer_forward(source, rr.length);
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+towire_nsec3param(ARGS_TOWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == 51);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(cctx);
+
+ dns_rdata_toregion(rdata, &sr);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline int
+compare_nsec3param(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 51);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_nsec3param(ARGS_FROMSTRUCT) {
+ dns_rdata_nsec3param_t *nsec3param = source;
+
+ REQUIRE(type == 51);
+ REQUIRE(source != NULL);
+ REQUIRE(nsec3param->common.rdtype == type);
+ REQUIRE(nsec3param->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ RETERR(uint8_tobuffer(nsec3param->hash, target));
+ RETERR(uint8_tobuffer(nsec3param->flags, target));
+ RETERR(uint16_tobuffer(nsec3param->iterations, target));
+ RETERR(uint8_tobuffer(nsec3param->salt_length, target));
+ RETERR(mem_tobuffer(target, nsec3param->salt,
+ nsec3param->salt_length));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+tostruct_nsec3param(ARGS_TOSTRUCT) {
+ isc_region_t region;
+ dns_rdata_nsec3param_t *nsec3param = target;
+
+ REQUIRE(rdata->type == 51);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ nsec3param->common.rdclass = rdata->rdclass;
+ nsec3param->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&nsec3param->common, link);
+
+ region.base = rdata->data;
+ region.length = rdata->length;
+ nsec3param->hash = uint8_consume_fromregion(&region);
+ nsec3param->flags = uint8_consume_fromregion(&region);
+ nsec3param->iterations = uint16_consume_fromregion(&region);
+
+ nsec3param->salt_length = uint8_consume_fromregion(&region);
+ nsec3param->salt = mem_maybedup(mctx, region.base,
+ nsec3param->salt_length);
+ if (nsec3param->salt == NULL)
+ return (ISC_R_NOMEMORY);
+ isc_region_consume(&region, nsec3param->salt_length);
+
+ nsec3param->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_nsec3param(ARGS_FREESTRUCT) {
+ dns_rdata_nsec3param_t *nsec3param = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(nsec3param->common.rdtype == 51);
+
+ if (nsec3param->mctx == NULL)
+ return;
+
+ if (nsec3param->salt != NULL)
+ isc_mem_free(nsec3param->mctx, nsec3param->salt);
+ nsec3param->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_nsec3param(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 51);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_nsec3param(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 51);
+
+ dns_rdata_toregion(rdata, &r);
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_nsec3param(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 51);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_nsec3param(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 51);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_NSEC3PARAM_51_C */
diff --git a/lib/dns/rdata/generic/nsec3param_51.h b/lib/dns/rdata/generic/nsec3param_51.h
new file mode 100644
index 0000000..2efd7e6
--- /dev/null
+++ b/lib/dns/rdata/generic/nsec3param_51.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2008 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 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.
+ */
+
+
+#ifndef GENERIC_NSEC3PARAM_51_H
+#define GENERIC_NSEC3PARAM_51_H 1
+
+/* $Id: nsec3param_51.h,v 1.4 2008/09/25 04:02:39 tbox Exp $ */
+
+/*!
+ * \brief Per RFC 5155 */
+
+#include <isc/iterated_hash.h>
+
+typedef struct dns_rdata_nsec3param {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ dns_hash_t hash;
+ unsigned char flags; /* DNS_NSEC3FLAG_* */
+ dns_iterations_t iterations;
+ unsigned char salt_length;
+ unsigned char *salt;
+} dns_rdata_nsec3param_t;
+
+#endif /* GENERIC_NSEC3PARAM_51_H */
diff --git a/lib/dns/rdata/generic/nsec_47.c b/lib/dns/rdata/generic/nsec_47.c
new file mode 100644
index 0000000..7e443d9
--- /dev/null
+++ b/lib/dns/rdata/generic/nsec_47.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2004, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: nsec_47.c,v 1.11 2008/07/15 23:47:21 tbox Exp $ */
+
+/* reviewed: Wed Mar 15 18:21:15 PST 2000 by brister */
+
+/* RFC 3845 */
+
+#ifndef RDATA_GENERIC_NSEC_47_C
+#define RDATA_GENERIC_NSEC_47_C
+
+/*
+ * The attributes do not include DNS_RDATATYPEATTR_SINGLETON
+ * because we must be able to handle a parent/child NSEC pair.
+ */
+#define RRTYPE_NSEC_ATTRIBUTES (DNS_RDATATYPEATTR_DNSSEC)
+
+static inline isc_result_t
+fromtext_nsec(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+ unsigned char bm[8*1024]; /* 64k bits */
+ dns_rdatatype_t covered;
+ int octet;
+ int window;
+
+ REQUIRE(type == 47);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ /*
+ * Next domain.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+
+ memset(bm, 0, sizeof(bm));
+ do {
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_string, ISC_TRUE));
+ if (token.type != isc_tokentype_string)
+ break;
+ RETTOK(dns_rdatatype_fromtext(&covered,
+ &token.value.as_textregion));
+ bm[covered/8] |= (0x80>>(covered%8));
+ } while (1);
+ isc_lex_ungettoken(lexer, &token);
+ for (window = 0; window < 256 ; window++) {
+ /*
+ * Find if we have a type in this window.
+ */
+ for (octet = 31; octet >= 0; octet--)
+ if (bm[window * 32 + octet] != 0)
+ break;
+ if (octet < 0)
+ continue;
+ RETERR(uint8_tobuffer(window, target));
+ RETERR(uint8_tobuffer(octet + 1, target));
+ RETERR(mem_tobuffer(target, &bm[window * 32], octet + 1));
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_nsec(ARGS_TOTEXT) {
+ isc_region_t sr;
+ unsigned int i, j, k;
+ dns_name_t name;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+ unsigned int window, len;
+
+ REQUIRE(rdata->type == 47);
+ REQUIRE(rdata->length != 0);
+
+ dns_name_init(&name, NULL);
+ dns_name_init(&prefix, NULL);
+ dns_rdata_toregion(rdata, &sr);
+ dns_name_fromregion(&name, &sr);
+ isc_region_consume(&sr, name_length(&name));
+ sub = name_prefix(&name, tctx->origin, &prefix);
+ RETERR(dns_name_totext(&prefix, sub, target));
+
+
+ for (i = 0; i < sr.length; i += len) {
+ INSIST(i + 2 <= sr.length);
+ window = sr.base[i];
+ len = sr.base[i + 1];
+ INSIST(len > 0 && len <= 32);
+ i += 2;
+ INSIST(i + len <= sr.length);
+ for (j = 0; j < len; j++) {
+ dns_rdatatype_t t;
+ if (sr.base[i + j] == 0)
+ continue;
+ for (k = 0; k < 8; k++) {
+ if ((sr.base[i + j] & (0x80 >> k)) == 0)
+ continue;
+ t = window * 256 + j * 8 + k;
+ RETERR(str_totext(" ", target));
+ if (dns_rdatatype_isknown(t)) {
+ RETERR(dns_rdatatype_totext(t, target));
+ } else {
+ char buf[sizeof("TYPE65535")];
+ sprintf(buf, "TYPE%u", t);
+ RETERR(str_totext(buf, target));
+ }
+ }
+ }
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static /* inline */ isc_result_t
+fromwire_nsec(ARGS_FROMWIRE) {
+ isc_region_t sr;
+ dns_name_t name;
+ unsigned int window, lastwindow = 0;
+ unsigned int len;
+ isc_boolean_t first = ISC_TRUE;
+ unsigned int i;
+
+ REQUIRE(type == 47);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_NONE);
+
+ dns_name_init(&name, NULL);
+ RETERR(dns_name_fromwire(&name, source, dctx, options, target));
+
+ isc_buffer_activeregion(source, &sr);
+ for (i = 0; i < sr.length; i += len) {
+ /*
+ * Check for overflow.
+ */
+ if (i + 2 > sr.length)
+ RETERR(DNS_R_FORMERR);
+ window = sr.base[i];
+ len = sr.base[i + 1];
+ i += 2;
+ /*
+ * Check that bitmap windows are in the correct order.
+ */
+ if (!first && window <= lastwindow)
+ RETERR(DNS_R_FORMERR);
+ /*
+ * Check for legal lengths.
+ */
+ if (len < 1 || len > 32)
+ RETERR(DNS_R_FORMERR);
+ /*
+ * Check for overflow.
+ */
+ if (i + len > sr.length)
+ RETERR(DNS_R_FORMERR);
+ /*
+ * The last octet of the bitmap must be non zero.
+ */
+ if (sr.base[i + len - 1] == 0)
+ RETERR(DNS_R_FORMERR);
+ lastwindow = window;
+ first = ISC_FALSE;
+ }
+ if (i != sr.length)
+ return (DNS_R_EXTRADATA);
+ if (first)
+ RETERR(DNS_R_FORMERR);
+ RETERR(mem_tobuffer(target, sr.base, sr.length));
+ isc_buffer_forward(source, sr.length);
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+towire_nsec(ARGS_TOWIRE) {
+ isc_region_t sr;
+ dns_name_t name;
+ dns_offsets_t offsets;
+
+ REQUIRE(rdata->type == 47);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_NONE);
+ dns_name_init(&name, offsets);
+ dns_rdata_toregion(rdata, &sr);
+ dns_name_fromregion(&name, &sr);
+ isc_region_consume(&sr, name_length(&name));
+ RETERR(dns_name_towire(&name, cctx, target));
+
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline int
+compare_nsec(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 47);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_nsec(ARGS_FROMSTRUCT) {
+ dns_rdata_nsec_t *nsec = source;
+ isc_region_t region;
+ unsigned int i, len, window, lastwindow = 0;
+ isc_boolean_t first = ISC_TRUE;
+
+ REQUIRE(type == 47);
+ REQUIRE(source != NULL);
+ REQUIRE(nsec->common.rdtype == type);
+ REQUIRE(nsec->common.rdclass == rdclass);
+ REQUIRE(nsec->typebits != NULL || nsec->len == 0);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_name_toregion(&nsec->next, &region);
+ RETERR(isc_buffer_copyregion(target, &region));
+ /*
+ * Perform sanity check.
+ */
+ for (i = 0; i < nsec->len ; i += len) {
+ INSIST(i + 2 <= nsec->len);
+ window = nsec->typebits[i];
+ len = nsec->typebits[i+1];
+ i += 2;
+ INSIST(first || window > lastwindow);
+ INSIST(len > 0 && len <= 32);
+ INSIST(i + len <= nsec->len);
+ INSIST(nsec->typebits[i + len - 1] != 0);
+ lastwindow = window;
+ first = ISC_FALSE;
+ }
+ INSIST(!first);
+ return (mem_tobuffer(target, nsec->typebits, nsec->len));
+}
+
+static inline isc_result_t
+tostruct_nsec(ARGS_TOSTRUCT) {
+ isc_region_t region;
+ dns_rdata_nsec_t *nsec = target;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 47);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ nsec->common.rdclass = rdata->rdclass;
+ nsec->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&nsec->common, link);
+
+ dns_name_init(&name, NULL);
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+ isc_region_consume(&region, name_length(&name));
+ dns_name_init(&nsec->next, NULL);
+ RETERR(name_duporclone(&name, mctx, &nsec->next));
+
+ nsec->len = region.length;
+ nsec->typebits = mem_maybedup(mctx, region.base, region.length);
+ if (nsec->typebits == NULL)
+ goto cleanup;
+
+ nsec->mctx = mctx;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (mctx != NULL)
+ dns_name_free(&nsec->next, mctx);
+ return (ISC_R_NOMEMORY);
+}
+
+static inline void
+freestruct_nsec(ARGS_FREESTRUCT) {
+ dns_rdata_nsec_t *nsec = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(nsec->common.rdtype == 47);
+
+ if (nsec->mctx == NULL)
+ return;
+
+ dns_name_free(&nsec->next, nsec->mctx);
+ if (nsec->typebits != NULL)
+ isc_mem_free(nsec->mctx, nsec->typebits);
+ nsec->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_nsec(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 47);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_nsec(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 47);
+
+ dns_rdata_toregion(rdata, &r);
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_nsec(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 47);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_nsec(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 47);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_NSEC_47_C */
diff --git a/lib/dns/rdata/generic/nsec_47.h b/lib/dns/rdata/generic/nsec_47.h
new file mode 100644
index 0000000..2b3c6b6
--- /dev/null
+++ b/lib/dns/rdata/generic/nsec_47.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+#ifndef GENERIC_NSEC_47_H
+#define GENERIC_NSEC_47_H 1
+
+/* $Id: nsec_47.h,v 1.10 2008/07/15 23:47:21 tbox Exp $ */
+
+/*!
+ * \brief Per RFC 3845 */
+
+typedef struct dns_rdata_nsec {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ dns_name_t next;
+ unsigned char *typebits;
+ isc_uint16_t len;
+} dns_rdata_nsec_t;
+
+#endif /* GENERIC_NSEC_47_H */
diff --git a/lib/dns/rdata/generic/null_10.c b/lib/dns/rdata/generic/null_10.c
new file mode 100644
index 0000000..00bb542
--- /dev/null
+++ b/lib/dns/rdata/generic/null_10.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: null_10.c,v 1.42 2007/06/19 23:47:17 tbox Exp $ */
+
+/* Reviewed: Thu Mar 16 13:57:50 PST 2000 by explorer */
+
+#ifndef RDATA_GENERIC_NULL_10_C
+#define RDATA_GENERIC_NULL_10_C
+
+#define RRTYPE_NULL_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_null(ARGS_FROMTEXT) {
+ REQUIRE(type == 10);
+
+ UNUSED(rdclass);
+ UNUSED(type);
+ UNUSED(lexer);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(target);
+ UNUSED(callbacks);
+
+ return (DNS_R_SYNTAX);
+}
+
+static inline isc_result_t
+totext_null(ARGS_TOTEXT) {
+ REQUIRE(rdata->type == 10);
+
+ UNUSED(rdata);
+ UNUSED(tctx);
+ UNUSED(target);
+
+ return (DNS_R_SYNTAX);
+}
+
+static inline isc_result_t
+fromwire_null(ARGS_FROMWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(type == 10);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(dctx);
+ UNUSED(options);
+
+ isc_buffer_activeregion(source, &sr);
+ isc_buffer_forward(source, sr.length);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline isc_result_t
+towire_null(ARGS_TOWIRE) {
+ REQUIRE(rdata->type == 10);
+
+ UNUSED(cctx);
+
+ return (mem_tobuffer(target, rdata->data, rdata->length));
+}
+
+static inline int
+compare_null(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 10);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_null(ARGS_FROMSTRUCT) {
+ dns_rdata_null_t *null = source;
+
+ REQUIRE(type == 10);
+ REQUIRE(source != NULL);
+ REQUIRE(null->common.rdtype == type);
+ REQUIRE(null->common.rdclass == rdclass);
+ REQUIRE(null->data != NULL || null->length == 0);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ return (mem_tobuffer(target, null->data, null->length));
+}
+
+static inline isc_result_t
+tostruct_null(ARGS_TOSTRUCT) {
+ dns_rdata_null_t *null = target;
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 10);
+ REQUIRE(target != NULL);
+
+ null->common.rdclass = rdata->rdclass;
+ null->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&null->common, link);
+
+ dns_rdata_toregion(rdata, &r);
+ null->length = r.length;
+ null->data = mem_maybedup(mctx, r.base, r.length);
+ if (null->data == NULL)
+ return (ISC_R_NOMEMORY);
+
+ null->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_null(ARGS_FREESTRUCT) {
+ dns_rdata_null_t *null = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(null->common.rdtype == 10);
+
+ if (null->mctx == NULL)
+ return;
+
+ if (null->data != NULL)
+ isc_mem_free(null->mctx, null->data);
+ null->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_null(ARGS_ADDLDATA) {
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ REQUIRE(rdata->type == 10);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_null(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 10);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_null(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 10);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_null(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 10);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_NULL_10_C */
diff --git a/lib/dns/rdata/generic/null_10.h b/lib/dns/rdata/generic/null_10.h
new file mode 100644
index 0000000..ceeb018
--- /dev/null
+++ b/lib/dns/rdata/generic/null_10.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* */
+#ifndef GENERIC_NULL_10_H
+#define GENERIC_NULL_10_H 1
+
+/* $Id: null_10.h,v 1.25 2007/06/19 23:47:17 tbox Exp $ */
+
+typedef struct dns_rdata_null {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ isc_uint16_t length;
+ unsigned char *data;
+} dns_rdata_null_t;
+
+
+#endif /* GENERIC_NULL_10_H */
diff --git a/lib/dns/rdata/generic/nxt_30.c b/lib/dns/rdata/generic/nxt_30.c
new file mode 100644
index 0000000..7ffb86c
--- /dev/null
+++ b/lib/dns/rdata/generic/nxt_30.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: nxt_30.c,v 1.63 2007/06/19 23:47:17 tbox Exp $ */
+
+/* reviewed: Wed Mar 15 18:21:15 PST 2000 by brister */
+
+/* RFC2535 */
+
+#ifndef RDATA_GENERIC_NXT_30_C
+#define RDATA_GENERIC_NXT_30_C
+
+/*
+ * The attributes do not include DNS_RDATATYPEATTR_SINGLETON
+ * because we must be able to handle a parent/child NXT pair.
+ */
+#define RRTYPE_NXT_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_nxt(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+ char *e;
+ unsigned char bm[8*1024]; /* 64k bits */
+ dns_rdatatype_t covered;
+ dns_rdatatype_t maxcovered = 0;
+ isc_boolean_t first = ISC_TRUE;
+ long n;
+
+ REQUIRE(type == 30);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ /*
+ * Next domain.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+
+ memset(bm, 0, sizeof(bm));
+ do {
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_string, ISC_TRUE));
+ if (token.type != isc_tokentype_string)
+ break;
+ n = strtol(DNS_AS_STR(token), &e, 10);
+ if (e != DNS_AS_STR(token) && *e == '\0') {
+ covered = (dns_rdatatype_t)n;
+ } else if (dns_rdatatype_fromtext(&covered,
+ &token.value.as_textregion) == DNS_R_UNKNOWN)
+ RETTOK(DNS_R_UNKNOWN);
+ /*
+ * NXT is only specified for types 1..127.
+ */
+ if (covered < 1 || covered > 127)
+ return (ISC_R_RANGE);
+ if (first || covered > maxcovered)
+ maxcovered = covered;
+ first = ISC_FALSE;
+ bm[covered/8] |= (0x80>>(covered%8));
+ } while (1);
+ isc_lex_ungettoken(lexer, &token);
+ if (first)
+ return (ISC_R_SUCCESS);
+ n = (maxcovered + 8) / 8;
+ return (mem_tobuffer(target, bm, n));
+}
+
+static inline isc_result_t
+totext_nxt(ARGS_TOTEXT) {
+ isc_region_t sr;
+ unsigned int i, j;
+ dns_name_t name;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+
+ REQUIRE(rdata->type == 30);
+ REQUIRE(rdata->length != 0);
+
+ dns_name_init(&name, NULL);
+ dns_name_init(&prefix, NULL);
+ dns_rdata_toregion(rdata, &sr);
+ dns_name_fromregion(&name, &sr);
+ isc_region_consume(&sr, name_length(&name));
+ sub = name_prefix(&name, tctx->origin, &prefix);
+ RETERR(dns_name_totext(&prefix, sub, target));
+
+ for (i = 0; i < sr.length; i++) {
+ if (sr.base[i] != 0)
+ for (j = 0; j < 8; j++)
+ if ((sr.base[i] & (0x80 >> j)) != 0) {
+ dns_rdatatype_t t = i * 8 + j;
+ RETERR(str_totext(" ", target));
+ if (dns_rdatatype_isknown(t)) {
+ RETERR(dns_rdatatype_totext(t,
+ target));
+ } else {
+ char buf[sizeof("65535")];
+ sprintf(buf, "%u", t);
+ RETERR(str_totext(buf,
+ target));
+ }
+ }
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_nxt(ARGS_FROMWIRE) {
+ isc_region_t sr;
+ dns_name_t name;
+
+ REQUIRE(type == 30);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_NONE);
+
+ dns_name_init(&name, NULL);
+ RETERR(dns_name_fromwire(&name, source, dctx, options, target));
+
+ isc_buffer_activeregion(source, &sr);
+ if (sr.length > 0 && (sr.base[0] & 0x80) == 0 &&
+ ((sr.length > 16) || sr.base[sr.length - 1] == 0))
+ return (DNS_R_BADBITMAP);
+ RETERR(mem_tobuffer(target, sr.base, sr.length));
+ isc_buffer_forward(source, sr.length);
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+towire_nxt(ARGS_TOWIRE) {
+ isc_region_t sr;
+ dns_name_t name;
+ dns_offsets_t offsets;
+
+ REQUIRE(rdata->type == 30);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_NONE);
+ dns_name_init(&name, offsets);
+ dns_rdata_toregion(rdata, &sr);
+ dns_name_fromregion(&name, &sr);
+ isc_region_consume(&sr, name_length(&name));
+ RETERR(dns_name_towire(&name, cctx, target));
+
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline int
+compare_nxt(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+ dns_name_t name1;
+ dns_name_t name2;
+ int order;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 30);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ dns_name_fromregion(&name1, &r1);
+ dns_name_fromregion(&name2, &r2);
+ order = dns_name_rdatacompare(&name1, &name2);
+ if (order != 0)
+ return (order);
+
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_nxt(ARGS_FROMSTRUCT) {
+ dns_rdata_nxt_t *nxt = source;
+ isc_region_t region;
+
+ REQUIRE(type == 30);
+ REQUIRE(source != NULL);
+ REQUIRE(nxt->common.rdtype == type);
+ REQUIRE(nxt->common.rdclass == rdclass);
+ REQUIRE(nxt->typebits != NULL || nxt->len == 0);
+ if (nxt->typebits != NULL && (nxt->typebits[0] & 0x80) == 0) {
+ REQUIRE(nxt->len <= 16);
+ REQUIRE(nxt->typebits[nxt->len - 1] != 0);
+ }
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_name_toregion(&nxt->next, &region);
+ RETERR(isc_buffer_copyregion(target, &region));
+
+ return (mem_tobuffer(target, nxt->typebits, nxt->len));
+}
+
+static inline isc_result_t
+tostruct_nxt(ARGS_TOSTRUCT) {
+ isc_region_t region;
+ dns_rdata_nxt_t *nxt = target;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 30);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ nxt->common.rdclass = rdata->rdclass;
+ nxt->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&nxt->common, link);
+
+ dns_name_init(&name, NULL);
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+ isc_region_consume(&region, name_length(&name));
+ dns_name_init(&nxt->next, NULL);
+ RETERR(name_duporclone(&name, mctx, &nxt->next));
+
+ nxt->len = region.length;
+ nxt->typebits = mem_maybedup(mctx, region.base, region.length);
+ if (nxt->typebits == NULL)
+ goto cleanup;
+
+ nxt->mctx = mctx;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (mctx != NULL)
+ dns_name_free(&nxt->next, mctx);
+ return (ISC_R_NOMEMORY);
+}
+
+static inline void
+freestruct_nxt(ARGS_FREESTRUCT) {
+ dns_rdata_nxt_t *nxt = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(nxt->common.rdtype == 30);
+
+ if (nxt->mctx == NULL)
+ return;
+
+ dns_name_free(&nxt->next, nxt->mctx);
+ if (nxt->typebits != NULL)
+ isc_mem_free(nxt->mctx, nxt->typebits);
+ nxt->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_nxt(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 30);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_nxt(ARGS_DIGEST) {
+ isc_region_t r;
+ dns_name_t name;
+ isc_result_t result;
+
+ REQUIRE(rdata->type == 30);
+
+ dns_rdata_toregion(rdata, &r);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+ result = dns_name_digest(&name, digest, arg);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ isc_region_consume(&r, name_length(&name));
+
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_nxt(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 30);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_nxt(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 30);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_NXT_30_C */
diff --git a/lib/dns/rdata/generic/nxt_30.h b/lib/dns/rdata/generic/nxt_30.h
new file mode 100644
index 0000000..e2e8688
--- /dev/null
+++ b/lib/dns/rdata/generic/nxt_30.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+#ifndef GENERIC_NXT_30_H
+#define GENERIC_NXT_30_H 1
+
+/* $Id: nxt_30.h,v 1.25 2007/06/19 23:47:17 tbox Exp $ */
+
+/*!
+ * \brief RFC2535 */
+
+typedef struct dns_rdata_nxt {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ dns_name_t next;
+ unsigned char *typebits;
+ isc_uint16_t len;
+} dns_rdata_nxt_t;
+
+#endif /* GENERIC_NXT_30_H */
diff --git a/lib/dns/rdata/generic/opt_41.c b/lib/dns/rdata/generic/opt_41.c
new file mode 100644
index 0000000..d2cfc2e
--- /dev/null
+++ b/lib/dns/rdata/generic/opt_41.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: opt_41.c,v 1.33 2007/06/19 23:47:17 tbox Exp $ */
+
+/* Reviewed: Thu Mar 16 14:06:44 PST 2000 by gson */
+
+/* RFC2671 */
+
+#ifndef RDATA_GENERIC_OPT_41_C
+#define RDATA_GENERIC_OPT_41_C
+
+#define RRTYPE_OPT_ATTRIBUTES (DNS_RDATATYPEATTR_SINGLETON | \
+ DNS_RDATATYPEATTR_META | \
+ DNS_RDATATYPEATTR_NOTQUESTION)
+
+static inline isc_result_t
+fromtext_opt(ARGS_FROMTEXT) {
+ /*
+ * OPT records do not have a text format.
+ */
+
+ REQUIRE(type == 41);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(lexer);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(target);
+ UNUSED(callbacks);
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static inline isc_result_t
+totext_opt(ARGS_TOTEXT) {
+ isc_region_t r;
+ isc_region_t or;
+ isc_uint16_t option;
+ isc_uint16_t length;
+ char buf[sizeof("64000 64000")];
+
+ /*
+ * OPT records do not have a text format.
+ */
+
+ REQUIRE(rdata->type == 41);
+
+ dns_rdata_toregion(rdata, &r);
+ while (r.length > 0) {
+ option = uint16_fromregion(&r);
+ isc_region_consume(&r, 2);
+ length = uint16_fromregion(&r);
+ isc_region_consume(&r, 2);
+ sprintf(buf, "%u %u", option, length);
+ RETERR(str_totext(buf, target));
+ INSIST(r.length >= length);
+ if (length > 0) {
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" (", target));
+ RETERR(str_totext(tctx->linebreak, target));
+ or = r;
+ or.length = length;
+ RETERR(isc_base64_totext(&or, tctx->width - 2,
+ tctx->linebreak, target));
+ isc_region_consume(&r, length);
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" )", target));
+ }
+ if (r.length > 0)
+ RETERR(str_totext(" ", target));
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_opt(ARGS_FROMWIRE) {
+ isc_region_t sregion;
+ isc_region_t tregion;
+ isc_uint16_t length;
+ unsigned int total;
+
+ REQUIRE(type == 41);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(dctx);
+ UNUSED(options);
+
+ isc_buffer_activeregion(source, &sregion);
+ total = 0;
+ while (sregion.length != 0) {
+ if (sregion.length < 4)
+ return (ISC_R_UNEXPECTEDEND);
+ /*
+ * Eat the 16bit option code. There is nothing to
+ * be done with it currently.
+ */
+ isc_region_consume(&sregion, 2);
+ length = uint16_fromregion(&sregion);
+ isc_region_consume(&sregion, 2);
+ total += 4;
+ if (sregion.length < length)
+ return (ISC_R_UNEXPECTEDEND);
+ isc_region_consume(&sregion, length);
+ total += length;
+ }
+
+ isc_buffer_activeregion(source, &sregion);
+ isc_buffer_availableregion(target, &tregion);
+ if (tregion.length < total)
+ return (ISC_R_NOSPACE);
+ memcpy(tregion.base, sregion.base, total);
+ isc_buffer_forward(source, total);
+ isc_buffer_add(target, total);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+towire_opt(ARGS_TOWIRE) {
+
+ REQUIRE(rdata->type == 41);
+
+ UNUSED(cctx);
+
+ return (mem_tobuffer(target, rdata->data, rdata->length));
+}
+
+static inline int
+compare_opt(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 41);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_opt(ARGS_FROMSTRUCT) {
+ dns_rdata_opt_t *opt = source;
+ isc_region_t region;
+ isc_uint16_t length;
+
+ REQUIRE(type == 41);
+ REQUIRE(source != NULL);
+ REQUIRE(opt->common.rdtype == type);
+ REQUIRE(opt->common.rdclass == rdclass);
+ REQUIRE(opt->options != NULL || opt->length == 0);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ region.base = opt->options;
+ region.length = opt->length;
+ while (region.length >= 4) {
+ isc_region_consume(&region, 2); /* opt */
+ length = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ if (region.length < length)
+ return (ISC_R_UNEXPECTEDEND);
+ isc_region_consume(&region, length);
+ }
+ if (region.length != 0)
+ return (ISC_R_UNEXPECTEDEND);
+
+ return (mem_tobuffer(target, opt->options, opt->length));
+}
+
+static inline isc_result_t
+tostruct_opt(ARGS_TOSTRUCT) {
+ dns_rdata_opt_t *opt = target;
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 41);
+ REQUIRE(target != NULL);
+
+ opt->common.rdclass = rdata->rdclass;
+ opt->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&opt->common, link);
+
+ dns_rdata_toregion(rdata, &r);
+ opt->length = r.length;
+ opt->options = mem_maybedup(mctx, r.base, r.length);
+ if (opt->options == NULL)
+ return (ISC_R_NOMEMORY);
+
+ opt->offset = 0;
+ opt->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_opt(ARGS_FREESTRUCT) {
+ dns_rdata_opt_t *opt = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(opt->common.rdtype == 41);
+
+ if (opt->mctx == NULL)
+ return;
+
+ if (opt->options != NULL)
+ isc_mem_free(opt->mctx, opt->options);
+ opt->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_opt(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 41);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_opt(ARGS_DIGEST) {
+
+ /*
+ * OPT records are not digested.
+ */
+
+ REQUIRE(rdata->type == 41);
+
+ UNUSED(rdata);
+ UNUSED(digest);
+ UNUSED(arg);
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static inline isc_boolean_t
+checkowner_opt(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 41);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (dns_name_equal(name, dns_rootname));
+}
+
+static inline isc_boolean_t
+checknames_opt(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 41);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_OPT_41_C */
diff --git a/lib/dns/rdata/generic/opt_41.h b/lib/dns/rdata/generic/opt_41.h
new file mode 100644
index 0000000..d6539cf
--- /dev/null
+++ b/lib/dns/rdata/generic/opt_41.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+#ifndef GENERIC_OPT_41_H
+#define GENERIC_OPT_41_H 1
+
+/* $Id: opt_41.h,v 1.18 2007/06/19 23:47:17 tbox Exp $ */
+
+/*!
+ * \brief Per RFC2671 */
+
+typedef struct dns_rdata_opt_opcode {
+ isc_uint16_t opcode;
+ isc_uint16_t length;
+ unsigned char *data;
+} dns_rdata_opt_opcode_t;
+
+typedef struct dns_rdata_opt {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ unsigned char *options;
+ isc_uint16_t length;
+ /* private */
+ isc_uint16_t offset;
+} dns_rdata_opt_t;
+
+/*
+ * ISC_LANG_BEGINDECLS and ISC_LANG_ENDDECLS are already done
+ * via rdatastructpre.h and rdatastructsuf.h.
+ */
+
+isc_result_t
+dns_rdata_opt_first(dns_rdata_opt_t *);
+
+isc_result_t
+dns_rdata_opt_next(dns_rdata_opt_t *);
+
+isc_result_t
+dns_rdata_opt_current(dns_rdata_opt_t *, dns_rdata_opt_opcode_t *);
+
+#endif /* GENERIC_OPT_41_H */
diff --git a/lib/dns/rdata/generic/proforma.c b/lib/dns/rdata/generic/proforma.c
new file mode 100644
index 0000000..879b761
--- /dev/null
+++ b/lib/dns/rdata/generic/proforma.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: proforma.c,v 1.36 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef RDATA_GENERIC_#_#_C
+#define RDATA_GENERIC_#_#_C
+
+#define RRTYPE_#_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_#(ARGS_FROMTEXT) {
+ isc_token_t token;
+
+ REQUIRE(type == #);
+ REQUIRE(rdclass == #);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static inline isc_result_t
+totext_#(ARGS_TOTEXT) {
+
+ REQUIRE(rdata->type == #);
+ REQUIRE(rdata->rdclass == #);
+ REQUIRE(rdata->length != 0); /* XXX */
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static inline isc_result_t
+fromwire_#(ARGS_FROMWIRE) {
+
+ REQUIRE(type == #);
+ REQUIRE(rdclass == #);
+
+ /* NONE or GLOBAL14 */
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_NONE);
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static inline isc_result_t
+towire_#(ARGS_TOWIRE) {
+
+ REQUIRE(rdata->type == #);
+ REQUIRE(rdata->rdclass == #);
+ REQUIRE(rdata->length != 0); /* XXX */
+
+ /* NONE or GLOBAL14 */
+ dns_compress_setmethods(cctx, DNS_COMPRESS_NONE);
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static inline int
+compare_#(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == #);
+ REQUIRE(rdata1->rdclass == #);
+ REQUIRE(rdata1->length != 0); /* XXX */
+ REQUIRE(rdata2->length != 0); /* XXX */
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_#(ARGS_FROMSTRUCT) {
+ dns_rdata_#_t *# = source;
+
+ REQUIRE(type == #);
+ REQUIRE(rdclass == #);
+ REQUIRE(source != NULL);
+ REQUIRE(#->common.rdtype == type);
+ REQUIRE(#->common.rdclass == rdclass);
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static inline isc_result_t
+tostruct_#(ARGS_TOSTRUCT) {
+
+ REQUIRE(rdata->type == #);
+ REQUIRE(rdata->rdclass == #);
+ REQUIRE(rdata->length != 0); /* XXX */
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static inline void
+freestruct_#(ARGS_FREESTRUCT) {
+ dns_rdata_#_t *# = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(#->common.rdtype == #);
+ REQUIRE(#->common.rdclass == #);
+
+}
+
+static inline isc_result_t
+additionaldata_#(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == #);
+ REQUIRE(rdata->rdclass == #);
+
+ (void)add;
+ (void)arg;
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_#(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == #);
+ REQUIRE(rdata->rdclass == #);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_#(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == #);
+ REQUIRE(rdclass == #);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_#(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == #);
+ REQUIRE(rdata->rdclass == #);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_#_#_C */
diff --git a/lib/dns/rdata/generic/proforma.h b/lib/dns/rdata/generic/proforma.h
new file mode 100644
index 0000000..e5c420a
--- /dev/null
+++ b/lib/dns/rdata/generic/proforma.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* */
+#ifndef GENERIC_PROFORMA_H
+#define GENERIC_PROFORMA_H 1
+
+/* $Id: proforma.h,v 1.23 2007/06/19 23:47:17 tbox Exp $ */
+
+typedef struct dns_rdata_# {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx; /* if required */
+ /* type & class specific elements */
+} dns_rdata_#_t;
+
+#endif /* GENERIC_PROFORMA_H */
diff --git a/lib/dns/rdata/generic/ptr_12.c b/lib/dns/rdata/generic/ptr_12.c
new file mode 100644
index 0000000..fbabcbf
--- /dev/null
+++ b/lib/dns/rdata/generic/ptr_12.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: ptr_12.c,v 1.43 2007/06/19 23:47:17 tbox Exp $ */
+
+/* Reviewed: Thu Mar 16 14:05:12 PST 2000 by explorer */
+
+#ifndef RDATA_GENERIC_PTR_12_C
+#define RDATA_GENERIC_PTR_12_C
+
+#define RRTYPE_PTR_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_ptr(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+
+ REQUIRE(type == 12);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+ if (rdclass == dns_rdataclass_in &&
+ (options & DNS_RDATA_CHECKNAMES) != 0 &&
+ (options & DNS_RDATA_CHECKREVERSE) != 0) {
+ isc_boolean_t ok;
+ ok = dns_name_ishostname(&name, ISC_FALSE);
+ if (!ok && (options & DNS_RDATA_CHECKNAMESFAIL) != 0)
+ RETTOK(DNS_R_BADNAME);
+ if (!ok && callbacks != NULL)
+ warn_badname(&name, lexer, callbacks);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_ptr(ARGS_TOTEXT) {
+ isc_region_t region;
+ dns_name_t name;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+
+ REQUIRE(rdata->type == 12);
+ REQUIRE(rdata->length != 0);
+
+ dns_name_init(&name, NULL);
+ dns_name_init(&prefix, NULL);
+
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+
+ sub = name_prefix(&name, tctx->origin, &prefix);
+
+ return (dns_name_totext(&prefix, sub, target));
+}
+
+static inline isc_result_t
+fromwire_ptr(ARGS_FROMWIRE) {
+ dns_name_t name;
+
+ REQUIRE(type == 12);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_GLOBAL14);
+
+ dns_name_init(&name, NULL);
+ return (dns_name_fromwire(&name, source, dctx, options, target));
+}
+
+static inline isc_result_t
+towire_ptr(ARGS_TOWIRE) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 12);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14);
+
+ dns_name_init(&name, offsets);
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+
+ return (dns_name_towire(&name, cctx, target));
+}
+
+static inline int
+compare_ptr(ARGS_COMPARE) {
+ dns_name_t name1;
+ dns_name_t name2;
+ isc_region_t region1;
+ isc_region_t region2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 12);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+
+ dns_name_fromregion(&name1, &region1);
+ dns_name_fromregion(&name2, &region2);
+
+ return (dns_name_rdatacompare(&name1, &name2));
+}
+
+static inline isc_result_t
+fromstruct_ptr(ARGS_FROMSTRUCT) {
+ dns_rdata_ptr_t *ptr = source;
+ isc_region_t region;
+
+ REQUIRE(type == 12);
+ REQUIRE(source != NULL);
+ REQUIRE(ptr->common.rdtype == type);
+ REQUIRE(ptr->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_name_toregion(&ptr->ptr, &region);
+ return (isc_buffer_copyregion(target, &region));
+}
+
+static inline isc_result_t
+tostruct_ptr(ARGS_TOSTRUCT) {
+ isc_region_t region;
+ dns_rdata_ptr_t *ptr = target;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 12);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ ptr->common.rdclass = rdata->rdclass;
+ ptr->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&ptr->common, link);
+
+ dns_name_init(&name, NULL);
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+ dns_name_init(&ptr->ptr, NULL);
+ RETERR(name_duporclone(&name, mctx, &ptr->ptr));
+ ptr->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_ptr(ARGS_FREESTRUCT) {
+ dns_rdata_ptr_t *ptr = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(ptr->common.rdtype == 12);
+
+ if (ptr->mctx == NULL)
+ return;
+
+ dns_name_free(&ptr->ptr, ptr->mctx);
+ ptr->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_ptr(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 12);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_ptr(ARGS_DIGEST) {
+ isc_region_t r;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 12);
+
+ dns_rdata_toregion(rdata, &r);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline isc_boolean_t
+checkowner_ptr(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 12);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static unsigned char ip6_arpa_data[] = "\003IP6\004ARPA";
+static unsigned char ip6_arpa_offsets[] = { 0, 4, 9 };
+static const dns_name_t ip6_arpa =
+{
+ DNS_NAME_MAGIC,
+ ip6_arpa_data, 10, 3,
+ DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
+ ip6_arpa_offsets, NULL,
+ {(void *)-1, (void *)-1},
+ {NULL, NULL}
+};
+
+static unsigned char ip6_int_data[] = "\003IP6\003INT";
+static unsigned char ip6_int_offsets[] = { 0, 4, 8 };
+static const dns_name_t ip6_int =
+{
+ DNS_NAME_MAGIC,
+ ip6_int_data, 9, 3,
+ DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
+ ip6_int_offsets, NULL,
+ {(void *)-1, (void *)-1},
+ {NULL, NULL}
+};
+
+static unsigned char in_addr_arpa_data[] = "\007IN-ADDR\004ARPA";
+static unsigned char in_addr_arpa_offsets[] = { 0, 8, 13 };
+static const dns_name_t in_addr_arpa =
+{
+ DNS_NAME_MAGIC,
+ in_addr_arpa_data, 14, 3,
+ DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
+ in_addr_arpa_offsets, NULL,
+ {(void *)-1, (void *)-1},
+ {NULL, NULL}
+};
+
+static inline isc_boolean_t
+checknames_ptr(ARGS_CHECKNAMES) {
+ isc_region_t region;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 12);
+
+ if (rdata->rdclass != dns_rdataclass_in)
+ return (ISC_TRUE);
+
+ if (dns_name_issubdomain(owner, &in_addr_arpa) ||
+ dns_name_issubdomain(owner, &ip6_arpa) ||
+ dns_name_issubdomain(owner, &ip6_int)) {
+ dns_rdata_toregion(rdata, &region);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &region);
+ if (!dns_name_ishostname(&name, ISC_FALSE)) {
+ if (bad != NULL)
+ dns_name_clone(&name, bad);
+ return (ISC_FALSE);
+ }
+ }
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_PTR_12_C */
diff --git a/lib/dns/rdata/generic/ptr_12.h b/lib/dns/rdata/generic/ptr_12.h
new file mode 100644
index 0000000..304dcc4
--- /dev/null
+++ b/lib/dns/rdata/generic/ptr_12.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* */
+#ifndef GENERIC_PTR_12_H
+#define GENERIC_PTR_12_H 1
+
+/* $Id: ptr_12.h,v 1.27 2007/06/19 23:47:17 tbox Exp $ */
+
+typedef struct dns_rdata_ptr {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ dns_name_t ptr;
+} dns_rdata_ptr_t;
+
+#endif /* GENERIC_PTR_12_H */
diff --git a/lib/dns/rdata/generic/rp_17.c b/lib/dns/rdata/generic/rp_17.c
new file mode 100644
index 0000000..557cb04
--- /dev/null
+++ b/lib/dns/rdata/generic/rp_17.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rp_17.c,v 1.42 2007/06/19 23:47:17 tbox Exp $ */
+
+/* RFC1183 */
+
+#ifndef RDATA_GENERIC_RP_17_C
+#define RDATA_GENERIC_RP_17_C
+
+#define RRTYPE_RP_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_rp(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+ int i;
+ isc_boolean_t ok;
+
+ REQUIRE(type == 17);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ origin = (origin != NULL) ? origin : dns_rootname;
+
+ for (i = 0; i < 2; i++) {
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_string,
+ ISC_FALSE));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ RETTOK(dns_name_fromtext(&name, &buffer, origin,
+ options, target));
+ ok = ISC_TRUE;
+ if ((options & DNS_RDATA_CHECKNAMES) != 0 && i == 0)
+ ok = dns_name_ismailbox(&name);
+ if (!ok && (options & DNS_RDATA_CHECKNAMESFAIL) != 0)
+ RETTOK(DNS_R_BADNAME);
+ if (!ok && callbacks != NULL)
+ warn_badname(&name, lexer, callbacks);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_rp(ARGS_TOTEXT) {
+ isc_region_t region;
+ dns_name_t rmail;
+ dns_name_t email;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+
+ REQUIRE(rdata->type == 17);
+ REQUIRE(rdata->length != 0);
+
+ dns_name_init(&rmail, NULL);
+ dns_name_init(&email, NULL);
+ dns_name_init(&prefix, NULL);
+
+ dns_rdata_toregion(rdata, &region);
+
+ dns_name_fromregion(&rmail, &region);
+ isc_region_consume(&region, rmail.length);
+
+ dns_name_fromregion(&email, &region);
+ isc_region_consume(&region, email.length);
+
+ sub = name_prefix(&rmail, tctx->origin, &prefix);
+ RETERR(dns_name_totext(&prefix, sub, target));
+
+ RETERR(str_totext(" ", target));
+
+ sub = name_prefix(&email, tctx->origin, &prefix);
+ return (dns_name_totext(&prefix, sub, target));
+}
+
+static inline isc_result_t
+fromwire_rp(ARGS_FROMWIRE) {
+ dns_name_t rmail;
+ dns_name_t email;
+
+ REQUIRE(type == 17);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_NONE);
+
+ dns_name_init(&rmail, NULL);
+ dns_name_init(&email, NULL);
+
+ RETERR(dns_name_fromwire(&rmail, source, dctx, options, target));
+ return (dns_name_fromwire(&email, source, dctx, options, target));
+}
+
+static inline isc_result_t
+towire_rp(ARGS_TOWIRE) {
+ isc_region_t region;
+ dns_name_t rmail;
+ dns_name_t email;
+ dns_offsets_t roffsets;
+ dns_offsets_t eoffsets;
+
+ REQUIRE(rdata->type == 17);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_NONE);
+ dns_name_init(&rmail, roffsets);
+ dns_name_init(&email, eoffsets);
+
+ dns_rdata_toregion(rdata, &region);
+
+ dns_name_fromregion(&rmail, &region);
+ isc_region_consume(&region, rmail.length);
+
+ RETERR(dns_name_towire(&rmail, cctx, target));
+
+ dns_name_fromregion(&rmail, &region);
+ isc_region_consume(&region, rmail.length);
+
+ return (dns_name_towire(&rmail, cctx, target));
+}
+
+static inline int
+compare_rp(ARGS_COMPARE) {
+ isc_region_t region1;
+ isc_region_t region2;
+ dns_name_t name1;
+ dns_name_t name2;
+ int order;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 17);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+
+ dns_name_fromregion(&name1, &region1);
+ dns_name_fromregion(&name2, &region2);
+
+ order = dns_name_rdatacompare(&name1, &name2);
+ if (order != 0)
+ return (order);
+
+ isc_region_consume(&region1, name_length(&name1));
+ isc_region_consume(&region2, name_length(&name2));
+
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+
+ dns_name_fromregion(&name1, &region1);
+ dns_name_fromregion(&name2, &region2);
+
+ return (dns_name_rdatacompare(&name1, &name2));
+}
+
+static inline isc_result_t
+fromstruct_rp(ARGS_FROMSTRUCT) {
+ dns_rdata_rp_t *rp = source;
+ isc_region_t region;
+
+ REQUIRE(type == 17);
+ REQUIRE(source != NULL);
+ REQUIRE(rp->common.rdtype == type);
+ REQUIRE(rp->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_name_toregion(&rp->mail, &region);
+ RETERR(isc_buffer_copyregion(target, &region));
+ dns_name_toregion(&rp->text, &region);
+ return (isc_buffer_copyregion(target, &region));
+}
+
+static inline isc_result_t
+tostruct_rp(ARGS_TOSTRUCT) {
+ isc_result_t result;
+ isc_region_t region;
+ dns_rdata_rp_t *rp = target;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 17);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ rp->common.rdclass = rdata->rdclass;
+ rp->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&rp->common, link);
+
+ dns_name_init(&name, NULL);
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+ dns_name_init(&rp->mail, NULL);
+ RETERR(name_duporclone(&name, mctx, &rp->mail));
+ isc_region_consume(&region, name_length(&name));
+ dns_name_fromregion(&name, &region);
+ dns_name_init(&rp->text, NULL);
+ result = name_duporclone(&name, mctx, &rp->text);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ rp->mctx = mctx;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (mctx != NULL)
+ dns_name_free(&rp->mail, mctx);
+ return (ISC_R_NOMEMORY);
+}
+
+static inline void
+freestruct_rp(ARGS_FREESTRUCT) {
+ dns_rdata_rp_t *rp = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(rp->common.rdtype == 17);
+
+ if (rp->mctx == NULL)
+ return;
+
+ dns_name_free(&rp->mail, rp->mctx);
+ dns_name_free(&rp->text, rp->mctx);
+ rp->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_rp(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 17);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_rp(ARGS_DIGEST) {
+ isc_region_t r;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 17);
+
+ dns_rdata_toregion(rdata, &r);
+ dns_name_init(&name, NULL);
+
+ dns_name_fromregion(&name, &r);
+ RETERR(dns_name_digest(&name, digest, arg));
+ isc_region_consume(&r, name_length(&name));
+
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline isc_boolean_t
+checkowner_rp(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 17);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_rp(ARGS_CHECKNAMES) {
+ isc_region_t region;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 17);
+
+ UNUSED(owner);
+
+ dns_rdata_toregion(rdata, &region);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &region);
+ if (!dns_name_ismailbox(&name)) {
+ if (bad != NULL)
+ dns_name_clone(&name, bad);
+ return (ISC_FALSE);
+ }
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_RP_17_C */
diff --git a/lib/dns/rdata/generic/rp_17.h b/lib/dns/rdata/generic/rp_17.h
new file mode 100644
index 0000000..6223038
--- /dev/null
+++ b/lib/dns/rdata/generic/rp_17.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+#ifndef GENERIC_RP_17_H
+#define GENERIC_RP_17_H 1
+
+/* $Id: rp_17.h,v 1.21 2007/06/19 23:47:17 tbox Exp $ */
+
+/*!
+ * \brief Per RFC1183 */
+
+typedef struct dns_rdata_rp {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ dns_name_t mail;
+ dns_name_t text;
+} dns_rdata_rp_t;
+
+
+#endif /* GENERIC_RP_17_H */
diff --git a/lib/dns/rdata/generic/rrsig_46.c b/lib/dns/rdata/generic/rrsig_46.c
new file mode 100644
index 0000000..a9af4bd
--- /dev/null
+++ b/lib/dns/rdata/generic/rrsig_46.c
@@ -0,0 +1,551 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rrsig_46.c,v 1.10 2007/06/19 23:47:17 tbox Exp $ */
+
+/* Reviewed: Fri Mar 17 09:05:02 PST 2000 by gson */
+
+/* RFC2535 */
+
+#ifndef RDATA_GENERIC_RRSIG_46_C
+#define RDATA_GENERIC_RRSIG_46_C
+
+#define RRTYPE_RRSIG_ATTRIBUTES (DNS_RDATATYPEATTR_DNSSEC)
+
+static inline isc_result_t
+fromtext_rrsig(ARGS_FROMTEXT) {
+ isc_token_t token;
+ unsigned char c;
+ long i;
+ dns_rdatatype_t covered;
+ char *e;
+ isc_result_t result;
+ dns_name_t name;
+ isc_buffer_t buffer;
+ isc_uint32_t time_signed, time_expire;
+
+ REQUIRE(type == 46);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ /*
+ * Type covered.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ result = dns_rdatatype_fromtext(&covered, &token.value.as_textregion);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTIMPLEMENTED) {
+ i = strtol(DNS_AS_STR(token), &e, 10);
+ if (i < 0 || i > 65535)
+ RETTOK(ISC_R_RANGE);
+ if (*e != 0)
+ RETTOK(result);
+ covered = (dns_rdatatype_t)i;
+ }
+ RETERR(uint16_tobuffer(covered, target));
+
+ /*
+ * Algorithm.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ RETTOK(dns_secalg_fromtext(&c, &token.value.as_textregion));
+ RETERR(mem_tobuffer(target, &c, 1));
+
+ /*
+ * Labels.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffU)
+ RETTOK(ISC_R_RANGE);
+ c = (unsigned char)token.value.as_ulong;
+ RETERR(mem_tobuffer(target, &c, 1));
+
+ /*
+ * Original ttl.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ RETERR(uint32_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Signature expiration.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ RETTOK(dns_time32_fromtext(DNS_AS_STR(token), &time_expire));
+ RETERR(uint32_tobuffer(time_expire, target));
+
+ /*
+ * Time signed.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ RETTOK(dns_time32_fromtext(DNS_AS_STR(token), &time_signed));
+ RETERR(uint32_tobuffer(time_signed, target));
+
+ /*
+ * Key footprint.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Signer.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+
+ /*
+ * Sig.
+ */
+ return (isc_base64_tobuffer(lexer, target, -1));
+}
+
+static inline isc_result_t
+totext_rrsig(ARGS_TOTEXT) {
+ isc_region_t sr;
+ char buf[sizeof("4294967295")];
+ dns_rdatatype_t covered;
+ unsigned long ttl;
+ unsigned long when;
+ unsigned long exp;
+ unsigned long foot;
+ dns_name_t name;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+
+ REQUIRE(rdata->type == 46);
+ REQUIRE(rdata->length != 0);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /*
+ * Type covered.
+ */
+ covered = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ /*
+ * XXXAG We should have something like dns_rdatatype_isknown()
+ * that does the right thing with type 0.
+ */
+ if (dns_rdatatype_isknown(covered) && covered != 0) {
+ RETERR(dns_rdatatype_totext(covered, target));
+ } else {
+ char buf[sizeof("TYPE65535")];
+ sprintf(buf, "TYPE%u", covered);
+ RETERR(str_totext(buf, target));
+ }
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Algorithm.
+ */
+ sprintf(buf, "%u", sr.base[0]);
+ isc_region_consume(&sr, 1);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Labels.
+ */
+ sprintf(buf, "%u", sr.base[0]);
+ isc_region_consume(&sr, 1);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Ttl.
+ */
+ ttl = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+ sprintf(buf, "%lu", ttl);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Sig exp.
+ */
+ exp = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+ RETERR(dns_time32_totext(exp, target));
+
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" (", target));
+ RETERR(str_totext(tctx->linebreak, target));
+
+ /*
+ * Time signed.
+ */
+ when = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+ RETERR(dns_time32_totext(when, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Footprint.
+ */
+ foot = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ sprintf(buf, "%lu", foot);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Signer.
+ */
+ dns_name_init(&name, NULL);
+ dns_name_init(&prefix, NULL);
+ dns_name_fromregion(&name, &sr);
+ isc_region_consume(&sr, name_length(&name));
+ sub = name_prefix(&name, tctx->origin, &prefix);
+ RETERR(dns_name_totext(&prefix, sub, target));
+
+ /*
+ * Sig.
+ */
+ RETERR(str_totext(tctx->linebreak, target));
+ RETERR(isc_base64_totext(&sr, tctx->width - 2,
+ tctx->linebreak, target));
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" )", target));
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_rrsig(ARGS_FROMWIRE) {
+ isc_region_t sr;
+ dns_name_t name;
+
+ REQUIRE(type == 46);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_NONE);
+
+ isc_buffer_activeregion(source, &sr);
+ /*
+ * type covered: 2
+ * algorithm: 1
+ * labels: 1
+ * original ttl: 4
+ * signature expiration: 4
+ * time signed: 4
+ * key footprint: 2
+ */
+ if (sr.length < 18)
+ return (ISC_R_UNEXPECTEDEND);
+
+ isc_buffer_forward(source, 18);
+ RETERR(mem_tobuffer(target, sr.base, 18));
+
+ /*
+ * Signer.
+ */
+ dns_name_init(&name, NULL);
+ RETERR(dns_name_fromwire(&name, source, dctx, options, target));
+
+ /*
+ * Sig.
+ */
+ isc_buffer_activeregion(source, &sr);
+ isc_buffer_forward(source, sr.length);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline isc_result_t
+towire_rrsig(ARGS_TOWIRE) {
+ isc_region_t sr;
+ dns_name_t name;
+ dns_offsets_t offsets;
+
+ REQUIRE(rdata->type == 46);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_NONE);
+ dns_rdata_toregion(rdata, &sr);
+ /*
+ * type covered: 2
+ * algorithm: 1
+ * labels: 1
+ * original ttl: 4
+ * signature expiration: 4
+ * time signed: 4
+ * key footprint: 2
+ */
+ RETERR(mem_tobuffer(target, sr.base, 18));
+ isc_region_consume(&sr, 18);
+
+ /*
+ * Signer.
+ */
+ dns_name_init(&name, offsets);
+ dns_name_fromregion(&name, &sr);
+ isc_region_consume(&sr, name_length(&name));
+ RETERR(dns_name_towire(&name, cctx, target));
+
+ /*
+ * Signature.
+ */
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline int
+compare_rrsig(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 46);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_rrsig(ARGS_FROMSTRUCT) {
+ dns_rdata_rrsig_t *sig = source;
+
+ REQUIRE(type == 46);
+ REQUIRE(source != NULL);
+ REQUIRE(sig->common.rdtype == type);
+ REQUIRE(sig->common.rdclass == rdclass);
+ REQUIRE(sig->signature != NULL || sig->siglen == 0);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ /*
+ * Type covered.
+ */
+ RETERR(uint16_tobuffer(sig->covered, target));
+
+ /*
+ * Algorithm.
+ */
+ RETERR(uint8_tobuffer(sig->algorithm, target));
+
+ /*
+ * Labels.
+ */
+ RETERR(uint8_tobuffer(sig->labels, target));
+
+ /*
+ * Original TTL.
+ */
+ RETERR(uint32_tobuffer(sig->originalttl, target));
+
+ /*
+ * Expire time.
+ */
+ RETERR(uint32_tobuffer(sig->timeexpire, target));
+
+ /*
+ * Time signed.
+ */
+ RETERR(uint32_tobuffer(sig->timesigned, target));
+
+ /*
+ * Key ID.
+ */
+ RETERR(uint16_tobuffer(sig->keyid, target));
+
+ /*
+ * Signer name.
+ */
+ RETERR(name_tobuffer(&sig->signer, target));
+
+ /*
+ * Signature.
+ */
+ return (mem_tobuffer(target, sig->signature, sig->siglen));
+}
+
+static inline isc_result_t
+tostruct_rrsig(ARGS_TOSTRUCT) {
+ isc_region_t sr;
+ dns_rdata_rrsig_t *sig = target;
+ dns_name_t signer;
+
+ REQUIRE(rdata->type == 46);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ sig->common.rdclass = rdata->rdclass;
+ sig->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&sig->common, link);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /*
+ * Type covered.
+ */
+ sig->covered = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+
+ /*
+ * Algorithm.
+ */
+ sig->algorithm = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+
+ /*
+ * Labels.
+ */
+ sig->labels = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+
+ /*
+ * Original TTL.
+ */
+ sig->originalttl = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+
+ /*
+ * Expire time.
+ */
+ sig->timeexpire = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+
+ /*
+ * Time signed.
+ */
+ sig->timesigned = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+
+ /*
+ * Key ID.
+ */
+ sig->keyid = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+
+ dns_name_init(&signer, NULL);
+ dns_name_fromregion(&signer, &sr);
+ dns_name_init(&sig->signer, NULL);
+ RETERR(name_duporclone(&signer, mctx, &sig->signer));
+ isc_region_consume(&sr, name_length(&sig->signer));
+
+ /*
+ * Signature.
+ */
+ sig->siglen = sr.length;
+ sig->signature = mem_maybedup(mctx, sr.base, sig->siglen);
+ if (sig->signature == NULL)
+ goto cleanup;
+
+
+ sig->mctx = mctx;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (mctx != NULL)
+ dns_name_free(&sig->signer, mctx);
+ return (ISC_R_NOMEMORY);
+}
+
+static inline void
+freestruct_rrsig(ARGS_FREESTRUCT) {
+ dns_rdata_rrsig_t *sig = (dns_rdata_rrsig_t *) source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(sig->common.rdtype == 46);
+
+ if (sig->mctx == NULL)
+ return;
+
+ dns_name_free(&sig->signer, sig->mctx);
+ if (sig->signature != NULL)
+ isc_mem_free(sig->mctx, sig->signature);
+ sig->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_rrsig(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 46);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_rrsig(ARGS_DIGEST) {
+
+ REQUIRE(rdata->type == 46);
+
+ UNUSED(rdata);
+ UNUSED(digest);
+ UNUSED(arg);
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static inline dns_rdatatype_t
+covers_rrsig(dns_rdata_t *rdata) {
+ dns_rdatatype_t type;
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 46);
+
+ dns_rdata_toregion(rdata, &r);
+ type = uint16_fromregion(&r);
+
+ return (type);
+}
+
+static inline isc_boolean_t
+checkowner_rrsig(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 46);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_rrsig(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 46);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_RRSIG_46_C */
diff --git a/lib/dns/rdata/generic/rrsig_46.h b/lib/dns/rdata/generic/rrsig_46.h
new file mode 100644
index 0000000..8e8dc4e
--- /dev/null
+++ b/lib/dns/rdata/generic/rrsig_46.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+#ifndef GENERIC_DNSSIG_46_H
+#define GENERIC_DNSSIG_46_H 1
+
+/* $Id: rrsig_46.h,v 1.7 2007/06/19 23:47:17 tbox Exp $ */
+
+/*!
+ * \brief Per RFC2535 */
+typedef struct dns_rdata_rrsig {
+ dns_rdatacommon_t common;
+ isc_mem_t * mctx;
+ dns_rdatatype_t covered;
+ dns_secalg_t algorithm;
+ isc_uint8_t labels;
+ isc_uint32_t originalttl;
+ isc_uint32_t timeexpire;
+ isc_uint32_t timesigned;
+ isc_uint16_t keyid;
+ dns_name_t signer;
+ isc_uint16_t siglen;
+ unsigned char * signature;
+} dns_rdata_rrsig_t;
+
+
+#endif /* GENERIC_DNSSIG_46_H */
diff --git a/lib/dns/rdata/generic/rt_21.c b/lib/dns/rdata/generic/rt_21.c
new file mode 100644
index 0000000..6444102
--- /dev/null
+++ b/lib/dns/rdata/generic/rt_21.c
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001, 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rt_21.c,v 1.46 2007/06/19 23:47:17 tbox Exp $ */
+
+/* reviewed: Thu Mar 16 15:02:31 PST 2000 by brister */
+
+/* RFC1183 */
+
+#ifndef RDATA_GENERIC_RT_21_C
+#define RDATA_GENERIC_RT_21_C
+
+#define RRTYPE_RT_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_rt(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+ isc_boolean_t ok;
+
+ REQUIRE(type == 21);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+ ok = ISC_TRUE;
+ if ((options & DNS_RDATA_CHECKNAMES) != 0)
+ ok = dns_name_ishostname(&name, ISC_FALSE);
+ if (!ok && (options & DNS_RDATA_CHECKNAMESFAIL) != 0)
+ RETTOK(DNS_R_BADNAME);
+ if (!ok && callbacks != NULL)
+ warn_badname(&name, lexer, callbacks);
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_rt(ARGS_TOTEXT) {
+ isc_region_t region;
+ dns_name_t name;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+ char buf[sizeof("64000")];
+ unsigned short num;
+
+ REQUIRE(rdata->type == 21);
+ REQUIRE(rdata->length != 0);
+
+ dns_name_init(&name, NULL);
+ dns_name_init(&prefix, NULL);
+
+ dns_rdata_toregion(rdata, &region);
+ num = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ sprintf(buf, "%u", num);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+ dns_name_fromregion(&name, &region);
+ sub = name_prefix(&name, tctx->origin, &prefix);
+ return (dns_name_totext(&prefix, sub, target));
+}
+
+static inline isc_result_t
+fromwire_rt(ARGS_FROMWIRE) {
+ dns_name_t name;
+ isc_region_t sregion;
+ isc_region_t tregion;
+
+ REQUIRE(type == 21);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_NONE);
+
+ dns_name_init(&name, NULL);
+
+ isc_buffer_activeregion(source, &sregion);
+ isc_buffer_availableregion(target, &tregion);
+ if (tregion.length < 2)
+ return (ISC_R_NOSPACE);
+ if (sregion.length < 2)
+ return (ISC_R_UNEXPECTEDEND);
+ memcpy(tregion.base, sregion.base, 2);
+ isc_buffer_forward(source, 2);
+ isc_buffer_add(target, 2);
+ return (dns_name_fromwire(&name, source, dctx, options, target));
+}
+
+static inline isc_result_t
+towire_rt(ARGS_TOWIRE) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t region;
+ isc_region_t tr;
+
+ REQUIRE(rdata->type == 21);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_NONE);
+ isc_buffer_availableregion(target, &tr);
+ dns_rdata_toregion(rdata, &region);
+ if (tr.length < 2)
+ return (ISC_R_NOSPACE);
+ memcpy(tr.base, region.base, 2);
+ isc_region_consume(&region, 2);
+ isc_buffer_add(target, 2);
+
+ dns_name_init(&name, offsets);
+ dns_name_fromregion(&name, &region);
+
+ return (dns_name_towire(&name, cctx, target));
+}
+
+static inline int
+compare_rt(ARGS_COMPARE) {
+ dns_name_t name1;
+ dns_name_t name2;
+ isc_region_t region1;
+ isc_region_t region2;
+ int order;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 21);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ order = memcmp(rdata1->data, rdata2->data, 2);
+ if (order != 0)
+ return (order < 0 ? -1 : 1);
+
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+
+ isc_region_consume(&region1, 2);
+ isc_region_consume(&region2, 2);
+
+ dns_name_fromregion(&name1, &region1);
+ dns_name_fromregion(&name2, &region2);
+
+ return (dns_name_rdatacompare(&name1, &name2));
+}
+
+static inline isc_result_t
+fromstruct_rt(ARGS_FROMSTRUCT) {
+ dns_rdata_rt_t *rt = source;
+ isc_region_t region;
+
+ REQUIRE(type == 21);
+ REQUIRE(source != NULL);
+ REQUIRE(rt->common.rdtype == type);
+ REQUIRE(rt->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ RETERR(uint16_tobuffer(rt->preference, target));
+ dns_name_toregion(&rt->host, &region);
+ return (isc_buffer_copyregion(target, &region));
+}
+
+static inline isc_result_t
+tostruct_rt(ARGS_TOSTRUCT) {
+ isc_region_t region;
+ dns_rdata_rt_t *rt = target;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 21);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ rt->common.rdclass = rdata->rdclass;
+ rt->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&rt->common, link);
+
+ dns_name_init(&name, NULL);
+ dns_rdata_toregion(rdata, &region);
+ rt->preference = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ dns_name_fromregion(&name, &region);
+ dns_name_init(&rt->host, NULL);
+ RETERR(name_duporclone(&name, mctx, &rt->host));
+
+ rt->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_rt(ARGS_FREESTRUCT) {
+ dns_rdata_rt_t *rt = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(rt->common.rdtype == 21);
+
+ if (rt->mctx == NULL)
+ return;
+
+ dns_name_free(&rt->host, rt->mctx);
+ rt->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_rt(ARGS_ADDLDATA) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t region;
+ isc_result_t result;
+
+ REQUIRE(rdata->type == 21);
+
+ dns_name_init(&name, offsets);
+ dns_rdata_toregion(rdata, &region);
+ isc_region_consume(&region, 2);
+ dns_name_fromregion(&name, &region);
+
+ result = (add)(arg, &name, dns_rdatatype_x25);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = (add)(arg, &name, dns_rdatatype_isdn);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ return ((add)(arg, &name, dns_rdatatype_a));
+}
+
+static inline isc_result_t
+digest_rt(ARGS_DIGEST) {
+ isc_region_t r1, r2;
+ isc_result_t result;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 21);
+
+ dns_rdata_toregion(rdata, &r1);
+ r2 = r1;
+ isc_region_consume(&r2, 2);
+ r1.length = 2;
+ result = (digest)(arg, &r1);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r2);
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline isc_boolean_t
+checkowner_rt(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 21);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_rt(ARGS_CHECKNAMES) {
+ isc_region_t region;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 21);
+
+ UNUSED(owner);
+
+ dns_rdata_toregion(rdata, &region);
+ isc_region_consume(&region, 2);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &region);
+ if (!dns_name_ishostname(&name, ISC_FALSE)) {
+ if (bad != NULL)
+ dns_name_clone(&name, bad);
+ return (ISC_FALSE);
+ }
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_RT_21_C */
diff --git a/lib/dns/rdata/generic/rt_21.h b/lib/dns/rdata/generic/rt_21.h
new file mode 100644
index 0000000..2c0e9fc
--- /dev/null
+++ b/lib/dns/rdata/generic/rt_21.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+#ifndef GENERIC_RT_21_H
+#define GENERIC_RT_21_H 1
+
+/* $Id: rt_21.h,v 1.21 2007/06/19 23:47:17 tbox Exp $ */
+
+/*!
+ * \brief Per RFC1183 */
+
+typedef struct dns_rdata_rt {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ isc_uint16_t preference;
+ dns_name_t host;
+} dns_rdata_rt_t;
+
+#endif /* GENERIC_RT_21_H */
diff --git a/lib/dns/rdata/generic/sig_24.c b/lib/dns/rdata/generic/sig_24.c
new file mode 100644
index 0000000..e79e1e4
--- /dev/null
+++ b/lib/dns/rdata/generic/sig_24.c
@@ -0,0 +1,578 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: sig_24.c,v 1.66 2007/06/19 23:47:17 tbox Exp $ */
+
+/* Reviewed: Fri Mar 17 09:05:02 PST 2000 by gson */
+
+/* RFC2535 */
+
+#ifndef RDATA_GENERIC_SIG_24_C
+#define RDATA_GENERIC_SIG_24_C
+
+#define RRTYPE_SIG_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_sig(ARGS_FROMTEXT) {
+ isc_token_t token;
+ unsigned char c;
+ long i;
+ dns_rdatatype_t covered;
+ char *e;
+ isc_result_t result;
+ dns_name_t name;
+ isc_buffer_t buffer;
+ isc_uint32_t time_signed, time_expire;
+
+ REQUIRE(type == 24);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ /*
+ * Type covered.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ result = dns_rdatatype_fromtext(&covered, &token.value.as_textregion);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTIMPLEMENTED) {
+ i = strtol(DNS_AS_STR(token), &e, 10);
+ if (i < 0 || i > 65535)
+ RETTOK(ISC_R_RANGE);
+ if (*e != 0)
+ RETTOK(result);
+ covered = (dns_rdatatype_t)i;
+ }
+ RETERR(uint16_tobuffer(covered, target));
+
+ /*
+ * Algorithm.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ RETTOK(dns_secalg_fromtext(&c, &token.value.as_textregion));
+ RETERR(mem_tobuffer(target, &c, 1));
+
+ /*
+ * Labels.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffU)
+ RETTOK(ISC_R_RANGE);
+ c = (unsigned char)token.value.as_ulong;
+ RETERR(mem_tobuffer(target, &c, 1));
+
+ /*
+ * Original ttl.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ RETERR(uint32_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Signature expiration.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ RETTOK(dns_time32_fromtext(DNS_AS_STR(token), &time_expire));
+ RETERR(uint32_tobuffer(time_expire, target));
+
+ /*
+ * Time signed.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ RETTOK(dns_time32_fromtext(DNS_AS_STR(token), &time_signed));
+ RETERR(uint32_tobuffer(time_signed, target));
+
+ /*
+ * Key footprint.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Signer.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+
+ /*
+ * Sig.
+ */
+ return (isc_base64_tobuffer(lexer, target, -1));
+}
+
+static inline isc_result_t
+totext_sig(ARGS_TOTEXT) {
+ isc_region_t sr;
+ char buf[sizeof("4294967295")];
+ dns_rdatatype_t covered;
+ unsigned long ttl;
+ unsigned long when;
+ unsigned long exp;
+ unsigned long foot;
+ dns_name_t name;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+
+ REQUIRE(rdata->type == 24);
+ REQUIRE(rdata->length != 0);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /*
+ * Type covered.
+ */
+ covered = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ /*
+ * XXXAG We should have something like dns_rdatatype_isknown()
+ * that does the right thing with type 0.
+ */
+ if (dns_rdatatype_isknown(covered) && covered != 0) {
+ RETERR(dns_rdatatype_totext(covered, target));
+ } else {
+ char buf[sizeof("65535")];
+ sprintf(buf, "%u", covered);
+ RETERR(str_totext(buf, target));
+ }
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Algorithm.
+ */
+ sprintf(buf, "%u", sr.base[0]);
+ isc_region_consume(&sr, 1);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Labels.
+ */
+ sprintf(buf, "%u", sr.base[0]);
+ isc_region_consume(&sr, 1);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Ttl.
+ */
+ ttl = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+ sprintf(buf, "%lu", ttl);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Sig exp.
+ */
+ exp = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+ RETERR(dns_time32_totext(exp, target));
+
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" (", target));
+ RETERR(str_totext(tctx->linebreak, target));
+
+ /*
+ * Time signed.
+ */
+ when = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+ RETERR(dns_time32_totext(when, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Footprint.
+ */
+ foot = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ sprintf(buf, "%lu", foot);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Signer.
+ */
+ dns_name_init(&name, NULL);
+ dns_name_init(&prefix, NULL);
+ dns_name_fromregion(&name, &sr);
+ isc_region_consume(&sr, name_length(&name));
+ sub = name_prefix(&name, tctx->origin, &prefix);
+ RETERR(dns_name_totext(&prefix, sub, target));
+
+ /*
+ * Sig.
+ */
+ RETERR(str_totext(tctx->linebreak, target));
+ RETERR(isc_base64_totext(&sr, tctx->width - 2,
+ tctx->linebreak, target));
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" )", target));
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_sig(ARGS_FROMWIRE) {
+ isc_region_t sr;
+ dns_name_t name;
+
+ REQUIRE(type == 24);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_NONE);
+
+ isc_buffer_activeregion(source, &sr);
+ /*
+ * type covered: 2
+ * algorithm: 1
+ * labels: 1
+ * original ttl: 4
+ * signature expiration: 4
+ * time signed: 4
+ * key footprint: 2
+ */
+ if (sr.length < 18)
+ return (ISC_R_UNEXPECTEDEND);
+
+ isc_buffer_forward(source, 18);
+ RETERR(mem_tobuffer(target, sr.base, 18));
+
+ /*
+ * Signer.
+ */
+ dns_name_init(&name, NULL);
+ RETERR(dns_name_fromwire(&name, source, dctx, options, target));
+
+ /*
+ * Sig.
+ */
+ isc_buffer_activeregion(source, &sr);
+ isc_buffer_forward(source, sr.length);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline isc_result_t
+towire_sig(ARGS_TOWIRE) {
+ isc_region_t sr;
+ dns_name_t name;
+ dns_offsets_t offsets;
+
+ REQUIRE(rdata->type == 24);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_NONE);
+ dns_rdata_toregion(rdata, &sr);
+ /*
+ * type covered: 2
+ * algorithm: 1
+ * labels: 1
+ * original ttl: 4
+ * signature expiration: 4
+ * time signed: 4
+ * key footprint: 2
+ */
+ RETERR(mem_tobuffer(target, sr.base, 18));
+ isc_region_consume(&sr, 18);
+
+ /*
+ * Signer.
+ */
+ dns_name_init(&name, offsets);
+ dns_name_fromregion(&name, &sr);
+ isc_region_consume(&sr, name_length(&name));
+ RETERR(dns_name_towire(&name, cctx, target));
+
+ /*
+ * Signature.
+ */
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline int
+compare_sig(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+ dns_name_t name1;
+ dns_name_t name2;
+ int order;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 24);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+
+ INSIST(r1.length > 18);
+ INSIST(r2.length > 18);
+ r1.length = 18;
+ r2.length = 18;
+ order = isc_region_compare(&r1, &r2);
+ if (order != 0)
+ return (order);
+
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ isc_region_consume(&r1, 18);
+ isc_region_consume(&r2, 18);
+ dns_name_fromregion(&name1, &r1);
+ dns_name_fromregion(&name2, &r2);
+ order = dns_name_rdatacompare(&name1, &name2);
+ if (order != 0)
+ return (order);
+
+ isc_region_consume(&r1, name_length(&name1));
+ isc_region_consume(&r2, name_length(&name2));
+
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_sig(ARGS_FROMSTRUCT) {
+ dns_rdata_sig_t *sig = source;
+
+ REQUIRE(type == 24);
+ REQUIRE(source != NULL);
+ REQUIRE(sig->common.rdtype == type);
+ REQUIRE(sig->common.rdclass == rdclass);
+ REQUIRE(sig->signature != NULL || sig->siglen == 0);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ /*
+ * Type covered.
+ */
+ RETERR(uint16_tobuffer(sig->covered, target));
+
+ /*
+ * Algorithm.
+ */
+ RETERR(uint8_tobuffer(sig->algorithm, target));
+
+ /*
+ * Labels.
+ */
+ RETERR(uint8_tobuffer(sig->labels, target));
+
+ /*
+ * Original TTL.
+ */
+ RETERR(uint32_tobuffer(sig->originalttl, target));
+
+ /*
+ * Expire time.
+ */
+ RETERR(uint32_tobuffer(sig->timeexpire, target));
+
+ /*
+ * Time signed.
+ */
+ RETERR(uint32_tobuffer(sig->timesigned, target));
+
+ /*
+ * Key ID.
+ */
+ RETERR(uint16_tobuffer(sig->keyid, target));
+
+ /*
+ * Signer name.
+ */
+ RETERR(name_tobuffer(&sig->signer, target));
+
+ /*
+ * Signature.
+ */
+ return (mem_tobuffer(target, sig->signature, sig->siglen));
+}
+
+static inline isc_result_t
+tostruct_sig(ARGS_TOSTRUCT) {
+ isc_region_t sr;
+ dns_rdata_sig_t *sig = target;
+ dns_name_t signer;
+
+ REQUIRE(rdata->type == 24);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ sig->common.rdclass = rdata->rdclass;
+ sig->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&sig->common, link);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /*
+ * Type covered.
+ */
+ sig->covered = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+
+ /*
+ * Algorithm.
+ */
+ sig->algorithm = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+
+ /*
+ * Labels.
+ */
+ sig->labels = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+
+ /*
+ * Original TTL.
+ */
+ sig->originalttl = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+
+ /*
+ * Expire time.
+ */
+ sig->timeexpire = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+
+ /*
+ * Time signed.
+ */
+ sig->timesigned = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+
+ /*
+ * Key ID.
+ */
+ sig->keyid = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+
+ dns_name_init(&signer, NULL);
+ dns_name_fromregion(&signer, &sr);
+ dns_name_init(&sig->signer, NULL);
+ RETERR(name_duporclone(&signer, mctx, &sig->signer));
+ isc_region_consume(&sr, name_length(&sig->signer));
+
+ /*
+ * Signature.
+ */
+ sig->siglen = sr.length;
+ sig->signature = mem_maybedup(mctx, sr.base, sig->siglen);
+ if (sig->signature == NULL)
+ goto cleanup;
+
+
+ sig->mctx = mctx;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (mctx != NULL)
+ dns_name_free(&sig->signer, mctx);
+ return (ISC_R_NOMEMORY);
+}
+
+static inline void
+freestruct_sig(ARGS_FREESTRUCT) {
+ dns_rdata_sig_t *sig = (dns_rdata_sig_t *) source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(sig->common.rdtype == 24);
+
+ if (sig->mctx == NULL)
+ return;
+
+ dns_name_free(&sig->signer, sig->mctx);
+ if (sig->signature != NULL)
+ isc_mem_free(sig->mctx, sig->signature);
+ sig->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_sig(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 24);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_sig(ARGS_DIGEST) {
+
+ REQUIRE(rdata->type == 24);
+
+ UNUSED(rdata);
+ UNUSED(digest);
+ UNUSED(arg);
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static inline dns_rdatatype_t
+covers_sig(dns_rdata_t *rdata) {
+ dns_rdatatype_t type;
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 24);
+
+ dns_rdata_toregion(rdata, &r);
+ type = uint16_fromregion(&r);
+
+ return (type);
+}
+
+static inline isc_boolean_t
+checkowner_sig(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 24);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_sig(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 24);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_SIG_24_C */
diff --git a/lib/dns/rdata/generic/sig_24.h b/lib/dns/rdata/generic/sig_24.h
new file mode 100644
index 0000000..7212d4d
--- /dev/null
+++ b/lib/dns/rdata/generic/sig_24.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+#ifndef GENERIC_SIG_24_H
+#define GENERIC_SIG_24_H 1
+
+/* $Id: sig_24.h,v 1.26 2007/06/19 23:47:17 tbox Exp $ */
+
+/*!
+ * \brief Per RFC2535 */
+
+typedef struct dns_rdata_sig_t {
+ dns_rdatacommon_t common;
+ isc_mem_t * mctx;
+ dns_rdatatype_t covered;
+ dns_secalg_t algorithm;
+ isc_uint8_t labels;
+ isc_uint32_t originalttl;
+ isc_uint32_t timeexpire;
+ isc_uint32_t timesigned;
+ isc_uint16_t keyid;
+ dns_name_t signer;
+ isc_uint16_t siglen;
+ unsigned char * signature;
+} dns_rdata_sig_t;
+
+
+#endif /* GENERIC_SIG_24_H */
diff --git a/lib/dns/rdata/generic/soa_6.c b/lib/dns/rdata/generic/soa_6.c
new file mode 100644
index 0000000..b135adf
--- /dev/null
+++ b/lib/dns/rdata/generic/soa_6.c
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: soa_6.c,v 1.61 2007/06/19 23:47:17 tbox Exp $ */
+
+/* Reviewed: Thu Mar 16 15:18:32 PST 2000 by explorer */
+
+#ifndef RDATA_GENERIC_SOA_6_C
+#define RDATA_GENERIC_SOA_6_C
+
+#define RRTYPE_SOA_ATTRIBUTES (DNS_RDATATYPEATTR_SINGLETON)
+
+static inline isc_result_t
+fromtext_soa(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+ int i;
+ isc_uint32_t n;
+ isc_boolean_t ok;
+
+ REQUIRE(type == 6);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ origin = (origin != NULL) ? origin : dns_rootname;
+
+ for (i = 0; i < 2; i++) {
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_string,
+ ISC_FALSE));
+
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ RETTOK(dns_name_fromtext(&name, &buffer, origin,
+ options, target));
+ ok = ISC_TRUE;
+ if ((options & DNS_RDATA_CHECKNAMES) != 0)
+ switch (i) {
+ case 0:
+ ok = dns_name_ishostname(&name, ISC_FALSE);
+ break;
+ case 1:
+ ok = dns_name_ismailbox(&name);
+ break;
+
+ }
+ if (!ok && (options & DNS_RDATA_CHECKNAMESFAIL) != 0)
+ RETTOK(DNS_R_BADNAME);
+ if (!ok && callbacks != NULL)
+ warn_badname(&name, lexer, callbacks);
+ }
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ RETERR(uint32_tobuffer(token.value.as_ulong, target));
+
+ for (i = 0; i < 4; i++) {
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_string,
+ ISC_FALSE));
+ RETTOK(dns_counter_fromtext(&token.value.as_textregion, &n));
+ RETERR(uint32_tobuffer(n, target));
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static const char *soa_fieldnames[5] = {
+ "serial", "refresh", "retry", "expire", "minimum"
+};
+
+static inline isc_result_t
+totext_soa(ARGS_TOTEXT) {
+ isc_region_t dregion;
+ dns_name_t mname;
+ dns_name_t rname;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+ int i;
+ isc_boolean_t multiline;
+ isc_boolean_t comment;
+
+ REQUIRE(rdata->type == 6);
+ REQUIRE(rdata->length != 0);
+
+ multiline = ISC_TF((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0);
+ comment = ISC_TF((tctx->flags & DNS_STYLEFLAG_COMMENT) != 0);
+
+ dns_name_init(&mname, NULL);
+ dns_name_init(&rname, NULL);
+ dns_name_init(&prefix, NULL);
+
+ dns_rdata_toregion(rdata, &dregion);
+
+ dns_name_fromregion(&mname, &dregion);
+ isc_region_consume(&dregion, name_length(&mname));
+
+ dns_name_fromregion(&rname, &dregion);
+ isc_region_consume(&dregion, name_length(&rname));
+
+ sub = name_prefix(&mname, tctx->origin, &prefix);
+ RETERR(dns_name_totext(&prefix, sub, target));
+
+ RETERR(str_totext(" ", target));
+
+ sub = name_prefix(&rname, tctx->origin, &prefix);
+ RETERR(dns_name_totext(&prefix, sub, target));
+
+ if (multiline)
+ RETERR(str_totext(" (" , target));
+ RETERR(str_totext(tctx->linebreak, target));
+
+ for (i = 0; i < 5; i++) {
+ char buf[sizeof("2147483647")];
+ unsigned long num;
+ unsigned int numlen;
+ num = uint32_fromregion(&dregion);
+ isc_region_consume(&dregion, 4);
+ numlen = sprintf(buf, "%lu", num);
+ INSIST(numlen > 0 && numlen < sizeof("2147483647"));
+ RETERR(str_totext(buf, target));
+ if (multiline && comment) {
+ RETERR(str_totext(" ; " + numlen, target));
+ RETERR(str_totext(soa_fieldnames[i], target));
+ /* Print times in week/day/hour/minute/second form */
+ if (i >= 1) {
+ RETERR(str_totext(" (", target));
+ RETERR(dns_ttl_totext(num, ISC_TRUE, target));
+ RETERR(str_totext(")", target));
+ }
+ RETERR(str_totext(tctx->linebreak, target));
+ } else if (i < 4) {
+ RETERR(str_totext(tctx->linebreak, target));
+ }
+ }
+
+ if (multiline)
+ RETERR(str_totext(")", target));
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_soa(ARGS_FROMWIRE) {
+ dns_name_t mname;
+ dns_name_t rname;
+ isc_region_t sregion;
+ isc_region_t tregion;
+
+ REQUIRE(type == 6);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_GLOBAL14);
+
+ dns_name_init(&mname, NULL);
+ dns_name_init(&rname, NULL);
+
+ RETERR(dns_name_fromwire(&mname, source, dctx, options, target));
+ RETERR(dns_name_fromwire(&rname, source, dctx, options, target));
+
+ isc_buffer_activeregion(source, &sregion);
+ isc_buffer_availableregion(target, &tregion);
+
+ if (sregion.length < 20)
+ return (ISC_R_UNEXPECTEDEND);
+ if (tregion.length < 20)
+ return (ISC_R_NOSPACE);
+
+ memcpy(tregion.base, sregion.base, 20);
+ isc_buffer_forward(source, 20);
+ isc_buffer_add(target, 20);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+towire_soa(ARGS_TOWIRE) {
+ isc_region_t sregion;
+ isc_region_t tregion;
+ dns_name_t mname;
+ dns_name_t rname;
+ dns_offsets_t moffsets;
+ dns_offsets_t roffsets;
+
+ REQUIRE(rdata->type == 6);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14);
+
+ dns_name_init(&mname, moffsets);
+ dns_name_init(&rname, roffsets);
+
+ dns_rdata_toregion(rdata, &sregion);
+
+ dns_name_fromregion(&mname, &sregion);
+ isc_region_consume(&sregion, name_length(&mname));
+ RETERR(dns_name_towire(&mname, cctx, target));
+
+ dns_name_fromregion(&rname, &sregion);
+ isc_region_consume(&sregion, name_length(&rname));
+ RETERR(dns_name_towire(&rname, cctx, target));
+
+ isc_buffer_availableregion(target, &tregion);
+ if (tregion.length < 20)
+ return (ISC_R_NOSPACE);
+
+ memcpy(tregion.base, sregion.base, 20);
+ isc_buffer_add(target, 20);
+ return (ISC_R_SUCCESS);
+}
+
+static inline int
+compare_soa(ARGS_COMPARE) {
+ isc_region_t region1;
+ isc_region_t region2;
+ dns_name_t name1;
+ dns_name_t name2;
+ int order;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 6);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+
+ dns_name_fromregion(&name1, &region1);
+ dns_name_fromregion(&name2, &region2);
+
+ order = dns_name_rdatacompare(&name1, &name2);
+ if (order != 0)
+ return (order);
+
+ isc_region_consume(&region1, name_length(&name1));
+ isc_region_consume(&region2, name_length(&name2));
+
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+
+ dns_name_fromregion(&name1, &region1);
+ dns_name_fromregion(&name2, &region2);
+
+ order = dns_name_rdatacompare(&name1, &name2);
+ if (order != 0)
+ return (order);
+
+ isc_region_consume(&region1, name_length(&name1));
+ isc_region_consume(&region2, name_length(&name2));
+
+ return (isc_region_compare(&region1, &region2));
+}
+
+static inline isc_result_t
+fromstruct_soa(ARGS_FROMSTRUCT) {
+ dns_rdata_soa_t *soa = source;
+ isc_region_t region;
+
+ REQUIRE(type == 6);
+ REQUIRE(source != NULL);
+ REQUIRE(soa->common.rdtype == type);
+ REQUIRE(soa->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_name_toregion(&soa->origin, &region);
+ RETERR(isc_buffer_copyregion(target, &region));
+ dns_name_toregion(&soa->contact, &region);
+ RETERR(isc_buffer_copyregion(target, &region));
+ RETERR(uint32_tobuffer(soa->serial, target));
+ RETERR(uint32_tobuffer(soa->refresh, target));
+ RETERR(uint32_tobuffer(soa->retry, target));
+ RETERR(uint32_tobuffer(soa->expire, target));
+ return (uint32_tobuffer(soa->minimum, target));
+}
+
+static inline isc_result_t
+tostruct_soa(ARGS_TOSTRUCT) {
+ isc_region_t region;
+ dns_rdata_soa_t *soa = target;
+ dns_name_t name;
+ isc_result_t result;
+
+ REQUIRE(rdata->type == 6);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ soa->common.rdclass = rdata->rdclass;
+ soa->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&soa->common, link);
+
+
+ dns_rdata_toregion(rdata, &region);
+
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &region);
+ isc_region_consume(&region, name_length(&name));
+ dns_name_init(&soa->origin, NULL);
+ RETERR(name_duporclone(&name, mctx, &soa->origin));
+
+ dns_name_fromregion(&name, &region);
+ isc_region_consume(&region, name_length(&name));
+ dns_name_init(&soa->contact, NULL);
+ result = name_duporclone(&name, mctx, &soa->contact);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ soa->serial = uint32_fromregion(&region);
+ isc_region_consume(&region, 4);
+
+ soa->refresh = uint32_fromregion(&region);
+ isc_region_consume(&region, 4);
+
+ soa->retry = uint32_fromregion(&region);
+ isc_region_consume(&region, 4);
+
+ soa->expire = uint32_fromregion(&region);
+ isc_region_consume(&region, 4);
+
+ soa->minimum = uint32_fromregion(&region);
+
+ soa->mctx = mctx;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (mctx != NULL)
+ dns_name_free(&soa->origin, mctx);
+ return (ISC_R_NOMEMORY);
+}
+
+static inline void
+freestruct_soa(ARGS_FREESTRUCT) {
+ dns_rdata_soa_t *soa = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(soa->common.rdtype == 6);
+
+ if (soa->mctx == NULL)
+ return;
+
+ dns_name_free(&soa->origin, soa->mctx);
+ dns_name_free(&soa->contact, soa->mctx);
+ soa->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_soa(ARGS_ADDLDATA) {
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ REQUIRE(rdata->type == 6);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_soa(ARGS_DIGEST) {
+ isc_region_t r;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 6);
+
+ dns_rdata_toregion(rdata, &r);
+
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+ RETERR(dns_name_digest(&name, digest, arg));
+ isc_region_consume(&r, name_length(&name));
+
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+ RETERR(dns_name_digest(&name, digest, arg));
+ isc_region_consume(&r, name_length(&name));
+
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_soa(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 6);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_soa(ARGS_CHECKNAMES) {
+ isc_region_t region;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 6);
+
+ UNUSED(owner);
+
+ dns_rdata_toregion(rdata, &region);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &region);
+ if (!dns_name_ishostname(&name, ISC_FALSE)) {
+ if (bad != NULL)
+ dns_name_clone(&name, bad);
+ return (ISC_FALSE);
+ }
+ isc_region_consume(&region, name_length(&name));
+ dns_name_fromregion(&name, &region);
+ if (!dns_name_ismailbox(&name)) {
+ if (bad != NULL)
+ dns_name_clone(&name, bad);
+ return (ISC_FALSE);
+ }
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_SOA_6_C */
diff --git a/lib/dns/rdata/generic/soa_6.h b/lib/dns/rdata/generic/soa_6.h
new file mode 100644
index 0000000..7443b04
--- /dev/null
+++ b/lib/dns/rdata/generic/soa_6.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* */
+#ifndef GENERIC_SOA_6_H
+#define GENERIC_SOA_6_H 1
+
+/* $Id: soa_6.h,v 1.32 2007/06/19 23:47:17 tbox Exp $ */
+
+typedef struct dns_rdata_soa {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ dns_name_t origin;
+ dns_name_t contact;
+ isc_uint32_t serial; /*%< host order */
+ isc_uint32_t refresh; /*%< host order */
+ isc_uint32_t retry; /*%< host order */
+ isc_uint32_t expire; /*%< host order */
+ isc_uint32_t minimum; /*%< host order */
+} dns_rdata_soa_t;
+
+
+#endif /* GENERIC_SOA_6_H */
diff --git a/lib/dns/rdata/generic/spf_99.c b/lib/dns/rdata/generic/spf_99.c
new file mode 100644
index 0000000..12e813e
--- /dev/null
+++ b/lib/dns/rdata/generic/spf_99.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: spf_99.c,v 1.4 2007/06/19 23:47:17 tbox Exp $ */
+
+/* Reviewed: Thu Mar 16 15:40:00 PST 2000 by bwelling */
+
+#ifndef RDATA_GENERIC_SPF_99_C
+#define RDATA_GENERIC_SPF_99_C
+
+#define RRTYPE_SPF_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_spf(ARGS_FROMTEXT) {
+ isc_token_t token;
+ int strings;
+
+ REQUIRE(type == 99);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ strings = 0;
+ for (;;) {
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_qstring,
+ ISC_TRUE));
+ if (token.type != isc_tokentype_qstring &&
+ token.type != isc_tokentype_string)
+ break;
+ RETTOK(txt_fromtext(&token.value.as_textregion, target));
+ strings++;
+ }
+ /* Let upper layer handle eol/eof. */
+ isc_lex_ungettoken(lexer, &token);
+ return (strings == 0 ? ISC_R_UNEXPECTEDEND : ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_spf(ARGS_TOTEXT) {
+ isc_region_t region;
+
+ UNUSED(tctx);
+
+ REQUIRE(rdata->type == 99);
+
+ dns_rdata_toregion(rdata, &region);
+
+ while (region.length > 0) {
+ RETERR(txt_totext(&region, target));
+ if (region.length > 0)
+ RETERR(str_totext(" ", target));
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_spf(ARGS_FROMWIRE) {
+ isc_result_t result;
+
+ REQUIRE(type == 99);
+
+ UNUSED(type);
+ UNUSED(dctx);
+ UNUSED(rdclass);
+ UNUSED(options);
+
+ do {
+ result = txt_fromwire(source, target);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ } while (!buffer_empty(source));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+towire_spf(ARGS_TOWIRE) {
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 99);
+
+ UNUSED(cctx);
+
+ isc_buffer_availableregion(target, &region);
+ if (region.length < rdata->length)
+ return (ISC_R_NOSPACE);
+
+ memcpy(region.base, rdata->data, rdata->length);
+ isc_buffer_add(target, rdata->length);
+ return (ISC_R_SUCCESS);
+}
+
+static inline int
+compare_spf(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 99);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_spf(ARGS_FROMSTRUCT) {
+ dns_rdata_spf_t *txt = source;
+ isc_region_t region;
+ isc_uint8_t length;
+
+ REQUIRE(type == 99);
+ REQUIRE(source != NULL);
+ REQUIRE(txt->common.rdtype == type);
+ REQUIRE(txt->common.rdclass == rdclass);
+ REQUIRE(txt->txt != NULL && txt->txt_len != 0);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ region.base = txt->txt;
+ region.length = txt->txt_len;
+ while (region.length > 0) {
+ length = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+ if (region.length <= length)
+ return (ISC_R_UNEXPECTEDEND);
+ isc_region_consume(&region, length);
+ }
+
+ return (mem_tobuffer(target, txt->txt, txt->txt_len));
+}
+
+static inline isc_result_t
+tostruct_spf(ARGS_TOSTRUCT) {
+ dns_rdata_spf_t *txt = target;
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 99);
+ REQUIRE(target != NULL);
+
+ txt->common.rdclass = rdata->rdclass;
+ txt->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&txt->common, link);
+
+ dns_rdata_toregion(rdata, &r);
+ txt->txt_len = r.length;
+ txt->txt = mem_maybedup(mctx, r.base, r.length);
+ if (txt->txt == NULL)
+ return (ISC_R_NOMEMORY);
+
+ txt->offset = 0;
+ txt->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_spf(ARGS_FREESTRUCT) {
+ dns_rdata_spf_t *txt = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(txt->common.rdtype == 99);
+
+ if (txt->mctx == NULL)
+ return;
+
+ if (txt->txt != NULL)
+ isc_mem_free(txt->mctx, txt->txt);
+ txt->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_spf(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 99);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_spf(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 99);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_spf(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 99);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_spf(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 99);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_SPF_99_C */
diff --git a/lib/dns/rdata/generic/spf_99.h b/lib/dns/rdata/generic/spf_99.h
new file mode 100644
index 0000000..be5e978
--- /dev/null
+++ b/lib/dns/rdata/generic/spf_99.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+#ifndef GENERIC_SPF_99_H
+#define GENERIC_SPF_99_H 1
+
+/* $Id: spf_99.h,v 1.4 2007/06/19 23:47:17 tbox Exp $ */
+
+typedef struct dns_rdata_spf_string {
+ isc_uint8_t length;
+ unsigned char *data;
+} dns_rdata_spf_string_t;
+
+typedef struct dns_rdata_spf {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ unsigned char *txt;
+ isc_uint16_t txt_len;
+ /* private */
+ isc_uint16_t offset;
+} dns_rdata_spf_t;
+
+/*
+ * ISC_LANG_BEGINDECLS and ISC_LANG_ENDDECLS are already done
+ * via rdatastructpre.h and rdatastructsuf.h.
+ */
+
+isc_result_t
+dns_rdata_spf_first(dns_rdata_spf_t *);
+
+isc_result_t
+dns_rdata_spf_next(dns_rdata_spf_t *);
+
+isc_result_t
+dns_rdata_spf_current(dns_rdata_spf_t *, dns_rdata_spf_string_t *);
+
+#endif /* GENERIC_SPF_99_H */
diff --git a/lib/dns/rdata/generic/sshfp_44.c b/lib/dns/rdata/generic/sshfp_44.c
new file mode 100644
index 0000000..570a3b7
--- /dev/null
+++ b/lib/dns/rdata/generic/sshfp_44.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2004, 2006, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: sshfp_44.c,v 1.7 2007/06/19 23:47:17 tbox Exp $ */
+
+/* RFC 4255 */
+
+#ifndef RDATA_GENERIC_SSHFP_44_C
+#define RDATA_GENERIC_SSHFP_44_C
+
+#define RRTYPE_SSHFP_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_sshfp(ARGS_FROMTEXT) {
+ isc_token_t token;
+
+ REQUIRE(type == 44);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ /*
+ * Algorithm.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint8_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Digest type.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint8_tobuffer(token.value.as_ulong, target));
+ type = (isc_uint16_t) token.value.as_ulong;
+
+ /*
+ * Digest.
+ */
+ return (isc_hex_tobuffer(lexer, target, -1));
+}
+
+static inline isc_result_t
+totext_sshfp(ARGS_TOTEXT) {
+ isc_region_t sr;
+ char buf[sizeof("64000 ")];
+ unsigned int n;
+
+ REQUIRE(rdata->type == 44);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(tctx);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /*
+ * Algorithm.
+ */
+ n = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+ sprintf(buf, "%u ", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Digest type.
+ */
+ n = uint8_fromregion(&sr);
+ isc_region_consume(&sr, 1);
+ sprintf(buf, "%u", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Digest.
+ */
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" (", target));
+ RETERR(str_totext(tctx->linebreak, target));
+ RETERR(isc_hex_totext(&sr, tctx->width - 2, tctx->linebreak, target));
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" )", target));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_sshfp(ARGS_FROMWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(type == 44);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(dctx);
+ UNUSED(options);
+
+ isc_buffer_activeregion(source, &sr);
+ if (sr.length < 4)
+ return (ISC_R_UNEXPECTEDEND);
+
+ isc_buffer_forward(source, sr.length);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline isc_result_t
+towire_sshfp(ARGS_TOWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == 44);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(cctx);
+
+ dns_rdata_toregion(rdata, &sr);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline int
+compare_sshfp(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 44);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_sshfp(ARGS_FROMSTRUCT) {
+ dns_rdata_sshfp_t *sshfp = source;
+
+ REQUIRE(type == 44);
+ REQUIRE(source != NULL);
+ REQUIRE(sshfp->common.rdtype == type);
+ REQUIRE(sshfp->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ RETERR(uint8_tobuffer(sshfp->algorithm, target));
+ RETERR(uint8_tobuffer(sshfp->digest_type, target));
+
+ return (mem_tobuffer(target, sshfp->digest, sshfp->length));
+}
+
+static inline isc_result_t
+tostruct_sshfp(ARGS_TOSTRUCT) {
+ dns_rdata_sshfp_t *sshfp = target;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 44);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ sshfp->common.rdclass = rdata->rdclass;
+ sshfp->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&sshfp->common, link);
+
+ dns_rdata_toregion(rdata, &region);
+
+ sshfp->algorithm = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+ sshfp->digest_type = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+ sshfp->length = region.length;
+
+ sshfp->digest = mem_maybedup(mctx, region.base, region.length);
+ if (sshfp->digest == NULL)
+ return (ISC_R_NOMEMORY);
+
+ sshfp->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_sshfp(ARGS_FREESTRUCT) {
+ dns_rdata_sshfp_t *sshfp = source;
+
+ REQUIRE(sshfp != NULL);
+ REQUIRE(sshfp->common.rdtype == 44);
+
+ if (sshfp->mctx == NULL)
+ return;
+
+ if (sshfp->digest != NULL)
+ isc_mem_free(sshfp->mctx, sshfp->digest);
+ sshfp->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_sshfp(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 44);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_sshfp(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 44);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_sshfp(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 44);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_sshfp(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 44);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_SSHFP_44_C */
diff --git a/lib/dns/rdata/generic/sshfp_44.h b/lib/dns/rdata/generic/sshfp_44.h
new file mode 100644
index 0000000..daea74c
--- /dev/null
+++ b/lib/dns/rdata/generic/sshfp_44.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: sshfp_44.h,v 1.8 2007/06/19 23:47:17 tbox Exp $ */
+
+/*!
+ * \brief Per RFC 4255 */
+
+#ifndef GENERIC_SSHFP_44_H
+#define GENERIC_SSHFP_44_H 1
+
+typedef struct dns_rdata_sshfp {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ isc_uint8_t algorithm;
+ isc_uint8_t digest_type;
+ isc_uint16_t length;
+ unsigned char *digest;
+} dns_rdata_sshfp_t;
+
+#endif /* GENERIC_SSHFP_44_H */
diff --git a/lib/dns/rdata/generic/tkey_249.c b/lib/dns/rdata/generic/tkey_249.c
new file mode 100644
index 0000000..2412c85
--- /dev/null
+++ b/lib/dns/rdata/generic/tkey_249.c
@@ -0,0 +1,555 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: tkey_249.c,v 1.57 2007/06/19 23:47:17 tbox Exp $ */
+
+/*
+ * Reviewed: Thu Mar 16 17:35:30 PST 2000 by halley.
+ */
+
+/* draft-ietf-dnsext-tkey-01.txt */
+
+#ifndef RDATA_GENERIC_TKEY_249_C
+#define RDATA_GENERIC_TKEY_249_C
+
+#define RRTYPE_TKEY_ATTRIBUTES (DNS_RDATATYPEATTR_META)
+
+static inline isc_result_t
+fromtext_tkey(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_rcode_t rcode;
+ dns_name_t name;
+ isc_buffer_t buffer;
+ long i;
+ char *e;
+
+ REQUIRE(type == 249);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ /*
+ * Algorithm.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+
+
+ /*
+ * Inception.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ RETERR(uint32_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Expiration.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ RETERR(uint32_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Mode.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Error.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ if (dns_tsigrcode_fromtext(&rcode, &token.value.as_textregion)
+ != ISC_R_SUCCESS)
+ {
+ i = strtol(DNS_AS_STR(token), &e, 10);
+ if (*e != 0)
+ RETTOK(DNS_R_UNKNOWN);
+ if (i < 0 || i > 0xffff)
+ RETTOK(ISC_R_RANGE);
+ rcode = (dns_rcode_t)i;
+ }
+ RETERR(uint16_tobuffer(rcode, target));
+
+ /*
+ * Key Size.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Key Data.
+ */
+ RETERR(isc_base64_tobuffer(lexer, target, (int)token.value.as_ulong));
+
+ /*
+ * Other Size.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Other Data.
+ */
+ return (isc_base64_tobuffer(lexer, target, (int)token.value.as_ulong));
+}
+
+static inline isc_result_t
+totext_tkey(ARGS_TOTEXT) {
+ isc_region_t sr, dr;
+ char buf[sizeof("4294967295 ")];
+ unsigned long n;
+ dns_name_t name;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+
+ REQUIRE(rdata->type == 249);
+ REQUIRE(rdata->length != 0);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /*
+ * Algorithm.
+ */
+ dns_name_init(&name, NULL);
+ dns_name_init(&prefix, NULL);
+ dns_name_fromregion(&name, &sr);
+ sub = name_prefix(&name, tctx->origin, &prefix);
+ RETERR(dns_name_totext(&prefix, sub, target));
+ RETERR(str_totext(" ", target));
+ isc_region_consume(&sr, name_length(&name));
+
+ /*
+ * Inception.
+ */
+ n = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+ sprintf(buf, "%lu ", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Expiration.
+ */
+ n = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+ sprintf(buf, "%lu ", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Mode.
+ */
+ n = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ sprintf(buf, "%lu ", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Error.
+ */
+ n = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ if (dns_tsigrcode_totext((dns_rcode_t)n, target) == ISC_R_SUCCESS)
+ RETERR(str_totext(" ", target));
+ else {
+ sprintf(buf, "%lu ", n);
+ RETERR(str_totext(buf, target));
+ }
+
+ /*
+ * Key Size.
+ */
+ n = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ sprintf(buf, "%lu", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Key Data.
+ */
+ REQUIRE(n <= sr.length);
+ dr = sr;
+ dr.length = n;
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" (", target));
+ RETERR(str_totext(tctx->linebreak, target));
+ RETERR(isc_base64_totext(&dr, tctx->width - 2,
+ tctx->linebreak, target));
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" ) ", target));
+ else
+ RETERR(str_totext(" ", target));
+ isc_region_consume(&sr, n);
+
+ /*
+ * Other Size.
+ */
+ n = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ sprintf(buf, "%lu", n);
+ RETERR(str_totext(buf, target));
+
+ /*
+ * Other Data.
+ */
+ REQUIRE(n <= sr.length);
+ if (n != 0U) {
+ dr = sr;
+ dr.length = n;
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" (", target));
+ RETERR(str_totext(tctx->linebreak, target));
+ RETERR(isc_base64_totext(&dr, tctx->width - 2,
+ tctx->linebreak, target));
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext(" )", target));
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_tkey(ARGS_FROMWIRE) {
+ isc_region_t sr;
+ unsigned long n;
+ dns_name_t name;
+
+ REQUIRE(type == 249);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_NONE);
+
+ /*
+ * Algorithm.
+ */
+ dns_name_init(&name, NULL);
+ RETERR(dns_name_fromwire(&name, source, dctx, options, target));
+
+ /*
+ * Inception: 4
+ * Expiration: 4
+ * Mode: 2
+ * Error: 2
+ */
+ isc_buffer_activeregion(source, &sr);
+ if (sr.length < 12)
+ return (ISC_R_UNEXPECTEDEND);
+ RETERR(mem_tobuffer(target, sr.base, 12));
+ isc_region_consume(&sr, 12);
+ isc_buffer_forward(source, 12);
+
+ /*
+ * Key Length + Key Data.
+ */
+ if (sr.length < 2)
+ return (ISC_R_UNEXPECTEDEND);
+ n = uint16_fromregion(&sr);
+ if (sr.length < n + 2)
+ return (ISC_R_UNEXPECTEDEND);
+ RETERR(mem_tobuffer(target, sr.base, n + 2));
+ isc_region_consume(&sr, n + 2);
+ isc_buffer_forward(source, n + 2);
+
+ /*
+ * Other Length + Other Data.
+ */
+ if (sr.length < 2)
+ return (ISC_R_UNEXPECTEDEND);
+ n = uint16_fromregion(&sr);
+ if (sr.length < n + 2)
+ return (ISC_R_UNEXPECTEDEND);
+ isc_buffer_forward(source, n + 2);
+ return (mem_tobuffer(target, sr.base, n + 2));
+}
+
+static inline isc_result_t
+towire_tkey(ARGS_TOWIRE) {
+ isc_region_t sr;
+ dns_name_t name;
+ dns_offsets_t offsets;
+
+ REQUIRE(rdata->type == 249);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_NONE);
+ /*
+ * Algorithm.
+ */
+ dns_rdata_toregion(rdata, &sr);
+ dns_name_init(&name, offsets);
+ dns_name_fromregion(&name, &sr);
+ RETERR(dns_name_towire(&name, cctx, target));
+ isc_region_consume(&sr, name_length(&name));
+
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline int
+compare_tkey(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+ dns_name_t name1;
+ dns_name_t name2;
+ int order;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 249);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ /*
+ * Algorithm.
+ */
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+ dns_name_fromregion(&name1, &r1);
+ dns_name_fromregion(&name2, &r2);
+ if ((order = dns_name_rdatacompare(&name1, &name2)) != 0)
+ return (order);
+ isc_region_consume(&r1, name_length(&name1));
+ isc_region_consume(&r2, name_length(&name2));
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_tkey(ARGS_FROMSTRUCT) {
+ dns_rdata_tkey_t *tkey = source;
+
+ REQUIRE(type == 249);
+ REQUIRE(source != NULL);
+ REQUIRE(tkey->common.rdtype == type);
+ REQUIRE(tkey->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ /*
+ * Algorithm Name.
+ */
+ RETERR(name_tobuffer(&tkey->algorithm, target));
+
+ /*
+ * Inception: 32 bits.
+ */
+ RETERR(uint32_tobuffer(tkey->inception, target));
+
+ /*
+ * Expire: 32 bits.
+ */
+ RETERR(uint32_tobuffer(tkey->expire, target));
+
+ /*
+ * Mode: 16 bits.
+ */
+ RETERR(uint16_tobuffer(tkey->mode, target));
+
+ /*
+ * Error: 16 bits.
+ */
+ RETERR(uint16_tobuffer(tkey->error, target));
+
+ /*
+ * Key size: 16 bits.
+ */
+ RETERR(uint16_tobuffer(tkey->keylen, target));
+
+ /*
+ * Key.
+ */
+ RETERR(mem_tobuffer(target, tkey->key, tkey->keylen));
+
+ /*
+ * Other size: 16 bits.
+ */
+ RETERR(uint16_tobuffer(tkey->otherlen, target));
+
+ /*
+ * Other data.
+ */
+ return (mem_tobuffer(target, tkey->other, tkey->otherlen));
+}
+
+static inline isc_result_t
+tostruct_tkey(ARGS_TOSTRUCT) {
+ dns_rdata_tkey_t *tkey = target;
+ dns_name_t alg;
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == 249);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ tkey->common.rdclass = rdata->rdclass;
+ tkey->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&tkey->common, link);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ /*
+ * Algorithm Name.
+ */
+ dns_name_init(&alg, NULL);
+ dns_name_fromregion(&alg, &sr);
+ dns_name_init(&tkey->algorithm, NULL);
+ RETERR(name_duporclone(&alg, mctx, &tkey->algorithm));
+ isc_region_consume(&sr, name_length(&tkey->algorithm));
+
+ /*
+ * Inception.
+ */
+ tkey->inception = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+
+ /*
+ * Expire.
+ */
+ tkey->expire = uint32_fromregion(&sr);
+ isc_region_consume(&sr, 4);
+
+ /*
+ * Mode.
+ */
+ tkey->mode = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+
+ /*
+ * Error.
+ */
+ tkey->error = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+
+ /*
+ * Key size.
+ */
+ tkey->keylen = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+
+ /*
+ * Key.
+ */
+ tkey->key = mem_maybedup(mctx, sr.base, tkey->keylen);
+ if (tkey->key == NULL)
+ goto cleanup;
+ isc_region_consume(&sr, tkey->keylen);
+
+ /*
+ * Other size.
+ */
+ tkey->otherlen = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+
+ /*
+ * Other.
+ */
+ tkey->other = mem_maybedup(mctx, sr.base, tkey->otherlen);
+ if (tkey->other == NULL)
+ goto cleanup;
+
+ tkey->mctx = mctx;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (mctx != NULL)
+ dns_name_free(&tkey->algorithm, mctx);
+ if (mctx != NULL && tkey->key != NULL)
+ isc_mem_free(mctx, tkey->key);
+ return (ISC_R_NOMEMORY);
+}
+
+static inline void
+freestruct_tkey(ARGS_FREESTRUCT) {
+ dns_rdata_tkey_t *tkey = (dns_rdata_tkey_t *) source;
+
+ REQUIRE(source != NULL);
+
+ if (tkey->mctx == NULL)
+ return;
+
+ dns_name_free(&tkey->algorithm, tkey->mctx);
+ if (tkey->key != NULL)
+ isc_mem_free(tkey->mctx, tkey->key);
+ if (tkey->other != NULL)
+ isc_mem_free(tkey->mctx, tkey->other);
+ tkey->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_tkey(ARGS_ADDLDATA) {
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ REQUIRE(rdata->type == 249);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_tkey(ARGS_DIGEST) {
+ UNUSED(rdata);
+ UNUSED(digest);
+ UNUSED(arg);
+
+ REQUIRE(rdata->type == 249);
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static inline isc_boolean_t
+checkowner_tkey(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 249);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_tkey(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 249);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_TKEY_249_C */
diff --git a/lib/dns/rdata/generic/tkey_249.h b/lib/dns/rdata/generic/tkey_249.h
new file mode 100644
index 0000000..34d5646
--- /dev/null
+++ b/lib/dns/rdata/generic/tkey_249.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001, 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+#ifndef GENERIC_TKEY_249_H
+#define GENERIC_TKEY_249_H 1
+
+/* $Id: tkey_249.h,v 1.24 2007/06/19 23:47:17 tbox Exp $ */
+
+/*!
+ * \brief Per draft-ietf-dnsind-tkey-00.txt */
+
+typedef struct dns_rdata_tkey {
+ dns_rdatacommon_t common;
+ isc_mem_t * mctx;
+ dns_name_t algorithm;
+ isc_uint32_t inception;
+ isc_uint32_t expire;
+ isc_uint16_t mode;
+ isc_uint16_t error;
+ isc_uint16_t keylen;
+ unsigned char * key;
+ isc_uint16_t otherlen;
+ unsigned char * other;
+} dns_rdata_tkey_t;
+
+
+#endif /* GENERIC_TKEY_249_H */
diff --git a/lib/dns/rdata/generic/txt_16.c b/lib/dns/rdata/generic/txt_16.c
new file mode 100644
index 0000000..a158a59
--- /dev/null
+++ b/lib/dns/rdata/generic/txt_16.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2004, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: txt_16.c,v 1.45 2008/02/15 23:46:51 tbox Exp $ */
+
+/* Reviewed: Thu Mar 16 15:40:00 PST 2000 by bwelling */
+
+#ifndef RDATA_GENERIC_TXT_16_C
+#define RDATA_GENERIC_TXT_16_C
+
+#define RRTYPE_TXT_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_txt(ARGS_FROMTEXT) {
+ isc_token_t token;
+ int strings;
+
+ REQUIRE(type == 16);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ strings = 0;
+ for (;;) {
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_qstring,
+ ISC_TRUE));
+ if (token.type != isc_tokentype_qstring &&
+ token.type != isc_tokentype_string)
+ break;
+ RETTOK(txt_fromtext(&token.value.as_textregion, target));
+ strings++;
+ }
+ /* Let upper layer handle eol/eof. */
+ isc_lex_ungettoken(lexer, &token);
+ return (strings == 0 ? ISC_R_UNEXPECTEDEND : ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_txt(ARGS_TOTEXT) {
+ isc_region_t region;
+
+ UNUSED(tctx);
+
+ REQUIRE(rdata->type == 16);
+
+ dns_rdata_toregion(rdata, &region);
+
+ while (region.length > 0) {
+ RETERR(txt_totext(&region, target));
+ if (region.length > 0)
+ RETERR(str_totext(" ", target));
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_txt(ARGS_FROMWIRE) {
+ isc_result_t result;
+
+ REQUIRE(type == 16);
+
+ UNUSED(type);
+ UNUSED(dctx);
+ UNUSED(rdclass);
+ UNUSED(options);
+
+ do {
+ result = txt_fromwire(source, target);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ } while (!buffer_empty(source));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+towire_txt(ARGS_TOWIRE) {
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 16);
+
+ UNUSED(cctx);
+
+ isc_buffer_availableregion(target, &region);
+ if (region.length < rdata->length)
+ return (ISC_R_NOSPACE);
+
+ memcpy(region.base, rdata->data, rdata->length);
+ isc_buffer_add(target, rdata->length);
+ return (ISC_R_SUCCESS);
+}
+
+static inline int
+compare_txt(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 16);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_txt(ARGS_FROMSTRUCT) {
+ dns_rdata_txt_t *txt = source;
+ isc_region_t region;
+ isc_uint8_t length;
+
+ REQUIRE(type == 16);
+ REQUIRE(source != NULL);
+ REQUIRE(txt->common.rdtype == type);
+ REQUIRE(txt->common.rdclass == rdclass);
+ REQUIRE(txt->txt != NULL && txt->txt_len != 0);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ region.base = txt->txt;
+ region.length = txt->txt_len;
+ while (region.length > 0) {
+ length = uint8_fromregion(&region);
+ isc_region_consume(&region, 1);
+ if (region.length < length)
+ return (ISC_R_UNEXPECTEDEND);
+ isc_region_consume(&region, length);
+ }
+
+ return (mem_tobuffer(target, txt->txt, txt->txt_len));
+}
+
+static inline isc_result_t
+tostruct_txt(ARGS_TOSTRUCT) {
+ dns_rdata_txt_t *txt = target;
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 16);
+ REQUIRE(target != NULL);
+
+ txt->common.rdclass = rdata->rdclass;
+ txt->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&txt->common, link);
+
+ dns_rdata_toregion(rdata, &r);
+ txt->txt_len = r.length;
+ txt->txt = mem_maybedup(mctx, r.base, r.length);
+ if (txt->txt == NULL)
+ return (ISC_R_NOMEMORY);
+
+ txt->offset = 0;
+ txt->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_txt(ARGS_FREESTRUCT) {
+ dns_rdata_txt_t *txt = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(txt->common.rdtype == 16);
+
+ if (txt->mctx == NULL)
+ return;
+
+ if (txt->txt != NULL)
+ isc_mem_free(txt->mctx, txt->txt);
+ txt->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_txt(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 16);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_txt(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 16);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_txt(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 16);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_txt(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 16);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_TXT_16_C */
diff --git a/lib/dns/rdata/generic/txt_16.h b/lib/dns/rdata/generic/txt_16.h
new file mode 100644
index 0000000..fc46486
--- /dev/null
+++ b/lib/dns/rdata/generic/txt_16.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* */
+#ifndef GENERIC_TXT_16_H
+#define GENERIC_TXT_16_H 1
+
+/* $Id: txt_16.h,v 1.28 2007/06/19 23:47:17 tbox Exp $ */
+
+typedef struct dns_rdata_txt_string {
+ isc_uint8_t length;
+ unsigned char *data;
+} dns_rdata_txt_string_t;
+
+typedef struct dns_rdata_txt {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ unsigned char *txt;
+ isc_uint16_t txt_len;
+ /* private */
+ isc_uint16_t offset;
+} dns_rdata_txt_t;
+
+/*
+ * ISC_LANG_BEGINDECLS and ISC_LANG_ENDDECLS are already done
+ * via rdatastructpre.h and rdatastructsuf.h.
+ */
+
+isc_result_t
+dns_rdata_txt_first(dns_rdata_txt_t *);
+
+isc_result_t
+dns_rdata_txt_next(dns_rdata_txt_t *);
+
+isc_result_t
+dns_rdata_txt_current(dns_rdata_txt_t *, dns_rdata_txt_string_t *);
+
+#endif /* GENERIC_TXT_16_H */
diff --git a/lib/dns/rdata/generic/unspec_103.c b/lib/dns/rdata/generic/unspec_103.c
new file mode 100644
index 0000000..384863e
--- /dev/null
+++ b/lib/dns/rdata/generic/unspec_103.c
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: unspec_103.c,v 1.35 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef RDATA_GENERIC_UNSPEC_103_C
+#define RDATA_GENERIC_UNSPEC_103_C
+
+#define RRTYPE_UNSPEC_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_unspec(ARGS_FROMTEXT) {
+
+ REQUIRE(type == 103);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ return (atob_tobuffer(lexer, target));
+}
+
+static inline isc_result_t
+totext_unspec(ARGS_TOTEXT) {
+
+ REQUIRE(rdata->type == 103);
+
+ UNUSED(tctx);
+
+ return (btoa_totext(rdata->data, rdata->length, target));
+}
+
+static inline isc_result_t
+fromwire_unspec(ARGS_FROMWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(type == 103);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(dctx);
+ UNUSED(options);
+
+ isc_buffer_activeregion(source, &sr);
+ isc_buffer_forward(source, sr.length);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline isc_result_t
+towire_unspec(ARGS_TOWIRE) {
+
+ REQUIRE(rdata->type == 103);
+
+ UNUSED(cctx);
+
+ return (mem_tobuffer(target, rdata->data, rdata->length));
+}
+
+static inline int
+compare_unspec(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 103);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_unspec(ARGS_FROMSTRUCT) {
+ dns_rdata_unspec_t *unspec = source;
+
+ REQUIRE(type == 103);
+ REQUIRE(source != NULL);
+ REQUIRE(unspec->common.rdtype == type);
+ REQUIRE(unspec->common.rdclass == rdclass);
+ REQUIRE(unspec->data != NULL || unspec->datalen == 0);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ return (mem_tobuffer(target, unspec->data, unspec->datalen));
+}
+
+static inline isc_result_t
+tostruct_unspec(ARGS_TOSTRUCT) {
+ dns_rdata_unspec_t *unspec = target;
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 103);
+ REQUIRE(target != NULL);
+
+ unspec->common.rdclass = rdata->rdclass;
+ unspec->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&unspec->common, link);
+
+ dns_rdata_toregion(rdata, &r);
+ unspec->datalen = r.length;
+ unspec->data = mem_maybedup(mctx, r.base, r.length);
+ if (unspec->data == NULL)
+ return (ISC_R_NOMEMORY);
+
+ unspec->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_unspec(ARGS_FREESTRUCT) {
+ dns_rdata_unspec_t *unspec = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(unspec->common.rdtype == 103);
+
+ if (unspec->mctx == NULL)
+ return;
+
+ if (unspec->data != NULL)
+ isc_mem_free(unspec->mctx, unspec->data);
+ unspec->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_unspec(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 103);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_unspec(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 103);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_unspec(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 103);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_unspec(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 103);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_UNSPEC_103_C */
diff --git a/lib/dns/rdata/generic/unspec_103.h b/lib/dns/rdata/generic/unspec_103.h
new file mode 100644
index 0000000..4b2d310
--- /dev/null
+++ b/lib/dns/rdata/generic/unspec_103.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* */
+#ifndef GENERIC_UNSPEC_103_H
+#define GENERIC_UNSPEC_103_H 1
+
+/* $Id: unspec_103.h,v 1.17 2007/06/19 23:47:17 tbox Exp $ */
+
+typedef struct dns_rdata_unspec_t {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ unsigned char *data;
+ isc_uint16_t datalen;
+} dns_rdata_unspec_t;
+
+#endif /* GENERIC_UNSPEC_103_H */
diff --git a/lib/dns/rdata/generic/x25_19.c b/lib/dns/rdata/generic/x25_19.c
new file mode 100644
index 0000000..c496aaf
--- /dev/null
+++ b/lib/dns/rdata/generic/x25_19.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: x25_19.c,v 1.39 2007/06/19 23:47:17 tbox Exp $ */
+
+/* Reviewed: Thu Mar 16 16:15:57 PST 2000 by bwelling */
+
+/* RFC1183 */
+
+#ifndef RDATA_GENERIC_X25_19_C
+#define RDATA_GENERIC_X25_19_C
+
+#define RRTYPE_X25_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_x25(ARGS_FROMTEXT) {
+ isc_token_t token;
+ unsigned int i;
+
+ REQUIRE(type == 19);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_qstring,
+ ISC_FALSE));
+ if (token.value.as_textregion.length < 4)
+ RETTOK(DNS_R_SYNTAX);
+ for (i = 0; i < token.value.as_textregion.length; i++)
+ if (!isdigit(token.value.as_textregion.base[i] & 0xff))
+ RETTOK(ISC_R_RANGE);
+ RETTOK(txt_fromtext(&token.value.as_textregion, target));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_x25(ARGS_TOTEXT) {
+ isc_region_t region;
+
+ UNUSED(tctx);
+
+ REQUIRE(rdata->type == 19);
+ REQUIRE(rdata->length != 0);
+
+ dns_rdata_toregion(rdata, &region);
+ return (txt_totext(&region, target));
+}
+
+static inline isc_result_t
+fromwire_x25(ARGS_FROMWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(type == 19);
+
+ UNUSED(type);
+ UNUSED(dctx);
+ UNUSED(rdclass);
+ UNUSED(options);
+
+ isc_buffer_activeregion(source, &sr);
+ if (sr.length < 5)
+ return (DNS_R_FORMERR);
+ return (txt_fromwire(source, target));
+}
+
+static inline isc_result_t
+towire_x25(ARGS_TOWIRE) {
+ UNUSED(cctx);
+
+ REQUIRE(rdata->type == 19);
+ REQUIRE(rdata->length != 0);
+
+ return (mem_tobuffer(target, rdata->data, rdata->length));
+}
+
+static inline int
+compare_x25(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 19);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_x25(ARGS_FROMSTRUCT) {
+ dns_rdata_x25_t *x25 = source;
+ isc_uint8_t i;
+
+ REQUIRE(type == 19);
+ REQUIRE(source != NULL);
+ REQUIRE(x25->common.rdtype == type);
+ REQUIRE(x25->common.rdclass == rdclass);
+ REQUIRE(x25->x25 != NULL && x25->x25_len != 0);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ if (x25->x25_len < 4)
+ return (ISC_R_RANGE);
+
+ for (i = 0; i < x25->x25_len; i++)
+ if (!isdigit(x25->x25[i] & 0xff))
+ return (ISC_R_RANGE);
+
+ RETERR(uint8_tobuffer(x25->x25_len, target));
+ return (mem_tobuffer(target, x25->x25, x25->x25_len));
+}
+
+static inline isc_result_t
+tostruct_x25(ARGS_TOSTRUCT) {
+ dns_rdata_x25_t *x25 = target;
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 19);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ x25->common.rdclass = rdata->rdclass;
+ x25->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&x25->common, link);
+
+ dns_rdata_toregion(rdata, &r);
+ x25->x25_len = uint8_fromregion(&r);
+ isc_region_consume(&r, 1);
+ x25->x25 = mem_maybedup(mctx, r.base, x25->x25_len);
+ if (x25->x25 == NULL)
+ return (ISC_R_NOMEMORY);
+
+ x25->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_x25(ARGS_FREESTRUCT) {
+ dns_rdata_x25_t *x25 = source;
+ REQUIRE(source != NULL);
+ REQUIRE(x25->common.rdtype == 19);
+
+ if (x25->mctx == NULL)
+ return;
+
+ if (x25->x25 != NULL)
+ isc_mem_free(x25->mctx, x25->x25);
+ x25->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_x25(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 19);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_x25(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 19);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_x25(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 19);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_x25(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 19);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_GENERIC_X25_19_C */
diff --git a/lib/dns/rdata/generic/x25_19.h b/lib/dns/rdata/generic/x25_19.h
new file mode 100644
index 0000000..5ebc230
--- /dev/null
+++ b/lib/dns/rdata/generic/x25_19.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+#ifndef GENERIC_X25_19_H
+#define GENERIC_X25_19_H 1
+
+/* $Id: x25_19.h,v 1.18 2007/06/19 23:47:17 tbox Exp $ */
+
+/*!
+ * \brief Per RFC1183 */
+
+typedef struct dns_rdata_x25 {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ unsigned char *x25;
+ isc_uint8_t x25_len;
+} dns_rdata_x25_t;
+
+#endif /* GENERIC_X25_19_H */
diff --git a/lib/dns/rdata/hs_4/a_1.c b/lib/dns/rdata/hs_4/a_1.c
new file mode 100644
index 0000000..487e8bc
--- /dev/null
+++ b/lib/dns/rdata/hs_4/a_1.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: a_1.c,v 1.31 2007/06/19 23:47:17 tbox Exp $ */
+
+/* reviewed: Thu Mar 16 15:58:36 PST 2000 by brister */
+
+#ifndef RDATA_HS_4_A_1_C
+#define RDATA_HS_4_A_1_C
+
+#include <isc/net.h>
+
+#define RRTYPE_A_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_hs_a(ARGS_FROMTEXT) {
+ isc_token_t token;
+ struct in_addr addr;
+ isc_region_t region;
+
+ REQUIRE(type == 1);
+ REQUIRE(rdclass == 4);
+
+ UNUSED(type);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(rdclass);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+
+ if (getquad(DNS_AS_STR(token), &addr, lexer, callbacks) != 1)
+ RETTOK(DNS_R_BADDOTTEDQUAD);
+ isc_buffer_availableregion(target, &region);
+ if (region.length < 4)
+ return (ISC_R_NOSPACE);
+ memcpy(region.base, &addr, 4);
+ isc_buffer_add(target, 4);
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_hs_a(ARGS_TOTEXT) {
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 1);
+ REQUIRE(rdata->rdclass == 4);
+ REQUIRE(rdata->length == 4);
+
+ UNUSED(tctx);
+
+ dns_rdata_toregion(rdata, &region);
+ return (inet_totext(AF_INET, &region, target));
+}
+
+static inline isc_result_t
+fromwire_hs_a(ARGS_FROMWIRE) {
+ isc_region_t sregion;
+ isc_region_t tregion;
+
+ REQUIRE(type == 1);
+ REQUIRE(rdclass == 4);
+
+ UNUSED(type);
+ UNUSED(dctx);
+ UNUSED(options);
+ UNUSED(rdclass);
+
+ isc_buffer_activeregion(source, &sregion);
+ isc_buffer_availableregion(target, &tregion);
+ if (sregion.length < 4)
+ return (ISC_R_UNEXPECTEDEND);
+ if (tregion.length < 4)
+ return (ISC_R_NOSPACE);
+
+ memcpy(tregion.base, sregion.base, 4);
+ isc_buffer_forward(source, 4);
+ isc_buffer_add(target, 4);
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+towire_hs_a(ARGS_TOWIRE) {
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 1);
+ REQUIRE(rdata->rdclass == 4);
+ REQUIRE(rdata->length == 4);
+
+ UNUSED(cctx);
+
+ isc_buffer_availableregion(target, &region);
+ if (region.length < rdata->length)
+ return (ISC_R_NOSPACE);
+ memcpy(region.base, rdata->data, rdata->length);
+ isc_buffer_add(target, 4);
+ return (ISC_R_SUCCESS);
+}
+
+static inline int
+compare_hs_a(ARGS_COMPARE) {
+ int order;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 1);
+ REQUIRE(rdata1->rdclass == 4);
+ REQUIRE(rdata1->length == 4);
+ REQUIRE(rdata2->length == 4);
+
+ order = memcmp(rdata1->data, rdata2->data, 4);
+ if (order != 0)
+ order = (order < 0) ? -1 : 1;
+
+ return (order);
+}
+
+static inline isc_result_t
+fromstruct_hs_a(ARGS_FROMSTRUCT) {
+ dns_rdata_hs_a_t *a = source;
+ isc_uint32_t n;
+
+ REQUIRE(type == 1);
+ REQUIRE(rdclass == 4);
+ REQUIRE(source != NULL);
+ REQUIRE(a->common.rdtype == type);
+ REQUIRE(a->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ n = ntohl(a->in_addr.s_addr);
+
+ return (uint32_tobuffer(n, target));
+}
+
+static inline isc_result_t
+tostruct_hs_a(ARGS_TOSTRUCT) {
+ dns_rdata_hs_a_t *a = target;
+ isc_uint32_t n;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 1);
+ REQUIRE(rdata->rdclass == 4);
+ REQUIRE(rdata->length == 4);
+
+ UNUSED(mctx);
+
+ a->common.rdclass = rdata->rdclass;
+ a->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&a->common, link);
+
+ dns_rdata_toregion(rdata, &region);
+ n = uint32_fromregion(&region);
+ a->in_addr.s_addr = htonl(n);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_hs_a(ARGS_FREESTRUCT) {
+ UNUSED(source);
+
+ REQUIRE(source != NULL);
+}
+
+static inline isc_result_t
+additionaldata_hs_a(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 1);
+ REQUIRE(rdata->rdclass == 4);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_hs_a(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 1);
+ REQUIRE(rdata->rdclass == 4);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_hs_a(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 1);
+ REQUIRE(rdclass == 4);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_hs_a(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 1);
+ REQUIRE(rdata->rdclass == 4);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_HS_4_A_1_C */
diff --git a/lib/dns/rdata/hs_4/a_1.h b/lib/dns/rdata/hs_4/a_1.h
new file mode 100644
index 0000000..dee812f
--- /dev/null
+++ b/lib/dns/rdata/hs_4/a_1.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* */
+#ifndef HS_4_A_1_H
+#define HS_4_A_1_H 1
+
+/* $Id: a_1.h,v 1.12 2007/06/19 23:47:17 tbox Exp $ */
+
+typedef struct dns_rdata_hs_a {
+ dns_rdatacommon_t common;
+ struct in_addr in_addr;
+} dns_rdata_hs_a_t;
+
+#endif /* HS_4_A_1_H */
diff --git a/lib/dns/rdata/in_1/a6_38.c b/lib/dns/rdata/in_1/a6_38.c
new file mode 100644
index 0000000..d4d42bb
--- /dev/null
+++ b/lib/dns/rdata/in_1/a6_38.c
@@ -0,0 +1,461 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: a6_38.c,v 1.54 2007/06/19 23:47:17 tbox Exp $ */
+
+/* RFC2874 */
+
+#ifndef RDATA_IN_1_A6_28_C
+#define RDATA_IN_1_A6_28_C
+
+#include <isc/net.h>
+
+#define RRTYPE_A6_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_in_a6(ARGS_FROMTEXT) {
+ isc_token_t token;
+ unsigned char addr[16];
+ unsigned char prefixlen;
+ unsigned char octets;
+ unsigned char mask;
+ dns_name_t name;
+ isc_buffer_t buffer;
+ isc_boolean_t ok;
+
+ REQUIRE(type == 38);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ /*
+ * Prefix length.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 128U)
+ RETTOK(ISC_R_RANGE);
+
+ prefixlen = (unsigned char)token.value.as_ulong;
+ RETERR(mem_tobuffer(target, &prefixlen, 1));
+
+ /*
+ * Suffix.
+ */
+ if (prefixlen != 128) {
+ /*
+ * Prefix 0..127.
+ */
+ octets = prefixlen/8;
+ /*
+ * Octets 0..15.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_string,
+ ISC_FALSE));
+ if (inet_pton(AF_INET6, DNS_AS_STR(token), addr) != 1)
+ RETTOK(DNS_R_BADAAAA);
+ mask = 0xff >> (prefixlen % 8);
+ addr[octets] &= mask;
+ RETERR(mem_tobuffer(target, &addr[octets], 16 - octets));
+ }
+
+ if (prefixlen == 0)
+ return (ISC_R_SUCCESS);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+ ok = ISC_TRUE;
+ if ((options & DNS_RDATA_CHECKNAMES) != 0)
+ ok = dns_name_ishostname(&name, ISC_FALSE);
+ if (!ok && (options & DNS_RDATA_CHECKNAMESFAIL) != 0)
+ RETTOK(DNS_R_BADNAME);
+ if (!ok && callbacks != NULL)
+ warn_badname(&name, lexer, callbacks);
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_in_a6(ARGS_TOTEXT) {
+ isc_region_t sr, ar;
+ unsigned char addr[16];
+ unsigned char prefixlen;
+ unsigned char octets;
+ unsigned char mask;
+ char buf[sizeof("128")];
+ dns_name_t name;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+
+ REQUIRE(rdata->type == 38);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(rdata->length != 0);
+
+ dns_rdata_toregion(rdata, &sr);
+ prefixlen = sr.base[0];
+ INSIST(prefixlen <= 128);
+ isc_region_consume(&sr, 1);
+ sprintf(buf, "%u", prefixlen);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ if (prefixlen != 128) {
+ octets = prefixlen/8;
+ memset(addr, 0, sizeof(addr));
+ memcpy(&addr[octets], sr.base, 16 - octets);
+ mask = 0xff >> (prefixlen % 8);
+ addr[octets] &= mask;
+ ar.base = addr;
+ ar.length = sizeof(addr);
+ RETERR(inet_totext(AF_INET6, &ar, target));
+ isc_region_consume(&sr, 16 - octets);
+ }
+
+ if (prefixlen == 0)
+ return (ISC_R_SUCCESS);
+
+ RETERR(str_totext(" ", target));
+ dns_name_init(&name, NULL);
+ dns_name_init(&prefix, NULL);
+ dns_name_fromregion(&name, &sr);
+ sub = name_prefix(&name, tctx->origin, &prefix);
+ return (dns_name_totext(&prefix, sub, target));
+}
+
+static inline isc_result_t
+fromwire_in_a6(ARGS_FROMWIRE) {
+ isc_region_t sr;
+ unsigned char prefixlen;
+ unsigned char octets;
+ unsigned char mask;
+ dns_name_t name;
+
+ REQUIRE(type == 38);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_NONE);
+
+ isc_buffer_activeregion(source, &sr);
+ /*
+ * Prefix length.
+ */
+ if (sr.length < 1)
+ return (ISC_R_UNEXPECTEDEND);
+ prefixlen = sr.base[0];
+ if (prefixlen > 128)
+ return (ISC_R_RANGE);
+ isc_region_consume(&sr, 1);
+ RETERR(mem_tobuffer(target, &prefixlen, 1));
+ isc_buffer_forward(source, 1);
+
+ /*
+ * Suffix.
+ */
+ if (prefixlen != 128) {
+ octets = 16 - prefixlen / 8;
+ if (sr.length < octets)
+ return (ISC_R_UNEXPECTEDEND);
+ mask = 0xff >> (prefixlen % 8);
+ sr.base[0] &= mask; /* Ensure pad bits are zero. */
+ RETERR(mem_tobuffer(target, sr.base, octets));
+ isc_buffer_forward(source, octets);
+ }
+
+ if (prefixlen == 0)
+ return (ISC_R_SUCCESS);
+
+ dns_name_init(&name, NULL);
+ return (dns_name_fromwire(&name, source, dctx, options, target));
+}
+
+static inline isc_result_t
+towire_in_a6(ARGS_TOWIRE) {
+ isc_region_t sr;
+ dns_name_t name;
+ dns_offsets_t offsets;
+ unsigned char prefixlen;
+ unsigned char octets;
+
+ REQUIRE(rdata->type == 38);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_NONE);
+ dns_rdata_toregion(rdata, &sr);
+ prefixlen = sr.base[0];
+ INSIST(prefixlen <= 128);
+
+ octets = 1 + 16 - prefixlen / 8;
+ RETERR(mem_tobuffer(target, sr.base, octets));
+ isc_region_consume(&sr, octets);
+
+ if (prefixlen == 0)
+ return (ISC_R_SUCCESS);
+
+ dns_name_init(&name, offsets);
+ dns_name_fromregion(&name, &sr);
+ return (dns_name_towire(&name, cctx, target));
+}
+
+static inline int
+compare_in_a6(ARGS_COMPARE) {
+ int order;
+ unsigned char prefixlen1, prefixlen2;
+ unsigned char octets;
+ dns_name_t name1;
+ dns_name_t name2;
+ isc_region_t region1;
+ isc_region_t region2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 38);
+ REQUIRE(rdata1->rdclass == 1);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+ prefixlen1 = region1.base[0];
+ prefixlen2 = region2.base[0];
+ isc_region_consume(&region1, 1);
+ isc_region_consume(&region2, 1);
+ if (prefixlen1 < prefixlen2)
+ return (-1);
+ else if (prefixlen1 > prefixlen2)
+ return (1);
+ /*
+ * Prefix lengths are equal.
+ */
+ octets = 16 - prefixlen1 / 8;
+
+ if (octets > 0) {
+ order = memcmp(region1.base, region2.base, octets);
+ if (order < 0)
+ return (-1);
+ else if (order > 0)
+ return (1);
+ /*
+ * Address suffixes are equal.
+ */
+ if (prefixlen1 == 0)
+ return (order);
+ isc_region_consume(&region1, octets);
+ isc_region_consume(&region2, octets);
+ }
+
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+ dns_name_fromregion(&name1, &region1);
+ dns_name_fromregion(&name2, &region2);
+ return (dns_name_rdatacompare(&name1, &name2));
+}
+
+static inline isc_result_t
+fromstruct_in_a6(ARGS_FROMSTRUCT) {
+ dns_rdata_in_a6_t *a6 = source;
+ isc_region_t region;
+ int octets;
+ isc_uint8_t bits;
+ isc_uint8_t first;
+ isc_uint8_t mask;
+
+ REQUIRE(type == 38);
+ REQUIRE(rdclass == 1);
+ REQUIRE(source != NULL);
+ REQUIRE(a6->common.rdtype == type);
+ REQUIRE(a6->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ if (a6->prefixlen > 128)
+ return (ISC_R_RANGE);
+
+ RETERR(uint8_tobuffer(a6->prefixlen, target));
+
+ /* Suffix */
+ if (a6->prefixlen != 128) {
+ octets = 16 - a6->prefixlen / 8;
+ bits = a6->prefixlen % 8;
+ if (bits != 0) {
+ mask = 0xffU >> bits;
+ first = a6->in6_addr.s6_addr[16 - octets] & mask;
+ RETERR(uint8_tobuffer(first, target));
+ octets--;
+ }
+ if (octets > 0)
+ RETERR(mem_tobuffer(target,
+ a6->in6_addr.s6_addr + 16 - octets,
+ octets));
+ }
+
+ if (a6->prefixlen == 0)
+ return (ISC_R_SUCCESS);
+ dns_name_toregion(&a6->prefix, &region);
+ return (isc_buffer_copyregion(target, &region));
+}
+
+static inline isc_result_t
+tostruct_in_a6(ARGS_TOSTRUCT) {
+ dns_rdata_in_a6_t *a6 = target;
+ unsigned char octets;
+ dns_name_t name;
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 38);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ a6->common.rdclass = rdata->rdclass;
+ a6->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&a6->common, link);
+
+ dns_rdata_toregion(rdata, &r);
+
+ a6->prefixlen = uint8_fromregion(&r);
+ isc_region_consume(&r, 1);
+ memset(a6->in6_addr.s6_addr, 0, sizeof(a6->in6_addr.s6_addr));
+
+ /*
+ * Suffix.
+ */
+ if (a6->prefixlen != 128) {
+ octets = 16 - a6->prefixlen / 8;
+ INSIST(r.length >= octets);
+ memcpy(a6->in6_addr.s6_addr + 16 - octets, r.base, octets);
+ isc_region_consume(&r, octets);
+ }
+
+ /*
+ * Prefix.
+ */
+ dns_name_init(&a6->prefix, NULL);
+ if (a6->prefixlen != 0) {
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+ RETERR(name_duporclone(&name, mctx, &a6->prefix));
+ }
+ a6->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_in_a6(ARGS_FREESTRUCT) {
+ dns_rdata_in_a6_t *a6 = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(a6->common.rdclass == 1);
+ REQUIRE(a6->common.rdtype == 38);
+
+ if (a6->mctx == NULL)
+ return;
+
+ if (dns_name_dynamic(&a6->prefix))
+ dns_name_free(&a6->prefix, a6->mctx);
+ a6->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_in_a6(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 38);
+ REQUIRE(rdata->rdclass == 1);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_in_a6(ARGS_DIGEST) {
+ isc_region_t r1, r2;
+ unsigned char prefixlen, octets;
+ isc_result_t result;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 38);
+ REQUIRE(rdata->rdclass == 1);
+
+ dns_rdata_toregion(rdata, &r1);
+ r2 = r1;
+ prefixlen = r1.base[0];
+ octets = 1 + 16 - prefixlen / 8;
+
+ r1.length = octets;
+ result = (digest)(arg, &r1);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (prefixlen == 0)
+ return (ISC_R_SUCCESS);
+
+ isc_region_consume(&r2, octets);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r2);
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline isc_boolean_t
+checkowner_in_a6(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 38);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ return (dns_name_ishostname(name, wildcard));
+}
+
+static inline isc_boolean_t
+checknames_in_a6(ARGS_CHECKNAMES) {
+ isc_region_t region;
+ dns_name_t name;
+ unsigned int prefixlen;
+
+ REQUIRE(rdata->type == 38);
+ REQUIRE(rdata->rdclass == 1);
+
+ UNUSED(owner);
+
+ dns_rdata_toregion(rdata, &region);
+ prefixlen = uint8_fromregion(&region);
+ if (prefixlen == 0)
+ return (ISC_TRUE);
+ isc_region_consume(&region, 1 + 16 - prefixlen / 8);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &region);
+ if (!dns_name_ishostname(&name, ISC_FALSE)) {
+ if (bad != NULL)
+ dns_name_clone(&name, bad);
+ return (ISC_FALSE);
+ }
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_IN_1_A6_38_C */
diff --git a/lib/dns/rdata/in_1/a6_38.h b/lib/dns/rdata/in_1/a6_38.h
new file mode 100644
index 0000000..75e53f1
--- /dev/null
+++ b/lib/dns/rdata/in_1/a6_38.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+#ifndef IN_1_A6_38_H
+#define IN_1_A6_38_H 1
+
+/* $Id: a6_38.h,v 1.24 2007/06/19 23:47:17 tbox Exp $ */
+
+/*!
+ * \brief Per RFC2874 */
+
+typedef struct dns_rdata_in_a6 {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ dns_name_t prefix;
+ isc_uint8_t prefixlen;
+ struct in6_addr in6_addr;
+} dns_rdata_in_a6_t;
+
+#endif /* IN_1_A6_38_H */
diff --git a/lib/dns/rdata/in_1/a_1.c b/lib/dns/rdata/in_1/a_1.c
new file mode 100644
index 0000000..d7644bc
--- /dev/null
+++ b/lib/dns/rdata/in_1/a_1.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: a_1.c,v 1.53 2007/06/19 23:47:17 tbox Exp $ */
+
+/* Reviewed: Thu Mar 16 16:52:50 PST 2000 by bwelling */
+
+#ifndef RDATA_IN_1_A_1_C
+#define RDATA_IN_1_A_1_C
+
+#include <string.h>
+
+#include <isc/net.h>
+
+#define RRTYPE_A_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_in_a(ARGS_FROMTEXT) {
+ isc_token_t token;
+ struct in_addr addr;
+ isc_region_t region;
+
+ REQUIRE(type == 1);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(rdclass);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+
+ if (getquad(DNS_AS_STR(token), &addr, lexer, callbacks) != 1)
+ RETTOK(DNS_R_BADDOTTEDQUAD);
+ isc_buffer_availableregion(target, &region);
+ if (region.length < 4)
+ return (ISC_R_NOSPACE);
+ memcpy(region.base, &addr, 4);
+ isc_buffer_add(target, 4);
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_in_a(ARGS_TOTEXT) {
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 1);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(rdata->length == 4);
+
+ UNUSED(tctx);
+
+ dns_rdata_toregion(rdata, &region);
+ return (inet_totext(AF_INET, &region, target));
+}
+
+static inline isc_result_t
+fromwire_in_a(ARGS_FROMWIRE) {
+ isc_region_t sregion;
+ isc_region_t tregion;
+
+ REQUIRE(type == 1);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(dctx);
+ UNUSED(options);
+ UNUSED(rdclass);
+
+ isc_buffer_activeregion(source, &sregion);
+ isc_buffer_availableregion(target, &tregion);
+ if (sregion.length < 4)
+ return (ISC_R_UNEXPECTEDEND);
+ if (tregion.length < 4)
+ return (ISC_R_NOSPACE);
+
+ memcpy(tregion.base, sregion.base, 4);
+ isc_buffer_forward(source, 4);
+ isc_buffer_add(target, 4);
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+towire_in_a(ARGS_TOWIRE) {
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 1);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(rdata->length == 4);
+
+ UNUSED(cctx);
+
+ isc_buffer_availableregion(target, &region);
+ if (region.length < rdata->length)
+ return (ISC_R_NOSPACE);
+ memcpy(region.base, rdata->data, rdata->length);
+ isc_buffer_add(target, 4);
+ return (ISC_R_SUCCESS);
+}
+
+static inline int
+compare_in_a(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 1);
+ REQUIRE(rdata1->rdclass == 1);
+ REQUIRE(rdata1->length == 4);
+ REQUIRE(rdata2->length == 4);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_in_a(ARGS_FROMSTRUCT) {
+ dns_rdata_in_a_t *a = source;
+ isc_uint32_t n;
+
+ REQUIRE(type == 1);
+ REQUIRE(rdclass == 1);
+ REQUIRE(source != NULL);
+ REQUIRE(a->common.rdtype == type);
+ REQUIRE(a->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ n = ntohl(a->in_addr.s_addr);
+
+ return (uint32_tobuffer(n, target));
+}
+
+
+static inline isc_result_t
+tostruct_in_a(ARGS_TOSTRUCT) {
+ dns_rdata_in_a_t *a = target;
+ isc_uint32_t n;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 1);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(rdata->length == 4);
+
+ UNUSED(mctx);
+
+ a->common.rdclass = rdata->rdclass;
+ a->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&a->common, link);
+
+ dns_rdata_toregion(rdata, &region);
+ n = uint32_fromregion(&region);
+ a->in_addr.s_addr = htonl(n);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_in_a(ARGS_FREESTRUCT) {
+ dns_rdata_in_a_t *a = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(a->common.rdtype == 1);
+ REQUIRE(a->common.rdclass == 1);
+
+ UNUSED(a);
+}
+
+static inline isc_result_t
+additionaldata_in_a(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 1);
+ REQUIRE(rdata->rdclass == 1);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_in_a(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 1);
+ REQUIRE(rdata->rdclass == 1);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_in_a(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 1);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ return (dns_name_ishostname(name, wildcard));
+}
+
+static inline isc_boolean_t
+checknames_in_a(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 1);
+ REQUIRE(rdata->rdclass == 1);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_IN_1_A_1_C */
diff --git a/lib/dns/rdata/in_1/a_1.h b/lib/dns/rdata/in_1/a_1.h
new file mode 100644
index 0000000..c192d1a
--- /dev/null
+++ b/lib/dns/rdata/in_1/a_1.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* */
+#ifndef IN_1_A_1_H
+#define IN_1_A_1_H 1
+
+/* $Id: a_1.h,v 1.28 2007/06/19 23:47:17 tbox Exp $ */
+
+typedef struct dns_rdata_in_a {
+ dns_rdatacommon_t common;
+ struct in_addr in_addr;
+} dns_rdata_in_a_t;
+
+#endif /* IN_1_A_1_H */
diff --git a/lib/dns/rdata/in_1/aaaa_28.c b/lib/dns/rdata/in_1/aaaa_28.c
new file mode 100644
index 0000000..d0503a9
--- /dev/null
+++ b/lib/dns/rdata/in_1/aaaa_28.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: aaaa_28.c,v 1.45 2007/06/19 23:47:17 tbox Exp $ */
+
+/* Reviewed: Thu Mar 16 16:52:50 PST 2000 by bwelling */
+
+/* RFC1886 */
+
+#ifndef RDATA_IN_1_AAAA_28_C
+#define RDATA_IN_1_AAAA_28_C
+
+#include <isc/net.h>
+
+#define RRTYPE_AAAA_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_in_aaaa(ARGS_FROMTEXT) {
+ isc_token_t token;
+ unsigned char addr[16];
+ isc_region_t region;
+
+ REQUIRE(type == 28);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+
+ if (inet_pton(AF_INET6, DNS_AS_STR(token), addr) != 1)
+ RETTOK(DNS_R_BADAAAA);
+ isc_buffer_availableregion(target, &region);
+ if (region.length < 16)
+ return (ISC_R_NOSPACE);
+ memcpy(region.base, addr, 16);
+ isc_buffer_add(target, 16);
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_in_aaaa(ARGS_TOTEXT) {
+ isc_region_t region;
+
+ UNUSED(tctx);
+
+ REQUIRE(rdata->type == 28);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(rdata->length == 16);
+
+ dns_rdata_toregion(rdata, &region);
+ return (inet_totext(AF_INET6, &region, target));
+}
+
+static inline isc_result_t
+fromwire_in_aaaa(ARGS_FROMWIRE) {
+ isc_region_t sregion;
+ isc_region_t tregion;
+
+ REQUIRE(type == 28);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(dctx);
+ UNUSED(options);
+ UNUSED(rdclass);
+
+ isc_buffer_activeregion(source, &sregion);
+ isc_buffer_availableregion(target, &tregion);
+ if (sregion.length < 16)
+ return (ISC_R_UNEXPECTEDEND);
+ if (tregion.length < 16)
+ return (ISC_R_NOSPACE);
+
+ memcpy(tregion.base, sregion.base, 16);
+ isc_buffer_forward(source, 16);
+ isc_buffer_add(target, 16);
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+towire_in_aaaa(ARGS_TOWIRE) {
+ isc_region_t region;
+
+ UNUSED(cctx);
+
+ REQUIRE(rdata->type == 28);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(rdata->length == 16);
+
+ isc_buffer_availableregion(target, &region);
+ if (region.length < rdata->length)
+ return (ISC_R_NOSPACE);
+ memcpy(region.base, rdata->data, rdata->length);
+ isc_buffer_add(target, 16);
+ return (ISC_R_SUCCESS);
+}
+
+static inline int
+compare_in_aaaa(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 28);
+ REQUIRE(rdata1->rdclass == 1);
+ REQUIRE(rdata1->length == 16);
+ REQUIRE(rdata2->length == 16);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_in_aaaa(ARGS_FROMSTRUCT) {
+ dns_rdata_in_aaaa_t *aaaa = source;
+
+ REQUIRE(type == 28);
+ REQUIRE(rdclass == 1);
+ REQUIRE(source != NULL);
+ REQUIRE(aaaa->common.rdtype == type);
+ REQUIRE(aaaa->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ return (mem_tobuffer(target, aaaa->in6_addr.s6_addr, 16));
+}
+
+static inline isc_result_t
+tostruct_in_aaaa(ARGS_TOSTRUCT) {
+ dns_rdata_in_aaaa_t *aaaa = target;
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 28);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length == 16);
+
+ UNUSED(mctx);
+
+ aaaa->common.rdclass = rdata->rdclass;
+ aaaa->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&aaaa->common, link);
+
+ dns_rdata_toregion(rdata, &r);
+ INSIST(r.length == 16);
+ memcpy(aaaa->in6_addr.s6_addr, r.base, 16);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_in_aaaa(ARGS_FREESTRUCT) {
+ dns_rdata_in_aaaa_t *aaaa = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(aaaa->common.rdclass == 1);
+ REQUIRE(aaaa->common.rdtype == 28);
+
+ UNUSED(aaaa);
+}
+
+static inline isc_result_t
+additionaldata_in_aaaa(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 28);
+ REQUIRE(rdata->rdclass == 1);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_in_aaaa(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 28);
+ REQUIRE(rdata->rdclass == 1);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_in_aaaa(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 28);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ return (dns_name_ishostname(name, wildcard));
+}
+
+static inline isc_boolean_t
+checknames_in_aaaa(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 28);
+ REQUIRE(rdata->rdclass == 1);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_IN_1_AAAA_28_C */
diff --git a/lib/dns/rdata/in_1/aaaa_28.h b/lib/dns/rdata/in_1/aaaa_28.h
new file mode 100644
index 0000000..54a0cb3
--- /dev/null
+++ b/lib/dns/rdata/in_1/aaaa_28.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+#ifndef IN_1_AAAA_28_H
+#define IN_1_AAAA_28_H 1
+
+/* $Id: aaaa_28.h,v 1.21 2007/06/19 23:47:17 tbox Exp $ */
+
+/*!
+ * \brief Per RFC1886 */
+
+typedef struct dns_rdata_in_aaaa {
+ dns_rdatacommon_t common;
+ struct in6_addr in6_addr;
+} dns_rdata_in_aaaa_t;
+
+#endif /* IN_1_AAAA_28_H */
diff --git a/lib/dns/rdata/in_1/apl_42.c b/lib/dns/rdata/in_1/apl_42.c
new file mode 100644
index 0000000..28ca68e
--- /dev/null
+++ b/lib/dns/rdata/in_1/apl_42.c
@@ -0,0 +1,453 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: apl_42.c,v 1.14 2008/01/22 23:28:04 tbox Exp $ */
+
+/* RFC3123 */
+
+#ifndef RDATA_IN_1_APL_42_C
+#define RDATA_IN_1_APL_42_C
+
+#define RRTYPE_APL_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_in_apl(ARGS_FROMTEXT) {
+ isc_token_t token;
+ unsigned char addr[16];
+ unsigned long afi;
+ isc_uint8_t prefix;
+ isc_uint8_t len;
+ isc_boolean_t neg;
+ char *cp, *ap, *slash;
+ int n;
+
+ REQUIRE(type == 42);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ do {
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_string, ISC_TRUE));
+ if (token.type != isc_tokentype_string)
+ break;
+
+ cp = DNS_AS_STR(token);
+ neg = ISC_TF(*cp == '!');
+ if (neg)
+ cp++;
+ afi = strtoul(cp, &ap, 10);
+ if (*ap++ != ':' || cp == ap)
+ RETTOK(DNS_R_SYNTAX);
+ if (afi > 0xffffU)
+ RETTOK(ISC_R_RANGE);
+ slash = strchr(ap, '/');
+ if (slash == NULL || slash == ap)
+ RETTOK(DNS_R_SYNTAX);
+ RETTOK(isc_parse_uint8(&prefix, slash + 1, 10));
+ switch (afi) {
+ case 1:
+ *slash = '\0';
+ n = inet_pton(AF_INET, ap, addr);
+ *slash = '/';
+ if (n != 1)
+ RETTOK(DNS_R_BADDOTTEDQUAD);
+ if (prefix > 32)
+ RETTOK(ISC_R_RANGE);
+ for (len = 4; len > 0; len--)
+ if (addr[len - 1] != 0)
+ break;
+ break;
+
+ case 2:
+ *slash = '\0';
+ n = inet_pton(AF_INET6, ap, addr);
+ *slash = '/';
+ if (n != 1)
+ RETTOK(DNS_R_BADAAAA);
+ if (prefix > 128)
+ RETTOK(ISC_R_RANGE);
+ for (len = 16; len > 0; len--)
+ if (addr[len - 1] != 0)
+ break;
+ break;
+
+ default:
+ RETTOK(ISC_R_NOTIMPLEMENTED);
+ }
+ RETERR(uint16_tobuffer(afi, target));
+ RETERR(uint8_tobuffer(prefix, target));
+ RETERR(uint8_tobuffer(len | ((neg) ? 0x80 : 0), target));
+ RETERR(mem_tobuffer(target, addr, len));
+ } while (1);
+
+ /*
+ * Let upper layer handle eol/eof.
+ */
+ isc_lex_ungettoken(lexer, &token);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_in_apl(ARGS_TOTEXT) {
+ isc_region_t sr;
+ isc_region_t ir;
+ isc_uint16_t afi;
+ isc_uint8_t prefix;
+ isc_uint8_t len;
+ isc_boolean_t neg;
+ unsigned char buf[16];
+ char txt[sizeof(" !64000")];
+ const char *sep = "";
+ int n;
+
+ REQUIRE(rdata->type == 42);
+ REQUIRE(rdata->rdclass == 1);
+
+ UNUSED(tctx);
+
+ dns_rdata_toregion(rdata, &sr);
+ ir.base = buf;
+ ir.length = sizeof(buf);
+
+ while (sr.length > 0) {
+ INSIST(sr.length >= 4);
+ afi = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ prefix = *sr.base;
+ isc_region_consume(&sr, 1);
+ len = (*sr.base & 0x7f);
+ neg = ISC_TF((*sr.base & 0x80) != 0);
+ isc_region_consume(&sr, 1);
+ INSIST(len <= sr.length);
+ n = snprintf(txt, sizeof(txt), "%s%s%u:", sep,
+ neg ? "!": "", afi);
+ INSIST(n < (int)sizeof(txt));
+ RETERR(str_totext(txt, target));
+ switch (afi) {
+ case 1:
+ INSIST(len <= 4);
+ INSIST(prefix <= 32);
+ memset(buf, 0, sizeof(buf));
+ memcpy(buf, sr.base, len);
+ RETERR(inet_totext(AF_INET, &ir, target));
+ break;
+
+ case 2:
+ INSIST(len <= 16);
+ INSIST(prefix <= 128);
+ memset(buf, 0, sizeof(buf));
+ memcpy(buf, sr.base, len);
+ RETERR(inet_totext(AF_INET6, &ir, target));
+ break;
+
+ default:
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+ n = snprintf(txt, sizeof(txt), "/%u", prefix);
+ INSIST(n < (int)sizeof(txt));
+ RETERR(str_totext(txt, target));
+ isc_region_consume(&sr, len);
+ sep = " ";
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_in_apl(ARGS_FROMWIRE) {
+ isc_region_t sr, sr2;
+ isc_region_t tr;
+ isc_uint16_t afi;
+ isc_uint8_t prefix;
+ isc_uint8_t len;
+
+ REQUIRE(type == 42);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(dctx);
+ UNUSED(rdclass);
+ UNUSED(options);
+
+ isc_buffer_activeregion(source, &sr);
+ isc_buffer_availableregion(target, &tr);
+ if (sr.length > tr.length)
+ return (ISC_R_NOSPACE);
+ sr2 = sr;
+
+ /* Zero or more items */
+ while (sr.length > 0) {
+ if (sr.length < 4)
+ return (ISC_R_UNEXPECTEDEND);
+ afi = uint16_fromregion(&sr);
+ isc_region_consume(&sr, 2);
+ prefix = *sr.base;
+ isc_region_consume(&sr, 1);
+ len = (*sr.base & 0x7f);
+ isc_region_consume(&sr, 1);
+ if (len > sr.length)
+ return (ISC_R_UNEXPECTEDEND);
+ switch (afi) {
+ case 1:
+ if (prefix > 32 || len > 4)
+ return (ISC_R_RANGE);
+ break;
+ case 2:
+ if (prefix > 128 || len > 16)
+ return (ISC_R_RANGE);
+ }
+ if (len > 0 && sr.base[len - 1] == 0)
+ return (DNS_R_FORMERR);
+ isc_region_consume(&sr, len);
+ }
+ isc_buffer_forward(source, sr2.length);
+ return (mem_tobuffer(target, sr2.base, sr2.length));
+}
+
+static inline isc_result_t
+towire_in_apl(ARGS_TOWIRE) {
+ UNUSED(cctx);
+
+ REQUIRE(rdata->type == 42);
+ REQUIRE(rdata->rdclass == 1);
+
+ return (mem_tobuffer(target, rdata->data, rdata->length));
+}
+
+static inline int
+compare_in_apl(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 42);
+ REQUIRE(rdata1->rdclass == 1);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_in_apl(ARGS_FROMSTRUCT) {
+ dns_rdata_in_apl_t *apl = source;
+ isc_buffer_t b;
+
+ REQUIRE(type == 42);
+ REQUIRE(rdclass == 1);
+ REQUIRE(source != NULL);
+ REQUIRE(apl->common.rdtype == type);
+ REQUIRE(apl->common.rdclass == rdclass);
+ REQUIRE(apl->apl != NULL || apl->apl_len == 0);
+
+ isc_buffer_init(&b, apl->apl, apl->apl_len);
+ isc_buffer_add(&b, apl->apl_len);
+ isc_buffer_setactive(&b, apl->apl_len);
+ return(fromwire_in_apl(rdclass, type, &b, NULL, ISC_FALSE, target));
+}
+
+static inline isc_result_t
+tostruct_in_apl(ARGS_TOSTRUCT) {
+ dns_rdata_in_apl_t *apl = target;
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 42);
+ REQUIRE(rdata->rdclass == 1);
+
+ apl->common.rdclass = rdata->rdclass;
+ apl->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&apl->common, link);
+
+ dns_rdata_toregion(rdata, &r);
+ apl->apl_len = r.length;
+ apl->apl = mem_maybedup(mctx, r.base, r.length);
+ if (apl->apl == NULL)
+ return (ISC_R_NOMEMORY);
+
+ apl->offset = 0;
+ apl->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_in_apl(ARGS_FREESTRUCT) {
+ dns_rdata_in_apl_t *apl = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(apl->common.rdtype == 42);
+ REQUIRE(apl->common.rdclass == 1);
+
+ if (apl->mctx == NULL)
+ return;
+ if (apl->apl != NULL)
+ isc_mem_free(apl->mctx, apl->apl);
+ apl->mctx = NULL;
+}
+
+isc_result_t
+dns_rdata_apl_first(dns_rdata_in_apl_t *apl) {
+ isc_uint32_t length;
+
+ REQUIRE(apl != NULL);
+ REQUIRE(apl->common.rdtype == 42);
+ REQUIRE(apl->common.rdclass == 1);
+ REQUIRE(apl->apl != NULL || apl->apl_len == 0);
+
+ /*
+ * If no APL return ISC_R_NOMORE.
+ */
+ if (apl->apl == NULL)
+ return (ISC_R_NOMORE);
+
+ /*
+ * Sanity check data.
+ */
+ INSIST(apl->apl_len > 3U);
+ length = apl->apl[apl->offset + 3] & 0x7f;
+ INSIST(length <= apl->apl_len);
+
+ apl->offset = 0;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_rdata_apl_next(dns_rdata_in_apl_t *apl) {
+ isc_uint32_t length;
+
+ REQUIRE(apl != NULL);
+ REQUIRE(apl->common.rdtype == 42);
+ REQUIRE(apl->common.rdclass == 1);
+ REQUIRE(apl->apl != NULL || apl->apl_len == 0);
+
+ /*
+ * No APL or have already reached the end return ISC_R_NOMORE.
+ */
+ if (apl->apl == NULL || apl->offset == apl->apl_len)
+ return (ISC_R_NOMORE);
+
+ /*
+ * Sanity check data.
+ */
+ INSIST(apl->offset < apl->apl_len);
+ INSIST(apl->apl_len > 3U);
+ INSIST(apl->offset <= apl->apl_len - 4U);
+ length = apl->apl[apl->offset + 3] & 0x7f;
+ /*
+ * 16 to 32 bits promotion as 'length' is 32 bits so there is
+ * no overflow problems.
+ */
+ INSIST(length + apl->offset <= apl->apl_len);
+
+ apl->offset += apl->apl[apl->offset + 3] & 0x7f;
+ return ((apl->offset >= apl->apl_len) ? ISC_R_SUCCESS : ISC_R_NOMORE);
+}
+
+isc_result_t
+dns_rdata_apl_current(dns_rdata_in_apl_t *apl, dns_rdata_apl_ent_t *ent) {
+ isc_uint32_t length;
+
+ REQUIRE(apl != NULL);
+ REQUIRE(apl->common.rdtype == 42);
+ REQUIRE(apl->common.rdclass == 1);
+ REQUIRE(ent != NULL);
+ REQUIRE(apl->apl != NULL || apl->apl_len == 0);
+ REQUIRE(apl->offset <= apl->apl_len);
+
+ if (apl->offset == apl->apl_len)
+ return (ISC_R_NOMORE);
+
+ /*
+ * Sanity check data.
+ */
+ INSIST(apl->apl_len > 3U);
+ INSIST(apl->offset <= apl->apl_len - 4U);
+ length = apl->apl[apl->offset + 3] & 0x7f;
+ /*
+ * 16 to 32 bits promotion as 'length' is 32 bits so there is
+ * no overflow problems.
+ */
+ INSIST(length + apl->offset <= apl->apl_len);
+
+ ent->family = (apl->apl[apl->offset] << 8) + apl->apl[apl->offset + 1];
+ ent->prefix = apl->apl[apl->offset + 2];
+ ent->length = apl->apl[apl->offset + 3] & 0x7f;
+ ent->negative = ISC_TF((apl->apl[apl->offset + 3] & 0x80) != 0);
+ if (ent->length != 0)
+ ent->data = &apl->apl[apl->offset + 4];
+ else
+ ent->data = NULL;
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+additionaldata_in_apl(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 42);
+ REQUIRE(rdata->rdclass == 1);
+
+ (void)add;
+ (void)arg;
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_in_apl(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 42);
+ REQUIRE(rdata->rdclass == 1);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_in_apl(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 42);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+
+static inline isc_boolean_t
+checknames_in_apl(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 42);
+ REQUIRE(rdata->rdclass == 1);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_IN_1_APL_42_C */
diff --git a/lib/dns/rdata/in_1/apl_42.h b/lib/dns/rdata/in_1/apl_42.h
new file mode 100644
index 0000000..2d01040
--- /dev/null
+++ b/lib/dns/rdata/in_1/apl_42.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* */
+#ifndef IN_1_APL_42_H
+#define IN_1_APL_42_H 1
+
+/* $Id: apl_42.h,v 1.6 2007/06/19 23:47:17 tbox Exp $ */
+
+typedef struct dns_rdata_apl_ent {
+ isc_boolean_t negative;
+ isc_uint16_t family;
+ isc_uint8_t prefix;
+ isc_uint8_t length;
+ unsigned char *data;
+} dns_rdata_apl_ent_t;
+
+typedef struct dns_rdata_in_apl {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ /* type & class specific elements */
+ unsigned char *apl;
+ isc_uint16_t apl_len;
+ /* private */
+ isc_uint16_t offset;
+} dns_rdata_in_apl_t;
+
+/*
+ * ISC_LANG_BEGINDECLS and ISC_LANG_ENDDECLS are already done
+ * via rdatastructpre.h and rdatastructsuf.h.
+ */
+
+isc_result_t
+dns_rdata_apl_first(dns_rdata_in_apl_t *);
+
+isc_result_t
+dns_rdata_apl_next(dns_rdata_in_apl_t *);
+
+isc_result_t
+dns_rdata_apl_current(dns_rdata_in_apl_t *, dns_rdata_apl_ent_t *);
+
+#endif /* IN_1_APL_42_H */
diff --git a/lib/dns/rdata/in_1/dhcid_49.c b/lib/dns/rdata/in_1/dhcid_49.c
new file mode 100644
index 0000000..27c4e4e
--- /dev/null
+++ b/lib/dns/rdata/in_1/dhcid_49.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2006, 2007 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 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.
+ */
+
+/* $Id: dhcid_49.c,v 1.5 2007/06/19 23:47:17 tbox Exp $ */
+
+/* RFC 4701 */
+
+#ifndef RDATA_IN_1_DHCID_49_C
+#define RDATA_IN_1_DHCID_49_C 1
+
+#define RRTYPE_DHCID_ATTRIBUTES 0
+
+static inline isc_result_t
+fromtext_in_dhcid(ARGS_FROMTEXT) {
+
+ REQUIRE(type == 49);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(callbacks);
+
+ return (isc_base64_tobuffer(lexer, target, -1));
+}
+
+static inline isc_result_t
+totext_in_dhcid(ARGS_TOTEXT) {
+ isc_region_t sr;
+ char buf[sizeof(" ; 64000 255 64000")];
+ size_t n;
+
+ REQUIRE(rdata->type == 49);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(rdata->length != 0);
+
+ dns_rdata_toregion(rdata, &sr);
+
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0)
+ RETERR(str_totext("( " /*)*/, target));
+ RETERR(isc_base64_totext(&sr, tctx->width - 2, tctx->linebreak,
+ target));
+ if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) {
+ RETERR(str_totext(/* ( */ " )", target));
+ if (rdata->length > 2) {
+ n = snprintf(buf, sizeof(buf), " ; %u %u %u",
+ sr.base[0] * 256 + sr.base[1],
+ sr.base[2], rdata->length - 3);
+ INSIST(n < sizeof(buf));
+ RETERR(str_totext(buf, target));
+ }
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_in_dhcid(ARGS_FROMWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(type == 49);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(dctx);
+ UNUSED(options);
+
+ isc_buffer_activeregion(source, &sr);
+ if (sr.length == 0)
+ return (ISC_R_UNEXPECTEDEND);
+
+ isc_buffer_forward(source, sr.length);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline isc_result_t
+towire_in_dhcid(ARGS_TOWIRE) {
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == 49);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(cctx);
+
+ dns_rdata_toregion(rdata, &sr);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline int
+compare_in_dhcid(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 49);
+ REQUIRE(rdata1->rdclass == 1);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_in_dhcid(ARGS_FROMSTRUCT) {
+ dns_rdata_in_dhcid_t *dhcid = source;
+
+ REQUIRE(type == 49);
+ REQUIRE(rdclass == 1);
+ REQUIRE(source != NULL);
+ REQUIRE(dhcid->common.rdtype == type);
+ REQUIRE(dhcid->common.rdclass == rdclass);
+ REQUIRE(dhcid->length != 0);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ return (mem_tobuffer(target, dhcid->dhcid, dhcid->length));
+}
+
+static inline isc_result_t
+tostruct_in_dhcid(ARGS_TOSTRUCT) {
+ dns_rdata_in_dhcid_t *dhcid = target;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 49);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ dhcid->common.rdclass = rdata->rdclass;
+ dhcid->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&dhcid->common, link);
+
+ dns_rdata_toregion(rdata, &region);
+
+ dhcid->dhcid = mem_maybedup(mctx, region.base, region.length);
+ if (dhcid->dhcid == NULL)
+ return (ISC_R_NOMEMORY);
+
+ dhcid->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_in_dhcid(ARGS_FREESTRUCT) {
+ dns_rdata_in_dhcid_t *dhcid = source;
+
+ REQUIRE(dhcid != NULL);
+ REQUIRE(dhcid->common.rdtype == 49);
+ REQUIRE(dhcid->common.rdclass == 1);
+
+ if (dhcid->mctx == NULL)
+ return;
+
+ if (dhcid->dhcid != NULL)
+ isc_mem_free(dhcid->mctx, dhcid->dhcid);
+ dhcid->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_in_dhcid(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 49);
+ REQUIRE(rdata->rdclass == 1);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_in_dhcid(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 49);
+ REQUIRE(rdata->rdclass == 1);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_in_dhcid(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 49);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_in_dhcid(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 49);
+ REQUIRE(rdata->rdclass == 1);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_IN_1_DHCID_49_C */
diff --git a/lib/dns/rdata/in_1/dhcid_49.h b/lib/dns/rdata/in_1/dhcid_49.h
new file mode 100644
index 0000000..2797192
--- /dev/null
+++ b/lib/dns/rdata/in_1/dhcid_49.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2006, 2007 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 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.
+ */
+
+/* */
+#ifndef IN_1_DHCID_49_H
+#define IN_1_DHCID_49_H 1
+
+/* $Id: dhcid_49.h,v 1.5 2007/06/19 23:47:17 tbox Exp $ */
+
+typedef struct dns_rdata_in_dhcid {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ unsigned char *dhcid;
+ unsigned int length;
+} dns_rdata_in_dhcid_t;
+
+#endif /* IN_1_DHCID_49_H */
diff --git a/lib/dns/rdata/in_1/kx_36.c b/lib/dns/rdata/in_1/kx_36.c
new file mode 100644
index 0000000..9df2e5e
--- /dev/null
+++ b/lib/dns/rdata/in_1/kx_36.c
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001, 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: kx_36.c,v 1.45 2007/06/19 23:47:17 tbox Exp $ */
+
+/* Reviewed: Thu Mar 16 17:24:54 PST 2000 by explorer */
+
+/* RFC2230 */
+
+#ifndef RDATA_IN_1_KX_36_C
+#define RDATA_IN_1_KX_36_C
+
+#define RRTYPE_KX_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_in_kx(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+
+ REQUIRE(type == 36);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_in_kx(ARGS_TOTEXT) {
+ isc_region_t region;
+ dns_name_t name;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+ char buf[sizeof("64000")];
+ unsigned short num;
+
+ REQUIRE(rdata->type == 36);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(rdata->length != 0);
+
+ dns_name_init(&name, NULL);
+ dns_name_init(&prefix, NULL);
+
+ dns_rdata_toregion(rdata, &region);
+ num = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ sprintf(buf, "%u", num);
+ RETERR(str_totext(buf, target));
+
+ RETERR(str_totext(" ", target));
+
+ dns_name_fromregion(&name, &region);
+ sub = name_prefix(&name, tctx->origin, &prefix);
+ return (dns_name_totext(&prefix, sub, target));
+}
+
+static inline isc_result_t
+fromwire_in_kx(ARGS_FROMWIRE) {
+ dns_name_t name;
+ isc_region_t sregion;
+
+ REQUIRE(type == 36);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_NONE);
+
+ dns_name_init(&name, NULL);
+
+ isc_buffer_activeregion(source, &sregion);
+ if (sregion.length < 2)
+ return (ISC_R_UNEXPECTEDEND);
+ RETERR(mem_tobuffer(target, sregion.base, 2));
+ isc_buffer_forward(source, 2);
+ return (dns_name_fromwire(&name, source, dctx, options, target));
+}
+
+static inline isc_result_t
+towire_in_kx(ARGS_TOWIRE) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 36);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_NONE);
+ dns_rdata_toregion(rdata, &region);
+ RETERR(mem_tobuffer(target, region.base, 2));
+ isc_region_consume(&region, 2);
+
+ dns_name_init(&name, offsets);
+ dns_name_fromregion(&name, &region);
+
+ return (dns_name_towire(&name, cctx, target));
+}
+
+static inline int
+compare_in_kx(ARGS_COMPARE) {
+ dns_name_t name1;
+ dns_name_t name2;
+ isc_region_t region1;
+ isc_region_t region2;
+ int order;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 36);
+ REQUIRE(rdata1->rdclass == 1);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ order = memcmp(rdata1->data, rdata2->data, 2);
+ if (order != 0)
+ return (order < 0 ? -1 : 1);
+
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+
+ isc_region_consume(&region1, 2);
+ isc_region_consume(&region2, 2);
+
+ dns_name_fromregion(&name1, &region1);
+ dns_name_fromregion(&name2, &region2);
+
+ return (dns_name_rdatacompare(&name1, &name2));
+}
+
+static inline isc_result_t
+fromstruct_in_kx(ARGS_FROMSTRUCT) {
+ dns_rdata_in_kx_t *kx = source;
+ isc_region_t region;
+
+ REQUIRE(type == 36);
+ REQUIRE(rdclass == 1);
+ REQUIRE(source != NULL);
+ REQUIRE(kx->common.rdtype == type);
+ REQUIRE(kx->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ RETERR(uint16_tobuffer(kx->preference, target));
+ dns_name_toregion(&kx->exchange, &region);
+ return (isc_buffer_copyregion(target, &region));
+}
+
+static inline isc_result_t
+tostruct_in_kx(ARGS_TOSTRUCT) {
+ isc_region_t region;
+ dns_rdata_in_kx_t *kx = target;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 36);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ kx->common.rdclass = rdata->rdclass;
+ kx->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&kx->common, link);
+
+ dns_name_init(&name, NULL);
+ dns_rdata_toregion(rdata, &region);
+
+ kx->preference = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+
+ dns_name_fromregion(&name, &region);
+ dns_name_init(&kx->exchange, NULL);
+ RETERR(name_duporclone(&name, mctx, &kx->exchange));
+ kx->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_in_kx(ARGS_FREESTRUCT) {
+ dns_rdata_in_kx_t *kx = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(kx->common.rdclass == 1);
+ REQUIRE(kx->common.rdtype == 36);
+
+ if (kx->mctx == NULL)
+ return;
+
+ dns_name_free(&kx->exchange, kx->mctx);
+ kx->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_in_kx(ARGS_ADDLDATA) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 36);
+ REQUIRE(rdata->rdclass == 1);
+
+ dns_name_init(&name, offsets);
+ dns_rdata_toregion(rdata, &region);
+ isc_region_consume(&region, 2);
+ dns_name_fromregion(&name, &region);
+
+ return ((add)(arg, &name, dns_rdatatype_a));
+}
+
+static inline isc_result_t
+digest_in_kx(ARGS_DIGEST) {
+ isc_region_t r1, r2;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 36);
+ REQUIRE(rdata->rdclass == 1);
+
+ dns_rdata_toregion(rdata, &r1);
+ r2 = r1;
+ isc_region_consume(&r2, 2);
+ r1.length = 2;
+ RETERR((digest)(arg, &r1));
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r2);
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline isc_boolean_t
+checkowner_in_kx(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 36);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_in_kx(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 36);
+ REQUIRE(rdata->rdclass == 1);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_IN_1_KX_36_C */
diff --git a/lib/dns/rdata/in_1/kx_36.h b/lib/dns/rdata/in_1/kx_36.h
new file mode 100644
index 0000000..391ae27
--- /dev/null
+++ b/lib/dns/rdata/in_1/kx_36.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+#ifndef IN_1_KX_36_H
+#define IN_1_KX_36_H 1
+
+/* $Id: kx_36.h,v 1.20 2007/06/19 23:47:17 tbox Exp $ */
+
+/*!
+ * \brief Per RFC2230 */
+
+typedef struct dns_rdata_in_kx {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ isc_uint16_t preference;
+ dns_name_t exchange;
+} dns_rdata_in_kx_t;
+
+#endif /* IN_1_KX_36_H */
diff --git a/lib/dns/rdata/in_1/naptr_35.c b/lib/dns/rdata/in_1/naptr_35.c
new file mode 100644
index 0000000..21ab44c
--- /dev/null
+++ b/lib/dns/rdata/in_1/naptr_35.c
@@ -0,0 +1,578 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001, 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: naptr_35.c,v 1.53 2008/02/15 23:46:51 tbox Exp $ */
+
+/* Reviewed: Thu Mar 16 16:52:50 PST 2000 by bwelling */
+
+/* RFC2915 */
+
+#ifndef RDATA_IN_1_NAPTR_35_C
+#define RDATA_IN_1_NAPTR_35_C
+
+#define RRTYPE_NAPTR_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_in_naptr(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+
+ REQUIRE(type == 35);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ /*
+ * Order.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Preference.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Flags.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_qstring,
+ ISC_FALSE));
+ RETTOK(txt_fromtext(&token.value.as_textregion, target));
+
+ /*
+ * Service.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_qstring,
+ ISC_FALSE));
+ RETTOK(txt_fromtext(&token.value.as_textregion, target));
+
+ /*
+ * Regexp.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_qstring,
+ ISC_FALSE));
+ RETTOK(txt_fromtext(&token.value.as_textregion, target));
+
+ /*
+ * Replacement.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_in_naptr(ARGS_TOTEXT) {
+ isc_region_t region;
+ dns_name_t name;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+ char buf[sizeof("64000")];
+ unsigned short num;
+
+ REQUIRE(rdata->type == 35);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(rdata->length != 0);
+
+ dns_name_init(&name, NULL);
+ dns_name_init(&prefix, NULL);
+
+ dns_rdata_toregion(rdata, &region);
+
+ /*
+ * Order.
+ */
+ num = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ sprintf(buf, "%u", num);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Preference.
+ */
+ num = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ sprintf(buf, "%u", num);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Flags.
+ */
+ RETERR(txt_totext(&region, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Service.
+ */
+ RETERR(txt_totext(&region, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Regexp.
+ */
+ RETERR(txt_totext(&region, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Replacement.
+ */
+ dns_name_fromregion(&name, &region);
+ sub = name_prefix(&name, tctx->origin, &prefix);
+ return (dns_name_totext(&prefix, sub, target));
+}
+
+static inline isc_result_t
+fromwire_in_naptr(ARGS_FROMWIRE) {
+ dns_name_t name;
+ isc_region_t sr;
+
+ REQUIRE(type == 35);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_NONE);
+
+ dns_name_init(&name, NULL);
+
+ /*
+ * Order, preference.
+ */
+ isc_buffer_activeregion(source, &sr);
+ if (sr.length < 4)
+ return (ISC_R_UNEXPECTEDEND);
+ RETERR(mem_tobuffer(target, sr.base, 4));
+ isc_buffer_forward(source, 4);
+
+ /*
+ * Flags.
+ */
+ RETERR(txt_fromwire(source, target));
+
+ /*
+ * Service.
+ */
+ RETERR(txt_fromwire(source, target));
+
+ /*
+ * Regexp.
+ */
+ RETERR(txt_fromwire(source, target));
+
+ /*
+ * Replacement.
+ */
+ return (dns_name_fromwire(&name, source, dctx, options, target));
+}
+
+static inline isc_result_t
+towire_in_naptr(ARGS_TOWIRE) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == 35);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_NONE);
+ /*
+ * Order, preference.
+ */
+ dns_rdata_toregion(rdata, &sr);
+ RETERR(mem_tobuffer(target, sr.base, 4));
+ isc_region_consume(&sr, 4);
+
+ /*
+ * Flags.
+ */
+ RETERR(mem_tobuffer(target, sr.base, sr.base[0] + 1));
+ isc_region_consume(&sr, sr.base[0] + 1);
+
+ /*
+ * Service.
+ */
+ RETERR(mem_tobuffer(target, sr.base, sr.base[0] + 1));
+ isc_region_consume(&sr, sr.base[0] + 1);
+
+ /*
+ * Regexp.
+ */
+ RETERR(mem_tobuffer(target, sr.base, sr.base[0] + 1));
+ isc_region_consume(&sr, sr.base[0] + 1);
+
+ /*
+ * Replacement.
+ */
+ dns_name_init(&name, offsets);
+ dns_name_fromregion(&name, &sr);
+ return (dns_name_towire(&name, cctx, target));
+}
+
+static inline int
+compare_in_naptr(ARGS_COMPARE) {
+ dns_name_t name1;
+ dns_name_t name2;
+ isc_region_t region1;
+ isc_region_t region2;
+ int order, len;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 35);
+ REQUIRE(rdata1->rdclass == 1);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+
+ /*
+ * Order, preference.
+ */
+ order = memcmp(region1.base, region2.base, 4);
+ if (order != 0)
+ return (order < 0 ? -1 : 1);
+ isc_region_consume(&region1, 4);
+ isc_region_consume(&region2, 4);
+
+ /*
+ * Flags.
+ */
+ len = ISC_MIN(region1.base[0], region2.base[0]);
+ order = memcmp(region1.base, region2.base, len + 1);
+ if (order != 0)
+ return (order < 0 ? -1 : 1);
+ isc_region_consume(&region1, region1.base[0] + 1);
+ isc_region_consume(&region2, region2.base[0] + 1);
+
+ /*
+ * Service.
+ */
+ len = ISC_MIN(region1.base[0], region2.base[0]);
+ order = memcmp(region1.base, region2.base, len + 1);
+ if (order != 0)
+ return (order < 0 ? -1 : 1);
+ isc_region_consume(&region1, region1.base[0] + 1);
+ isc_region_consume(&region2, region2.base[0] + 1);
+
+ /*
+ * Regexp.
+ */
+ len = ISC_MIN(region1.base[0], region2.base[0]);
+ order = memcmp(region1.base, region2.base, len + 1);
+ if (order != 0)
+ return (order < 0 ? -1 : 1);
+ isc_region_consume(&region1, region1.base[0] + 1);
+ isc_region_consume(&region2, region2.base[0] + 1);
+
+ /*
+ * Replacement.
+ */
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+
+ dns_name_fromregion(&name1, &region1);
+ dns_name_fromregion(&name2, &region2);
+
+ return (dns_name_rdatacompare(&name1, &name2));
+}
+
+static inline isc_result_t
+fromstruct_in_naptr(ARGS_FROMSTRUCT) {
+ dns_rdata_in_naptr_t *naptr = source;
+ isc_region_t region;
+
+ REQUIRE(type == 35);
+ REQUIRE(rdclass == 1);
+ REQUIRE(source != NULL);
+ REQUIRE(naptr->common.rdtype == type);
+ REQUIRE(naptr->common.rdclass == rdclass);
+ REQUIRE(naptr->flags != NULL || naptr->flags_len == 0);
+ REQUIRE(naptr->service != NULL || naptr->service_len == 0);
+ REQUIRE(naptr->regexp != NULL || naptr->regexp_len == 0);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ RETERR(uint16_tobuffer(naptr->order, target));
+ RETERR(uint16_tobuffer(naptr->preference, target));
+ RETERR(uint8_tobuffer(naptr->flags_len, target));
+ RETERR(mem_tobuffer(target, naptr->flags, naptr->flags_len));
+ RETERR(uint8_tobuffer(naptr->service_len, target));
+ RETERR(mem_tobuffer(target, naptr->service, naptr->service_len));
+ RETERR(uint8_tobuffer(naptr->regexp_len, target));
+ RETERR(mem_tobuffer(target, naptr->regexp, naptr->regexp_len));
+ dns_name_toregion(&naptr->replacement, &region);
+ return (isc_buffer_copyregion(target, &region));
+}
+
+static inline isc_result_t
+tostruct_in_naptr(ARGS_TOSTRUCT) {
+ dns_rdata_in_naptr_t *naptr = target;
+ isc_region_t r;
+ isc_result_t result;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 35);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ naptr->common.rdclass = rdata->rdclass;
+ naptr->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&naptr->common, link);
+
+ naptr->flags = NULL;
+ naptr->service = NULL;
+ naptr->regexp = NULL;
+
+ dns_rdata_toregion(rdata, &r);
+
+ naptr->order = uint16_fromregion(&r);
+ isc_region_consume(&r, 2);
+
+ naptr->preference = uint16_fromregion(&r);
+ isc_region_consume(&r, 2);
+
+ naptr->flags_len = uint8_fromregion(&r);
+ isc_region_consume(&r, 1);
+ INSIST(naptr->flags_len <= r.length);
+ naptr->flags = mem_maybedup(mctx, r.base, naptr->flags_len);
+ if (naptr->flags == NULL)
+ goto cleanup;
+ isc_region_consume(&r, naptr->flags_len);
+
+ naptr->service_len = uint8_fromregion(&r);
+ isc_region_consume(&r, 1);
+ INSIST(naptr->service_len <= r.length);
+ naptr->service = mem_maybedup(mctx, r.base, naptr->service_len);
+ if (naptr->service == NULL)
+ goto cleanup;
+ isc_region_consume(&r, naptr->service_len);
+
+ naptr->regexp_len = uint8_fromregion(&r);
+ isc_region_consume(&r, 1);
+ INSIST(naptr->regexp_len <= r.length);
+ naptr->regexp = mem_maybedup(mctx, r.base, naptr->regexp_len);
+ if (naptr->regexp == NULL)
+ goto cleanup;
+ isc_region_consume(&r, naptr->regexp_len);
+
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+ dns_name_init(&naptr->replacement, NULL);
+ result = name_duporclone(&name, mctx, &naptr->replacement);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ naptr->mctx = mctx;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (mctx != NULL && naptr->flags != NULL)
+ isc_mem_free(mctx, naptr->flags);
+ if (mctx != NULL && naptr->service != NULL)
+ isc_mem_free(mctx, naptr->service);
+ if (mctx != NULL && naptr->regexp != NULL)
+ isc_mem_free(mctx, naptr->regexp);
+ return (ISC_R_NOMEMORY);
+}
+
+static inline void
+freestruct_in_naptr(ARGS_FREESTRUCT) {
+ dns_rdata_in_naptr_t *naptr = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(naptr->common.rdclass == 1);
+ REQUIRE(naptr->common.rdtype == 35);
+
+ if (naptr->mctx == NULL)
+ return;
+
+ if (naptr->flags != NULL)
+ isc_mem_free(naptr->mctx, naptr->flags);
+ if (naptr->service != NULL)
+ isc_mem_free(naptr->mctx, naptr->service);
+ if (naptr->regexp != NULL)
+ isc_mem_free(naptr->mctx, naptr->regexp);
+ dns_name_free(&naptr->replacement, naptr->mctx);
+ naptr->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_in_naptr(ARGS_ADDLDATA) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t sr;
+ dns_rdatatype_t atype;
+ unsigned int i, flagslen;
+ char *cp;
+
+ REQUIRE(rdata->type == 35);
+ REQUIRE(rdata->rdclass == 1);
+
+ /*
+ * Order, preference.
+ */
+ dns_rdata_toregion(rdata, &sr);
+ isc_region_consume(&sr, 4);
+
+ /*
+ * Flags.
+ */
+ atype = 0;
+ flagslen = sr.base[0];
+ cp = (char *)&sr.base[1];
+ for (i = 0; i < flagslen; i++, cp++) {
+ if (*cp == 'S' || *cp == 's') {
+ atype = dns_rdatatype_srv;
+ break;
+ }
+ if (*cp == 'A' || *cp == 'a') {
+ atype = dns_rdatatype_a;
+ break;
+ }
+ }
+ isc_region_consume(&sr, flagslen + 1);
+
+ /*
+ * Service.
+ */
+ isc_region_consume(&sr, sr.base[0] + 1);
+
+ /*
+ * Regexp.
+ */
+ isc_region_consume(&sr, sr.base[0] + 1);
+
+ /*
+ * Replacement.
+ */
+ dns_name_init(&name, offsets);
+ dns_name_fromregion(&name, &sr);
+
+ if (atype != 0)
+ return ((add)(arg, &name, atype));
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_in_naptr(ARGS_DIGEST) {
+ isc_region_t r1, r2;
+ unsigned int length, n;
+ isc_result_t result;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 35);
+ REQUIRE(rdata->rdclass == 1);
+
+ dns_rdata_toregion(rdata, &r1);
+ r2 = r1;
+ length = 0;
+
+ /*
+ * Order, preference.
+ */
+ length += 4;
+ isc_region_consume(&r2, 4);
+
+ /*
+ * Flags.
+ */
+ n = r2.base[0] + 1;
+ length += n;
+ isc_region_consume(&r2, n);
+
+ /*
+ * Service.
+ */
+ n = r2.base[0] + 1;
+ length += n;
+ isc_region_consume(&r2, n);
+
+ /*
+ * Regexp.
+ */
+ n = r2.base[0] + 1;
+ length += n;
+ isc_region_consume(&r2, n);
+
+ /*
+ * Digest the RR up to the replacement name.
+ */
+ r1.length = length;
+ result = (digest)(arg, &r1);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * Replacement.
+ */
+
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r2);
+
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline isc_boolean_t
+checkowner_in_naptr(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 35);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_in_naptr(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 35);
+ REQUIRE(rdata->rdclass == 1);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_IN_1_NAPTR_35_C */
diff --git a/lib/dns/rdata/in_1/naptr_35.h b/lib/dns/rdata/in_1/naptr_35.h
new file mode 100644
index 0000000..503f7a8
--- /dev/null
+++ b/lib/dns/rdata/in_1/naptr_35.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+#ifndef IN_1_NAPTR_35_H
+#define IN_1_NAPTR_35_H 1
+
+/* $Id: naptr_35.h,v 1.23 2007/06/19 23:47:17 tbox Exp $ */
+
+/*!
+ * \brief Per RFC2915 */
+
+typedef struct dns_rdata_in_naptr {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ isc_uint16_t order;
+ isc_uint16_t preference;
+ char *flags;
+ isc_uint8_t flags_len;
+ char *service;
+ isc_uint8_t service_len;
+ char *regexp;
+ isc_uint8_t regexp_len;
+ dns_name_t replacement;
+} dns_rdata_in_naptr_t;
+
+#endif /* IN_1_NAPTR_35_H */
diff --git a/lib/dns/rdata/in_1/nsap-ptr_23.c b/lib/dns/rdata/in_1/nsap-ptr_23.c
new file mode 100644
index 0000000..2da7869
--- /dev/null
+++ b/lib/dns/rdata/in_1/nsap-ptr_23.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: nsap-ptr_23.c,v 1.38 2007/06/19 23:47:17 tbox Exp $ */
+
+/* Reviewed: Fri Mar 17 10:16:02 PST 2000 by gson */
+
+/* RFC1348. Obsoleted in RFC 1706 - use PTR instead. */
+
+#ifndef RDATA_IN_1_NSAP_PTR_23_C
+#define RDATA_IN_1_NSAP_PTR_23_C
+
+#define RRTYPE_NSAP_PTR_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_in_nsap_ptr(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+
+ REQUIRE(type == 23);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_in_nsap_ptr(ARGS_TOTEXT) {
+ isc_region_t region;
+ dns_name_t name;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+
+ REQUIRE(rdata->type == 23);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(rdata->length != 0);
+
+ dns_name_init(&name, NULL);
+ dns_name_init(&prefix, NULL);
+
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+
+ sub = name_prefix(&name, tctx->origin, &prefix);
+
+ return (dns_name_totext(&prefix, sub, target));
+}
+
+static inline isc_result_t
+fromwire_in_nsap_ptr(ARGS_FROMWIRE) {
+ dns_name_t name;
+
+ REQUIRE(type == 23);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_NONE);
+
+ dns_name_init(&name, NULL);
+ return (dns_name_fromwire(&name, source, dctx, options, target));
+}
+
+static inline isc_result_t
+towire_in_nsap_ptr(ARGS_TOWIRE) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 23);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_NONE);
+ dns_name_init(&name, offsets);
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+
+ return (dns_name_towire(&name, cctx, target));
+}
+
+static inline int
+compare_in_nsap_ptr(ARGS_COMPARE) {
+ dns_name_t name1;
+ dns_name_t name2;
+ isc_region_t region1;
+ isc_region_t region2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 23);
+ REQUIRE(rdata1->rdclass == 1);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+
+ dns_name_fromregion(&name1, &region1);
+ dns_name_fromregion(&name2, &region2);
+
+ return (dns_name_rdatacompare(&name1, &name2));
+}
+
+static inline isc_result_t
+fromstruct_in_nsap_ptr(ARGS_FROMSTRUCT) {
+ dns_rdata_in_nsap_ptr_t *nsap_ptr = source;
+ isc_region_t region;
+
+ REQUIRE(type == 23);
+ REQUIRE(rdclass == 1);
+ REQUIRE(source != NULL);
+ REQUIRE(nsap_ptr->common.rdtype == type);
+ REQUIRE(nsap_ptr->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_name_toregion(&nsap_ptr->owner, &region);
+ return (isc_buffer_copyregion(target, &region));
+}
+
+static inline isc_result_t
+tostruct_in_nsap_ptr(ARGS_TOSTRUCT) {
+ isc_region_t region;
+ dns_rdata_in_nsap_ptr_t *nsap_ptr = target;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 23);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ nsap_ptr->common.rdclass = rdata->rdclass;
+ nsap_ptr->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&nsap_ptr->common, link);
+
+ dns_name_init(&name, NULL);
+ dns_rdata_toregion(rdata, &region);
+ dns_name_fromregion(&name, &region);
+ dns_name_init(&nsap_ptr->owner, NULL);
+ RETERR(name_duporclone(&name, mctx, &nsap_ptr->owner));
+ nsap_ptr->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_in_nsap_ptr(ARGS_FREESTRUCT) {
+ dns_rdata_in_nsap_ptr_t *nsap_ptr = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(nsap_ptr->common.rdclass == 1);
+ REQUIRE(nsap_ptr->common.rdtype == 23);
+
+ if (nsap_ptr->mctx == NULL)
+ return;
+
+ dns_name_free(&nsap_ptr->owner, nsap_ptr->mctx);
+ nsap_ptr->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_in_nsap_ptr(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 23);
+ REQUIRE(rdata->rdclass == 1);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_in_nsap_ptr(ARGS_DIGEST) {
+ isc_region_t r;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 23);
+ REQUIRE(rdata->rdclass == 1);
+
+ dns_rdata_toregion(rdata, &r);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r);
+
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline isc_boolean_t
+checkowner_in_nsap_ptr(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 23);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_in_nsap_ptr(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 23);
+ REQUIRE(rdata->rdclass == 1);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_IN_1_NSAP_PTR_23_C */
diff --git a/lib/dns/rdata/in_1/nsap-ptr_23.h b/lib/dns/rdata/in_1/nsap-ptr_23.h
new file mode 100644
index 0000000..14a8b19
--- /dev/null
+++ b/lib/dns/rdata/in_1/nsap-ptr_23.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+#ifndef IN_1_NSAP_PTR_23_H
+#define IN_1_NSAP_PTR_23_H 1
+
+/* $Id: nsap-ptr_23.h,v 1.19 2007/06/19 23:47:17 tbox Exp $ */
+
+/*!
+ * \brief Per RFC1348. Obsoleted in RFC 1706 - use PTR instead. */
+
+typedef struct dns_rdata_in_nsap_ptr {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ dns_name_t owner;
+} dns_rdata_in_nsap_ptr_t;
+
+#endif /* IN_1_NSAP_PTR_23_H */
diff --git a/lib/dns/rdata/in_1/nsap_22.c b/lib/dns/rdata/in_1/nsap_22.c
new file mode 100644
index 0000000..c25f560
--- /dev/null
+++ b/lib/dns/rdata/in_1/nsap_22.c
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: nsap_22.c,v 1.42 2007/06/19 23:47:17 tbox Exp $ */
+
+/* Reviewed: Fri Mar 17 10:41:07 PST 2000 by gson */
+
+/* RFC1706 */
+
+#ifndef RDATA_IN_1_NSAP_22_C
+#define RDATA_IN_1_NSAP_22_C
+
+#define RRTYPE_NSAP_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_in_nsap(ARGS_FROMTEXT) {
+ isc_token_t token;
+ isc_textregion_t *sr;
+ int n;
+ int digits;
+ unsigned char c = 0;
+
+ REQUIRE(type == 22);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ /* 0x<hex.string.with.periods> */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ sr = &token.value.as_textregion;
+ if (sr->length < 2)
+ RETTOK(ISC_R_UNEXPECTEDEND);
+ if (sr->base[0] != '0' || (sr->base[1] != 'x' && sr->base[1] != 'X'))
+ RETTOK(DNS_R_SYNTAX);
+ isc_textregion_consume(sr, 2);
+ digits = 0;
+ n = 0;
+ while (sr->length > 0) {
+ if (sr->base[0] == '.') {
+ isc_textregion_consume(sr, 1);
+ continue;
+ }
+ if ((n = hexvalue(sr->base[0])) == -1)
+ RETTOK(DNS_R_SYNTAX);
+ c <<= 4;
+ c += n;
+ if (++digits == 2) {
+ RETERR(mem_tobuffer(target, &c, 1));
+ digits = 0;
+ }
+ isc_textregion_consume(sr, 1);
+ }
+ if (digits)
+ RETTOK(ISC_R_UNEXPECTEDEND);
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_in_nsap(ARGS_TOTEXT) {
+ isc_region_t region;
+ char buf[sizeof("xx")];
+
+ REQUIRE(rdata->type == 22);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(tctx);
+
+ dns_rdata_toregion(rdata, &region);
+ RETERR(str_totext("0x", target));
+ while (region.length != 0) {
+ sprintf(buf, "%02x", region.base[0]);
+ isc_region_consume(&region, 1);
+ RETERR(str_totext(buf, target));
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_in_nsap(ARGS_FROMWIRE) {
+ isc_region_t region;
+
+ REQUIRE(type == 22);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(dctx);
+ UNUSED(options);
+ UNUSED(rdclass);
+
+ isc_buffer_activeregion(source, &region);
+ if (region.length < 1)
+ return (ISC_R_UNEXPECTEDEND);
+
+ RETERR(mem_tobuffer(target, region.base, region.length));
+ isc_buffer_forward(source, region.length);
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+towire_in_nsap(ARGS_TOWIRE) {
+ REQUIRE(rdata->type == 22);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(rdata->length != 0);
+
+ UNUSED(cctx);
+
+ return (mem_tobuffer(target, rdata->data, rdata->length));
+}
+
+static inline int
+compare_in_nsap(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 22);
+ REQUIRE(rdata1->rdclass == 1);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_in_nsap(ARGS_FROMSTRUCT) {
+ dns_rdata_in_nsap_t *nsap = source;
+
+ REQUIRE(type == 22);
+ REQUIRE(rdclass == 1);
+ REQUIRE(source != NULL);
+ REQUIRE(nsap->common.rdtype == type);
+ REQUIRE(nsap->common.rdclass == rdclass);
+ REQUIRE(nsap->nsap != NULL || nsap->nsap_len == 0);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ return (mem_tobuffer(target, nsap->nsap, nsap->nsap_len));
+}
+
+static inline isc_result_t
+tostruct_in_nsap(ARGS_TOSTRUCT) {
+ dns_rdata_in_nsap_t *nsap = target;
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 22);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ nsap->common.rdclass = rdata->rdclass;
+ nsap->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&nsap->common, link);
+
+ dns_rdata_toregion(rdata, &r);
+ nsap->nsap_len = r.length;
+ nsap->nsap = mem_maybedup(mctx, r.base, r.length);
+ if (nsap->nsap == NULL)
+ return (ISC_R_NOMEMORY);
+
+ nsap->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_in_nsap(ARGS_FREESTRUCT) {
+ dns_rdata_in_nsap_t *nsap = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(nsap->common.rdclass == 1);
+ REQUIRE(nsap->common.rdtype == 22);
+
+ if (nsap->mctx == NULL)
+ return;
+
+ if (nsap->nsap != NULL)
+ isc_mem_free(nsap->mctx, nsap->nsap);
+ nsap->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_in_nsap(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 22);
+ REQUIRE(rdata->rdclass == 1);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_in_nsap(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 22);
+ REQUIRE(rdata->rdclass == 1);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_in_nsap(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 22);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_in_nsap(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 22);
+ REQUIRE(rdata->rdclass == 1);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_IN_1_NSAP_22_C */
diff --git a/lib/dns/rdata/in_1/nsap_22.h b/lib/dns/rdata/in_1/nsap_22.h
new file mode 100644
index 0000000..11e3f66
--- /dev/null
+++ b/lib/dns/rdata/in_1/nsap_22.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+#ifndef IN_1_NSAP_22_H
+#define IN_1_NSAP_22_H 1
+
+/* $Id: nsap_22.h,v 1.18 2007/06/19 23:47:17 tbox Exp $ */
+
+/*!
+ * \brief Per RFC1706 */
+
+typedef struct dns_rdata_in_nsap {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ unsigned char *nsap;
+ isc_uint16_t nsap_len;
+} dns_rdata_in_nsap_t;
+
+#endif /* IN_1_NSAP_22_H */
diff --git a/lib/dns/rdata/in_1/px_26.c b/lib/dns/rdata/in_1/px_26.c
new file mode 100644
index 0000000..1d17f2f
--- /dev/null
+++ b/lib/dns/rdata/in_1/px_26.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001, 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: px_26.c,v 1.43 2007/06/19 23:47:17 tbox Exp $ */
+
+/* Reviewed: Mon Mar 20 10:44:27 PST 2000 */
+
+/* RFC2163 */
+
+#ifndef RDATA_IN_1_PX_26_C
+#define RDATA_IN_1_PX_26_C
+
+#define RRTYPE_PX_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_in_px(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+
+ REQUIRE(type == 26);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ /*
+ * Preference.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * MAP822.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+
+ /*
+ * MAPX400.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_in_px(ARGS_TOTEXT) {
+ isc_region_t region;
+ dns_name_t name;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+ char buf[sizeof("64000")];
+ unsigned short num;
+
+ REQUIRE(rdata->type == 26);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(rdata->length != 0);
+
+ dns_name_init(&name, NULL);
+ dns_name_init(&prefix, NULL);
+
+ /*
+ * Preference.
+ */
+ dns_rdata_toregion(rdata, &region);
+ num = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ sprintf(buf, "%u", num);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * MAP822.
+ */
+ dns_name_fromregion(&name, &region);
+ sub = name_prefix(&name, tctx->origin, &prefix);
+ isc_region_consume(&region, name_length(&name));
+ RETERR(dns_name_totext(&prefix, sub, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * MAPX400.
+ */
+ dns_name_fromregion(&name, &region);
+ sub = name_prefix(&name, tctx->origin, &prefix);
+ return(dns_name_totext(&prefix, sub, target));
+}
+
+static inline isc_result_t
+fromwire_in_px(ARGS_FROMWIRE) {
+ dns_name_t name;
+ isc_region_t sregion;
+
+ REQUIRE(type == 26);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_NONE);
+
+ dns_name_init(&name, NULL);
+
+ /*
+ * Preference.
+ */
+ isc_buffer_activeregion(source, &sregion);
+ if (sregion.length < 2)
+ return (ISC_R_UNEXPECTEDEND);
+ RETERR(mem_tobuffer(target, sregion.base, 2));
+ isc_buffer_forward(source, 2);
+
+ /*
+ * MAP822.
+ */
+ RETERR(dns_name_fromwire(&name, source, dctx, options, target));
+
+ /*
+ * MAPX400.
+ */
+ return (dns_name_fromwire(&name, source, dctx, options, target));
+}
+
+static inline isc_result_t
+towire_in_px(ARGS_TOWIRE) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 26);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_NONE);
+ /*
+ * Preference.
+ */
+ dns_rdata_toregion(rdata, &region);
+ RETERR(mem_tobuffer(target, region.base, 2));
+ isc_region_consume(&region, 2);
+
+ /*
+ * MAP822.
+ */
+ dns_name_init(&name, offsets);
+ dns_name_fromregion(&name, &region);
+ RETERR(dns_name_towire(&name, cctx, target));
+ isc_region_consume(&region, name_length(&name));
+
+ /*
+ * MAPX400.
+ */
+ dns_name_init(&name, offsets);
+ dns_name_fromregion(&name, &region);
+ return (dns_name_towire(&name, cctx, target));
+}
+
+static inline int
+compare_in_px(ARGS_COMPARE) {
+ dns_name_t name1;
+ dns_name_t name2;
+ isc_region_t region1;
+ isc_region_t region2;
+ int order;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 26);
+ REQUIRE(rdata1->rdclass == 1);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ order = memcmp(rdata1->data, rdata2->data, 2);
+ if (order != 0)
+ return (order < 0 ? -1 : 1);
+
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+
+ isc_region_consume(&region1, 2);
+ isc_region_consume(&region2, 2);
+
+ dns_name_fromregion(&name1, &region1);
+ dns_name_fromregion(&name2, &region2);
+
+ order = dns_name_rdatacompare(&name1, &name2);
+ if (order != 0)
+ return (order);
+
+ isc_region_consume(&region1, name_length(&name1));
+ isc_region_consume(&region2, name_length(&name2));
+
+ dns_name_fromregion(&name1, &region1);
+ dns_name_fromregion(&name2, &region2);
+
+ return (dns_name_rdatacompare(&name1, &name2));
+}
+
+static inline isc_result_t
+fromstruct_in_px(ARGS_FROMSTRUCT) {
+ dns_rdata_in_px_t *px = source;
+ isc_region_t region;
+
+ REQUIRE(type == 26);
+ REQUIRE(rdclass == 1);
+ REQUIRE(source != NULL);
+ REQUIRE(px->common.rdtype == type);
+ REQUIRE(px->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ RETERR(uint16_tobuffer(px->preference, target));
+ dns_name_toregion(&px->map822, &region);
+ RETERR(isc_buffer_copyregion(target, &region));
+ dns_name_toregion(&px->mapx400, &region);
+ return (isc_buffer_copyregion(target, &region));
+}
+
+static inline isc_result_t
+tostruct_in_px(ARGS_TOSTRUCT) {
+ dns_rdata_in_px_t *px = target;
+ dns_name_t name;
+ isc_region_t region;
+ isc_result_t result;
+
+ REQUIRE(rdata->type == 26);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ px->common.rdclass = rdata->rdclass;
+ px->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&px->common, link);
+
+ dns_name_init(&name, NULL);
+ dns_rdata_toregion(rdata, &region);
+
+ px->preference = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+
+ dns_name_fromregion(&name, &region);
+
+ dns_name_init(&px->map822, NULL);
+ RETERR(name_duporclone(&name, mctx, &px->map822));
+ isc_region_consume(&region, name_length(&px->map822));
+
+ dns_name_init(&px->mapx400, NULL);
+ result = name_duporclone(&name, mctx, &px->mapx400);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ px->mctx = mctx;
+ return (result);
+
+ cleanup:
+ dns_name_free(&px->map822, mctx);
+ return (ISC_R_NOMEMORY);
+}
+
+static inline void
+freestruct_in_px(ARGS_FREESTRUCT) {
+ dns_rdata_in_px_t *px = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(px->common.rdclass == 1);
+ REQUIRE(px->common.rdtype == 26);
+
+ if (px->mctx == NULL)
+ return;
+
+ dns_name_free(&px->map822, px->mctx);
+ dns_name_free(&px->mapx400, px->mctx);
+ px->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_in_px(ARGS_ADDLDATA) {
+ REQUIRE(rdata->type == 26);
+ REQUIRE(rdata->rdclass == 1);
+
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_in_px(ARGS_DIGEST) {
+ isc_region_t r1, r2;
+ dns_name_t name;
+ isc_result_t result;
+
+ REQUIRE(rdata->type == 26);
+ REQUIRE(rdata->rdclass == 1);
+
+ dns_rdata_toregion(rdata, &r1);
+ r2 = r1;
+ isc_region_consume(&r2, 2);
+ r1.length = 2;
+ result = (digest)(arg, &r1);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r2);
+ result = dns_name_digest(&name, digest, arg);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ isc_region_consume(&r2, name_length(&name));
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r2);
+
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline isc_boolean_t
+checkowner_in_px(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 26);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_in_px(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 26);
+ REQUIRE(rdata->rdclass == 1);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_IN_1_PX_26_C */
diff --git a/lib/dns/rdata/in_1/px_26.h b/lib/dns/rdata/in_1/px_26.h
new file mode 100644
index 0000000..69a7bae
--- /dev/null
+++ b/lib/dns/rdata/in_1/px_26.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+#ifndef IN_1_PX_26_H
+#define IN_1_PX_26_H 1
+
+/* $Id: px_26.h,v 1.19 2007/06/19 23:47:17 tbox Exp $ */
+
+/*!
+ * \brief Per RFC2163 */
+
+typedef struct dns_rdata_in_px {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ isc_uint16_t preference;
+ dns_name_t map822;
+ dns_name_t mapx400;
+} dns_rdata_in_px_t;
+
+#endif /* IN_1_PX_26_H */
diff --git a/lib/dns/rdata/in_1/srv_33.c b/lib/dns/rdata/in_1/srv_33.c
new file mode 100644
index 0000000..7bc85cd
--- /dev/null
+++ b/lib/dns/rdata/in_1/srv_33.c
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001, 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: srv_33.c,v 1.45 2007/06/19 23:47:17 tbox Exp $ */
+
+/* Reviewed: Fri Mar 17 13:01:00 PST 2000 by bwelling */
+
+/* RFC2782 */
+
+#ifndef RDATA_IN_1_SRV_33_C
+#define RDATA_IN_1_SRV_33_C
+
+#define RRTYPE_SRV_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_in_srv(ARGS_FROMTEXT) {
+ isc_token_t token;
+ dns_name_t name;
+ isc_buffer_t buffer;
+ isc_boolean_t ok;
+
+ REQUIRE(type == 33);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(callbacks);
+
+ /*
+ * Priority.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Weight.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Port.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number,
+ ISC_FALSE));
+ if (token.value.as_ulong > 0xffffU)
+ RETTOK(ISC_R_RANGE);
+ RETERR(uint16_tobuffer(token.value.as_ulong, target));
+
+ /*
+ * Target.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+ dns_name_init(&name, NULL);
+ buffer_fromregion(&buffer, &token.value.as_region);
+ origin = (origin != NULL) ? origin : dns_rootname;
+ RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target));
+ ok = ISC_TRUE;
+ if ((options & DNS_RDATA_CHECKNAMES) != 0)
+ ok = dns_name_ishostname(&name, ISC_FALSE);
+ if (!ok && (options & DNS_RDATA_CHECKNAMESFAIL) != 0)
+ RETTOK(DNS_R_BADNAME);
+ if (!ok && callbacks != NULL)
+ warn_badname(&name, lexer, callbacks);
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+totext_in_srv(ARGS_TOTEXT) {
+ isc_region_t region;
+ dns_name_t name;
+ dns_name_t prefix;
+ isc_boolean_t sub;
+ char buf[sizeof("64000")];
+ unsigned short num;
+
+ REQUIRE(rdata->type == 33);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(rdata->length != 0);
+
+ dns_name_init(&name, NULL);
+ dns_name_init(&prefix, NULL);
+
+ /*
+ * Priority.
+ */
+ dns_rdata_toregion(rdata, &region);
+ num = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ sprintf(buf, "%u", num);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Weight.
+ */
+ num = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ sprintf(buf, "%u", num);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Port.
+ */
+ num = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ sprintf(buf, "%u", num);
+ RETERR(str_totext(buf, target));
+ RETERR(str_totext(" ", target));
+
+ /*
+ * Target.
+ */
+ dns_name_fromregion(&name, &region);
+ sub = name_prefix(&name, tctx->origin, &prefix);
+ return (dns_name_totext(&prefix, sub, target));
+}
+
+static inline isc_result_t
+fromwire_in_srv(ARGS_FROMWIRE) {
+ dns_name_t name;
+ isc_region_t sr;
+
+ REQUIRE(type == 33);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ dns_decompress_setmethods(dctx, DNS_COMPRESS_NONE);
+
+ dns_name_init(&name, NULL);
+
+ /*
+ * Priority, weight, port.
+ */
+ isc_buffer_activeregion(source, &sr);
+ if (sr.length < 6)
+ return (ISC_R_UNEXPECTEDEND);
+ RETERR(mem_tobuffer(target, sr.base, 6));
+ isc_buffer_forward(source, 6);
+
+ /*
+ * Target.
+ */
+ return (dns_name_fromwire(&name, source, dctx, options, target));
+}
+
+static inline isc_result_t
+towire_in_srv(ARGS_TOWIRE) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t sr;
+
+ REQUIRE(rdata->type == 33);
+ REQUIRE(rdata->length != 0);
+
+ dns_compress_setmethods(cctx, DNS_COMPRESS_NONE);
+ /*
+ * Priority, weight, port.
+ */
+ dns_rdata_toregion(rdata, &sr);
+ RETERR(mem_tobuffer(target, sr.base, 6));
+ isc_region_consume(&sr, 6);
+
+ /*
+ * Target.
+ */
+ dns_name_init(&name, offsets);
+ dns_name_fromregion(&name, &sr);
+ return (dns_name_towire(&name, cctx, target));
+}
+
+static inline int
+compare_in_srv(ARGS_COMPARE) {
+ dns_name_t name1;
+ dns_name_t name2;
+ isc_region_t region1;
+ isc_region_t region2;
+ int order;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 33);
+ REQUIRE(rdata1->rdclass == 1);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ /*
+ * Priority, weight, port.
+ */
+ order = memcmp(rdata1->data, rdata2->data, 6);
+ if (order != 0)
+ return (order < 0 ? -1 : 1);
+
+ /*
+ * Target.
+ */
+ dns_name_init(&name1, NULL);
+ dns_name_init(&name2, NULL);
+
+ dns_rdata_toregion(rdata1, &region1);
+ dns_rdata_toregion(rdata2, &region2);
+
+ isc_region_consume(&region1, 6);
+ isc_region_consume(&region2, 6);
+
+ dns_name_fromregion(&name1, &region1);
+ dns_name_fromregion(&name2, &region2);
+
+ return (dns_name_rdatacompare(&name1, &name2));
+}
+
+static inline isc_result_t
+fromstruct_in_srv(ARGS_FROMSTRUCT) {
+ dns_rdata_in_srv_t *srv = source;
+ isc_region_t region;
+
+ REQUIRE(type == 33);
+ REQUIRE(rdclass == 1);
+ REQUIRE(source != NULL);
+ REQUIRE(srv->common.rdtype == type);
+ REQUIRE(srv->common.rdclass == rdclass);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ RETERR(uint16_tobuffer(srv->priority, target));
+ RETERR(uint16_tobuffer(srv->weight, target));
+ RETERR(uint16_tobuffer(srv->port, target));
+ dns_name_toregion(&srv->target, &region);
+ return (isc_buffer_copyregion(target, &region));
+}
+
+static inline isc_result_t
+tostruct_in_srv(ARGS_TOSTRUCT) {
+ isc_region_t region;
+ dns_rdata_in_srv_t *srv = target;
+ dns_name_t name;
+
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(rdata->type == 33);
+ REQUIRE(target != NULL);
+ REQUIRE(rdata->length != 0);
+
+ srv->common.rdclass = rdata->rdclass;
+ srv->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&srv->common, link);
+
+ dns_name_init(&name, NULL);
+ dns_rdata_toregion(rdata, &region);
+ srv->priority = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ srv->weight = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ srv->port = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ dns_name_fromregion(&name, &region);
+ dns_name_init(&srv->target, NULL);
+ RETERR(name_duporclone(&name, mctx, &srv->target));
+ srv->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_in_srv(ARGS_FREESTRUCT) {
+ dns_rdata_in_srv_t *srv = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(srv->common.rdclass == 1);
+ REQUIRE(srv->common.rdtype == 33);
+
+ if (srv->mctx == NULL)
+ return;
+
+ dns_name_free(&srv->target, srv->mctx);
+ srv->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_in_srv(ARGS_ADDLDATA) {
+ dns_name_t name;
+ dns_offsets_t offsets;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 33);
+ REQUIRE(rdata->rdclass == 1);
+
+ dns_name_init(&name, offsets);
+ dns_rdata_toregion(rdata, &region);
+ isc_region_consume(&region, 6);
+ dns_name_fromregion(&name, &region);
+
+ return ((add)(arg, &name, dns_rdatatype_a));
+}
+
+static inline isc_result_t
+digest_in_srv(ARGS_DIGEST) {
+ isc_region_t r1, r2;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 33);
+ REQUIRE(rdata->rdclass == 1);
+
+ dns_rdata_toregion(rdata, &r1);
+ r2 = r1;
+ isc_region_consume(&r2, 6);
+ r1.length = 6;
+ RETERR((digest)(arg, &r1));
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &r2);
+ return (dns_name_digest(&name, digest, arg));
+}
+
+static inline isc_boolean_t
+checkowner_in_srv(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 33);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(name);
+ UNUSED(type);
+ UNUSED(rdclass);
+ UNUSED(wildcard);
+
+ return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+checknames_in_srv(ARGS_CHECKNAMES) {
+ isc_region_t region;
+ dns_name_t name;
+
+ REQUIRE(rdata->type == 33);
+ REQUIRE(rdata->rdclass == 1);
+
+ UNUSED(owner);
+
+ dns_rdata_toregion(rdata, &region);
+ isc_region_consume(&region, 6);
+ dns_name_init(&name, NULL);
+ dns_name_fromregion(&name, &region);
+ if (!dns_name_ishostname(&name, ISC_FALSE)) {
+ if (bad != NULL)
+ dns_name_clone(&name, bad);
+ return (ISC_FALSE);
+ }
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_IN_1_SRV_33_C */
diff --git a/lib/dns/rdata/in_1/srv_33.h b/lib/dns/rdata/in_1/srv_33.h
new file mode 100644
index 0000000..e019698
--- /dev/null
+++ b/lib/dns/rdata/in_1/srv_33.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+#ifndef IN_1_SRV_33_H
+#define IN_1_SRV_33_H 1
+
+/* $Id: srv_33.h,v 1.19 2007/06/19 23:47:17 tbox Exp $ */
+
+/* Reviewed: Fri Mar 17 13:01:00 PST 2000 by bwelling */
+
+/*!
+ * \brief Per RFC2782 */
+
+typedef struct dns_rdata_in_srv {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ isc_uint16_t priority;
+ isc_uint16_t weight;
+ isc_uint16_t port;
+ dns_name_t target;
+} dns_rdata_in_srv_t;
+
+#endif /* IN_1_SRV_33_H */
diff --git a/lib/dns/rdata/in_1/wks_11.c b/lib/dns/rdata/in_1/wks_11.c
new file mode 100644
index 0000000..fca9011
--- /dev/null
+++ b/lib/dns/rdata/in_1/wks_11.c
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: wks_11.c,v 1.54 2007/06/19 23:47:17 tbox Exp $ */
+
+/* Reviewed: Fri Mar 17 15:01:49 PST 2000 by explorer */
+
+#ifndef RDATA_IN_1_WKS_11_C
+#define RDATA_IN_1_WKS_11_C
+
+#include <limits.h>
+#include <stdlib.h>
+
+#include <isc/net.h>
+#include <isc/netdb.h>
+
+#define RRTYPE_WKS_ATTRIBUTES (0)
+
+static inline isc_result_t
+fromtext_in_wks(ARGS_FROMTEXT) {
+ isc_token_t token;
+ isc_region_t region;
+ struct in_addr addr;
+ struct protoent *pe;
+ struct servent *se;
+ char *e;
+ long proto;
+ unsigned char bm[8*1024]; /* 64k bits */
+ long port;
+ long maxport = -1;
+ const char *ps = NULL;
+ unsigned int n;
+ char service[32];
+ int i;
+
+ REQUIRE(type == 11);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(origin);
+ UNUSED(options);
+ UNUSED(rdclass);
+
+ /*
+ * IPv4 dotted quad.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+
+ isc_buffer_availableregion(target, &region);
+ if (getquad(DNS_AS_STR(token), &addr, lexer, callbacks) != 1)
+ RETTOK(DNS_R_BADDOTTEDQUAD);
+ if (region.length < 4)
+ return (ISC_R_NOSPACE);
+ memcpy(region.base, &addr, 4);
+ isc_buffer_add(target, 4);
+
+ /*
+ * Protocol.
+ */
+ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string,
+ ISC_FALSE));
+
+ proto = strtol(DNS_AS_STR(token), &e, 10);
+ if (*e == 0)
+ ;
+ else if ((pe = getprotobyname(DNS_AS_STR(token))) != NULL)
+ proto = pe->p_proto;
+ else
+ RETTOK(DNS_R_UNKNOWNPROTO);
+ if (proto < 0 || proto > 0xff)
+ RETTOK(ISC_R_RANGE);
+
+ if (proto == IPPROTO_TCP)
+ ps = "tcp";
+ else if (proto == IPPROTO_UDP)
+ ps = "udp";
+
+ RETERR(uint8_tobuffer(proto, target));
+
+ memset(bm, 0, sizeof(bm));
+ do {
+ RETERR(isc_lex_getmastertoken(lexer, &token,
+ isc_tokentype_string, ISC_TRUE));
+ if (token.type != isc_tokentype_string)
+ break;
+
+ /*
+ * Lowercase the service string as some getservbyname() are
+ * case sensitive and the database is usually in lowercase.
+ */
+ strncpy(service, DNS_AS_STR(token), sizeof(service));
+ service[sizeof(service)-1] = '\0';
+ for (i = strlen(service) - 1; i >= 0; i--)
+ if (isupper(service[i]&0xff))
+ service[i] = tolower(service[i]&0xff);
+
+ port = strtol(DNS_AS_STR(token), &e, 10);
+ if (*e == 0)
+ ;
+ else if ((se = getservbyname(service, ps)) != NULL)
+ port = ntohs(se->s_port);
+ else if ((se = getservbyname(DNS_AS_STR(token), ps))
+ != NULL)
+ port = ntohs(se->s_port);
+ else
+ RETTOK(DNS_R_UNKNOWNSERVICE);
+ if (port < 0 || port > 0xffff)
+ RETTOK(ISC_R_RANGE);
+ if (port > maxport)
+ maxport = port;
+ bm[port / 8] |= (0x80 >> (port % 8));
+ } while (1);
+
+ /*
+ * Let upper layer handle eol/eof.
+ */
+ isc_lex_ungettoken(lexer, &token);
+
+ n = (maxport + 8) / 8;
+ return (mem_tobuffer(target, bm, n));
+}
+
+static inline isc_result_t
+totext_in_wks(ARGS_TOTEXT) {
+ isc_region_t sr;
+ unsigned short proto;
+ char buf[sizeof("65535")];
+ unsigned int i, j;
+
+ UNUSED(tctx);
+
+ REQUIRE(rdata->type == 11);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(rdata->length >= 5);
+
+ dns_rdata_toregion(rdata, &sr);
+ RETERR(inet_totext(AF_INET, &sr, target));
+ isc_region_consume(&sr, 4);
+
+ proto = uint8_fromregion(&sr);
+ sprintf(buf, "%u", proto);
+ RETERR(str_totext(" ", target));
+ RETERR(str_totext(buf, target));
+ isc_region_consume(&sr, 1);
+
+ for (i = 0; i < sr.length; i++) {
+ if (sr.base[i] != 0)
+ for (j = 0; j < 8; j++)
+ if ((sr.base[i] & (0x80 >> j)) != 0) {
+ sprintf(buf, "%u", i * 8 + j);
+ RETERR(str_totext(" ", target));
+ RETERR(str_totext(buf, target));
+ }
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+fromwire_in_wks(ARGS_FROMWIRE) {
+ isc_region_t sr;
+ isc_region_t tr;
+
+ REQUIRE(type == 11);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(dctx);
+ UNUSED(options);
+ UNUSED(rdclass);
+
+ isc_buffer_activeregion(source, &sr);
+ isc_buffer_availableregion(target, &tr);
+
+ if (sr.length < 5)
+ return (ISC_R_UNEXPECTEDEND);
+ if (sr.length > 8 * 1024 + 5)
+ return (DNS_R_EXTRADATA);
+ if (tr.length < sr.length)
+ return (ISC_R_NOSPACE);
+
+ memcpy(tr.base, sr.base, sr.length);
+ isc_buffer_add(target, sr.length);
+ isc_buffer_forward(source, sr.length);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+towire_in_wks(ARGS_TOWIRE) {
+ isc_region_t sr;
+
+ UNUSED(cctx);
+
+ REQUIRE(rdata->type == 11);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(rdata->length != 0);
+
+ dns_rdata_toregion(rdata, &sr);
+ return (mem_tobuffer(target, sr.base, sr.length));
+}
+
+static inline int
+compare_in_wks(ARGS_COMPARE) {
+ isc_region_t r1;
+ isc_region_t r2;
+
+ REQUIRE(rdata1->type == rdata2->type);
+ REQUIRE(rdata1->rdclass == rdata2->rdclass);
+ REQUIRE(rdata1->type == 11);
+ REQUIRE(rdata1->rdclass == 1);
+ REQUIRE(rdata1->length != 0);
+ REQUIRE(rdata2->length != 0);
+
+ dns_rdata_toregion(rdata1, &r1);
+ dns_rdata_toregion(rdata2, &r2);
+ return (isc_region_compare(&r1, &r2));
+}
+
+static inline isc_result_t
+fromstruct_in_wks(ARGS_FROMSTRUCT) {
+ dns_rdata_in_wks_t *wks = source;
+ isc_uint32_t a;
+
+ REQUIRE(type == 11);
+ REQUIRE(rdclass == 1);
+ REQUIRE(source != NULL);
+ REQUIRE(wks->common.rdtype == type);
+ REQUIRE(wks->common.rdclass == rdclass);
+ REQUIRE(wks->map != NULL || wks->map_len == 0);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ a = ntohl(wks->in_addr.s_addr);
+ RETERR(uint32_tobuffer(a, target));
+ RETERR(uint16_tobuffer(wks->protocol, target));
+ return (mem_tobuffer(target, wks->map, wks->map_len));
+}
+
+static inline isc_result_t
+tostruct_in_wks(ARGS_TOSTRUCT) {
+ dns_rdata_in_wks_t *wks = target;
+ isc_uint32_t n;
+ isc_region_t region;
+
+ REQUIRE(rdata->type == 11);
+ REQUIRE(rdata->rdclass == 1);
+ REQUIRE(rdata->length != 0);
+
+ wks->common.rdclass = rdata->rdclass;
+ wks->common.rdtype = rdata->type;
+ ISC_LINK_INIT(&wks->common, link);
+
+ dns_rdata_toregion(rdata, &region);
+ n = uint32_fromregion(&region);
+ wks->in_addr.s_addr = htonl(n);
+ isc_region_consume(&region, 4);
+ wks->protocol = uint16_fromregion(&region);
+ isc_region_consume(&region, 2);
+ wks->map_len = region.length;
+ wks->map = mem_maybedup(mctx, region.base, region.length);
+ if (wks->map == NULL)
+ return (ISC_R_NOMEMORY);
+ wks->mctx = mctx;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+freestruct_in_wks(ARGS_FREESTRUCT) {
+ dns_rdata_in_wks_t *wks = source;
+
+ REQUIRE(source != NULL);
+ REQUIRE(wks->common.rdtype == 11);
+ REQUIRE(wks->common.rdclass == 1);
+
+ if (wks->mctx == NULL)
+ return;
+
+ if (wks->map != NULL)
+ isc_mem_free(wks->mctx, wks->map);
+ wks->mctx = NULL;
+}
+
+static inline isc_result_t
+additionaldata_in_wks(ARGS_ADDLDATA) {
+ UNUSED(rdata);
+ UNUSED(add);
+ UNUSED(arg);
+
+ REQUIRE(rdata->type == 11);
+ REQUIRE(rdata->rdclass == 1);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+digest_in_wks(ARGS_DIGEST) {
+ isc_region_t r;
+
+ REQUIRE(rdata->type == 11);
+ REQUIRE(rdata->rdclass == 1);
+
+ dns_rdata_toregion(rdata, &r);
+
+ return ((digest)(arg, &r));
+}
+
+static inline isc_boolean_t
+checkowner_in_wks(ARGS_CHECKOWNER) {
+
+ REQUIRE(type == 11);
+ REQUIRE(rdclass == 1);
+
+ UNUSED(type);
+ UNUSED(rdclass);
+
+ return (dns_name_ishostname(name, wildcard));
+}
+
+static inline isc_boolean_t
+checknames_in_wks(ARGS_CHECKNAMES) {
+
+ REQUIRE(rdata->type == 11);
+ REQUIRE(rdata->rdclass == 1);
+
+ UNUSED(rdata);
+ UNUSED(owner);
+ UNUSED(bad);
+
+ return (ISC_TRUE);
+}
+
+#endif /* RDATA_IN_1_WKS_11_C */
diff --git a/lib/dns/rdata/in_1/wks_11.h b/lib/dns/rdata/in_1/wks_11.h
new file mode 100644
index 0000000..2fd26e8
--- /dev/null
+++ b/lib/dns/rdata/in_1/wks_11.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+#ifndef IN_1_WKS_11_H
+#define IN_1_WKS_11_H 1
+
+/* $Id: wks_11.h,v 1.22 2007/06/19 23:47:17 tbox Exp $ */
+
+typedef struct dns_rdata_in_wks {
+ dns_rdatacommon_t common;
+ isc_mem_t *mctx;
+ struct in_addr in_addr;
+ isc_uint16_t protocol;
+ unsigned char *map;
+ isc_uint16_t map_len;
+} dns_rdata_in_wks_t;
+
+#endif /* IN_1_WKS_11_H */
diff --git a/lib/dns/rdata/rdatastructpre.h b/lib/dns/rdata/rdatastructpre.h
new file mode 100644
index 0000000..ab7e051
--- /dev/null
+++ b/lib/dns/rdata/rdatastructpre.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rdatastructpre.h,v 1.16 2007/06/19 23:47:17 tbox Exp $ */
+
+#ifndef DNS_RDATASTRUCT_H
+#define DNS_RDATASTRUCT_H 1
+
+#include <isc/lang.h>
+#include <isc/sockaddr.h>
+
+#include <dns/name.h>
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+typedef struct dns_rdatacommon {
+ dns_rdataclass_t rdclass;
+ dns_rdatatype_t rdtype;
+ ISC_LINK(struct dns_rdatacommon) link;
+} dns_rdatacommon_t;
+
+#define DNS_RDATACOMMON_INIT(_data, _rdtype, _rdclass) \
+ do { \
+ (_data)->common.rdtype = (_rdtype); \
+ (_data)->common.rdclass = (_rdclass); \
+ ISC_LINK_INIT(&(_data)->common, link); \
+ } while (0)
diff --git a/lib/dns/rdata/rdatastructsuf.h b/lib/dns/rdata/rdatastructsuf.h
new file mode 100644
index 0000000..3ba1275
--- /dev/null
+++ b/lib/dns/rdata/rdatastructsuf.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rdatastructsuf.h,v 1.10 2007/06/19 23:47:17 tbox Exp $ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_RDATASTRUCT_H */
diff --git a/lib/dns/rdatalist.c b/lib/dns/rdatalist.c
new file mode 100644
index 0000000..d6f11ae
--- /dev/null
+++ b/lib/dns/rdatalist.c
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001, 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rdatalist.c,v 1.36 2008/09/24 02:46:22 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stddef.h>
+
+#include <isc/util.h>
+
+#include <dns/name.h>
+#include <dns/nsec3.h>
+#include <dns/rdata.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+
+#include "rdatalist_p.h"
+
+static dns_rdatasetmethods_t methods = {
+ isc__rdatalist_disassociate,
+ isc__rdatalist_first,
+ isc__rdatalist_next,
+ isc__rdatalist_current,
+ isc__rdatalist_clone,
+ isc__rdatalist_count,
+ isc__rdatalist_addnoqname,
+ isc__rdatalist_getnoqname,
+ isc__rdatalist_addclosest,
+ isc__rdatalist_getclosest,
+ NULL,
+ NULL,
+ NULL
+};
+
+void
+dns_rdatalist_init(dns_rdatalist_t *rdatalist) {
+
+ /*
+ * Initialize rdatalist.
+ */
+
+ rdatalist->rdclass = 0;
+ rdatalist->type = 0;
+ rdatalist->covers = 0;
+ rdatalist->ttl = 0;
+ ISC_LIST_INIT(rdatalist->rdata);
+ ISC_LINK_INIT(rdatalist, link);
+}
+
+isc_result_t
+dns_rdatalist_tordataset(dns_rdatalist_t *rdatalist,
+ dns_rdataset_t *rdataset)
+{
+ /*
+ * Make 'rdataset' refer to the rdata in 'rdatalist'.
+ */
+
+ REQUIRE(rdatalist != NULL);
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(! dns_rdataset_isassociated(rdataset));
+
+ rdataset->methods = &methods;
+ rdataset->rdclass = rdatalist->rdclass;
+ rdataset->type = rdatalist->type;
+ rdataset->covers = rdatalist->covers;
+ rdataset->ttl = rdatalist->ttl;
+ rdataset->trust = 0;
+ rdataset->private1 = rdatalist;
+ rdataset->private2 = NULL;
+ rdataset->private3 = NULL;
+ rdataset->privateuint4 = 0;
+ rdataset->private5 = NULL;
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_rdatalist_fromrdataset(dns_rdataset_t *rdataset,
+ dns_rdatalist_t **rdatalist)
+{
+ REQUIRE(rdatalist != NULL && rdataset != NULL);
+ *rdatalist = rdataset->private1;
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc__rdatalist_disassociate(dns_rdataset_t *rdataset) {
+ UNUSED(rdataset);
+}
+
+isc_result_t
+isc__rdatalist_first(dns_rdataset_t *rdataset) {
+ dns_rdatalist_t *rdatalist;
+
+ rdatalist = rdataset->private1;
+ rdataset->private2 = ISC_LIST_HEAD(rdatalist->rdata);
+
+ if (rdataset->private2 == NULL)
+ return (ISC_R_NOMORE);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc__rdatalist_next(dns_rdataset_t *rdataset) {
+ dns_rdata_t *rdata;
+
+ rdata = rdataset->private2;
+ if (rdata == NULL)
+ return (ISC_R_NOMORE);
+
+ rdataset->private2 = ISC_LIST_NEXT(rdata, link);
+
+ if (rdataset->private2 == NULL)
+ return (ISC_R_NOMORE);
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+isc__rdatalist_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) {
+ dns_rdata_t *list_rdata;
+
+ list_rdata = rdataset->private2;
+ INSIST(list_rdata != NULL);
+
+ dns_rdata_clone(list_rdata, rdata);
+}
+
+void
+isc__rdatalist_clone(dns_rdataset_t *source, dns_rdataset_t *target) {
+ *target = *source;
+
+ /*
+ * Reset iterator state.
+ */
+ target->private2 = NULL;
+}
+
+unsigned int
+isc__rdatalist_count(dns_rdataset_t *rdataset) {
+ dns_rdatalist_t *rdatalist;
+ dns_rdata_t *rdata;
+ unsigned int count;
+
+ rdatalist = rdataset->private1;
+
+ count = 0;
+ for (rdata = ISC_LIST_HEAD(rdatalist->rdata);
+ rdata != NULL;
+ rdata = ISC_LIST_NEXT(rdata, link))
+ count++;
+
+ return (count);
+}
+
+isc_result_t
+isc__rdatalist_addnoqname(dns_rdataset_t *rdataset, dns_name_t *name) {
+ dns_rdataset_t *neg = NULL;
+ dns_rdataset_t *negsig = NULL;
+ dns_rdataset_t *rdset;
+ dns_ttl_t ttl;
+
+ for (rdset = ISC_LIST_HEAD(name->list);
+ rdset != NULL;
+ rdset = ISC_LIST_NEXT(rdset, link))
+ {
+ if (rdset->rdclass != rdataset->rdclass)
+ continue;
+ if (rdset->type == dns_rdatatype_nsec ||
+ rdset->type == dns_rdatatype_nsec3)
+ neg = rdset;
+ }
+ if (neg == NULL)
+ return (ISC_R_NOTFOUND);
+
+ for (rdset = ISC_LIST_HEAD(name->list);
+ rdset != NULL;
+ rdset = ISC_LIST_NEXT(rdset, link))
+ {
+ if (rdset->type == dns_rdatatype_rrsig &&
+ rdset->covers == neg->type)
+ negsig = rdset;
+ }
+
+ if (negsig == NULL)
+ return (ISC_R_NOTFOUND);
+ /*
+ * Minimise ttl.
+ */
+ ttl = rdataset->ttl;
+ if (neg->ttl < ttl)
+ ttl = neg->ttl;
+ if (negsig->ttl < ttl)
+ ttl = negsig->ttl;
+ rdataset->ttl = neg->ttl = negsig->ttl = ttl;
+ rdataset->attributes |= DNS_RDATASETATTR_NOQNAME;
+ rdataset->private6 = name;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc__rdatalist_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name,
+ dns_rdataset_t *neg, dns_rdataset_t *negsig)
+{
+ dns_rdataclass_t rdclass = rdataset->rdclass;
+ dns_rdataset_t *tneg = NULL;
+ dns_rdataset_t *tnegsig = NULL;
+ dns_name_t *noqname = rdataset->private6;
+
+ REQUIRE((rdataset->attributes & DNS_RDATASETATTR_NOQNAME) != 0);
+ (void)dns_name_dynamic(noqname); /* Sanity Check. */
+
+ for (rdataset = ISC_LIST_HEAD(noqname->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link))
+ {
+ if (rdataset->rdclass != rdclass)
+ continue;
+ if (rdataset->type == dns_rdatatype_nsec ||
+ rdataset->type == dns_rdatatype_nsec3)
+ tneg = rdataset;
+ }
+ if (tneg == NULL)
+ return (ISC_R_NOTFOUND);
+
+ for (rdataset = ISC_LIST_HEAD(noqname->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link))
+ {
+ if (rdataset->type == dns_rdatatype_rrsig &&
+ rdataset->covers == tneg->type)
+ tnegsig = rdataset;
+ }
+ if (tnegsig == NULL)
+ return (ISC_R_NOTFOUND);
+
+ dns_name_clone(noqname, name);
+ dns_rdataset_clone(tneg, neg);
+ dns_rdataset_clone(tnegsig, negsig);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc__rdatalist_addclosest(dns_rdataset_t *rdataset, dns_name_t *name) {
+ dns_rdataset_t *neg = NULL;
+ dns_rdataset_t *negsig = NULL;
+ dns_rdataset_t *rdset;
+ dns_ttl_t ttl;
+
+ for (rdset = ISC_LIST_HEAD(name->list);
+ rdset != NULL;
+ rdset = ISC_LIST_NEXT(rdset, link))
+ {
+ if (rdset->rdclass != rdataset->rdclass)
+ continue;
+ if (rdset->type == dns_rdatatype_nsec ||
+ rdset->type == dns_rdatatype_nsec3)
+ neg = rdset;
+ }
+ if (neg == NULL)
+ return (ISC_R_NOTFOUND);
+
+ for (rdset = ISC_LIST_HEAD(name->list);
+ rdset != NULL;
+ rdset = ISC_LIST_NEXT(rdset, link))
+ {
+ if (rdset->type == dns_rdatatype_rrsig &&
+ rdset->covers == neg->type)
+ negsig = rdset;
+ }
+
+ if (negsig == NULL)
+ return (ISC_R_NOTFOUND);
+ /*
+ * Minimise ttl.
+ */
+ ttl = rdataset->ttl;
+ if (neg->ttl < ttl)
+ ttl = neg->ttl;
+ if (negsig->ttl < ttl)
+ ttl = negsig->ttl;
+ rdataset->ttl = neg->ttl = negsig->ttl = ttl;
+ rdataset->attributes |= DNS_RDATASETATTR_CLOSEST;
+ rdataset->private7 = name;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc__rdatalist_getclosest(dns_rdataset_t *rdataset, dns_name_t *name,
+ dns_rdataset_t *neg, dns_rdataset_t *negsig)
+{
+ dns_rdataclass_t rdclass = rdataset->rdclass;
+ dns_rdataset_t *tneg = NULL;
+ dns_rdataset_t *tnegsig = NULL;
+ dns_name_t *closest = rdataset->private7;
+
+ REQUIRE((rdataset->attributes & DNS_RDATASETATTR_CLOSEST) != 0);
+ (void)dns_name_dynamic(closest); /* Sanity Check. */
+
+ for (rdataset = ISC_LIST_HEAD(closest->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link))
+ {
+ if (rdataset->rdclass != rdclass)
+ continue;
+ if (rdataset->type == dns_rdatatype_nsec ||
+ rdataset->type == dns_rdatatype_nsec3)
+ tneg = rdataset;
+ }
+ if (tneg == NULL)
+ return (ISC_R_NOTFOUND);
+
+ for (rdataset = ISC_LIST_HEAD(closest->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link))
+ {
+ if (rdataset->type == dns_rdatatype_rrsig &&
+ rdataset->covers == tneg->type)
+ tnegsig = rdataset;
+ }
+ if (tnegsig == NULL)
+ return (ISC_R_NOTFOUND);
+
+ dns_name_clone(closest, name);
+ dns_rdataset_clone(tneg, neg);
+ dns_rdataset_clone(tnegsig, negsig);
+ return (ISC_R_SUCCESS);
+}
diff --git a/lib/dns/rdatalist_p.h b/lib/dns/rdatalist_p.h
new file mode 100644
index 0000000..3e73e20
--- /dev/null
+++ b/lib/dns/rdatalist_p.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rdatalist_p.h,v 1.11 2008/09/25 04:02:38 tbox Exp $ */
+
+#ifndef DNS_RDATALIST_P_H
+#define DNS_RDATALIST_P_H
+
+/*! \file */
+
+#include <isc/result.h>
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+void
+isc__rdatalist_disassociate(dns_rdataset_t *rdatasetp);
+
+isc_result_t
+isc__rdatalist_first(dns_rdataset_t *rdataset);
+
+isc_result_t
+isc__rdatalist_next(dns_rdataset_t *rdataset);
+
+void
+isc__rdatalist_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata);
+
+void
+isc__rdatalist_clone(dns_rdataset_t *source, dns_rdataset_t *target);
+
+unsigned int
+isc__rdatalist_count(dns_rdataset_t *rdataset);
+
+isc_result_t
+isc__rdatalist_addnoqname(dns_rdataset_t *rdataset, dns_name_t *name);
+
+isc_result_t
+isc__rdatalist_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name,
+ dns_rdataset_t *neg, dns_rdataset_t *negsig);
+
+isc_result_t
+isc__rdatalist_addclosest(dns_rdataset_t *rdataset, dns_name_t *name);
+
+isc_result_t
+isc__rdatalist_getclosest(dns_rdataset_t *rdataset, dns_name_t *name,
+ dns_rdataset_t *neg, dns_rdataset_t *negsig);
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_RDATALIST_P_H */
diff --git a/lib/dns/rdataset.c b/lib/dns/rdataset.c
new file mode 100644
index 0000000..451c6b8
--- /dev/null
+++ b/lib/dns/rdataset.c
@@ -0,0 +1,734 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rdataset.c,v 1.82 2008/09/24 02:46:22 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdlib.h>
+
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/random.h>
+#include <isc/util.h>
+
+#include <dns/name.h>
+#include <dns/ncache.h>
+#include <dns/rdata.h>
+#include <dns/rdataset.h>
+#include <dns/compress.h>
+
+void
+dns_rdataset_init(dns_rdataset_t *rdataset) {
+
+ /*
+ * Make 'rdataset' a valid, disassociated rdataset.
+ */
+
+ REQUIRE(rdataset != NULL);
+
+ rdataset->magic = DNS_RDATASET_MAGIC;
+ rdataset->methods = NULL;
+ ISC_LINK_INIT(rdataset, link);
+ rdataset->rdclass = 0;
+ rdataset->type = 0;
+ rdataset->ttl = 0;
+ rdataset->trust = 0;
+ rdataset->covers = 0;
+ rdataset->attributes = 0;
+ rdataset->count = ISC_UINT32_MAX;
+ rdataset->private1 = NULL;
+ rdataset->private2 = NULL;
+ rdataset->private3 = NULL;
+ rdataset->privateuint4 = 0;
+ rdataset->private5 = NULL;
+ rdataset->private6 = NULL;
+ rdataset->resign = 0;
+}
+
+void
+dns_rdataset_invalidate(dns_rdataset_t *rdataset) {
+
+ /*
+ * Invalidate 'rdataset'.
+ */
+
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(rdataset->methods == NULL);
+
+ rdataset->magic = 0;
+ ISC_LINK_INIT(rdataset, link);
+ rdataset->rdclass = 0;
+ rdataset->type = 0;
+ rdataset->ttl = 0;
+ rdataset->trust = 0;
+ rdataset->covers = 0;
+ rdataset->attributes = 0;
+ rdataset->count = ISC_UINT32_MAX;
+ rdataset->private1 = NULL;
+ rdataset->private2 = NULL;
+ rdataset->private3 = NULL;
+ rdataset->privateuint4 = 0;
+ rdataset->private5 = NULL;
+}
+
+void
+dns_rdataset_disassociate(dns_rdataset_t *rdataset) {
+
+ /*
+ * Disassociate 'rdataset' from its rdata, allowing it to be reused.
+ */
+
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(rdataset->methods != NULL);
+
+ (rdataset->methods->disassociate)(rdataset);
+ rdataset->methods = NULL;
+ ISC_LINK_INIT(rdataset, link);
+ rdataset->rdclass = 0;
+ rdataset->type = 0;
+ rdataset->ttl = 0;
+ rdataset->trust = 0;
+ rdataset->covers = 0;
+ rdataset->attributes = 0;
+ rdataset->count = ISC_UINT32_MAX;
+ rdataset->private1 = NULL;
+ rdataset->private2 = NULL;
+ rdataset->private3 = NULL;
+ rdataset->privateuint4 = 0;
+ rdataset->private5 = NULL;
+ rdataset->private6 = NULL;
+}
+
+isc_boolean_t
+dns_rdataset_isassociated(dns_rdataset_t *rdataset) {
+ /*
+ * Is 'rdataset' associated?
+ */
+
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+
+ if (rdataset->methods != NULL)
+ return (ISC_TRUE);
+
+ return (ISC_FALSE);
+}
+
+static void
+question_disassociate(dns_rdataset_t *rdataset) {
+ UNUSED(rdataset);
+}
+
+static isc_result_t
+question_cursor(dns_rdataset_t *rdataset) {
+ UNUSED(rdataset);
+
+ return (ISC_R_NOMORE);
+}
+
+static void
+question_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) {
+ /*
+ * This routine should never be called.
+ */
+ UNUSED(rdataset);
+ UNUSED(rdata);
+
+ REQUIRE(0);
+}
+
+static void
+question_clone(dns_rdataset_t *source, dns_rdataset_t *target) {
+ *target = *source;
+}
+
+static unsigned int
+question_count(dns_rdataset_t *rdataset) {
+ /*
+ * This routine should never be called.
+ */
+ UNUSED(rdataset);
+ REQUIRE(0);
+
+ return (0);
+}
+
+static dns_rdatasetmethods_t question_methods = {
+ question_disassociate,
+ question_cursor,
+ question_cursor,
+ question_current,
+ question_clone,
+ question_count,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+void
+dns_rdataset_makequestion(dns_rdataset_t *rdataset, dns_rdataclass_t rdclass,
+ dns_rdatatype_t type)
+{
+
+ /*
+ * Make 'rdataset' a valid, associated, question rdataset, with a
+ * question class of 'rdclass' and type 'type'.
+ */
+
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(rdataset->methods == NULL);
+
+ rdataset->methods = &question_methods;
+ rdataset->rdclass = rdclass;
+ rdataset->type = type;
+ rdataset->attributes |= DNS_RDATASETATTR_QUESTION;
+}
+
+unsigned int
+dns_rdataset_count(dns_rdataset_t *rdataset) {
+
+ /*
+ * Return the number of records in 'rdataset'.
+ */
+
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(rdataset->methods != NULL);
+
+ return ((rdataset->methods->count)(rdataset));
+}
+
+void
+dns_rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target) {
+
+ /*
+ * Make 'target' refer to the same rdataset as 'source'.
+ */
+
+ REQUIRE(DNS_RDATASET_VALID(source));
+ REQUIRE(source->methods != NULL);
+ REQUIRE(DNS_RDATASET_VALID(target));
+ REQUIRE(target->methods == NULL);
+
+ (source->methods->clone)(source, target);
+}
+
+isc_result_t
+dns_rdataset_first(dns_rdataset_t *rdataset) {
+
+ /*
+ * Move the rdata cursor to the first rdata in the rdataset (if any).
+ */
+
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(rdataset->methods != NULL);
+
+ return ((rdataset->methods->first)(rdataset));
+}
+
+isc_result_t
+dns_rdataset_next(dns_rdataset_t *rdataset) {
+
+ /*
+ * Move the rdata cursor to the next rdata in the rdataset (if any).
+ */
+
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(rdataset->methods != NULL);
+
+ return ((rdataset->methods->next)(rdataset));
+}
+
+void
+dns_rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) {
+
+ /*
+ * Make 'rdata' refer to the current rdata.
+ */
+
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(rdataset->methods != NULL);
+
+ (rdataset->methods->current)(rdataset, rdata);
+}
+
+#define MAX_SHUFFLE 32
+#define WANT_FIXED(r) (((r)->attributes & DNS_RDATASETATTR_FIXEDORDER) != 0)
+#define WANT_RANDOM(r) (((r)->attributes & DNS_RDATASETATTR_RANDOMIZE) != 0)
+
+struct towire_sort {
+ int key;
+ dns_rdata_t *rdata;
+};
+
+static int
+towire_compare(const void *av, const void *bv) {
+ const struct towire_sort *a = (const struct towire_sort *) av;
+ const struct towire_sort *b = (const struct towire_sort *) bv;
+ return (a->key - b->key);
+}
+
+static isc_result_t
+towiresorted(dns_rdataset_t *rdataset, const dns_name_t *owner_name,
+ dns_compress_t *cctx, isc_buffer_t *target,
+ dns_rdatasetorderfunc_t order, const void *order_arg,
+ isc_boolean_t partial, unsigned int options,
+ unsigned int *countp, void **state)
+{
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_region_t r;
+ isc_result_t result;
+ unsigned int i, count, added, choice;
+ isc_buffer_t savedbuffer, rdlen, rrbuffer;
+ unsigned int headlen;
+ isc_boolean_t question = ISC_FALSE;
+ isc_boolean_t shuffle = ISC_FALSE;
+ dns_rdata_t *shuffled = NULL, shuffled_fixed[MAX_SHUFFLE];
+ struct towire_sort *sorted = NULL, sorted_fixed[MAX_SHUFFLE];
+
+ UNUSED(state);
+
+ /*
+ * Convert 'rdataset' to wire format, compressing names as specified
+ * in cctx, and storing the result in 'target'.
+ */
+
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(countp != NULL);
+ REQUIRE((order == NULL) == (order_arg == NULL));
+ REQUIRE(cctx != NULL && cctx->mctx != NULL);
+
+ count = 0;
+ if ((rdataset->attributes & DNS_RDATASETATTR_QUESTION) != 0) {
+ question = ISC_TRUE;
+ count = 1;
+ result = dns_rdataset_first(rdataset);
+ INSIST(result == ISC_R_NOMORE);
+ } else if (rdataset->type == 0) {
+ /*
+ * This is a negative caching rdataset.
+ */
+ unsigned int ncache_opts = 0;
+ if ((options & DNS_RDATASETTOWIRE_OMITDNSSEC) != 0)
+ ncache_opts |= DNS_NCACHETOWIRE_OMITDNSSEC;
+ return (dns_ncache_towire(rdataset, cctx, target, ncache_opts,
+ countp));
+ } else {
+ count = (rdataset->methods->count)(rdataset);
+ result = dns_rdataset_first(rdataset);
+ if (result == ISC_R_NOMORE)
+ return (ISC_R_SUCCESS);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ /*
+ * Do we want to shuffle this anwer?
+ */
+ if (!question && count > 1 &&
+ (!WANT_FIXED(rdataset) || order != NULL) &&
+ rdataset->type != dns_rdatatype_rrsig)
+ shuffle = ISC_TRUE;
+
+ if (shuffle && count > MAX_SHUFFLE) {
+ shuffled = isc_mem_get(cctx->mctx, count * sizeof(*shuffled));
+ sorted = isc_mem_get(cctx->mctx, count * sizeof(*sorted));
+ if (shuffled == NULL || sorted == NULL)
+ shuffle = ISC_FALSE;
+ } else {
+ shuffled = shuffled_fixed;
+ sorted = sorted_fixed;
+ }
+
+ if (shuffle) {
+ /*
+ * First we get handles to all of the rdata.
+ */
+ i = 0;
+ do {
+ INSIST(i < count);
+ dns_rdata_init(&shuffled[i]);
+ dns_rdataset_current(rdataset, &shuffled[i]);
+ i++;
+ result = dns_rdataset_next(rdataset);
+ } while (result == ISC_R_SUCCESS);
+ if (result != ISC_R_NOMORE)
+ goto cleanup;
+ INSIST(i == count);
+
+ /*
+ * Now we shuffle.
+ */
+ if (WANT_FIXED(rdataset)) {
+ /*
+ * 'Fixed' order.
+ */
+ INSIST(order != NULL);
+ for (i = 0; i < count; i++) {
+ sorted[i].key = (*order)(&shuffled[i],
+ order_arg);
+ sorted[i].rdata = &shuffled[i];
+ }
+ } else if (WANT_RANDOM(rdataset)) {
+ /*
+ * 'Random' order.
+ */
+ for (i = 0; i < count; i++) {
+ dns_rdata_t rdata;
+ isc_uint32_t val;
+
+ isc_random_get(&val);
+ choice = i + (val % (count - i));
+ rdata = shuffled[i];
+ shuffled[i] = shuffled[choice];
+ shuffled[choice] = rdata;
+ if (order != NULL)
+ sorted[i].key = (*order)(&shuffled[i],
+ order_arg);
+ else
+ sorted[i].key = 0; /* Unused */
+ sorted[i].rdata = &shuffled[i];
+ }
+ } else {
+ /*
+ * "Cyclic" order.
+ */
+ isc_uint32_t val;
+ unsigned int j;
+
+ val = rdataset->count;
+ if (val == ISC_UINT32_MAX)
+ isc_random_get(&val);
+ j = val % count;
+ for (i = 0; i < count; i++) {
+ if (order != NULL)
+ sorted[j].key = (*order)(&shuffled[i],
+ order_arg);
+ else
+ sorted[j].key = 0; /* Unused */
+ sorted[j].rdata = &shuffled[i];
+ j++;
+ if (j == count)
+ j = 0; /* Wrap around. */
+ }
+ }
+
+ /*
+ * Sorted order.
+ */
+ if (order != NULL)
+ qsort(sorted, count, sizeof(sorted[0]),
+ towire_compare);
+ }
+
+ savedbuffer = *target;
+ i = 0;
+ added = 0;
+
+ do {
+ /*
+ * Copy out the name, type, class, ttl.
+ */
+
+ rrbuffer = *target;
+ dns_compress_setmethods(cctx, DNS_COMPRESS_GLOBAL14);
+ result = dns_name_towire(owner_name, cctx, target);
+ if (result != ISC_R_SUCCESS)
+ goto rollback;
+ headlen = sizeof(dns_rdataclass_t) + sizeof(dns_rdatatype_t);
+ if (!question)
+ headlen += sizeof(dns_ttl_t)
+ + 2; /* XXX 2 for rdata len */
+ isc_buffer_availableregion(target, &r);
+ if (r.length < headlen) {
+ result = ISC_R_NOSPACE;
+ goto rollback;
+ }
+ isc_buffer_putuint16(target, rdataset->type);
+ isc_buffer_putuint16(target, rdataset->rdclass);
+ if (!question) {
+ isc_buffer_putuint32(target, rdataset->ttl);
+
+ /*
+ * Save space for rdlen.
+ */
+ rdlen = *target;
+ isc_buffer_add(target, 2);
+
+ /*
+ * Copy out the rdata
+ */
+ if (shuffle)
+ rdata = *(sorted[i].rdata);
+ else {
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(rdataset, &rdata);
+ }
+ result = dns_rdata_towire(&rdata, cctx, target);
+ if (result != ISC_R_SUCCESS)
+ goto rollback;
+ INSIST((target->used >= rdlen.used + 2) &&
+ (target->used - rdlen.used - 2 < 65536));
+ isc_buffer_putuint16(&rdlen,
+ (isc_uint16_t)(target->used -
+ rdlen.used - 2));
+ added++;
+ }
+
+ if (shuffle) {
+ i++;
+ if (i == count)
+ result = ISC_R_NOMORE;
+ else
+ result = ISC_R_SUCCESS;
+ } else {
+ result = dns_rdataset_next(rdataset);
+ }
+ } while (result == ISC_R_SUCCESS);
+
+ if (result != ISC_R_NOMORE)
+ goto rollback;
+
+ *countp += count;
+
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+
+ rollback:
+ if (partial && result == ISC_R_NOSPACE) {
+ INSIST(rrbuffer.used < 65536);
+ dns_compress_rollback(cctx, (isc_uint16_t)rrbuffer.used);
+ *countp += added;
+ *target = rrbuffer;
+ goto cleanup;
+ }
+ INSIST(savedbuffer.used < 65536);
+ dns_compress_rollback(cctx, (isc_uint16_t)savedbuffer.used);
+ *countp = 0;
+ *target = savedbuffer;
+
+ cleanup:
+ if (sorted != NULL && sorted != sorted_fixed)
+ isc_mem_put(cctx->mctx, sorted, count * sizeof(*sorted));
+ if (shuffled != NULL && shuffled != shuffled_fixed)
+ isc_mem_put(cctx->mctx, shuffled, count * sizeof(*shuffled));
+ return (result);
+}
+
+isc_result_t
+dns_rdataset_towiresorted(dns_rdataset_t *rdataset,
+ const dns_name_t *owner_name,
+ dns_compress_t *cctx,
+ isc_buffer_t *target,
+ dns_rdatasetorderfunc_t order,
+ const void *order_arg,
+ unsigned int options,
+ unsigned int *countp)
+{
+ return (towiresorted(rdataset, owner_name, cctx, target,
+ order, order_arg, ISC_FALSE, options,
+ countp, NULL));
+}
+
+isc_result_t
+dns_rdataset_towirepartial(dns_rdataset_t *rdataset,
+ const dns_name_t *owner_name,
+ dns_compress_t *cctx,
+ isc_buffer_t *target,
+ dns_rdatasetorderfunc_t order,
+ const void *order_arg,
+ unsigned int options,
+ unsigned int *countp,
+ void **state)
+{
+ REQUIRE(state == NULL); /* XXX remove when implemented */
+ return (towiresorted(rdataset, owner_name, cctx, target,
+ order, order_arg, ISC_TRUE, options,
+ countp, state));
+}
+
+isc_result_t
+dns_rdataset_towire(dns_rdataset_t *rdataset,
+ dns_name_t *owner_name,
+ dns_compress_t *cctx,
+ isc_buffer_t *target,
+ unsigned int options,
+ unsigned int *countp)
+{
+ return (towiresorted(rdataset, owner_name, cctx, target,
+ NULL, NULL, ISC_FALSE, options, countp, NULL));
+}
+
+isc_result_t
+dns_rdataset_additionaldata(dns_rdataset_t *rdataset,
+ dns_additionaldatafunc_t add, void *arg)
+{
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_result_t result;
+
+ /*
+ * For each rdata in rdataset, call 'add' for each name and type in the
+ * rdata which is subject to additional section processing.
+ */
+
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE((rdataset->attributes & DNS_RDATASETATTR_QUESTION) == 0);
+
+ result = dns_rdataset_first(rdataset);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ do {
+ dns_rdataset_current(rdataset, &rdata);
+ result = dns_rdata_additionaldata(&rdata, add, arg);
+ if (result == ISC_R_SUCCESS)
+ result = dns_rdataset_next(rdataset);
+ dns_rdata_reset(&rdata);
+ } while (result == ISC_R_SUCCESS);
+
+ if (result != ISC_R_NOMORE)
+ return (result);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_rdataset_addnoqname(dns_rdataset_t *rdataset, dns_name_t *name) {
+
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(rdataset->methods != NULL);
+ if (rdataset->methods->addnoqname == NULL)
+ return (ISC_R_NOTIMPLEMENTED);
+ return((rdataset->methods->addnoqname)(rdataset, name));
+}
+
+isc_result_t
+dns_rdataset_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name,
+ dns_rdataset_t *neg, dns_rdataset_t *negsig)
+{
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(rdataset->methods != NULL);
+
+ if (rdataset->methods->getnoqname == NULL)
+ return (ISC_R_NOTIMPLEMENTED);
+ return((rdataset->methods->getnoqname)(rdataset, name, neg, negsig));
+}
+
+isc_result_t
+dns_rdataset_addclosest(dns_rdataset_t *rdataset, dns_name_t *name) {
+
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(rdataset->methods != NULL);
+ if (rdataset->methods->addclosest == NULL)
+ return (ISC_R_NOTIMPLEMENTED);
+ return((rdataset->methods->addclosest)(rdataset, name));
+}
+
+isc_result_t
+dns_rdataset_getclosest(dns_rdataset_t *rdataset, dns_name_t *name,
+ dns_rdataset_t *neg, dns_rdataset_t *negsig)
+{
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(rdataset->methods != NULL);
+
+ if (rdataset->methods->getclosest == NULL)
+ return (ISC_R_NOTIMPLEMENTED);
+ return((rdataset->methods->getclosest)(rdataset, name, neg, negsig));
+}
+
+/*
+ * Additional cache stuff
+ */
+isc_result_t
+dns_rdataset_getadditional(dns_rdataset_t *rdataset,
+ dns_rdatasetadditional_t type,
+ dns_rdatatype_t qtype,
+ dns_acache_t *acache,
+ dns_zone_t **zonep,
+ dns_db_t **dbp,
+ dns_dbversion_t **versionp,
+ dns_dbnode_t **nodep,
+ dns_name_t *fname,
+ dns_message_t *msg,
+ isc_stdtime_t now)
+{
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(rdataset->methods != NULL);
+ REQUIRE(zonep == NULL || *zonep == NULL);
+ REQUIRE(dbp != NULL && *dbp == NULL);
+ REQUIRE(versionp != NULL && *versionp == NULL);
+ REQUIRE(nodep != NULL && *nodep == NULL);
+ REQUIRE(fname != NULL);
+ REQUIRE(msg != NULL);
+
+ if (acache != NULL && rdataset->methods->getadditional != NULL) {
+ return ((rdataset->methods->getadditional)(rdataset, type,
+ qtype, acache,
+ zonep, dbp,
+ versionp, nodep,
+ fname, msg, now));
+ }
+
+ return (ISC_R_FAILURE);
+}
+
+isc_result_t
+dns_rdataset_setadditional(dns_rdataset_t *rdataset,
+ dns_rdatasetadditional_t type,
+ dns_rdatatype_t qtype,
+ dns_acache_t *acache,
+ dns_zone_t *zone,
+ dns_db_t *db,
+ dns_dbversion_t *version,
+ dns_dbnode_t *node,
+ dns_name_t *fname)
+{
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(rdataset->methods != NULL);
+
+ if (acache != NULL && rdataset->methods->setadditional != NULL) {
+ return ((rdataset->methods->setadditional)(rdataset, type,
+ qtype, acache, zone,
+ db, version,
+ node, fname));
+ }
+
+ return (ISC_R_FAILURE);
+}
+
+isc_result_t
+dns_rdataset_putadditional(dns_acache_t *acache,
+ dns_rdataset_t *rdataset,
+ dns_rdatasetadditional_t type,
+ dns_rdatatype_t qtype)
+{
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(rdataset->methods != NULL);
+
+ if (acache != NULL && rdataset->methods->putadditional != NULL) {
+ return ((rdataset->methods->putadditional)(acache, rdataset,
+ type, qtype));
+ }
+
+ return (ISC_R_FAILURE);
+}
+
diff --git a/lib/dns/rdatasetiter.c b/lib/dns/rdatasetiter.c
new file mode 100644
index 0000000..7ed3030
--- /dev/null
+++ b/lib/dns/rdatasetiter.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rdatasetiter.c,v 1.16 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stddef.h>
+
+#include <isc/util.h>
+
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+
+void
+dns_rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp) {
+ /*
+ * Destroy '*iteratorp'.
+ */
+
+ REQUIRE(iteratorp != NULL);
+ REQUIRE(DNS_RDATASETITER_VALID(*iteratorp));
+
+ (*iteratorp)->methods->destroy(iteratorp);
+
+ ENSURE(*iteratorp == NULL);
+}
+
+isc_result_t
+dns_rdatasetiter_first(dns_rdatasetiter_t *iterator) {
+ /*
+ * Move the rdataset cursor to the first rdataset at the node (if any).
+ */
+
+ REQUIRE(DNS_RDATASETITER_VALID(iterator));
+
+ return (iterator->methods->first(iterator));
+}
+
+isc_result_t
+dns_rdatasetiter_next(dns_rdatasetiter_t *iterator) {
+ /*
+ * Move the rdataset cursor to the next rdataset at the node (if any).
+ */
+
+ REQUIRE(DNS_RDATASETITER_VALID(iterator));
+
+ return (iterator->methods->next(iterator));
+}
+
+void
+dns_rdatasetiter_current(dns_rdatasetiter_t *iterator,
+ dns_rdataset_t *rdataset)
+{
+ /*
+ * Return the current rdataset.
+ */
+
+ REQUIRE(DNS_RDATASETITER_VALID(iterator));
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(! dns_rdataset_isassociated(rdataset));
+
+ iterator->methods->current(iterator, rdataset);
+}
diff --git a/lib/dns/rdataslab.c b/lib/dns/rdataslab.c
new file mode 100644
index 0000000..610d44f
--- /dev/null
+++ b/lib/dns/rdataslab.c
@@ -0,0 +1,1092 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rdataslab.c,v 1.48 2008/09/24 02:46:22 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdlib.h>
+
+#include <isc/mem.h>
+#include <isc/region.h>
+#include <isc/string.h> /* Required for HP/UX (and others?) */
+#include <isc/util.h>
+
+#include <dns/result.h>
+#include <dns/rdata.h>
+#include <dns/rdataset.h>
+#include <dns/rdataslab.h>
+
+/*
+ * The rdataslab structure allows iteration to occur in both load order
+ * and DNSSEC order. The structure is as follows:
+ *
+ * header (reservelen bytes)
+ * record count (2 bytes)
+ * offset table (4 x record count bytes in load order)
+ * data records
+ * data length (2 bytes)
+ * order (2 bytes)
+ * meta data (1 byte for RRSIG's)
+ * data (data length bytes)
+ *
+ * If DNS_RDATASET_FIXED is defined to be zero (0) the format of a
+ * rdataslab is as follows:
+ *
+ * header (reservelen bytes)
+ * record count (2 bytes)
+ * data records
+ * data length (2 bytes)
+ * data (data length bytes)
+ *
+ * Offsets are from the end of the header.
+ *
+ * Load order traversal is performed by walking the offset table to find
+ * the start of the record (DNS_RDATASET_FIXED = 1).
+ *
+ * DNSSEC order traversal is performed by walking the data records.
+ *
+ * The order is stored with record to allow for efficient reconstuction of
+ * of the offset table following a merge or subtraction.
+ *
+ * The iterator methods here currently only support DNSSEC order iteration.
+ *
+ * The iterator methods in rbtdb support both load order and DNSSEC order
+ * iteration.
+ *
+ * WARNING:
+ * rbtdb.c directly interacts with the slab's raw structures. If the
+ * structure changes then rbtdb.c also needs to be updated to reflect
+ * the changes. See the areas tagged with "RDATASLAB".
+ */
+
+struct xrdata {
+ dns_rdata_t rdata;
+ unsigned int order;
+};
+
+/*% Note: the "const void *" are just to make qsort happy. */
+static int
+compare_rdata(const void *p1, const void *p2) {
+ const struct xrdata *x1 = p1;
+ const struct xrdata *x2 = p2;
+ return (dns_rdata_compare(&x1->rdata, &x2->rdata));
+}
+
+#if DNS_RDATASET_FIXED
+static void
+fillin_offsets(unsigned char *offsetbase, unsigned int *offsettable,
+ unsigned length)
+{
+ unsigned int i, j;
+ unsigned char *raw;
+
+ for (i = 0, j = 0; i < length; i++) {
+
+ if (offsettable[i] == 0)
+ continue;
+
+ /*
+ * Fill in offset table.
+ */
+ raw = &offsetbase[j*4 + 2];
+ *raw++ = (offsettable[i] & 0xff000000) >> 24;
+ *raw++ = (offsettable[i] & 0xff0000) >> 16;
+ *raw++ = (offsettable[i] & 0xff00) >> 8;
+ *raw = offsettable[i] & 0xff;
+
+ /*
+ * Fill in table index.
+ */
+ raw = offsetbase + offsettable[i] + 2;
+ *raw++ = (j & 0xff00) >> 8;
+ *raw = j++ & 0xff;
+ }
+}
+#endif
+
+isc_result_t
+dns_rdataslab_fromrdataset(dns_rdataset_t *rdataset, isc_mem_t *mctx,
+ isc_region_t *region, unsigned int reservelen)
+{
+ struct xrdata *x;
+ unsigned char *rawbuf;
+#if DNS_RDATASET_FIXED
+ unsigned char *offsetbase;
+#endif
+ unsigned int buflen;
+ isc_result_t result;
+ unsigned int nitems;
+ unsigned int nalloc;
+ unsigned int i;
+#if DNS_RDATASET_FIXED
+ unsigned int *offsettable;
+#endif
+ unsigned int length;
+
+ buflen = reservelen + 2;
+
+ nalloc = dns_rdataset_count(rdataset);
+ nitems = nalloc;
+ if (nitems == 0)
+ return (ISC_R_FAILURE);
+
+ if (nalloc > 0xffff)
+ return (ISC_R_NOSPACE);
+
+ x = isc_mem_get(mctx, nalloc * sizeof(struct xrdata));
+ if (x == NULL)
+ return (ISC_R_NOMEMORY);
+
+ /*
+ * Save all of the rdata members into an array.
+ */
+ result = dns_rdataset_first(rdataset);
+ if (result != ISC_R_SUCCESS)
+ goto free_rdatas;
+ for (i = 0; i < nalloc && result == ISC_R_SUCCESS; i++) {
+ INSIST(result == ISC_R_SUCCESS);
+ dns_rdata_init(&x[i].rdata);
+ dns_rdataset_current(rdataset, &x[i].rdata);
+#if DNS_RDATASET_FIXED
+ x[i].order = i;
+#endif
+ result = dns_rdataset_next(rdataset);
+ }
+ if (result != ISC_R_NOMORE)
+ goto free_rdatas;
+ if (i != nalloc) {
+ /*
+ * Somehow we iterated over fewer rdatas than
+ * dns_rdataset_count() said there were!
+ */
+ result = ISC_R_FAILURE;
+ goto free_rdatas;
+ }
+
+ /*
+ * Put into DNSSEC order.
+ */
+ qsort(x, nalloc, sizeof(struct xrdata), compare_rdata);
+
+ /*
+ * Remove duplicates and compute the total storage required.
+ *
+ * If an rdata is not a duplicate, accumulate the storage size
+ * required for the rdata. We do not store the class, type, etc,
+ * just the rdata, so our overhead is 2 bytes for the number of
+ * records, and 8 for each rdata, (length(2), offset(4) and order(2))
+ * and then the rdata itself.
+ */
+ for (i = 1; i < nalloc; i++) {
+ if (compare_rdata(&x[i-1].rdata, &x[i].rdata) == 0) {
+ x[i-1].rdata.data = NULL;
+ x[i-1].rdata.length = 0;
+#if DNS_RDATASET_FIXED
+ /*
+ * Preserve the least order so A, B, A -> A, B
+ * after duplicate removal.
+ */
+ if (x[i-1].order < x[i].order)
+ x[i].order = x[i-1].order;
+#endif
+ nitems--;
+ } else {
+#if DNS_RDATASET_FIXED
+ buflen += (8 + x[i-1].rdata.length);
+#else
+ buflen += (2 + x[i-1].rdata.length);
+#endif
+ /*
+ * Provide space to store the per RR meta data.
+ */
+ if (rdataset->type == dns_rdatatype_rrsig)
+ buflen++;
+ }
+ }
+ /*
+ * Don't forget the last item!
+ */
+#if DNS_RDATASET_FIXED
+ buflen += (8 + x[i-1].rdata.length);
+#else
+ buflen += (2 + x[i-1].rdata.length);
+#endif
+ /*
+ * Provide space to store the per RR meta data.
+ */
+ if (rdataset->type == dns_rdatatype_rrsig)
+ buflen++;
+
+ /*
+ * Ensure that singleton types are actually singletons.
+ */
+ if (nitems > 1 && dns_rdatatype_issingleton(rdataset->type)) {
+ /*
+ * We have a singleton type, but there's more than one
+ * RR in the rdataset.
+ */
+ result = DNS_R_SINGLETON;
+ goto free_rdatas;
+ }
+
+ /*
+ * Allocate the memory, set up a buffer, start copying in
+ * data.
+ */
+ rawbuf = isc_mem_get(mctx, buflen);
+ if (rawbuf == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto free_rdatas;
+ }
+
+#if DNS_RDATASET_FIXED
+ /* Allocate temporary offset table. */
+ offsettable = isc_mem_get(mctx, nalloc * sizeof(unsigned int));
+ if (offsettable == NULL) {
+ isc_mem_put(mctx, rawbuf, buflen);
+ result = ISC_R_NOMEMORY;
+ goto free_rdatas;
+ }
+ memset(offsettable, 0, nalloc * sizeof(unsigned int));
+#endif
+
+ region->base = rawbuf;
+ region->length = buflen;
+
+ rawbuf += reservelen;
+#if DNS_RDATASET_FIXED
+ offsetbase = rawbuf;
+#endif
+
+ *rawbuf++ = (nitems & 0xff00) >> 8;
+ *rawbuf++ = (nitems & 0x00ff);
+
+#if DNS_RDATASET_FIXED
+ /* Skip load order table. Filled in later. */
+ rawbuf += nitems * 4;
+#endif
+
+ for (i = 0; i < nalloc; i++) {
+ if (x[i].rdata.data == NULL)
+ continue;
+#if DNS_RDATASET_FIXED
+ offsettable[x[i].order] = rawbuf - offsetbase;
+#endif
+ length = x[i].rdata.length;
+ if (rdataset->type == dns_rdatatype_rrsig)
+ length++;
+ *rawbuf++ = (length & 0xff00) >> 8;
+ *rawbuf++ = (length & 0x00ff);
+#if DNS_RDATASET_FIXED
+ rawbuf += 2; /* filled in later */
+#endif
+ /*
+ * Store the per RR meta data.
+ */
+ if (rdataset->type == dns_rdatatype_rrsig) {
+ *rawbuf++ |= (x[i].rdata.flags & DNS_RDATA_OFFLINE) ?
+ DNS_RDATASLAB_OFFLINE : 0;
+ }
+ memcpy(rawbuf, x[i].rdata.data, x[i].rdata.length);
+ rawbuf += x[i].rdata.length;
+ }
+
+#if DNS_RDATASET_FIXED
+ fillin_offsets(offsetbase, offsettable, nalloc);
+ isc_mem_put(mctx, offsettable, nalloc * sizeof(unsigned int));
+#endif
+
+ result = ISC_R_SUCCESS;
+
+ free_rdatas:
+ isc_mem_put(mctx, x, nalloc * sizeof(struct xrdata));
+ return (result);
+}
+
+static void
+rdataset_disassociate(dns_rdataset_t *rdataset) {
+ UNUSED(rdataset);
+}
+
+static isc_result_t
+rdataset_first(dns_rdataset_t *rdataset) {
+ unsigned char *raw = rdataset->private3;
+ unsigned int count;
+
+ count = raw[0] * 256 + raw[1];
+ if (count == 0) {
+ rdataset->private5 = NULL;
+ return (ISC_R_NOMORE);
+ }
+#if DNS_RDATASET_FIXED
+ raw += 2 + (4 * count);
+#else
+ raw += 2;
+#endif
+ /*
+ * The privateuint4 field is the number of rdata beyond the cursor
+ * position, so we decrement the total count by one before storing
+ * it.
+ */
+ count--;
+ rdataset->privateuint4 = count;
+ rdataset->private5 = raw;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+rdataset_next(dns_rdataset_t *rdataset) {
+ unsigned int count;
+ unsigned int length;
+ unsigned char *raw;
+
+ count = rdataset->privateuint4;
+ if (count == 0)
+ return (ISC_R_NOMORE);
+ count--;
+ rdataset->privateuint4 = count;
+ raw = rdataset->private5;
+ length = raw[0] * 256 + raw[1];
+#if DNS_RDATASET_FIXED
+ raw += length + 4;
+#else
+ raw += length + 2;
+#endif
+ rdataset->private5 = raw;
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) {
+ unsigned char *raw = rdataset->private5;
+ isc_region_t r;
+ unsigned int length;
+ unsigned int flags = 0;
+
+ REQUIRE(raw != NULL);
+
+ length = raw[0] * 256 + raw[1];
+#if DNS_RDATASET_FIXED
+ raw += 4;
+#else
+ raw += 2;
+#endif
+ if (rdataset->type == dns_rdatatype_rrsig) {
+ if (*raw & DNS_RDATASLAB_OFFLINE)
+ flags |= DNS_RDATA_OFFLINE;
+ length--;
+ raw++;
+ }
+ r.length = length;
+ r.base = raw;
+ dns_rdata_fromregion(rdata, rdataset->rdclass, rdataset->type, &r);
+ rdata->flags |= flags;
+}
+
+static void
+rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target) {
+ *target = *source;
+
+ /*
+ * Reset iterator state.
+ */
+ target->privateuint4 = 0;
+ target->private5 = NULL;
+}
+
+static unsigned int
+rdataset_count(dns_rdataset_t *rdataset) {
+ unsigned char *raw = rdataset->private3;
+ unsigned int count;
+
+ count = raw[0] * 256 + raw[1];
+
+ return (count);
+}
+
+static dns_rdatasetmethods_t rdataset_methods = {
+ rdataset_disassociate,
+ rdataset_first,
+ rdataset_next,
+ rdataset_current,
+ rdataset_clone,
+ rdataset_count,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+void
+dns_rdataslab_tordataset(unsigned char *slab, unsigned int reservelen,
+ dns_rdataclass_t rdclass, dns_rdatatype_t rdtype,
+ dns_rdatatype_t covers, dns_ttl_t ttl,
+ dns_rdataset_t *rdataset)
+{
+ REQUIRE(slab != NULL);
+ REQUIRE(!dns_rdataset_isassociated(rdataset));
+
+ rdataset->methods = &rdataset_methods;
+ rdataset->rdclass = rdclass;
+ rdataset->type = rdtype;
+ rdataset->covers = covers;
+ rdataset->ttl = ttl;
+ rdataset->trust = 0;
+ rdataset->private1 = NULL;
+ rdataset->private2 = NULL;
+ rdataset->private3 = slab + reservelen;
+
+ /*
+ * Reset iterator state.
+ */
+ rdataset->privateuint4 = 0;
+ rdataset->private5 = NULL;
+}
+
+unsigned int
+dns_rdataslab_size(unsigned char *slab, unsigned int reservelen) {
+ unsigned int count, length;
+ unsigned char *current;
+
+ REQUIRE(slab != NULL);
+
+ current = slab + reservelen;
+ count = *current++ * 256;
+ count += *current++;
+#if DNS_RDATASET_FIXED
+ current += (4 * count);
+#endif
+ while (count > 0) {
+ count--;
+ length = *current++ * 256;
+ length += *current++;
+#if DNS_RDATASET_FIXED
+ current += length + 2;
+#else
+ current += length;
+#endif
+ }
+
+ return ((unsigned int)(current - slab));
+}
+
+/*
+ * Make the dns_rdata_t 'rdata' refer to the slab item
+ * beginning at '*current', which is part of a slab of type
+ * 'type' and class 'rdclass', and advance '*current' to
+ * point to the next item in the slab.
+ */
+static inline void
+rdata_from_slab(unsigned char **current,
+ dns_rdataclass_t rdclass, dns_rdatatype_t type,
+ dns_rdata_t *rdata)
+{
+ unsigned char *tcurrent = *current;
+ isc_region_t region;
+ unsigned int length;
+ isc_boolean_t offline = ISC_FALSE;
+
+ length = *tcurrent++ * 256;
+ length += *tcurrent++;
+
+ if (type == dns_rdatatype_rrsig) {
+ if ((*tcurrent & DNS_RDATASLAB_OFFLINE) != 0)
+ offline = ISC_TRUE;
+ length--;
+ tcurrent++;
+ }
+ region.length = length;
+#if DNS_RDATASET_FIXED
+ tcurrent += 2;
+#endif
+ region.base = tcurrent;
+ tcurrent += region.length;
+ dns_rdata_fromregion(rdata, rdclass, type, &region);
+ if (offline)
+ rdata->flags |= DNS_RDATA_OFFLINE;
+ *current = tcurrent;
+}
+
+/*
+ * Return true iff 'slab' (slab data of type 'type' and class 'rdclass')
+ * contains an rdata identical to 'rdata'. This does case insensitive
+ * comparisons per DNSSEC.
+ */
+static inline isc_boolean_t
+rdata_in_slab(unsigned char *slab, unsigned int reservelen,
+ dns_rdataclass_t rdclass, dns_rdatatype_t type,
+ dns_rdata_t *rdata)
+{
+ unsigned int count, i;
+ unsigned char *current;
+ dns_rdata_t trdata = DNS_RDATA_INIT;
+ int n;
+
+ current = slab + reservelen;
+ count = *current++ * 256;
+ count += *current++;
+
+#if DNS_RDATASET_FIXED
+ current += (4 * count);
+#endif
+
+ for (i = 0; i < count; i++) {
+ rdata_from_slab(&current, rdclass, type, &trdata);
+
+ n = dns_rdata_compare(&trdata, rdata);
+ if (n == 0)
+ return (ISC_TRUE);
+ if (n > 0) /* In DNSSEC order. */
+ break;
+ dns_rdata_reset(&trdata);
+ }
+ return (ISC_FALSE);
+}
+
+isc_result_t
+dns_rdataslab_merge(unsigned char *oslab, unsigned char *nslab,
+ unsigned int reservelen, isc_mem_t *mctx,
+ dns_rdataclass_t rdclass, dns_rdatatype_t type,
+ unsigned int flags, unsigned char **tslabp)
+{
+ unsigned char *ocurrent, *ostart, *ncurrent, *tstart, *tcurrent, *data;
+ unsigned int ocount, ncount, count, olength, tlength, tcount, length;
+ dns_rdata_t ordata = DNS_RDATA_INIT;
+ dns_rdata_t nrdata = DNS_RDATA_INIT;
+ isc_boolean_t added_something = ISC_FALSE;
+ unsigned int oadded = 0;
+ unsigned int nadded = 0;
+ unsigned int nncount = 0;
+#if DNS_RDATASET_FIXED
+ unsigned int oncount;
+ unsigned int norder = 0;
+ unsigned int oorder = 0;
+ unsigned char *offsetbase;
+ unsigned int *offsettable;
+#endif
+
+ /*
+ * XXX Need parameter to allow "delete rdatasets in nslab" merge,
+ * or perhaps another merge routine for this purpose.
+ */
+
+ REQUIRE(tslabp != NULL && *tslabp == NULL);
+ REQUIRE(oslab != NULL && nslab != NULL);
+
+ ocurrent = oslab + reservelen;
+ ocount = *ocurrent++ * 256;
+ ocount += *ocurrent++;
+#if DNS_RDATASET_FIXED
+ ocurrent += (4 * ocount);
+#endif
+ ostart = ocurrent;
+ ncurrent = nslab + reservelen;
+ ncount = *ncurrent++ * 256;
+ ncount += *ncurrent++;
+#if DNS_RDATASET_FIXED
+ ncurrent += (4 * ncount);
+#endif
+ INSIST(ocount > 0 && ncount > 0);
+
+#if DNS_RDATASET_FIXED
+ oncount = ncount;
+#endif
+
+ /*
+ * Yes, this is inefficient!
+ */
+
+ /*
+ * Figure out the length of the old slab's data.
+ */
+ olength = 0;
+ for (count = 0; count < ocount; count++) {
+ length = *ocurrent++ * 256;
+ length += *ocurrent++;
+#if DNS_RDATASET_FIXED
+ olength += length + 8;
+ ocurrent += length + 2;
+#else
+ olength += length + 2;
+ ocurrent += length;
+#endif
+ }
+
+ /*
+ * Start figuring out the target length and count.
+ */
+ tlength = reservelen + 2 + olength;
+ tcount = ocount;
+
+ /*
+ * Add in the length of rdata in the new slab that aren't in
+ * the old slab.
+ */
+ do {
+ dns_rdata_init(&nrdata);
+ rdata_from_slab(&ncurrent, rdclass, type, &nrdata);
+ if (!rdata_in_slab(oslab, reservelen, rdclass, type, &nrdata))
+ {
+ /*
+ * This rdata isn't in the old slab.
+ */
+#if DNS_RDATASET_FIXED
+ tlength += nrdata.length + 8;
+#else
+ tlength += nrdata.length + 2;
+#endif
+ if (type == dns_rdatatype_rrsig)
+ tlength++;
+ tcount++;
+ nncount++;
+ added_something = ISC_TRUE;
+ }
+ ncount--;
+ } while (ncount > 0);
+ ncount = nncount;
+
+ if (((flags & DNS_RDATASLAB_EXACT) != 0) &&
+ (tcount != ncount + ocount))
+ return (DNS_R_NOTEXACT);
+
+ if (!added_something && (flags & DNS_RDATASLAB_FORCE) == 0)
+ return (DNS_R_UNCHANGED);
+
+ /*
+ * Ensure that singleton types are actually singletons.
+ */
+ if (tcount > 1 && dns_rdatatype_issingleton(type)) {
+ /*
+ * We have a singleton type, but there's more than one
+ * RR in the rdataset.
+ */
+ return (DNS_R_SINGLETON);
+ }
+
+ if (tcount > 0xffff)
+ return (ISC_R_NOSPACE);
+
+ /*
+ * Copy the reserved area from the new slab.
+ */
+ tstart = isc_mem_get(mctx, tlength);
+ if (tstart == NULL)
+ return (ISC_R_NOMEMORY);
+ memcpy(tstart, nslab, reservelen);
+ tcurrent = tstart + reservelen;
+#if DNS_RDATASET_FIXED
+ offsetbase = tcurrent;
+#endif
+
+ /*
+ * Write the new count.
+ */
+ *tcurrent++ = (tcount & 0xff00) >> 8;
+ *tcurrent++ = (tcount & 0x00ff);
+
+#if DNS_RDATASET_FIXED
+ /*
+ * Skip offset table.
+ */
+ tcurrent += (tcount * 4);
+
+ offsettable = isc_mem_get(mctx,
+ (ocount + oncount) * sizeof(unsigned int));
+ if (offsettable == NULL) {
+ isc_mem_put(mctx, tstart, tlength);
+ return (ISC_R_NOMEMORY);
+ }
+ memset(offsettable, 0, (ocount + oncount) * sizeof(unsigned int));
+#endif
+
+ /*
+ * Merge the two slabs.
+ */
+ ocurrent = ostart;
+ INSIST(ocount != 0);
+#if DNS_RDATASET_FIXED
+ oorder = ocurrent[2] * 256 + ocurrent[3];
+ INSIST(oorder < ocount);
+#endif
+ rdata_from_slab(&ocurrent, rdclass, type, &ordata);
+
+ ncurrent = nslab + reservelen + 2;
+#if DNS_RDATASET_FIXED
+ ncurrent += (4 * oncount);
+#endif
+
+ if (ncount > 0) {
+ do {
+ dns_rdata_reset(&nrdata);
+#if DNS_RDATASET_FIXED
+ norder = ncurrent[2] * 256 + ncurrent[3];
+
+ INSIST(norder < oncount);
+#endif
+ rdata_from_slab(&ncurrent, rdclass, type, &nrdata);
+ } while (rdata_in_slab(oslab, reservelen, rdclass,
+ type, &nrdata));
+ }
+
+ while (oadded < ocount || nadded < ncount) {
+ isc_boolean_t fromold;
+ if (oadded == ocount)
+ fromold = ISC_FALSE;
+ else if (nadded == ncount)
+ fromold = ISC_TRUE;
+ else
+ fromold = ISC_TF(compare_rdata(&ordata, &nrdata) < 0);
+ if (fromold) {
+#if DNS_RDATASET_FIXED
+ offsettable[oorder] = tcurrent - offsetbase;
+#endif
+ length = ordata.length;
+ data = ordata.data;
+ if (type == dns_rdatatype_rrsig) {
+ length++;
+ data--;
+ }
+ *tcurrent++ = (length & 0xff00) >> 8;
+ *tcurrent++ = (length & 0x00ff);
+#if DNS_RDATASET_FIXED
+ tcurrent += 2; /* fill in later */
+#endif
+ memcpy(tcurrent, data, length);
+ tcurrent += length;
+ oadded++;
+ if (oadded < ocount) {
+ dns_rdata_reset(&ordata);
+#if DNS_RDATASET_FIXED
+ oorder = ocurrent[2] * 256 + ocurrent[3];
+ INSIST(oorder < ocount);
+#endif
+ rdata_from_slab(&ocurrent, rdclass, type,
+ &ordata);
+ }
+ } else {
+#if DNS_RDATASET_FIXED
+ offsettable[ocount + norder] = tcurrent - offsetbase;
+#endif
+ length = nrdata.length;
+ data = nrdata.data;
+ if (type == dns_rdatatype_rrsig) {
+ length++;
+ data--;
+ }
+ *tcurrent++ = (length & 0xff00) >> 8;
+ *tcurrent++ = (length & 0x00ff);
+#if DNS_RDATASET_FIXED
+ tcurrent += 2; /* fill in later */
+#endif
+ memcpy(tcurrent, data, length);
+ tcurrent += length;
+ nadded++;
+ if (nadded < ncount) {
+ do {
+ dns_rdata_reset(&nrdata);
+#if DNS_RDATASET_FIXED
+ norder = ncurrent[2] * 256 + ncurrent[3];
+ INSIST(norder < oncount);
+#endif
+ rdata_from_slab(&ncurrent, rdclass,
+ type, &nrdata);
+ } while (rdata_in_slab(oslab, reservelen,
+ rdclass, type,
+ &nrdata));
+ }
+ }
+ }
+
+#if DNS_RDATASET_FIXED
+ fillin_offsets(offsetbase, offsettable, ocount + oncount);
+
+ isc_mem_put(mctx, offsettable,
+ (ocount + oncount) * sizeof(unsigned int));
+#endif
+
+ INSIST(tcurrent == tstart + tlength);
+
+ *tslabp = tstart;
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_rdataslab_subtract(unsigned char *mslab, unsigned char *sslab,
+ unsigned int reservelen, isc_mem_t *mctx,
+ dns_rdataclass_t rdclass, dns_rdatatype_t type,
+ unsigned int flags, unsigned char **tslabp)
+{
+ unsigned char *mcurrent, *sstart, *scurrent, *tstart, *tcurrent;
+ unsigned int mcount, scount, rcount ,count, tlength, tcount, i;
+ dns_rdata_t srdata = DNS_RDATA_INIT;
+ dns_rdata_t mrdata = DNS_RDATA_INIT;
+#if DNS_RDATASET_FIXED
+ unsigned char *offsetbase;
+ unsigned int *offsettable;
+ unsigned int order;
+#endif
+
+ REQUIRE(tslabp != NULL && *tslabp == NULL);
+ REQUIRE(mslab != NULL && sslab != NULL);
+
+ mcurrent = mslab + reservelen;
+ mcount = *mcurrent++ * 256;
+ mcount += *mcurrent++;
+ scurrent = sslab + reservelen;
+ scount = *scurrent++ * 256;
+ scount += *scurrent++;
+ INSIST(mcount > 0 && scount > 0);
+
+ /*
+ * Yes, this is inefficient!
+ */
+
+ /*
+ * Start figuring out the target length and count.
+ */
+ tlength = reservelen + 2;
+ tcount = 0;
+ rcount = 0;
+
+#if DNS_RDATASET_FIXED
+ mcurrent += 4 * mcount;
+ scurrent += 4 * scount;
+#endif
+ sstart = scurrent;
+
+ /*
+ * Add in the length of rdata in the mslab that aren't in
+ * the sslab.
+ */
+ for (i = 0; i < mcount; i++) {
+ unsigned char *mrdatabegin = mcurrent;
+ rdata_from_slab(&mcurrent, rdclass, type, &mrdata);
+ scurrent = sstart;
+ for (count = 0; count < scount; count++) {
+ dns_rdata_reset(&srdata);
+ rdata_from_slab(&scurrent, rdclass, type, &srdata);
+ if (dns_rdata_compare(&mrdata, &srdata) == 0)
+ break;
+ }
+ if (count == scount) {
+ /*
+ * This rdata isn't in the sslab, and thus isn't
+ * being subtracted.
+ */
+ tlength += mcurrent - mrdatabegin;
+ tcount++;
+ } else
+ rcount++;
+ dns_rdata_reset(&mrdata);
+ }
+
+#if DNS_RDATASET_FIXED
+ tlength += (4 * tcount);
+#endif
+
+ /*
+ * Check that all the records originally existed. The numeric
+ * check only works as rdataslabs do not contain duplicates.
+ */
+ if (((flags & DNS_RDATASLAB_EXACT) != 0) && (rcount != scount))
+ return (DNS_R_NOTEXACT);
+
+ /*
+ * Don't continue if the new rdataslab would be empty.
+ */
+ if (tcount == 0)
+ return (DNS_R_NXRRSET);
+
+ /*
+ * If nothing is going to change, we can stop.
+ */
+ if (rcount == 0)
+ return (DNS_R_UNCHANGED);
+
+ /*
+ * Copy the reserved area from the mslab.
+ */
+ tstart = isc_mem_get(mctx, tlength);
+ if (tstart == NULL)
+ return (ISC_R_NOMEMORY);
+ memcpy(tstart, mslab, reservelen);
+ tcurrent = tstart + reservelen;
+#if DNS_RDATASET_FIXED
+ offsetbase = tcurrent;
+
+ offsettable = isc_mem_get(mctx, mcount * sizeof(unsigned int));
+ if (offsettable == NULL) {
+ isc_mem_put(mctx, tstart, tlength);
+ return (ISC_R_NOMEMORY);
+ }
+ memset(offsettable, 0, mcount * sizeof(unsigned int));
+#endif
+
+ /*
+ * Write the new count.
+ */
+ *tcurrent++ = (tcount & 0xff00) >> 8;
+ *tcurrent++ = (tcount & 0x00ff);
+
+#if DNS_RDATASET_FIXED
+ tcurrent += (4 * tcount);
+#endif
+
+ /*
+ * Copy the parts of mslab not in sslab.
+ */
+ mcurrent = mslab + reservelen;
+ mcount = *mcurrent++ * 256;
+ mcount += *mcurrent++;
+#if DNS_RDATASET_FIXED
+ mcurrent += (4 * mcount);
+#endif
+ for (i = 0; i < mcount; i++) {
+ unsigned char *mrdatabegin = mcurrent;
+#if DNS_RDATASET_FIXED
+ order = mcurrent[2] * 256 + mcurrent[3];
+ INSIST(order < mcount);
+#endif
+ rdata_from_slab(&mcurrent, rdclass, type, &mrdata);
+ scurrent = sstart;
+ for (count = 0; count < scount; count++) {
+ dns_rdata_reset(&srdata);
+ rdata_from_slab(&scurrent, rdclass, type, &srdata);
+ if (dns_rdata_compare(&mrdata, &srdata) == 0)
+ break;
+ }
+ if (count == scount) {
+ /*
+ * This rdata isn't in the sslab, and thus should be
+ * copied to the tslab.
+ */
+ unsigned int length = mcurrent - mrdatabegin;
+#if DNS_RDATASET_FIXED
+ offsettable[order] = tcurrent - offsetbase;
+#endif
+ memcpy(tcurrent, mrdatabegin, length);
+ tcurrent += length;
+ }
+ dns_rdata_reset(&mrdata);
+ }
+
+#if DNS_RDATASET_FIXED
+ fillin_offsets(offsetbase, offsettable, mcount);
+
+ isc_mem_put(mctx, offsettable, mcount * sizeof(unsigned int));
+#endif
+
+ INSIST(tcurrent == tstart + tlength);
+
+ *tslabp = tstart;
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_boolean_t
+dns_rdataslab_equal(unsigned char *slab1, unsigned char *slab2,
+ unsigned int reservelen)
+{
+ unsigned char *current1, *current2;
+ unsigned int count1, count2;
+ unsigned int length1, length2;
+
+ current1 = slab1 + reservelen;
+ count1 = *current1++ * 256;
+ count1 += *current1++;
+
+ current2 = slab2 + reservelen;
+ count2 = *current2++ * 256;
+ count2 += *current2++;
+
+ if (count1 != count2)
+ return (ISC_FALSE);
+
+#if DNS_RDATASET_FIXED
+ current1 += (4 * count1);
+ current2 += (4 * count2);
+#endif
+
+ while (count1 > 0) {
+ length1 = *current1++ * 256;
+ length1 += *current1++;
+
+ length2 = *current2++ * 256;
+ length2 += *current2++;
+
+#if DNS_RDATASET_FIXED
+ current1 += 2;
+ current2 += 2;
+#endif
+
+ if (length1 != length2 ||
+ memcmp(current1, current2, length1) != 0)
+ return (ISC_FALSE);
+
+ current1 += length1;
+ current2 += length1;
+
+ count1--;
+ }
+ return (ISC_TRUE);
+}
+
+isc_boolean_t
+dns_rdataslab_equalx(unsigned char *slab1, unsigned char *slab2,
+ unsigned int reservelen, dns_rdataclass_t rdclass,
+ dns_rdatatype_t type)
+{
+ unsigned char *current1, *current2;
+ unsigned int count1, count2;
+ dns_rdata_t rdata1 = DNS_RDATA_INIT;
+ dns_rdata_t rdata2 = DNS_RDATA_INIT;
+
+ current1 = slab1 + reservelen;
+ count1 = *current1++ * 256;
+ count1 += *current1++;
+
+ current2 = slab2 + reservelen;
+ count2 = *current2++ * 256;
+ count2 += *current2++;
+
+ if (count1 != count2)
+ return (ISC_FALSE);
+
+#if DNS_RDATASET_FIXED
+ current1 += (4 * count1);
+ current2 += (4 * count2);
+#endif
+
+ while (count1-- > 0) {
+ rdata_from_slab(&current1, rdclass, type, &rdata1);
+ rdata_from_slab(&current2, rdclass, type, &rdata2);
+ if (dns_rdata_compare(&rdata1, &rdata2) != 0)
+ return (ISC_FALSE);
+ dns_rdata_reset(&rdata1);
+ dns_rdata_reset(&rdata2);
+ }
+ return (ISC_TRUE);
+}
diff --git a/lib/dns/request.c b/lib/dns/request.c
new file mode 100644
index 0000000..af75668
--- /dev/null
+++ b/lib/dns/request.c
@@ -0,0 +1,1497 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: request.c,v 1.82 2008/07/22 03:43:04 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/task.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+#include <dns/acl.h>
+#include <dns/compress.h>
+#include <dns/dispatch.h>
+#include <dns/events.h>
+#include <dns/log.h>
+#include <dns/message.h>
+#include <dns/rdata.h>
+#include <dns/rdatastruct.h>
+#include <dns/request.h>
+#include <dns/result.h>
+#include <dns/tsig.h>
+
+#define REQUESTMGR_MAGIC ISC_MAGIC('R', 'q', 'u', 'M')
+#define VALID_REQUESTMGR(mgr) ISC_MAGIC_VALID(mgr, REQUESTMGR_MAGIC)
+
+#define REQUEST_MAGIC ISC_MAGIC('R', 'q', 'u', '!')
+#define VALID_REQUEST(request) ISC_MAGIC_VALID(request, REQUEST_MAGIC)
+
+typedef ISC_LIST(dns_request_t) dns_requestlist_t;
+
+#define DNS_REQUEST_NLOCKS 7
+
+struct dns_requestmgr {
+ unsigned int magic;
+ isc_mutex_t lock;
+ isc_mem_t *mctx;
+
+ /* locked */
+ isc_int32_t eref;
+ isc_int32_t iref;
+ isc_timermgr_t *timermgr;
+ isc_socketmgr_t *socketmgr;
+ isc_taskmgr_t *taskmgr;
+ dns_dispatchmgr_t *dispatchmgr;
+ dns_dispatch_t *dispatchv4;
+ dns_dispatch_t *dispatchv6;
+ isc_boolean_t exiting;
+ isc_eventlist_t whenshutdown;
+ unsigned int hash;
+ isc_mutex_t locks[DNS_REQUEST_NLOCKS];
+ dns_requestlist_t requests;
+};
+
+struct dns_request {
+ unsigned int magic;
+ unsigned int hash;
+ isc_mem_t *mctx;
+ isc_int32_t flags;
+ ISC_LINK(dns_request_t) link;
+ isc_buffer_t *query;
+ isc_buffer_t *answer;
+ dns_requestevent_t *event;
+ dns_dispatch_t *dispatch;
+ dns_dispentry_t *dispentry;
+ isc_timer_t *timer;
+ dns_requestmgr_t *requestmgr;
+ isc_buffer_t *tsig;
+ dns_tsigkey_t *tsigkey;
+ isc_event_t ctlevent;
+ isc_boolean_t canceling; /* ctlevent outstanding */
+ isc_sockaddr_t destaddr;
+ unsigned int udpcount;
+};
+
+#define DNS_REQUEST_F_CONNECTING 0x0001
+#define DNS_REQUEST_F_SENDING 0x0002
+#define DNS_REQUEST_F_CANCELED 0x0004 /*%< ctlevent received, or otherwise
+ synchronously canceled */
+#define DNS_REQUEST_F_TIMEDOUT 0x0008 /*%< cancelled due to a timeout */
+#define DNS_REQUEST_F_TCP 0x0010 /*%< This request used TCP */
+#define DNS_REQUEST_CANCELED(r) \
+ (((r)->flags & DNS_REQUEST_F_CANCELED) != 0)
+#define DNS_REQUEST_CONNECTING(r) \
+ (((r)->flags & DNS_REQUEST_F_CONNECTING) != 0)
+#define DNS_REQUEST_SENDING(r) \
+ (((r)->flags & DNS_REQUEST_F_SENDING) != 0)
+#define DNS_REQUEST_TIMEDOUT(r) \
+ (((r)->flags & DNS_REQUEST_F_TIMEDOUT) != 0)
+
+
+/***
+ *** Forward
+ ***/
+
+static void mgr_destroy(dns_requestmgr_t *requestmgr);
+static void mgr_shutdown(dns_requestmgr_t *requestmgr);
+static unsigned int mgr_gethash(dns_requestmgr_t *requestmgr);
+static void send_shutdown_events(dns_requestmgr_t *requestmgr);
+
+static isc_result_t req_render(dns_message_t *message, isc_buffer_t **buffer,
+ unsigned int options, isc_mem_t *mctx);
+static void req_senddone(isc_task_t *task, isc_event_t *event);
+static void req_response(isc_task_t *task, isc_event_t *event);
+static void req_timeout(isc_task_t *task, isc_event_t *event);
+static isc_socket_t * req_getsocket(dns_request_t *request);
+static void req_connected(isc_task_t *task, isc_event_t *event);
+static void req_sendevent(dns_request_t *request, isc_result_t result);
+static void req_cancel(dns_request_t *request);
+static void req_destroy(dns_request_t *request);
+static void req_log(int level, const char *fmt, ...) ISC_FORMAT_PRINTF(2, 3);
+static void do_cancel(isc_task_t *task, isc_event_t *event);
+
+/***
+ *** Public
+ ***/
+
+isc_result_t
+dns_requestmgr_create(isc_mem_t *mctx,
+ isc_timermgr_t *timermgr,
+ isc_socketmgr_t *socketmgr,
+ isc_taskmgr_t *taskmgr,
+ dns_dispatchmgr_t *dispatchmgr,
+ dns_dispatch_t *dispatchv4,
+ dns_dispatch_t *dispatchv6,
+ dns_requestmgr_t **requestmgrp)
+{
+ dns_requestmgr_t *requestmgr;
+ isc_socket_t *socket;
+ isc_result_t result;
+ int i;
+ unsigned int dispattr;
+
+ req_log(ISC_LOG_DEBUG(3), "dns_requestmgr_create");
+
+ REQUIRE(requestmgrp != NULL && *requestmgrp == NULL);
+ REQUIRE(timermgr != NULL);
+ REQUIRE(socketmgr != NULL);
+ REQUIRE(taskmgr != NULL);
+ REQUIRE(dispatchmgr != NULL);
+ UNUSED(socket);
+ if (dispatchv4 != NULL) {
+ dispattr = dns_dispatch_getattributes(dispatchv4);
+ REQUIRE((dispattr & DNS_DISPATCHATTR_UDP) != 0);
+ }
+ if (dispatchv6 != NULL) {
+ dispattr = dns_dispatch_getattributes(dispatchv6);
+ REQUIRE((dispattr & DNS_DISPATCHATTR_UDP) != 0);
+ }
+
+ requestmgr = isc_mem_get(mctx, sizeof(*requestmgr));
+ if (requestmgr == NULL)
+ return (ISC_R_NOMEMORY);
+
+ result = isc_mutex_init(&requestmgr->lock);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, requestmgr, sizeof(*requestmgr));
+ return (result);
+ }
+ for (i = 0; i < DNS_REQUEST_NLOCKS; i++) {
+ result = isc_mutex_init(&requestmgr->locks[i]);
+ if (result != ISC_R_SUCCESS) {
+ while (--i >= 0)
+ DESTROYLOCK(&requestmgr->locks[i]);
+ DESTROYLOCK(&requestmgr->lock);
+ isc_mem_put(mctx, requestmgr, sizeof(*requestmgr));
+ return (result);
+ }
+ }
+ requestmgr->timermgr = timermgr;
+ requestmgr->socketmgr = socketmgr;
+ requestmgr->taskmgr = taskmgr;
+ requestmgr->dispatchmgr = dispatchmgr;
+ requestmgr->dispatchv4 = NULL;
+ if (dispatchv4 != NULL)
+ dns_dispatch_attach(dispatchv4, &requestmgr->dispatchv4);
+ requestmgr->dispatchv6 = NULL;
+ if (dispatchv6 != NULL)
+ dns_dispatch_attach(dispatchv6, &requestmgr->dispatchv6);
+ requestmgr->mctx = NULL;
+ isc_mem_attach(mctx, &requestmgr->mctx);
+ requestmgr->eref = 1; /* implict attach */
+ requestmgr->iref = 0;
+ ISC_LIST_INIT(requestmgr->whenshutdown);
+ ISC_LIST_INIT(requestmgr->requests);
+ requestmgr->exiting = ISC_FALSE;
+ requestmgr->hash = 0;
+ requestmgr->magic = REQUESTMGR_MAGIC;
+
+ req_log(ISC_LOG_DEBUG(3), "dns_requestmgr_create: %p", requestmgr);
+
+ *requestmgrp = requestmgr;
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_requestmgr_whenshutdown(dns_requestmgr_t *requestmgr, isc_task_t *task,
+ isc_event_t **eventp)
+{
+ isc_task_t *clone;
+ isc_event_t *event;
+
+ req_log(ISC_LOG_DEBUG(3), "dns_requestmgr_whenshutdown");
+
+ REQUIRE(VALID_REQUESTMGR(requestmgr));
+ REQUIRE(eventp != NULL);
+
+ event = *eventp;
+ *eventp = NULL;
+
+ LOCK(&requestmgr->lock);
+
+ if (requestmgr->exiting) {
+ /*
+ * We're already shutdown. Send the event.
+ */
+ event->ev_sender = requestmgr;
+ isc_task_send(task, &event);
+ } else {
+ clone = NULL;
+ isc_task_attach(task, &clone);
+ event->ev_sender = clone;
+ ISC_LIST_APPEND(requestmgr->whenshutdown, event, ev_link);
+ }
+ UNLOCK(&requestmgr->lock);
+}
+
+void
+dns_requestmgr_shutdown(dns_requestmgr_t *requestmgr) {
+
+ REQUIRE(VALID_REQUESTMGR(requestmgr));
+
+ req_log(ISC_LOG_DEBUG(3), "dns_requestmgr_shutdown: %p", requestmgr);
+
+ LOCK(&requestmgr->lock);
+ mgr_shutdown(requestmgr);
+ UNLOCK(&requestmgr->lock);
+}
+
+static void
+mgr_shutdown(dns_requestmgr_t *requestmgr) {
+ dns_request_t *request;
+
+ /*
+ * Caller holds lock.
+ */
+ if (!requestmgr->exiting) {
+ requestmgr->exiting = ISC_TRUE;
+ for (request = ISC_LIST_HEAD(requestmgr->requests);
+ request != NULL;
+ request = ISC_LIST_NEXT(request, link)) {
+ dns_request_cancel(request);
+ }
+ if (requestmgr->iref == 0) {
+ INSIST(ISC_LIST_EMPTY(requestmgr->requests));
+ send_shutdown_events(requestmgr);
+ }
+ }
+}
+
+static void
+requestmgr_attach(dns_requestmgr_t *source, dns_requestmgr_t **targetp) {
+
+ /*
+ * Locked by caller.
+ */
+
+ REQUIRE(VALID_REQUESTMGR(source));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ REQUIRE(!source->exiting);
+
+ source->iref++;
+ *targetp = source;
+
+ req_log(ISC_LOG_DEBUG(3), "requestmgr_attach: %p: eref %d iref %d",
+ source, source->eref, source->iref);
+}
+
+static void
+requestmgr_detach(dns_requestmgr_t **requestmgrp) {
+ dns_requestmgr_t *requestmgr;
+ isc_boolean_t need_destroy = ISC_FALSE;
+
+ REQUIRE(requestmgrp != NULL);
+ requestmgr = *requestmgrp;
+ REQUIRE(VALID_REQUESTMGR(requestmgr));
+
+ *requestmgrp = NULL;
+ LOCK(&requestmgr->lock);
+ INSIST(requestmgr->iref > 0);
+ requestmgr->iref--;
+
+ req_log(ISC_LOG_DEBUG(3), "requestmgr_detach: %p: eref %d iref %d",
+ requestmgr, requestmgr->eref, requestmgr->iref);
+
+ if (requestmgr->iref == 0 && requestmgr->exiting) {
+ INSIST(ISC_LIST_HEAD(requestmgr->requests) == NULL);
+ send_shutdown_events(requestmgr);
+ if (requestmgr->eref == 0)
+ need_destroy = ISC_TRUE;
+ }
+ UNLOCK(&requestmgr->lock);
+
+ if (need_destroy)
+ mgr_destroy(requestmgr);
+}
+
+void
+dns_requestmgr_attach(dns_requestmgr_t *source, dns_requestmgr_t **targetp) {
+
+ REQUIRE(VALID_REQUESTMGR(source));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+ REQUIRE(!source->exiting);
+
+ LOCK(&source->lock);
+ source->eref++;
+ *targetp = source;
+ UNLOCK(&source->lock);
+
+ req_log(ISC_LOG_DEBUG(3), "dns_requestmgr_attach: %p: eref %d iref %d",
+ source, source->eref, source->iref);
+}
+
+void
+dns_requestmgr_detach(dns_requestmgr_t **requestmgrp) {
+ dns_requestmgr_t *requestmgr;
+ isc_boolean_t need_destroy = ISC_FALSE;
+
+ REQUIRE(requestmgrp != NULL);
+ requestmgr = *requestmgrp;
+ REQUIRE(VALID_REQUESTMGR(requestmgr));
+
+ LOCK(&requestmgr->lock);
+ INSIST(requestmgr->eref > 0);
+ requestmgr->eref--;
+
+ req_log(ISC_LOG_DEBUG(3), "dns_requestmgr_detach: %p: eref %d iref %d",
+ requestmgr, requestmgr->eref, requestmgr->iref);
+
+ if (requestmgr->eref == 0 && requestmgr->iref == 0) {
+ INSIST(requestmgr->exiting &&
+ ISC_LIST_HEAD(requestmgr->requests) == NULL);
+ need_destroy = ISC_TRUE;
+ }
+ UNLOCK(&requestmgr->lock);
+
+ if (need_destroy)
+ mgr_destroy(requestmgr);
+
+ *requestmgrp = NULL;
+}
+
+static void
+send_shutdown_events(dns_requestmgr_t *requestmgr) {
+ isc_event_t *event, *next_event;
+ isc_task_t *etask;
+
+ req_log(ISC_LOG_DEBUG(3), "send_shutdown_events: %p", requestmgr);
+
+ /*
+ * Caller must be holding the manager lock.
+ */
+ for (event = ISC_LIST_HEAD(requestmgr->whenshutdown);
+ event != NULL;
+ event = next_event) {
+ next_event = ISC_LIST_NEXT(event, ev_link);
+ ISC_LIST_UNLINK(requestmgr->whenshutdown, event, ev_link);
+ etask = event->ev_sender;
+ event->ev_sender = requestmgr;
+ isc_task_sendanddetach(&etask, &event);
+ }
+}
+
+static void
+mgr_destroy(dns_requestmgr_t *requestmgr) {
+ int i;
+ isc_mem_t *mctx;
+
+ req_log(ISC_LOG_DEBUG(3), "mgr_destroy");
+
+ REQUIRE(requestmgr->eref == 0);
+ REQUIRE(requestmgr->iref == 0);
+
+ DESTROYLOCK(&requestmgr->lock);
+ for (i = 0; i < DNS_REQUEST_NLOCKS; i++)
+ DESTROYLOCK(&requestmgr->locks[i]);
+ if (requestmgr->dispatchv4 != NULL)
+ dns_dispatch_detach(&requestmgr->dispatchv4);
+ if (requestmgr->dispatchv6 != NULL)
+ dns_dispatch_detach(&requestmgr->dispatchv6);
+ requestmgr->magic = 0;
+ mctx = requestmgr->mctx;
+ isc_mem_put(mctx, requestmgr, sizeof(*requestmgr));
+ isc_mem_detach(&mctx);
+}
+
+static unsigned int
+mgr_gethash(dns_requestmgr_t *requestmgr) {
+ req_log(ISC_LOG_DEBUG(3), "mgr_gethash");
+ /*
+ * Locked by caller.
+ */
+ requestmgr->hash++;
+ return (requestmgr->hash % DNS_REQUEST_NLOCKS);
+}
+
+static inline isc_result_t
+req_send(dns_request_t *request, isc_task_t *task, isc_sockaddr_t *address) {
+ isc_region_t r;
+ isc_socket_t *socket;
+ isc_result_t result;
+ unsigned int dispattr;
+
+ req_log(ISC_LOG_DEBUG(3), "req_send: request %p", request);
+
+ REQUIRE(VALID_REQUEST(request));
+ dispattr = dns_dispatch_getattributes(request->dispatch);
+ socket = req_getsocket(request);
+ isc_buffer_usedregion(request->query, &r);
+ /*
+ * We could connect the socket when we are using an exclusive dispatch
+ * as we do in resolver.c, but we prefer implementation simplicity
+ * at this moment.
+ */
+ result = isc_socket_sendto(socket, &r, task, req_senddone,
+ request, address, NULL);
+ if (result == ISC_R_SUCCESS)
+ request->flags |= DNS_REQUEST_F_SENDING;
+ return (result);
+}
+
+static isc_result_t
+new_request(isc_mem_t *mctx, dns_request_t **requestp) {
+ dns_request_t *request;
+
+ request = isc_mem_get(mctx, sizeof(*request));
+ if (request == NULL)
+ return (ISC_R_NOMEMORY);
+
+ /*
+ * Zero structure.
+ */
+ request->magic = 0;
+ request->mctx = NULL;
+ request->flags = 0;
+ ISC_LINK_INIT(request, link);
+ request->query = NULL;
+ request->answer = NULL;
+ request->event = NULL;
+ request->dispatch = NULL;
+ request->dispentry = NULL;
+ request->timer = NULL;
+ request->requestmgr = NULL;
+ request->tsig = NULL;
+ request->tsigkey = NULL;
+ ISC_EVENT_INIT(&request->ctlevent, sizeof(request->ctlevent), 0, NULL,
+ DNS_EVENT_REQUESTCONTROL, do_cancel, request, NULL,
+ NULL, NULL);
+ request->canceling = ISC_FALSE;
+ request->udpcount = 0;
+
+ isc_mem_attach(mctx, &request->mctx);
+
+ request->magic = REQUEST_MAGIC;
+ *requestp = request;
+ return (ISC_R_SUCCESS);
+}
+
+
+static isc_boolean_t
+isblackholed(dns_dispatchmgr_t *dispatchmgr, isc_sockaddr_t *destaddr) {
+ dns_acl_t *blackhole;
+ isc_netaddr_t netaddr;
+ int match;
+ isc_boolean_t drop = ISC_FALSE;
+ char netaddrstr[ISC_NETADDR_FORMATSIZE];
+
+ blackhole = dns_dispatchmgr_getblackhole(dispatchmgr);
+ if (blackhole != NULL) {
+ isc_netaddr_fromsockaddr(&netaddr, destaddr);
+ if (dns_acl_match(&netaddr, NULL, blackhole,
+ NULL, &match, NULL) == ISC_R_SUCCESS &&
+ match > 0)
+ drop = ISC_TRUE;
+ }
+ if (drop) {
+ isc_netaddr_format(&netaddr, netaddrstr, sizeof(netaddrstr));
+ req_log(ISC_LOG_DEBUG(10), "blackholed address %s", netaddrstr);
+ }
+ return (drop);
+}
+
+static isc_result_t
+create_tcp_dispatch(dns_requestmgr_t *requestmgr, isc_sockaddr_t *srcaddr,
+ isc_sockaddr_t *destaddr, dns_dispatch_t **dispatchp)
+{
+ isc_result_t result;
+ isc_socket_t *socket = NULL;
+ isc_sockaddr_t src;
+ unsigned int attrs;
+ isc_sockaddr_t bind_any;
+
+ result = isc_socket_create(requestmgr->socketmgr,
+ isc_sockaddr_pf(destaddr),
+ isc_sockettype_tcp, &socket);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+#ifndef BROKEN_TCP_BIND_BEFORE_CONNECT
+ if (srcaddr == NULL) {
+ isc_sockaddr_anyofpf(&bind_any,
+ isc_sockaddr_pf(destaddr));
+ result = isc_socket_bind(socket, &bind_any, 0);
+ } else {
+ src = *srcaddr;
+ isc_sockaddr_setport(&src, 0);
+ result = isc_socket_bind(socket, &src, 0);
+ }
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+#endif
+ attrs = 0;
+ attrs |= DNS_DISPATCHATTR_TCP;
+ attrs |= DNS_DISPATCHATTR_PRIVATE;
+ if (isc_sockaddr_pf(destaddr) == AF_INET)
+ attrs |= DNS_DISPATCHATTR_IPV4;
+ else
+ attrs |= DNS_DISPATCHATTR_IPV6;
+ attrs |= DNS_DISPATCHATTR_MAKEQUERY;
+ result = dns_dispatch_createtcp(requestmgr->dispatchmgr,
+ socket, requestmgr->taskmgr,
+ 4096, 2, 1, 1, 3, attrs,
+ dispatchp);
+cleanup:
+ isc_socket_detach(&socket);
+ return (result);
+}
+
+static isc_result_t
+find_udp_dispatch(dns_requestmgr_t *requestmgr, isc_sockaddr_t *srcaddr,
+ isc_sockaddr_t *destaddr, dns_dispatch_t **dispatchp)
+{
+ dns_dispatch_t *disp = NULL;
+ unsigned int attrs, attrmask;
+
+ if (srcaddr == NULL) {
+ switch (isc_sockaddr_pf(destaddr)) {
+ case PF_INET:
+ disp = requestmgr->dispatchv4;
+ break;
+
+ case PF_INET6:
+ disp = requestmgr->dispatchv6;
+ break;
+
+ default:
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+ if (disp == NULL)
+ return (ISC_R_FAMILYNOSUPPORT);
+ dns_dispatch_attach(disp, dispatchp);
+ return (ISC_R_SUCCESS);
+ }
+ attrs = 0;
+ attrs |= DNS_DISPATCHATTR_UDP;
+ switch (isc_sockaddr_pf(srcaddr)) {
+ case PF_INET:
+ attrs |= DNS_DISPATCHATTR_IPV4;
+ break;
+
+ case PF_INET6:
+ attrs |= DNS_DISPATCHATTR_IPV6;
+ break;
+
+ default:
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+ attrmask = 0;
+ attrmask |= DNS_DISPATCHATTR_UDP;
+ attrmask |= DNS_DISPATCHATTR_TCP;
+ attrmask |= DNS_DISPATCHATTR_IPV4;
+ attrmask |= DNS_DISPATCHATTR_IPV6;
+ return (dns_dispatch_getudp(requestmgr->dispatchmgr,
+ requestmgr->socketmgr,
+ requestmgr->taskmgr,
+ srcaddr, 4096,
+ 1000, 32768, 16411, 16433,
+ attrs, attrmask,
+ dispatchp));
+}
+
+static isc_result_t
+get_dispatch(isc_boolean_t tcp, dns_requestmgr_t *requestmgr,
+ isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
+ dns_dispatch_t **dispatchp)
+{
+ isc_result_t result;
+ if (tcp)
+ result = create_tcp_dispatch(requestmgr, srcaddr,
+ destaddr, dispatchp);
+ else
+ result = find_udp_dispatch(requestmgr, srcaddr,
+ destaddr, dispatchp);
+ return (result);
+}
+
+static isc_result_t
+set_timer(isc_timer_t *timer, unsigned int timeout, unsigned int udpresend) {
+ isc_time_t expires;
+ isc_interval_t interval;
+ isc_result_t result;
+ isc_timertype_t timertype;
+
+ isc_interval_set(&interval, timeout, 0);
+ result = isc_time_nowplusinterval(&expires, &interval);
+ isc_interval_set(&interval, udpresend, 0);
+
+ timertype = udpresend != 0 ? isc_timertype_limited : isc_timertype_once;
+ if (result == ISC_R_SUCCESS)
+ result = isc_timer_reset(timer, timertype, &expires,
+ &interval, ISC_FALSE);
+ return (result);
+}
+
+isc_result_t
+dns_request_createraw(dns_requestmgr_t *requestmgr, isc_buffer_t *msgbuf,
+ isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
+ unsigned int options, unsigned int timeout,
+ isc_task_t *task, isc_taskaction_t action, void *arg,
+ dns_request_t **requestp)
+{
+ return(dns_request_createraw3(requestmgr, msgbuf, srcaddr, destaddr,
+ options, timeout, 0, 0, task, action,
+ arg, requestp));
+}
+
+isc_result_t
+dns_request_createraw2(dns_requestmgr_t *requestmgr, isc_buffer_t *msgbuf,
+ isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
+ unsigned int options, unsigned int timeout,
+ unsigned int udptimeout, isc_task_t *task,
+ isc_taskaction_t action, void *arg,
+ dns_request_t **requestp)
+{
+ unsigned int udpretries = 0;
+
+ if (udptimeout != 0)
+ udpretries = timeout / udptimeout;
+
+ return (dns_request_createraw3(requestmgr, msgbuf, srcaddr, destaddr,
+ options, timeout, udptimeout,
+ udpretries, task, action, arg,
+ requestp));
+}
+
+isc_result_t
+dns_request_createraw3(dns_requestmgr_t *requestmgr, isc_buffer_t *msgbuf,
+ isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
+ unsigned int options, unsigned int timeout,
+ unsigned int udptimeout, unsigned int udpretries,
+ isc_task_t *task, isc_taskaction_t action, void *arg,
+ dns_request_t **requestp)
+{
+ dns_request_t *request = NULL;
+ isc_task_t *tclone = NULL;
+ isc_socket_t *socket = NULL;
+ isc_result_t result;
+ isc_mem_t *mctx;
+ dns_messageid_t id;
+ isc_boolean_t tcp = ISC_FALSE;
+ isc_region_t r;
+
+ REQUIRE(VALID_REQUESTMGR(requestmgr));
+ REQUIRE(msgbuf != NULL);
+ REQUIRE(destaddr != NULL);
+ REQUIRE(task != NULL);
+ REQUIRE(action != NULL);
+ REQUIRE(requestp != NULL && *requestp == NULL);
+ REQUIRE(timeout > 0);
+ if (srcaddr != NULL)
+ REQUIRE(isc_sockaddr_pf(srcaddr) == isc_sockaddr_pf(destaddr));
+
+ mctx = requestmgr->mctx;
+
+ req_log(ISC_LOG_DEBUG(3), "dns_request_createraw");
+
+ if (isblackholed(requestmgr->dispatchmgr, destaddr))
+ return (DNS_R_BLACKHOLED);
+
+ request = NULL;
+ result = new_request(mctx, &request);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (udptimeout == 0 && udpretries != 0) {
+ udptimeout = timeout / (udpretries + 1);
+ if (udptimeout == 0)
+ udptimeout = 1;
+ }
+ request->udpcount = udpretries;
+
+ /*
+ * Create timer now. We will set it below once.
+ */
+ result = isc_timer_create(requestmgr->timermgr, isc_timertype_inactive,
+ NULL, NULL, task, req_timeout, request,
+ &request->timer);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ request->event = (dns_requestevent_t *)
+ isc_event_allocate(mctx, task, DNS_EVENT_REQUESTDONE,
+ action, arg, sizeof(dns_requestevent_t));
+ if (request->event == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ isc_task_attach(task, &tclone);
+ request->event->ev_sender = task;
+ request->event->request = request;
+ request->event->result = ISC_R_FAILURE;
+
+ isc_buffer_usedregion(msgbuf, &r);
+ if (r.length < DNS_MESSAGE_HEADERLEN || r.length > 65535) {
+ result = DNS_R_FORMERR;
+ goto cleanup;
+ }
+
+ if ((options & DNS_REQUESTOPT_TCP) != 0 || r.length > 512)
+ tcp = ISC_TRUE;
+
+ result = get_dispatch(tcp, requestmgr, srcaddr, destaddr,
+ &request->dispatch);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = dns_dispatch_addresponse2(request->dispatch, destaddr, task,
+ req_response, request, &id,
+ &request->dispentry,
+ requestmgr->socketmgr);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ socket = req_getsocket(request);
+ INSIST(socket != NULL);
+
+ result = isc_buffer_allocate(mctx, &request->query,
+ r.length + (tcp ? 2 : 0));
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ if (tcp)
+ isc_buffer_putuint16(request->query, (isc_uint16_t)r.length);
+ result = isc_buffer_copyregion(request->query, &r);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ /* Add message ID. */
+ isc_buffer_usedregion(request->query, &r);
+ if (tcp)
+ isc_region_consume(&r, 2);
+ r.base[0] = (id>>8) & 0xff;
+ r.base[1] = id & 0xff;
+
+ LOCK(&requestmgr->lock);
+ if (requestmgr->exiting) {
+ UNLOCK(&requestmgr->lock);
+ result = ISC_R_SHUTTINGDOWN;
+ goto cleanup;
+ }
+ requestmgr_attach(requestmgr, &request->requestmgr);
+ request->hash = mgr_gethash(requestmgr);
+ ISC_LIST_APPEND(requestmgr->requests, request, link);
+ UNLOCK(&requestmgr->lock);
+
+ result = set_timer(request->timer, timeout, tcp ? 0 : udptimeout);
+ if (result != ISC_R_SUCCESS)
+ goto unlink;
+
+ request->destaddr = *destaddr;
+ if (tcp) {
+ result = isc_socket_connect(socket, destaddr, task,
+ req_connected, request);
+ if (result != ISC_R_SUCCESS)
+ goto unlink;
+ request->flags |= DNS_REQUEST_F_CONNECTING|DNS_REQUEST_F_TCP;
+ } else {
+ result = req_send(request, task, destaddr);
+ if (result != ISC_R_SUCCESS)
+ goto unlink;
+ }
+
+ req_log(ISC_LOG_DEBUG(3), "dns_request_createraw: request %p",
+ request);
+ *requestp = request;
+ return (ISC_R_SUCCESS);
+
+ unlink:
+ LOCK(&requestmgr->lock);
+ ISC_LIST_UNLINK(requestmgr->requests, request, link);
+ UNLOCK(&requestmgr->lock);
+
+ cleanup:
+ if (tclone != NULL)
+ isc_task_detach(&tclone);
+ req_destroy(request);
+ req_log(ISC_LOG_DEBUG(3), "dns_request_createraw: failed %s",
+ dns_result_totext(result));
+ return (result);
+}
+
+isc_result_t
+dns_request_create(dns_requestmgr_t *requestmgr, dns_message_t *message,
+ isc_sockaddr_t *address, unsigned int options,
+ dns_tsigkey_t *key,
+ unsigned int timeout, isc_task_t *task,
+ isc_taskaction_t action, void *arg,
+ dns_request_t **requestp)
+{
+ return (dns_request_createvia3(requestmgr, message, NULL, address,
+ options, key, timeout, 0, 0, task,
+ action, arg, requestp));
+}
+
+isc_result_t
+dns_request_createvia(dns_requestmgr_t *requestmgr, dns_message_t *message,
+ isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
+ unsigned int options, dns_tsigkey_t *key,
+ unsigned int timeout, isc_task_t *task,
+ isc_taskaction_t action, void *arg,
+ dns_request_t **requestp)
+{
+ return(dns_request_createvia3(requestmgr, message, srcaddr, destaddr,
+ options, key, timeout, 0, 0, task,
+ action, arg, requestp));
+}
+
+isc_result_t
+dns_request_createvia2(dns_requestmgr_t *requestmgr, dns_message_t *message,
+ isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
+ unsigned int options, dns_tsigkey_t *key,
+ unsigned int timeout, unsigned int udptimeout,
+ isc_task_t *task, isc_taskaction_t action, void *arg,
+ dns_request_t **requestp)
+{
+ unsigned int udpretries = 0;
+
+ if (udptimeout != 0)
+ udpretries = timeout / udptimeout;
+ return (dns_request_createvia3(requestmgr, message, srcaddr, destaddr,
+ options, key, timeout, udptimeout,
+ udpretries, task, action, arg,
+ requestp));
+}
+
+isc_result_t
+dns_request_createvia3(dns_requestmgr_t *requestmgr, dns_message_t *message,
+ isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
+ unsigned int options, dns_tsigkey_t *key,
+ unsigned int timeout, unsigned int udptimeout,
+ unsigned int udpretries, isc_task_t *task,
+ isc_taskaction_t action, void *arg,
+ dns_request_t **requestp)
+{
+ dns_request_t *request = NULL;
+ isc_task_t *tclone = NULL;
+ isc_socket_t *socket = NULL;
+ isc_result_t result;
+ isc_mem_t *mctx;
+ dns_messageid_t id;
+ isc_boolean_t tcp;
+ isc_boolean_t setkey = ISC_TRUE;
+
+ REQUIRE(VALID_REQUESTMGR(requestmgr));
+ REQUIRE(message != NULL);
+ REQUIRE(destaddr != NULL);
+ REQUIRE(task != NULL);
+ REQUIRE(action != NULL);
+ REQUIRE(requestp != NULL && *requestp == NULL);
+ REQUIRE(timeout > 0);
+ if (srcaddr != NULL)
+ REQUIRE(isc_sockaddr_pf(srcaddr) == isc_sockaddr_pf(destaddr));
+
+ mctx = requestmgr->mctx;
+
+ req_log(ISC_LOG_DEBUG(3), "dns_request_createvia");
+
+ if (isblackholed(requestmgr->dispatchmgr, destaddr))
+ return (DNS_R_BLACKHOLED);
+
+ request = NULL;
+ result = new_request(mctx, &request);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (udptimeout == 0 && udpretries != 0) {
+ udptimeout = timeout / (udpretries + 1);
+ if (udptimeout == 0)
+ udptimeout = 1;
+ }
+ request->udpcount = udpretries;
+
+ /*
+ * Create timer now. We will set it below once.
+ */
+ result = isc_timer_create(requestmgr->timermgr, isc_timertype_inactive,
+ NULL, NULL, task, req_timeout, request,
+ &request->timer);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ request->event = (dns_requestevent_t *)
+ isc_event_allocate(mctx, task, DNS_EVENT_REQUESTDONE,
+ action, arg, sizeof(dns_requestevent_t));
+ if (request->event == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ isc_task_attach(task, &tclone);
+ request->event->ev_sender = task;
+ request->event->request = request;
+ request->event->result = ISC_R_FAILURE;
+ if (key != NULL)
+ dns_tsigkey_attach(key, &request->tsigkey);
+
+ use_tcp:
+ tcp = ISC_TF((options & DNS_REQUESTOPT_TCP) != 0);
+ result = get_dispatch(tcp, requestmgr, srcaddr, destaddr,
+ &request->dispatch);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = dns_dispatch_addresponse2(request->dispatch, destaddr, task,
+ req_response, request, &id,
+ &request->dispentry,
+ requestmgr->socketmgr);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ socket = req_getsocket(request);
+ INSIST(socket != NULL);
+
+ message->id = id;
+ if (setkey) {
+ result = dns_message_settsigkey(message, request->tsigkey);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+ result = req_render(message, &request->query, options, mctx);
+ if (result == DNS_R_USETCP &&
+ (options & DNS_REQUESTOPT_TCP) == 0) {
+ /*
+ * Try again using TCP.
+ */
+ dns_message_renderreset(message);
+ dns_dispatch_removeresponse(&request->dispentry, NULL);
+ dns_dispatch_detach(&request->dispatch);
+ socket = NULL;
+ options |= DNS_REQUESTOPT_TCP;
+ setkey = ISC_FALSE;
+ goto use_tcp;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = dns_message_getquerytsig(message, mctx, &request->tsig);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ LOCK(&requestmgr->lock);
+ if (requestmgr->exiting) {
+ UNLOCK(&requestmgr->lock);
+ result = ISC_R_SHUTTINGDOWN;
+ goto cleanup;
+ }
+ requestmgr_attach(requestmgr, &request->requestmgr);
+ request->hash = mgr_gethash(requestmgr);
+ ISC_LIST_APPEND(requestmgr->requests, request, link);
+ UNLOCK(&requestmgr->lock);
+
+ result = set_timer(request->timer, timeout, tcp ? 0 : udptimeout);
+ if (result != ISC_R_SUCCESS)
+ goto unlink;
+
+ request->destaddr = *destaddr;
+ if (tcp) {
+ result = isc_socket_connect(socket, destaddr, task,
+ req_connected, request);
+ if (result != ISC_R_SUCCESS)
+ goto unlink;
+ request->flags |= DNS_REQUEST_F_CONNECTING|DNS_REQUEST_F_TCP;
+ } else {
+ result = req_send(request, task, destaddr);
+ if (result != ISC_R_SUCCESS)
+ goto unlink;
+ }
+
+ req_log(ISC_LOG_DEBUG(3), "dns_request_createvia: request %p",
+ request);
+ *requestp = request;
+ return (ISC_R_SUCCESS);
+
+ unlink:
+ LOCK(&requestmgr->lock);
+ ISC_LIST_UNLINK(requestmgr->requests, request, link);
+ UNLOCK(&requestmgr->lock);
+
+ cleanup:
+ if (tclone != NULL)
+ isc_task_detach(&tclone);
+ req_destroy(request);
+ req_log(ISC_LOG_DEBUG(3), "dns_request_createvia: failed %s",
+ dns_result_totext(result));
+ return (result);
+}
+
+static isc_result_t
+req_render(dns_message_t *message, isc_buffer_t **bufferp,
+ unsigned int options, isc_mem_t *mctx)
+{
+ isc_buffer_t *buf1 = NULL;
+ isc_buffer_t *buf2 = NULL;
+ isc_result_t result;
+ isc_region_t r;
+ isc_boolean_t tcp = ISC_FALSE;
+ dns_compress_t cctx;
+ isc_boolean_t cleanup_cctx = ISC_FALSE;
+
+ REQUIRE(bufferp != NULL && *bufferp == NULL);
+
+ req_log(ISC_LOG_DEBUG(3), "request_render");
+
+ /*
+ * Create buffer able to hold largest possible message.
+ */
+ result = isc_buffer_allocate(mctx, &buf1, 65535);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_compress_init(&cctx, -1, mctx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ cleanup_cctx = ISC_TRUE;
+
+ /*
+ * Render message.
+ */
+ result = dns_message_renderbegin(message, &cctx, buf1);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_message_rendersection(message, DNS_SECTION_QUESTION, 0);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_message_rendersection(message, DNS_SECTION_ANSWER, 0);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_message_rendersection(message, DNS_SECTION_AUTHORITY, 0);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_message_rendersection(message, DNS_SECTION_ADDITIONAL, 0);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_message_renderend(message);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ dns_compress_invalidate(&cctx);
+ cleanup_cctx = ISC_FALSE;
+
+ /*
+ * Copy rendered message to exact sized buffer.
+ */
+ isc_buffer_usedregion(buf1, &r);
+ if ((options & DNS_REQUESTOPT_TCP) != 0) {
+ tcp = ISC_TRUE;
+ } else if (r.length > 512) {
+ result = DNS_R_USETCP;
+ goto cleanup;
+ }
+ result = isc_buffer_allocate(mctx, &buf2, r.length + (tcp ? 2 : 0));
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ if (tcp)
+ isc_buffer_putuint16(buf2, (isc_uint16_t)r.length);
+ result = isc_buffer_copyregion(buf2, &r);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ /*
+ * Cleanup and return.
+ */
+ isc_buffer_free(&buf1);
+ *bufferp = buf2;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ dns_message_renderreset(message);
+ if (buf1 != NULL)
+ isc_buffer_free(&buf1);
+ if (buf2 != NULL)
+ isc_buffer_free(&buf2);
+ if (cleanup_cctx)
+ dns_compress_invalidate(&cctx);
+ return (result);
+}
+
+
+/*
+ * If this request is no longer waiting for events,
+ * send the completion event. This will ultimately
+ * cause the request to be destroyed.
+ *
+ * Requires:
+ * 'request' is locked by the caller.
+ */
+static void
+send_if_done(dns_request_t *request, isc_result_t result) {
+ if (!DNS_REQUEST_CONNECTING(request) &&
+ !DNS_REQUEST_SENDING(request) &&
+ !request->canceling)
+ req_sendevent(request, result);
+}
+
+/*
+ * Handle the control event.
+ */
+static void
+do_cancel(isc_task_t *task, isc_event_t *event) {
+ dns_request_t *request = event->ev_arg;
+ UNUSED(task);
+ INSIST(event->ev_type == DNS_EVENT_REQUESTCONTROL);
+ LOCK(&request->requestmgr->locks[request->hash]);
+ request->canceling = ISC_FALSE;
+ if (!DNS_REQUEST_CANCELED(request))
+ req_cancel(request);
+ send_if_done(request, ISC_R_CANCELED);
+ UNLOCK(&request->requestmgr->locks[request->hash]);
+}
+
+void
+dns_request_cancel(dns_request_t *request) {
+ REQUIRE(VALID_REQUEST(request));
+
+ req_log(ISC_LOG_DEBUG(3), "dns_request_cancel: request %p", request);
+
+ REQUIRE(VALID_REQUEST(request));
+
+ LOCK(&request->requestmgr->locks[request->hash]);
+ if (!request->canceling && !DNS_REQUEST_CANCELED(request)) {
+ isc_event_t *ev = &request->ctlevent;
+ isc_task_send(request->event->ev_sender, &ev);
+ request->canceling = ISC_TRUE;
+ }
+ UNLOCK(&request->requestmgr->locks[request->hash]);
+}
+
+isc_result_t
+dns_request_getresponse(dns_request_t *request, dns_message_t *message,
+ unsigned int options)
+{
+ isc_result_t result;
+
+ REQUIRE(VALID_REQUEST(request));
+ REQUIRE(request->answer != NULL);
+
+ req_log(ISC_LOG_DEBUG(3), "dns_request_getresponse: request %p",
+ request);
+
+ result = dns_message_setquerytsig(message, request->tsig);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = dns_message_settsigkey(message, request->tsigkey);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = dns_message_parse(message, request->answer, options);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (request->tsigkey != NULL)
+ result = dns_tsig_verify(request->answer, message, NULL, NULL);
+ return (result);
+}
+
+isc_boolean_t
+dns_request_usedtcp(dns_request_t *request) {
+ REQUIRE(VALID_REQUEST(request));
+
+ return (ISC_TF((request->flags & DNS_REQUEST_F_TCP) != 0));
+}
+
+void
+dns_request_destroy(dns_request_t **requestp) {
+ dns_request_t *request;
+
+ REQUIRE(requestp != NULL && VALID_REQUEST(*requestp));
+
+ request = *requestp;
+
+ req_log(ISC_LOG_DEBUG(3), "dns_request_destroy: request %p", request);
+
+ LOCK(&request->requestmgr->lock);
+ LOCK(&request->requestmgr->locks[request->hash]);
+ ISC_LIST_UNLINK(request->requestmgr->requests, request, link);
+ INSIST(!DNS_REQUEST_CONNECTING(request));
+ INSIST(!DNS_REQUEST_SENDING(request));
+ UNLOCK(&request->requestmgr->locks[request->hash]);
+ UNLOCK(&request->requestmgr->lock);
+
+ /*
+ * These should have been cleaned up by req_cancel() before
+ * the completion event was sent.
+ */
+ INSIST(!ISC_LINK_LINKED(request, link));
+ INSIST(request->dispentry == NULL);
+ INSIST(request->dispatch == NULL);
+ INSIST(request->timer == NULL);
+
+ req_destroy(request);
+
+ *requestp = NULL;
+}
+
+/***
+ *** Private: request.
+ ***/
+
+static isc_socket_t *
+req_getsocket(dns_request_t *request) {
+ unsigned int dispattr;
+ isc_socket_t *socket;
+
+ dispattr = dns_dispatch_getattributes(request->dispatch);
+ if ((dispattr & DNS_DISPATCHATTR_EXCLUSIVE) != 0) {
+ INSIST(request->dispentry != NULL);
+ socket = dns_dispatch_getentrysocket(request->dispentry);
+ } else
+ socket = dns_dispatch_getsocket(request->dispatch);
+
+ return (socket);
+}
+
+static void
+req_connected(isc_task_t *task, isc_event_t *event) {
+ isc_socketevent_t *sevent = (isc_socketevent_t *)event;
+ isc_result_t result;
+ dns_request_t *request = event->ev_arg;
+
+ REQUIRE(event->ev_type == ISC_SOCKEVENT_CONNECT);
+ REQUIRE(VALID_REQUEST(request));
+ REQUIRE(DNS_REQUEST_CONNECTING(request));
+
+ req_log(ISC_LOG_DEBUG(3), "req_connected: request %p", request);
+
+ LOCK(&request->requestmgr->locks[request->hash]);
+ request->flags &= ~DNS_REQUEST_F_CONNECTING;
+
+ if (DNS_REQUEST_CANCELED(request)) {
+ /*
+ * Send delayed event.
+ */
+ if (DNS_REQUEST_TIMEDOUT(request))
+ send_if_done(request, ISC_R_TIMEDOUT);
+ else
+ send_if_done(request, ISC_R_CANCELED);
+ } else {
+ dns_dispatch_starttcp(request->dispatch);
+ result = sevent->result;
+ if (result == ISC_R_SUCCESS)
+ result = req_send(request, task, NULL);
+
+ if (result != ISC_R_SUCCESS) {
+ req_cancel(request);
+ send_if_done(request, ISC_R_CANCELED);
+ }
+ }
+ UNLOCK(&request->requestmgr->locks[request->hash]);
+ isc_event_free(&event);
+}
+
+static void
+req_senddone(isc_task_t *task, isc_event_t *event) {
+ isc_socketevent_t *sevent = (isc_socketevent_t *)event;
+ dns_request_t *request = event->ev_arg;
+
+ REQUIRE(event->ev_type == ISC_SOCKEVENT_SENDDONE);
+ REQUIRE(VALID_REQUEST(request));
+ REQUIRE(DNS_REQUEST_SENDING(request));
+
+ req_log(ISC_LOG_DEBUG(3), "req_senddone: request %p", request);
+
+ UNUSED(task);
+
+ LOCK(&request->requestmgr->locks[request->hash]);
+ request->flags &= ~DNS_REQUEST_F_SENDING;
+
+ if (DNS_REQUEST_CANCELED(request)) {
+ /*
+ * Send delayed event.
+ */
+ if (DNS_REQUEST_TIMEDOUT(request))
+ send_if_done(request, ISC_R_TIMEDOUT);
+ else
+ send_if_done(request, ISC_R_CANCELED);
+ } else if (sevent->result != ISC_R_SUCCESS) {
+ req_cancel(request);
+ send_if_done(request, ISC_R_CANCELED);
+ }
+ UNLOCK(&request->requestmgr->locks[request->hash]);
+
+ isc_event_free(&event);
+}
+
+static void
+req_response(isc_task_t *task, isc_event_t *event) {
+ isc_result_t result;
+ dns_request_t *request = event->ev_arg;
+ dns_dispatchevent_t *devent = (dns_dispatchevent_t *)event;
+ isc_region_t r;
+
+ REQUIRE(VALID_REQUEST(request));
+ REQUIRE(event->ev_type == DNS_EVENT_DISPATCH);
+
+ UNUSED(task);
+
+ req_log(ISC_LOG_DEBUG(3), "req_response: request %p: %s", request,
+ dns_result_totext(devent->result));
+
+ LOCK(&request->requestmgr->locks[request->hash]);
+ result = devent->result;
+ if (result != ISC_R_SUCCESS)
+ goto done;
+
+ /*
+ * Copy buffer to request.
+ */
+ isc_buffer_usedregion(&devent->buffer, &r);
+ result = isc_buffer_allocate(request->mctx, &request->answer,
+ r.length);
+ if (result != ISC_R_SUCCESS)
+ goto done;
+ result = isc_buffer_copyregion(request->answer, &r);
+ if (result != ISC_R_SUCCESS)
+ isc_buffer_free(&request->answer);
+ done:
+ /*
+ * Cleanup.
+ */
+ dns_dispatch_removeresponse(&request->dispentry, &devent);
+ req_cancel(request);
+ /*
+ * Send completion event.
+ */
+ send_if_done(request, result);
+ UNLOCK(&request->requestmgr->locks[request->hash]);
+}
+
+static void
+req_timeout(isc_task_t *task, isc_event_t *event) {
+ dns_request_t *request = event->ev_arg;
+ isc_result_t result;
+
+ REQUIRE(VALID_REQUEST(request));
+
+ req_log(ISC_LOG_DEBUG(3), "req_timeout: request %p", request);
+
+ UNUSED(task);
+ LOCK(&request->requestmgr->locks[request->hash]);
+ if (event->ev_type == ISC_TIMEREVENT_TICK &&
+ request->udpcount-- != 0) {
+ if (! DNS_REQUEST_SENDING(request)) {
+ result = req_send(request, task, &request->destaddr);
+ if (result != ISC_R_SUCCESS) {
+ req_cancel(request);
+ send_if_done(request, result);
+ }
+ }
+ } else {
+ request->flags |= DNS_REQUEST_F_TIMEDOUT;
+ req_cancel(request);
+ send_if_done(request, ISC_R_TIMEDOUT);
+ }
+ UNLOCK(&request->requestmgr->locks[request->hash]);
+ isc_event_free(&event);
+}
+
+static void
+req_sendevent(dns_request_t *request, isc_result_t result) {
+ isc_task_t *task;
+
+ REQUIRE(VALID_REQUEST(request));
+
+ req_log(ISC_LOG_DEBUG(3), "req_sendevent: request %p", request);
+
+ /*
+ * Lock held by caller.
+ */
+ task = request->event->ev_sender;
+ request->event->ev_sender = request;
+ request->event->result = result;
+ isc_task_sendanddetach(&task, (isc_event_t **)&request->event);
+}
+
+static void
+req_destroy(dns_request_t *request) {
+ isc_mem_t *mctx;
+
+ REQUIRE(VALID_REQUEST(request));
+
+ req_log(ISC_LOG_DEBUG(3), "req_destroy: request %p", request);
+
+ request->magic = 0;
+ if (request->query != NULL)
+ isc_buffer_free(&request->query);
+ if (request->answer != NULL)
+ isc_buffer_free(&request->answer);
+ if (request->event != NULL)
+ isc_event_free((isc_event_t **)&request->event);
+ if (request->dispentry != NULL)
+ dns_dispatch_removeresponse(&request->dispentry, NULL);
+ if (request->dispatch != NULL)
+ dns_dispatch_detach(&request->dispatch);
+ if (request->timer != NULL)
+ isc_timer_detach(&request->timer);
+ if (request->tsig != NULL)
+ isc_buffer_free(&request->tsig);
+ if (request->tsigkey != NULL)
+ dns_tsigkey_detach(&request->tsigkey);
+ if (request->requestmgr != NULL)
+ requestmgr_detach(&request->requestmgr);
+ mctx = request->mctx;
+ isc_mem_put(mctx, request, sizeof(*request));
+ isc_mem_detach(&mctx);
+}
+
+/*
+ * Stop the current request. Must be called from the request's task.
+ */
+static void
+req_cancel(dns_request_t *request) {
+ isc_socket_t *socket;
+ unsigned int dispattr;
+
+ REQUIRE(VALID_REQUEST(request));
+
+ req_log(ISC_LOG_DEBUG(3), "req_cancel: request %p", request);
+
+ /*
+ * Lock held by caller.
+ */
+ request->flags |= DNS_REQUEST_F_CANCELED;
+
+ if (request->timer != NULL)
+ isc_timer_detach(&request->timer);
+ dispattr = dns_dispatch_getattributes(request->dispatch);
+ socket = NULL;
+ if (DNS_REQUEST_CONNECTING(request) || DNS_REQUEST_SENDING(request)) {
+ if ((dispattr & DNS_DISPATCHATTR_EXCLUSIVE) != 0) {
+ if (request->dispentry != NULL) {
+ socket = dns_dispatch_getentrysocket(
+ request->dispentry);
+ }
+ } else
+ socket = dns_dispatch_getsocket(request->dispatch);
+ if (DNS_REQUEST_CONNECTING(request) && socket != NULL)
+ isc_socket_cancel(socket, NULL, ISC_SOCKCANCEL_CONNECT);
+ if (DNS_REQUEST_SENDING(request) && socket != NULL)
+ isc_socket_cancel(socket, NULL, ISC_SOCKCANCEL_SEND);
+ }
+ if (request->dispentry != NULL)
+ dns_dispatch_removeresponse(&request->dispentry, NULL);
+ dns_dispatch_detach(&request->dispatch);
+}
+
+static void
+req_log(int level, const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_REQUEST, level, fmt, ap);
+ va_end(ap);
+}
diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c
new file mode 100644
index 0000000..9d1c2fe
--- /dev/null
+++ b/lib/dns/resolver.c
@@ -0,0 +1,7602 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: resolver.c,v 1.384 2008/11/07 00:52:34 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/platform.h>
+#include <isc/print.h>
+#include <isc/string.h>
+#include <isc/random.h>
+#include <isc/task.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+#include <dns/acl.h>
+#include <dns/adb.h>
+#include <dns/cache.h>
+#include <dns/db.h>
+#include <dns/dispatch.h>
+#include <dns/ds.h>
+#include <dns/events.h>
+#include <dns/forward.h>
+#include <dns/keytable.h>
+#include <dns/log.h>
+#include <dns/message.h>
+#include <dns/ncache.h>
+#include <dns/opcode.h>
+#include <dns/peer.h>
+#include <dns/rbt.h>
+#include <dns/rcode.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatastruct.h>
+#include <dns/rdatatype.h>
+#include <dns/resolver.h>
+#include <dns/result.h>
+#include <dns/rootns.h>
+#include <dns/stats.h>
+#include <dns/tsig.h>
+#include <dns/validator.h>
+
+#define DNS_RESOLVER_TRACE
+#ifdef DNS_RESOLVER_TRACE
+#define RTRACE(m) isc_log_write(dns_lctx, \
+ DNS_LOGCATEGORY_RESOLVER, \
+ DNS_LOGMODULE_RESOLVER, \
+ ISC_LOG_DEBUG(3), \
+ "res %p: %s", res, (m))
+#define RRTRACE(r, m) isc_log_write(dns_lctx, \
+ DNS_LOGCATEGORY_RESOLVER, \
+ DNS_LOGMODULE_RESOLVER, \
+ ISC_LOG_DEBUG(3), \
+ "res %p: %s", (r), (m))
+#define FCTXTRACE(m) isc_log_write(dns_lctx, \
+ DNS_LOGCATEGORY_RESOLVER, \
+ DNS_LOGMODULE_RESOLVER, \
+ ISC_LOG_DEBUG(3), \
+ "fctx %p(%s'): %s", fctx, fctx->info, (m))
+#define FCTXTRACE2(m1, m2) \
+ isc_log_write(dns_lctx, \
+ DNS_LOGCATEGORY_RESOLVER, \
+ DNS_LOGMODULE_RESOLVER, \
+ ISC_LOG_DEBUG(3), \
+ "fctx %p(%s): %s %s", \
+ fctx, fctx->info, (m1), (m2))
+#define FTRACE(m) isc_log_write(dns_lctx, \
+ DNS_LOGCATEGORY_RESOLVER, \
+ DNS_LOGMODULE_RESOLVER, \
+ ISC_LOG_DEBUG(3), \
+ "fetch %p (fctx %p(%s)): %s", \
+ fetch, fetch->private, \
+ fetch->private->info, (m))
+#define QTRACE(m) isc_log_write(dns_lctx, \
+ DNS_LOGCATEGORY_RESOLVER, \
+ DNS_LOGMODULE_RESOLVER, \
+ ISC_LOG_DEBUG(3), \
+ "resquery %p (fctx %p(%s)): %s", \
+ query, query->fctx, \
+ query->fctx->info, (m))
+#else
+#define RTRACE(m)
+#define RRTRACE(r, m)
+#define FCTXTRACE(m)
+#define FTRACE(m)
+#define QTRACE(m)
+#endif
+
+/*%
+ * Maximum EDNS0 input packet size.
+ */
+#define RECV_BUFFER_SIZE 4096 /* XXXRTH Constant. */
+
+/*%
+ * This defines the maximum number of timeouts we will permit before we
+ * disable EDNS0 on the query.
+ */
+#define MAX_EDNS0_TIMEOUTS 3
+
+typedef struct fetchctx fetchctx_t;
+
+typedef struct query {
+ /* Locked by task event serialization. */
+ unsigned int magic;
+ fetchctx_t * fctx;
+ isc_mem_t * mctx;
+ dns_dispatchmgr_t * dispatchmgr;
+ dns_dispatch_t * dispatch;
+ isc_boolean_t exclusivesocket;
+ dns_adbaddrinfo_t * addrinfo;
+ isc_socket_t * tcpsocket;
+ isc_time_t start;
+ dns_messageid_t id;
+ dns_dispentry_t * dispentry;
+ ISC_LINK(struct query) link;
+ isc_buffer_t buffer;
+ isc_buffer_t *tsig;
+ dns_tsigkey_t *tsigkey;
+ unsigned int options;
+ unsigned int attributes;
+ unsigned int sends;
+ unsigned int connects;
+ unsigned char data[512];
+} resquery_t;
+
+#define QUERY_MAGIC ISC_MAGIC('Q', '!', '!', '!')
+#define VALID_QUERY(query) ISC_MAGIC_VALID(query, QUERY_MAGIC)
+
+#define RESQUERY_ATTR_CANCELED 0x02
+
+#define RESQUERY_CONNECTING(q) ((q)->connects > 0)
+#define RESQUERY_CANCELED(q) (((q)->attributes & \
+ RESQUERY_ATTR_CANCELED) != 0)
+#define RESQUERY_SENDING(q) ((q)->sends > 0)
+
+typedef enum {
+ fetchstate_init = 0, /*%< Start event has not run yet. */
+ fetchstate_active,
+ fetchstate_done /*%< FETCHDONE events posted. */
+} fetchstate;
+
+struct fetchctx {
+ /*% Not locked. */
+ unsigned int magic;
+ dns_resolver_t * res;
+ dns_name_t name;
+ dns_rdatatype_t type;
+ unsigned int options;
+ unsigned int bucketnum;
+ char * info;
+ /*% Locked by appropriate bucket lock. */
+ fetchstate state;
+ isc_boolean_t want_shutdown;
+ isc_boolean_t cloned;
+ isc_boolean_t spilled;
+ unsigned int references;
+ isc_event_t control_event;
+ ISC_LINK(struct fetchctx) link;
+ ISC_LIST(dns_fetchevent_t) events;
+ /*% Locked by task event serialization. */
+ dns_name_t domain;
+ dns_rdataset_t nameservers;
+ unsigned int attributes;
+ isc_timer_t * timer;
+ isc_time_t expires;
+ isc_interval_t interval;
+ dns_message_t * qmessage;
+ dns_message_t * rmessage;
+ ISC_LIST(resquery_t) queries;
+ dns_adbfindlist_t finds;
+ dns_adbfind_t * find;
+ dns_adbfindlist_t altfinds;
+ dns_adbfind_t * altfind;
+ dns_adbaddrinfolist_t forwaddrs;
+ dns_adbaddrinfolist_t altaddrs;
+ isc_sockaddrlist_t forwarders;
+ dns_fwdpolicy_t fwdpolicy;
+ isc_sockaddrlist_t bad;
+ isc_sockaddrlist_t edns;
+ isc_sockaddrlist_t edns512;
+ dns_validator_t *validator;
+ ISC_LIST(dns_validator_t) validators;
+ dns_db_t * cache;
+ dns_adb_t * adb;
+
+ /*%
+ * The number of events we're waiting for.
+ */
+ unsigned int pending;
+
+ /*%
+ * The number of times we've "restarted" the current
+ * nameserver set. This acts as a failsafe to prevent
+ * us from pounding constantly on a particular set of
+ * servers that, for whatever reason, are not giving
+ * us useful responses, but are responding in such a
+ * way that they are not marked "bad".
+ */
+ unsigned int restarts;
+
+ /*%
+ * The number of timeouts that have occurred since we
+ * last successfully received a response packet. This
+ * is used for EDNS0 black hole detection.
+ */
+ unsigned int timeouts;
+ /*%
+ * Look aside state for DS lookups.
+ */
+ dns_name_t nsname;
+ dns_fetch_t * nsfetch;
+ dns_rdataset_t nsrrset;
+
+ /*%
+ * Number of queries that reference this context.
+ */
+ unsigned int nqueries;
+
+ /*%
+ * The reason to print when logging a successful
+ * response to a query.
+ */
+ const char * reason;
+
+ /*%
+ * Random numbers to use for mixing up server addresses.
+ */
+ isc_uint32_t rand_buf;
+ isc_uint32_t rand_bits;
+};
+
+#define FCTX_MAGIC ISC_MAGIC('F', '!', '!', '!')
+#define VALID_FCTX(fctx) ISC_MAGIC_VALID(fctx, FCTX_MAGIC)
+
+#define FCTX_ATTR_HAVEANSWER 0x0001
+#define FCTX_ATTR_GLUING 0x0002
+#define FCTX_ATTR_ADDRWAIT 0x0004
+#define FCTX_ATTR_SHUTTINGDOWN 0x0008
+#define FCTX_ATTR_WANTCACHE 0x0010
+#define FCTX_ATTR_WANTNCACHE 0x0020
+#define FCTX_ATTR_NEEDEDNS0 0x0040
+#define FCTX_ATTR_TRIEDFIND 0x0080
+#define FCTX_ATTR_TRIEDALT 0x0100
+
+#define HAVE_ANSWER(f) (((f)->attributes & FCTX_ATTR_HAVEANSWER) != \
+ 0)
+#define GLUING(f) (((f)->attributes & FCTX_ATTR_GLUING) != \
+ 0)
+#define ADDRWAIT(f) (((f)->attributes & FCTX_ATTR_ADDRWAIT) != \
+ 0)
+#define SHUTTINGDOWN(f) (((f)->attributes & FCTX_ATTR_SHUTTINGDOWN) \
+ != 0)
+#define WANTCACHE(f) (((f)->attributes & FCTX_ATTR_WANTCACHE) != 0)
+#define WANTNCACHE(f) (((f)->attributes & FCTX_ATTR_WANTNCACHE) != 0)
+#define NEEDEDNS0(f) (((f)->attributes & FCTX_ATTR_NEEDEDNS0) != 0)
+#define TRIEDFIND(f) (((f)->attributes & FCTX_ATTR_TRIEDFIND) != 0)
+#define TRIEDALT(f) (((f)->attributes & FCTX_ATTR_TRIEDALT) != 0)
+
+typedef struct {
+ dns_adbaddrinfo_t * addrinfo;
+ fetchctx_t * fctx;
+} dns_valarg_t;
+
+struct dns_fetch {
+ unsigned int magic;
+ fetchctx_t * private;
+};
+
+#define DNS_FETCH_MAGIC ISC_MAGIC('F', 't', 'c', 'h')
+#define DNS_FETCH_VALID(fetch) ISC_MAGIC_VALID(fetch, DNS_FETCH_MAGIC)
+
+typedef struct fctxbucket {
+ isc_task_t * task;
+ isc_mutex_t lock;
+ ISC_LIST(fetchctx_t) fctxs;
+ isc_boolean_t exiting;
+ isc_mem_t * mctx;
+} fctxbucket_t;
+
+typedef struct alternate {
+ isc_boolean_t isaddress;
+ union {
+ isc_sockaddr_t addr;
+ struct {
+ dns_name_t name;
+ in_port_t port;
+ } _n;
+ } _u;
+ ISC_LINK(struct alternate) link;
+} alternate_t;
+
+struct dns_resolver {
+ /* Unlocked. */
+ unsigned int magic;
+ isc_mem_t * mctx;
+ isc_mutex_t lock;
+ isc_mutex_t nlock;
+ isc_mutex_t primelock;
+ dns_rdataclass_t rdclass;
+ isc_socketmgr_t * socketmgr;
+ isc_timermgr_t * timermgr;
+ isc_taskmgr_t * taskmgr;
+ dns_view_t * view;
+ isc_boolean_t frozen;
+ unsigned int options;
+ dns_dispatchmgr_t * dispatchmgr;
+ dns_dispatch_t * dispatchv4;
+ isc_boolean_t exclusivev4;
+ dns_dispatch_t * dispatchv6;
+ isc_boolean_t exclusivev6;
+ unsigned int ndisps;
+ unsigned int nbuckets;
+ fctxbucket_t * buckets;
+ isc_uint32_t lame_ttl;
+ ISC_LIST(alternate_t) alternates;
+ isc_uint16_t udpsize;
+#if USE_ALGLOCK
+ isc_rwlock_t alglock;
+#endif
+ dns_rbt_t * algorithms;
+#if USE_MBSLOCK
+ isc_rwlock_t mbslock;
+#endif
+ dns_rbt_t * mustbesecure;
+ unsigned int spillatmax;
+ unsigned int spillatmin;
+ isc_timer_t * spillattimer;
+ isc_boolean_t zero_no_soa_ttl;
+
+ /* Locked by lock. */
+ unsigned int references;
+ isc_boolean_t exiting;
+ isc_eventlist_t whenshutdown;
+ unsigned int activebuckets;
+ isc_boolean_t priming;
+ unsigned int spillat; /* clients-per-query */
+ unsigned int nextdisp;
+ /* Locked by primelock. */
+ dns_fetch_t * primefetch;
+ /* Locked by nlock. */
+ unsigned int nfctx;
+};
+
+#define RES_MAGIC ISC_MAGIC('R', 'e', 's', '!')
+#define VALID_RESOLVER(res) ISC_MAGIC_VALID(res, RES_MAGIC)
+
+/*%
+ * Private addrinfo flags. These must not conflict with DNS_FETCHOPT_NOEDNS0,
+ * which we also use as an addrinfo flag.
+ */
+#define FCTX_ADDRINFO_MARK 0x0001
+#define FCTX_ADDRINFO_FORWARDER 0x1000
+#define FCTX_ADDRINFO_TRIED 0x2000
+#define UNMARKED(a) (((a)->flags & FCTX_ADDRINFO_MARK) \
+ == 0)
+#define ISFORWARDER(a) (((a)->flags & \
+ FCTX_ADDRINFO_FORWARDER) != 0)
+#define TRIED(a) (((a)->flags & \
+ FCTX_ADDRINFO_TRIED) != 0)
+
+#define NXDOMAIN(r) (((r)->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0)
+
+static void destroy(dns_resolver_t *res);
+static void empty_bucket(dns_resolver_t *res);
+static isc_result_t resquery_send(resquery_t *query);
+static void resquery_response(isc_task_t *task, isc_event_t *event);
+static void resquery_connected(isc_task_t *task, isc_event_t *event);
+static void fctx_try(fetchctx_t *fctx, isc_boolean_t retrying);
+static isc_boolean_t fctx_destroy(fetchctx_t *fctx);
+static isc_result_t ncache_adderesult(dns_message_t *message,
+ dns_db_t *cache, dns_dbnode_t *node,
+ dns_rdatatype_t covers,
+ isc_stdtime_t now, dns_ttl_t maxttl,
+ isc_boolean_t optout,
+ dns_rdataset_t *ardataset,
+ isc_result_t *eresultp);
+static void validated(isc_task_t *task, isc_event_t *event);
+static void maybe_destroy(fetchctx_t *fctx);
+static void add_bad(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
+ isc_result_t reason);
+
+/*%
+ * Increment resolver-related statistics counters.
+ */
+static inline void
+inc_stats(dns_resolver_t *res, dns_statscounter_t counter) {
+ if (res->view->resstats != NULL)
+ dns_generalstats_increment(res->view->resstats, counter);
+}
+
+static isc_result_t
+valcreate(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, dns_name_t *name,
+ dns_rdatatype_t type, dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset, unsigned int valoptions,
+ isc_task_t *task)
+{
+ dns_validator_t *validator = NULL;
+ dns_valarg_t *valarg;
+ isc_result_t result;
+
+ valarg = isc_mem_get(fctx->res->buckets[fctx->bucketnum].mctx,
+ sizeof(*valarg));
+ if (valarg == NULL)
+ return (ISC_R_NOMEMORY);
+
+ valarg->fctx = fctx;
+ valarg->addrinfo = addrinfo;
+
+ if (!ISC_LIST_EMPTY(fctx->validators))
+ INSIST((valoptions & DNS_VALIDATOR_DEFER) != 0);
+
+ result = dns_validator_create(fctx->res->view, name, type, rdataset,
+ sigrdataset, fctx->rmessage,
+ valoptions, task, validated, valarg,
+ &validator);
+ if (result == ISC_R_SUCCESS) {
+ inc_stats(fctx->res, dns_resstatscounter_val);
+ if ((valoptions & DNS_VALIDATOR_DEFER) == 0) {
+ INSIST(fctx->validator == NULL);
+ fctx->validator = validator;
+ }
+ ISC_LIST_APPEND(fctx->validators, validator, link);
+ } else
+ isc_mem_put(fctx->res->buckets[fctx->bucketnum].mctx,
+ valarg, sizeof(*valarg));
+ return (result);
+}
+
+static isc_boolean_t
+fix_mustbedelegationornxdomain(dns_message_t *message, fetchctx_t *fctx) {
+ dns_name_t *name;
+ dns_name_t *domain = &fctx->domain;
+ dns_rdataset_t *rdataset;
+ dns_rdatatype_t type;
+ isc_result_t result;
+ isc_boolean_t keep_auth = ISC_FALSE;
+
+ if (message->rcode == dns_rcode_nxdomain)
+ return (ISC_FALSE);
+
+ /*
+ * Look for BIND 8 style delegations.
+ * Also look for answers to ANY queries where the duplicate NS RRset
+ * may have been stripped from the authority section.
+ */
+ if (message->counts[DNS_SECTION_ANSWER] != 0 &&
+ (fctx->type == dns_rdatatype_ns ||
+ fctx->type == dns_rdatatype_any)) {
+ result = dns_message_firstname(message, DNS_SECTION_ANSWER);
+ while (result == ISC_R_SUCCESS) {
+ name = NULL;
+ dns_message_currentname(message, DNS_SECTION_ANSWER,
+ &name);
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link)) {
+ type = rdataset->type;
+ if (type != dns_rdatatype_ns)
+ continue;
+ if (dns_name_issubdomain(name, domain))
+ return (ISC_FALSE);
+ }
+ result = dns_message_nextname(message,
+ DNS_SECTION_ANSWER);
+ }
+ }
+
+ /* Look for referral. */
+ if (message->counts[DNS_SECTION_AUTHORITY] == 0)
+ goto munge;
+
+ result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
+ while (result == ISC_R_SUCCESS) {
+ name = NULL;
+ dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link)) {
+ type = rdataset->type;
+ if (type == dns_rdatatype_soa &&
+ dns_name_equal(name, domain))
+ keep_auth = ISC_TRUE;
+ if (type != dns_rdatatype_ns &&
+ type != dns_rdatatype_soa)
+ continue;
+ if (dns_name_equal(name, domain))
+ goto munge;
+ if (dns_name_issubdomain(name, domain))
+ return (ISC_FALSE);
+ }
+ result = dns_message_nextname(message, DNS_SECTION_AUTHORITY);
+ }
+
+ munge:
+ message->rcode = dns_rcode_nxdomain;
+ message->counts[DNS_SECTION_ANSWER] = 0;
+ if (!keep_auth)
+ message->counts[DNS_SECTION_AUTHORITY] = 0;
+ message->counts[DNS_SECTION_ADDITIONAL] = 0;
+ return (ISC_TRUE);
+}
+
+static inline isc_result_t
+fctx_starttimer(fetchctx_t *fctx) {
+ /*
+ * Start the lifetime timer for fctx.
+ *
+ * This is also used for stopping the idle timer; in that
+ * case we must purge events already posted to ensure that
+ * no further idle events are delivered.
+ */
+ return (isc_timer_reset(fctx->timer, isc_timertype_once,
+ &fctx->expires, NULL, ISC_TRUE));
+}
+
+static inline void
+fctx_stoptimer(fetchctx_t *fctx) {
+ isc_result_t result;
+
+ /*
+ * We don't return a result if resetting the timer to inactive fails
+ * since there's nothing to be done about it. Resetting to inactive
+ * should never fail anyway, since the code as currently written
+ * cannot fail in that case.
+ */
+ result = isc_timer_reset(fctx->timer, isc_timertype_inactive,
+ NULL, NULL, ISC_TRUE);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_timer_reset(): %s",
+ isc_result_totext(result));
+ }
+}
+
+
+static inline isc_result_t
+fctx_startidletimer(fetchctx_t *fctx, isc_interval_t *interval) {
+ /*
+ * Start the idle timer for fctx. The lifetime timer continues
+ * to be in effect.
+ */
+ return (isc_timer_reset(fctx->timer, isc_timertype_once,
+ &fctx->expires, interval, ISC_FALSE));
+}
+
+/*
+ * Stopping the idle timer is equivalent to calling fctx_starttimer(), but
+ * we use fctx_stopidletimer for readability in the code below.
+ */
+#define fctx_stopidletimer fctx_starttimer
+
+
+static inline void
+resquery_destroy(resquery_t **queryp) {
+ resquery_t *query;
+
+ REQUIRE(queryp != NULL);
+ query = *queryp;
+ REQUIRE(!ISC_LINK_LINKED(query, link));
+
+ INSIST(query->tcpsocket == NULL);
+
+ query->fctx->nqueries--;
+ if (SHUTTINGDOWN(query->fctx))
+ maybe_destroy(query->fctx); /* Locks bucket. */
+ query->magic = 0;
+ isc_mem_put(query->mctx, query, sizeof(*query));
+ *queryp = NULL;
+}
+
+static void
+fctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp,
+ isc_time_t *finish, isc_boolean_t no_response)
+{
+ fetchctx_t *fctx;
+ resquery_t *query;
+ unsigned int rtt;
+ unsigned int factor;
+ dns_adbfind_t *find;
+ dns_adbaddrinfo_t *addrinfo;
+ isc_socket_t *socket;
+
+ query = *queryp;
+ fctx = query->fctx;
+
+ FCTXTRACE("cancelquery");
+
+ REQUIRE(!RESQUERY_CANCELED(query));
+
+ query->attributes |= RESQUERY_ATTR_CANCELED;
+
+ /*
+ * Should we update the RTT?
+ */
+ if (finish != NULL || no_response) {
+ if (finish != NULL) {
+ /*
+ * We have both the start and finish times for this
+ * packet, so we can compute a real RTT.
+ */
+ rtt = (unsigned int)isc_time_microdiff(finish,
+ &query->start);
+ factor = DNS_ADB_RTTADJDEFAULT;
+ } else {
+ /*
+ * We don't have an RTT for this query. Maybe the
+ * packet was lost, or maybe this server is very
+ * slow. We don't know. Increase the RTT.
+ */
+ INSIST(no_response);
+ rtt = query->addrinfo->srtt + 200000;
+ if (rtt > 10000000)
+ rtt = 10000000;
+ /*
+ * Replace the current RTT with our value.
+ */
+ factor = DNS_ADB_RTTADJREPLACE;
+ }
+ dns_adb_adjustsrtt(fctx->adb, query->addrinfo, rtt, factor);
+ }
+
+ /* Remember that the server has been tried. */
+ if (!TRIED(query->addrinfo)) {
+ dns_adb_changeflags(fctx->adb, query->addrinfo,
+ FCTX_ADDRINFO_TRIED, FCTX_ADDRINFO_TRIED);
+ }
+
+ /*
+ * Age RTTs of servers not tried.
+ */
+ factor = DNS_ADB_RTTADJAGE;
+ if (finish != NULL)
+ for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs);
+ addrinfo != NULL;
+ addrinfo = ISC_LIST_NEXT(addrinfo, publink))
+ if (UNMARKED(addrinfo))
+ dns_adb_adjustsrtt(fctx->adb, addrinfo,
+ 0, factor);
+
+ if (finish != NULL && TRIEDFIND(fctx))
+ for (find = ISC_LIST_HEAD(fctx->finds);
+ find != NULL;
+ find = ISC_LIST_NEXT(find, publink))
+ for (addrinfo = ISC_LIST_HEAD(find->list);
+ addrinfo != NULL;
+ addrinfo = ISC_LIST_NEXT(addrinfo, publink))
+ if (UNMARKED(addrinfo))
+ dns_adb_adjustsrtt(fctx->adb, addrinfo,
+ 0, factor);
+
+ if (finish != NULL && TRIEDALT(fctx)) {
+ for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs);
+ addrinfo != NULL;
+ addrinfo = ISC_LIST_NEXT(addrinfo, publink))
+ if (UNMARKED(addrinfo))
+ dns_adb_adjustsrtt(fctx->adb, addrinfo,
+ 0, factor);
+ for (find = ISC_LIST_HEAD(fctx->altfinds);
+ find != NULL;
+ find = ISC_LIST_NEXT(find, publink))
+ for (addrinfo = ISC_LIST_HEAD(find->list);
+ addrinfo != NULL;
+ addrinfo = ISC_LIST_NEXT(addrinfo, publink))
+ if (UNMARKED(addrinfo))
+ dns_adb_adjustsrtt(fctx->adb, addrinfo,
+ 0, factor);
+ }
+
+ /*
+ * Check for any outstanding socket events. If they exist, cancel
+ * them and let the event handlers finish the cleanup. The resolver
+ * only needs to worry about managing the connect and send events;
+ * the dispatcher manages the recv events.
+ */
+ if (RESQUERY_CONNECTING(query)) {
+ /*
+ * Cancel the connect.
+ */
+ if (query->tcpsocket != NULL) {
+ isc_socket_cancel(query->tcpsocket, NULL,
+ ISC_SOCKCANCEL_CONNECT);
+ } else if (query->dispentry != NULL) {
+ INSIST(query->exclusivesocket);
+ socket = dns_dispatch_getentrysocket(query->dispentry);
+ if (socket != NULL)
+ isc_socket_cancel(socket, NULL,
+ ISC_SOCKCANCEL_CONNECT);
+ }
+ } else if (RESQUERY_SENDING(query)) {
+ /*
+ * Cancel the pending send.
+ */
+ if (query->exclusivesocket && query->dispentry != NULL)
+ socket = dns_dispatch_getentrysocket(query->dispentry);
+ else
+ socket = dns_dispatch_getsocket(query->dispatch);
+ if (socket != NULL)
+ isc_socket_cancel(socket, NULL, ISC_SOCKCANCEL_SEND);
+ }
+
+ if (query->dispentry != NULL)
+ dns_dispatch_removeresponse(&query->dispentry, deventp);
+
+ ISC_LIST_UNLINK(fctx->queries, query, link);
+
+ if (query->tsig != NULL)
+ isc_buffer_free(&query->tsig);
+
+ if (query->tsigkey != NULL)
+ dns_tsigkey_detach(&query->tsigkey);
+
+ if (query->dispatch != NULL)
+ dns_dispatch_detach(&query->dispatch);
+
+ if (! (RESQUERY_CONNECTING(query) || RESQUERY_SENDING(query)))
+ /*
+ * It's safe to destroy the query now.
+ */
+ resquery_destroy(&query);
+}
+
+static void
+fctx_cancelqueries(fetchctx_t *fctx, isc_boolean_t no_response) {
+ resquery_t *query, *next_query;
+
+ FCTXTRACE("cancelqueries");
+
+ for (query = ISC_LIST_HEAD(fctx->queries);
+ query != NULL;
+ query = next_query) {
+ next_query = ISC_LIST_NEXT(query, link);
+ fctx_cancelquery(&query, NULL, NULL, no_response);
+ }
+}
+
+static void
+fctx_cleanupfinds(fetchctx_t *fctx) {
+ dns_adbfind_t *find, *next_find;
+
+ REQUIRE(ISC_LIST_EMPTY(fctx->queries));
+
+ for (find = ISC_LIST_HEAD(fctx->finds);
+ find != NULL;
+ find = next_find) {
+ next_find = ISC_LIST_NEXT(find, publink);
+ ISC_LIST_UNLINK(fctx->finds, find, publink);
+ dns_adb_destroyfind(&find);
+ }
+ fctx->find = NULL;
+}
+
+static void
+fctx_cleanupaltfinds(fetchctx_t *fctx) {
+ dns_adbfind_t *find, *next_find;
+
+ REQUIRE(ISC_LIST_EMPTY(fctx->queries));
+
+ for (find = ISC_LIST_HEAD(fctx->altfinds);
+ find != NULL;
+ find = next_find) {
+ next_find = ISC_LIST_NEXT(find, publink);
+ ISC_LIST_UNLINK(fctx->altfinds, find, publink);
+ dns_adb_destroyfind(&find);
+ }
+ fctx->altfind = NULL;
+}
+
+static void
+fctx_cleanupforwaddrs(fetchctx_t *fctx) {
+ dns_adbaddrinfo_t *addr, *next_addr;
+
+ REQUIRE(ISC_LIST_EMPTY(fctx->queries));
+
+ for (addr = ISC_LIST_HEAD(fctx->forwaddrs);
+ addr != NULL;
+ addr = next_addr) {
+ next_addr = ISC_LIST_NEXT(addr, publink);
+ ISC_LIST_UNLINK(fctx->forwaddrs, addr, publink);
+ dns_adb_freeaddrinfo(fctx->adb, &addr);
+ }
+}
+
+static void
+fctx_cleanupaltaddrs(fetchctx_t *fctx) {
+ dns_adbaddrinfo_t *addr, *next_addr;
+
+ REQUIRE(ISC_LIST_EMPTY(fctx->queries));
+
+ for (addr = ISC_LIST_HEAD(fctx->altaddrs);
+ addr != NULL;
+ addr = next_addr) {
+ next_addr = ISC_LIST_NEXT(addr, publink);
+ ISC_LIST_UNLINK(fctx->altaddrs, addr, publink);
+ dns_adb_freeaddrinfo(fctx->adb, &addr);
+ }
+}
+
+static inline void
+fctx_stopeverything(fetchctx_t *fctx, isc_boolean_t no_response) {
+ FCTXTRACE("stopeverything");
+ fctx_cancelqueries(fctx, no_response);
+ fctx_cleanupfinds(fctx);
+ fctx_cleanupaltfinds(fctx);
+ fctx_cleanupforwaddrs(fctx);
+ fctx_cleanupaltaddrs(fctx);
+ fctx_stoptimer(fctx);
+}
+
+static inline void
+fctx_sendevents(fetchctx_t *fctx, isc_result_t result) {
+ dns_fetchevent_t *event, *next_event;
+ isc_task_t *task;
+ unsigned int count = 0;
+ isc_interval_t i;
+ isc_boolean_t logit = ISC_FALSE;
+ unsigned int old_spillat;
+ unsigned int new_spillat = 0; /* initialized to silence
+ compiler warnings */
+
+ /*
+ * Caller must be holding the appropriate bucket lock.
+ */
+ REQUIRE(fctx->state == fetchstate_done);
+
+ FCTXTRACE("sendevents");
+
+ for (event = ISC_LIST_HEAD(fctx->events);
+ event != NULL;
+ event = next_event) {
+ next_event = ISC_LIST_NEXT(event, ev_link);
+ ISC_LIST_UNLINK(fctx->events, event, ev_link);
+ task = event->ev_sender;
+ event->ev_sender = fctx;
+ if (!HAVE_ANSWER(fctx))
+ event->result = result;
+
+ INSIST(result != ISC_R_SUCCESS ||
+ dns_rdataset_isassociated(event->rdataset) ||
+ fctx->type == dns_rdatatype_any ||
+ fctx->type == dns_rdatatype_rrsig ||
+ fctx->type == dns_rdatatype_sig);
+
+ /*
+ * Negative results must be indicated in event->result.
+ */
+ if (dns_rdataset_isassociated(event->rdataset) &&
+ event->rdataset->type == dns_rdatatype_none) {
+ INSIST(event->result == DNS_R_NCACHENXDOMAIN ||
+ event->result == DNS_R_NCACHENXRRSET);
+ }
+
+ isc_task_sendanddetach(&task, ISC_EVENT_PTR(&event));
+ count++;
+ }
+
+ if ((fctx->attributes & FCTX_ATTR_HAVEANSWER) != 0 &&
+ fctx->spilled &&
+ (count < fctx->res->spillatmax || fctx->res->spillatmax == 0)) {
+ LOCK(&fctx->res->lock);
+ if (count == fctx->res->spillat && !fctx->res->exiting) {
+ old_spillat = fctx->res->spillat;
+ fctx->res->spillat += 5;
+ if (fctx->res->spillat > fctx->res->spillatmax &&
+ fctx->res->spillatmax != 0)
+ fctx->res->spillat = fctx->res->spillatmax;
+ new_spillat = fctx->res->spillat;
+ if (new_spillat != old_spillat) {
+ logit = ISC_TRUE;
+ }
+ isc_interval_set(&i, 20 * 60, 0);
+ result = isc_timer_reset(fctx->res->spillattimer,
+ isc_timertype_ticker, NULL,
+ &i, ISC_TRUE);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ }
+ UNLOCK(&fctx->res->lock);
+ if (logit)
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+ DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE,
+ "clients-per-query increased to %u",
+ new_spillat);
+ }
+}
+
+static inline void
+log_edns(fetchctx_t *fctx) {
+ char domainbuf[DNS_NAME_FORMATSIZE];
+
+ if (fctx->reason == NULL)
+ return;
+
+ dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_EDNS_DISABLED,
+ DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO,
+ "success resolving '%s' (in '%s'?) after %s",
+ fctx->info, domainbuf, fctx->reason);
+
+ fctx->reason = NULL;
+}
+
+static void
+fctx_done(fetchctx_t *fctx, isc_result_t result) {
+ dns_resolver_t *res;
+ isc_boolean_t no_response;
+
+ FCTXTRACE("done");
+
+ res = fctx->res;
+
+ if (result == ISC_R_SUCCESS) {
+ /*%
+ * Log any deferred EDNS timeout messages.
+ */
+ log_edns(fctx);
+ no_response = ISC_TRUE;
+ } else
+ no_response = ISC_FALSE;
+
+ fctx->reason = NULL;
+ fctx_stopeverything(fctx, no_response);
+
+ LOCK(&res->buckets[fctx->bucketnum].lock);
+
+ fctx->state = fetchstate_done;
+ fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
+ fctx_sendevents(fctx, result);
+
+ UNLOCK(&res->buckets[fctx->bucketnum].lock);
+}
+
+static void
+process_sendevent(resquery_t *query, isc_event_t *event) {
+ isc_socketevent_t *sevent = (isc_socketevent_t *)event;
+ isc_boolean_t retry = ISC_FALSE;
+ isc_result_t result;
+ fetchctx_t *fctx;
+
+ fctx = query->fctx;
+
+ if (RESQUERY_CANCELED(query)) {
+ if (query->sends == 0 && query->connects == 0) {
+ /*
+ * This query was canceled while the
+ * isc_socket_sendto/connect() was in progress.
+ */
+ if (query->tcpsocket != NULL)
+ isc_socket_detach(&query->tcpsocket);
+ resquery_destroy(&query);
+ }
+ } else {
+ switch (sevent->result) {
+ case ISC_R_SUCCESS:
+ break;
+
+ case ISC_R_HOSTUNREACH:
+ case ISC_R_NETUNREACH:
+ case ISC_R_NOPERM:
+ case ISC_R_ADDRNOTAVAIL:
+ case ISC_R_CONNREFUSED:
+
+ /*
+ * No route to remote.
+ */
+ add_bad(fctx, query->addrinfo, sevent->result);
+ fctx_cancelquery(&query, NULL, NULL, ISC_TRUE);
+ retry = ISC_TRUE;
+ break;
+
+ default:
+ fctx_cancelquery(&query, NULL, NULL, ISC_FALSE);
+ break;
+ }
+ }
+
+ isc_event_free(&event);
+
+ if (retry) {
+ /*
+ * Behave as if the idle timer has expired. For TCP
+ * this may not actually reflect the latest timer.
+ */
+ fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
+ result = fctx_stopidletimer(fctx);
+ if (result != ISC_R_SUCCESS)
+ fctx_done(fctx, result);
+ else
+ fctx_try(fctx, ISC_TRUE);
+ }
+}
+
+static void
+resquery_udpconnected(isc_task_t *task, isc_event_t *event) {
+ resquery_t *query = event->ev_arg;
+
+ REQUIRE(event->ev_type == ISC_SOCKEVENT_CONNECT);
+
+ QTRACE("udpconnected");
+
+ UNUSED(task);
+
+ INSIST(RESQUERY_CONNECTING(query));
+
+ query->connects--;
+
+ process_sendevent(query, event);
+}
+
+static void
+resquery_senddone(isc_task_t *task, isc_event_t *event) {
+ resquery_t *query = event->ev_arg;
+
+ REQUIRE(event->ev_type == ISC_SOCKEVENT_SENDDONE);
+
+ QTRACE("senddone");
+
+ /*
+ * XXXRTH
+ *
+ * Currently we don't wait for the senddone event before retrying
+ * a query. This means that if we get really behind, we may end
+ * up doing extra work!
+ */
+
+ UNUSED(task);
+
+ INSIST(RESQUERY_SENDING(query));
+
+ query->sends--;
+
+ process_sendevent(query, event);
+}
+
+static inline isc_result_t
+fctx_addopt(dns_message_t *message, unsigned int version,
+ isc_uint16_t udpsize, isc_boolean_t request_nsid)
+{
+ dns_rdataset_t *rdataset;
+ dns_rdatalist_t *rdatalist;
+ dns_rdata_t *rdata;
+ isc_result_t result;
+
+ rdatalist = NULL;
+ result = dns_message_gettemprdatalist(message, &rdatalist);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ rdata = NULL;
+ result = dns_message_gettemprdata(message, &rdata);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ rdataset = NULL;
+ result = dns_message_gettemprdataset(message, &rdataset);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ dns_rdataset_init(rdataset);
+
+ rdatalist->type = dns_rdatatype_opt;
+ rdatalist->covers = 0;
+
+ /*
+ * Set Maximum UDP buffer size.
+ */
+ rdatalist->rdclass = udpsize;
+
+ /*
+ * Set EXTENDED-RCODE and Z to 0, DO to 1.
+ */
+ rdatalist->ttl = (version << 16);
+ rdatalist->ttl |= DNS_MESSAGEEXTFLAG_DO;
+
+ /*
+ * Set EDNS options if applicable
+ */
+ if (request_nsid) {
+ /* Send empty NSID option (RFC5001) */
+ unsigned char data[4];
+ isc_buffer_t buf;
+
+ isc_buffer_init(&buf, data, sizeof(data));
+ isc_buffer_putuint16(&buf, DNS_OPT_NSID);
+ isc_buffer_putuint16(&buf, 0);
+ rdata->data = data;
+ rdata->length = sizeof(data);
+ } else {
+ rdata->data = NULL;
+ rdata->length = 0;
+ }
+
+ rdata->rdclass = rdatalist->rdclass;
+ rdata->type = rdatalist->type;
+ rdata->flags = 0;
+
+ ISC_LIST_INIT(rdatalist->rdata);
+ ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
+ RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, rdataset) == ISC_R_SUCCESS);
+
+ return (dns_message_setopt(message, rdataset));
+}
+
+static inline void
+fctx_setretryinterval(fetchctx_t *fctx, unsigned int rtt) {
+ unsigned int seconds;
+ unsigned int us;
+
+ /*
+ * We retry every .8 seconds the first two times through the address
+ * list, and then we do exponential back-off.
+ */
+ if (fctx->restarts < 3)
+ us = 800000;
+ else
+ us = (800000 << (fctx->restarts - 2));
+
+ /*
+ * Double the round-trip time.
+ */
+ rtt *= 2;
+
+ /*
+ * Always wait for at least the doubled round-trip time.
+ */
+ if (us < rtt)
+ us = rtt;
+
+ /*
+ * But don't ever wait for more than 10 seconds.
+ */
+ if (us > 10000000)
+ us = 10000000;
+
+ seconds = us / 1000000;
+ us -= seconds * 1000000;
+ isc_interval_set(&fctx->interval, seconds, us * 1000);
+}
+
+static isc_result_t
+fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
+ unsigned int options)
+{
+ dns_resolver_t *res;
+ isc_task_t *task;
+ isc_result_t result;
+ resquery_t *query;
+ isc_sockaddr_t addr;
+ isc_boolean_t have_addr = ISC_FALSE;
+ unsigned int srtt;
+
+ FCTXTRACE("query");
+
+ res = fctx->res;
+ task = res->buckets[fctx->bucketnum].task;
+
+ srtt = addrinfo->srtt;
+ if (ISFORWARDER(addrinfo) && srtt < 1000000)
+ srtt = 1000000;
+
+ fctx_setretryinterval(fctx, srtt);
+ result = fctx_startidletimer(fctx, &fctx->interval);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ INSIST(ISC_LIST_EMPTY(fctx->validators));
+
+ dns_message_reset(fctx->rmessage, DNS_MESSAGE_INTENTPARSE);
+
+ query = isc_mem_get(res->buckets[fctx->bucketnum].mctx,
+ sizeof(*query));
+ if (query == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto stop_idle_timer;
+ }
+ query->mctx = res->buckets[fctx->bucketnum].mctx;
+ query->options = options;
+ query->attributes = 0;
+ query->sends = 0;
+ query->connects = 0;
+ /*
+ * Note that the caller MUST guarantee that 'addrinfo' will remain
+ * valid until this query is canceled.
+ */
+ query->addrinfo = addrinfo;
+ TIME_NOW(&query->start);
+
+ /*
+ * If this is a TCP query, then we need to make a socket and
+ * a dispatch for it here. Otherwise we use the resolver's
+ * shared dispatch.
+ */
+ query->dispatchmgr = res->dispatchmgr;
+ query->dispatch = NULL;
+ query->exclusivesocket = ISC_FALSE;
+ query->tcpsocket = NULL;
+ if (res->view->peers != NULL) {
+ dns_peer_t *peer = NULL;
+ isc_netaddr_t dstip;
+ isc_netaddr_fromsockaddr(&dstip, &addrinfo->sockaddr);
+ result = dns_peerlist_peerbyaddr(res->view->peers,
+ &dstip, &peer);
+ if (result == ISC_R_SUCCESS) {
+ result = dns_peer_getquerysource(peer, &addr);
+ if (result == ISC_R_SUCCESS)
+ have_addr = ISC_TRUE;
+ }
+ }
+
+ if ((query->options & DNS_FETCHOPT_TCP) != 0) {
+ int pf;
+
+ pf = isc_sockaddr_pf(&addrinfo->sockaddr);
+ if (!have_addr) {
+ switch (pf) {
+ case PF_INET:
+ result =
+ dns_dispatch_getlocaladdress(res->dispatchv4,
+ &addr);
+ break;
+ case PF_INET6:
+ result =
+ dns_dispatch_getlocaladdress(res->dispatchv6,
+ &addr);
+ break;
+ default:
+ result = ISC_R_NOTIMPLEMENTED;
+ break;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_query;
+ }
+ isc_sockaddr_setport(&addr, 0);
+
+ result = isc_socket_create(res->socketmgr, pf,
+ isc_sockettype_tcp,
+ &query->tcpsocket);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_query;
+
+#ifndef BROKEN_TCP_BIND_BEFORE_CONNECT
+ result = isc_socket_bind(query->tcpsocket, &addr, 0);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_socket;
+#endif
+
+ /*
+ * A dispatch will be created once the connect succeeds.
+ */
+ } else {
+ if (have_addr) {
+ unsigned int attrs, attrmask;
+ attrs = DNS_DISPATCHATTR_UDP;
+ switch (isc_sockaddr_pf(&addr)) {
+ case AF_INET:
+ attrs |= DNS_DISPATCHATTR_IPV4;
+ break;
+ case AF_INET6:
+ attrs |= DNS_DISPATCHATTR_IPV6;
+ break;
+ default:
+ result = ISC_R_NOTIMPLEMENTED;
+ goto cleanup_query;
+ }
+ attrmask = DNS_DISPATCHATTR_UDP;
+ attrmask |= DNS_DISPATCHATTR_TCP;
+ attrmask |= DNS_DISPATCHATTR_IPV4;
+ attrmask |= DNS_DISPATCHATTR_IPV6;
+ result = dns_dispatch_getudp(res->dispatchmgr,
+ res->socketmgr,
+ res->taskmgr, &addr,
+ 4096, 1000, 32768, 16411,
+ 16433, attrs, attrmask,
+ &query->dispatch);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_query;
+ } else {
+ switch (isc_sockaddr_pf(&addrinfo->sockaddr)) {
+ case PF_INET:
+ dns_dispatch_attach(res->dispatchv4,
+ &query->dispatch);
+ query->exclusivesocket = res->exclusivev4;
+ break;
+ case PF_INET6:
+ dns_dispatch_attach(res->dispatchv6,
+ &query->dispatch);
+ query->exclusivesocket = res->exclusivev6;
+ break;
+ default:
+ result = ISC_R_NOTIMPLEMENTED;
+ goto cleanup_query;
+ }
+ }
+ /*
+ * We should always have a valid dispatcher here. If we
+ * don't support a protocol family, then its dispatcher
+ * will be NULL, but we shouldn't be finding addresses for
+ * protocol types we don't support, so the dispatcher
+ * we found should never be NULL.
+ */
+ INSIST(query->dispatch != NULL);
+ }
+
+ query->dispentry = NULL;
+ query->fctx = fctx;
+ query->tsig = NULL;
+ query->tsigkey = NULL;
+ ISC_LINK_INIT(query, link);
+ query->magic = QUERY_MAGIC;
+
+ if ((query->options & DNS_FETCHOPT_TCP) != 0) {
+ /*
+ * Connect to the remote server.
+ *
+ * XXXRTH Should we attach to the socket?
+ */
+ result = isc_socket_connect(query->tcpsocket,
+ &addrinfo->sockaddr, task,
+ resquery_connected, query);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_socket;
+ query->connects++;
+ QTRACE("connecting via TCP");
+ } else {
+ result = resquery_send(query);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_dispatch;
+ }
+
+ ISC_LIST_APPEND(fctx->queries, query, link);
+ query->fctx->nqueries++;
+ if (isc_sockaddr_pf(&addrinfo->sockaddr) == PF_INET)
+ inc_stats(res, dns_resstatscounter_queryv4);
+ else
+ inc_stats(res, dns_resstatscounter_queryv6);
+ if (res->view->resquerystats != NULL)
+ dns_rdatatypestats_increment(res->view->resquerystats,
+ fctx->type);
+
+ return (ISC_R_SUCCESS);
+
+ cleanup_socket:
+ isc_socket_detach(&query->tcpsocket);
+
+ cleanup_dispatch:
+ if (query->dispatch != NULL)
+ dns_dispatch_detach(&query->dispatch);
+
+ cleanup_query:
+ query->magic = 0;
+ isc_mem_put(res->buckets[fctx->bucketnum].mctx,
+ query, sizeof(*query));
+
+ stop_idle_timer:
+ RUNTIME_CHECK(fctx_stopidletimer(fctx) == ISC_R_SUCCESS);
+
+ return (result);
+}
+
+static isc_boolean_t
+triededns(fetchctx_t *fctx, isc_sockaddr_t *address) {
+ isc_sockaddr_t *sa;
+
+ for (sa = ISC_LIST_HEAD(fctx->edns);
+ sa != NULL;
+ sa = ISC_LIST_NEXT(sa, link)) {
+ if (isc_sockaddr_equal(sa, address))
+ return (ISC_TRUE);
+ }
+
+ return (ISC_FALSE);
+}
+
+static void
+add_triededns(fetchctx_t *fctx, isc_sockaddr_t *address) {
+ isc_sockaddr_t *sa;
+
+ if (triededns(fctx, address))
+ return;
+
+ sa = isc_mem_get(fctx->res->buckets[fctx->bucketnum].mctx,
+ sizeof(*sa));
+ if (sa == NULL)
+ return;
+
+ *sa = *address;
+ ISC_LIST_INITANDAPPEND(fctx->edns, sa, link);
+}
+
+static isc_boolean_t
+triededns512(fetchctx_t *fctx, isc_sockaddr_t *address) {
+ isc_sockaddr_t *sa;
+
+ for (sa = ISC_LIST_HEAD(fctx->edns512);
+ sa != NULL;
+ sa = ISC_LIST_NEXT(sa, link)) {
+ if (isc_sockaddr_equal(sa, address))
+ return (ISC_TRUE);
+ }
+
+ return (ISC_FALSE);
+}
+
+static void
+add_triededns512(fetchctx_t *fctx, isc_sockaddr_t *address) {
+ isc_sockaddr_t *sa;
+
+ if (triededns512(fctx, address))
+ return;
+
+ sa = isc_mem_get(fctx->res->buckets[fctx->bucketnum].mctx,
+ sizeof(*sa));
+ if (sa == NULL)
+ return;
+
+ *sa = *address;
+ ISC_LIST_INITANDAPPEND(fctx->edns512, sa, link);
+}
+
+static isc_result_t
+resquery_send(resquery_t *query) {
+ fetchctx_t *fctx;
+ isc_result_t result;
+ dns_name_t *qname = NULL;
+ dns_rdataset_t *qrdataset = NULL;
+ isc_region_t r;
+ dns_resolver_t *res;
+ isc_task_t *task;
+ isc_socket_t *socket;
+ isc_buffer_t tcpbuffer;
+ isc_sockaddr_t *address;
+ isc_buffer_t *buffer;
+ isc_netaddr_t ipaddr;
+ dns_tsigkey_t *tsigkey = NULL;
+ dns_peer_t *peer = NULL;
+ isc_boolean_t useedns;
+ dns_compress_t cctx;
+ isc_boolean_t cleanup_cctx = ISC_FALSE;
+ isc_boolean_t secure_domain;
+
+ fctx = query->fctx;
+ QTRACE("send");
+
+ res = fctx->res;
+ task = res->buckets[fctx->bucketnum].task;
+ address = NULL;
+
+ if ((query->options & DNS_FETCHOPT_TCP) != 0) {
+ /*
+ * Reserve space for the TCP message length.
+ */
+ isc_buffer_init(&tcpbuffer, query->data, sizeof(query->data));
+ isc_buffer_init(&query->buffer, query->data + 2,
+ sizeof(query->data) - 2);
+ buffer = &tcpbuffer;
+ } else {
+ isc_buffer_init(&query->buffer, query->data,
+ sizeof(query->data));
+ buffer = &query->buffer;
+ }
+
+ result = dns_message_gettempname(fctx->qmessage, &qname);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_temps;
+ result = dns_message_gettemprdataset(fctx->qmessage, &qrdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_temps;
+
+ /*
+ * Get a query id from the dispatch.
+ */
+ result = dns_dispatch_addresponse2(query->dispatch,
+ &query->addrinfo->sockaddr,
+ task,
+ resquery_response,
+ query,
+ &query->id,
+ &query->dispentry,
+ res->socketmgr);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_temps;
+
+ fctx->qmessage->opcode = dns_opcode_query;
+
+ /*
+ * Set up question.
+ */
+ dns_name_init(qname, NULL);
+ dns_name_clone(&fctx->name, qname);
+ dns_rdataset_init(qrdataset);
+ dns_rdataset_makequestion(qrdataset, res->rdclass, fctx->type);
+ ISC_LIST_APPEND(qname->list, qrdataset, link);
+ dns_message_addname(fctx->qmessage, qname, DNS_SECTION_QUESTION);
+ qname = NULL;
+ qrdataset = NULL;
+
+ /*
+ * Set RD if the client has requested that we do a recursive query,
+ * or if we're sending to a forwarder.
+ */
+ if ((query->options & DNS_FETCHOPT_RECURSIVE) != 0 ||
+ ISFORWARDER(query->addrinfo))
+ fctx->qmessage->flags |= DNS_MESSAGEFLAG_RD;
+
+ /*
+ * Set CD if the client says don't validate or the question is
+ * under a secure entry point.
+ */
+ if ((query->options & DNS_FETCHOPT_NOVALIDATE) != 0) {
+ fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD;
+ } else if (res->view->enablevalidation) {
+ result = dns_keytable_issecuredomain(res->view->secroots,
+ &fctx->name,
+ &secure_domain);
+ if (result != ISC_R_SUCCESS)
+ secure_domain = ISC_FALSE;
+ if (res->view->dlv != NULL)
+ secure_domain = ISC_TRUE;
+ if (secure_domain)
+ fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD;
+ }
+
+ /*
+ * We don't have to set opcode because it defaults to query.
+ */
+ fctx->qmessage->id = query->id;
+
+ /*
+ * Convert the question to wire format.
+ */
+ result = dns_compress_init(&cctx, -1, fctx->res->mctx);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_message;
+ cleanup_cctx = ISC_TRUE;
+
+ result = dns_message_renderbegin(fctx->qmessage, &cctx,
+ &query->buffer);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_message;
+
+ result = dns_message_rendersection(fctx->qmessage,
+ DNS_SECTION_QUESTION, 0);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_message;
+
+ peer = NULL;
+ isc_netaddr_fromsockaddr(&ipaddr, &query->addrinfo->sockaddr);
+ (void) dns_peerlist_peerbyaddr(fctx->res->view->peers, &ipaddr, &peer);
+
+ /*
+ * The ADB does not know about servers with "edns no". Check this,
+ * and then inform the ADB for future use.
+ */
+ if ((query->addrinfo->flags & DNS_FETCHOPT_NOEDNS0) == 0 &&
+ peer != NULL &&
+ dns_peer_getsupportedns(peer, &useedns) == ISC_R_SUCCESS &&
+ !useedns)
+ {
+ query->options |= DNS_FETCHOPT_NOEDNS0;
+ dns_adb_changeflags(fctx->adb, query->addrinfo,
+ DNS_FETCHOPT_NOEDNS0,
+ DNS_FETCHOPT_NOEDNS0);
+ }
+
+ /* Sync NOEDNS0 flag in addrinfo->flags and options now */
+ if ((query->addrinfo->flags & DNS_FETCHOPT_NOEDNS0) != 0)
+ query->options |= DNS_FETCHOPT_NOEDNS0;
+
+ /*
+ * Use EDNS0, unless the caller doesn't want it, or we know that
+ * the remote server doesn't like it.
+ */
+
+ if ((triededns512(fctx, &query->addrinfo->sockaddr) ||
+ fctx->timeouts >= (MAX_EDNS0_TIMEOUTS * 2)) &&
+ (query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
+ query->options |= DNS_FETCHOPT_NOEDNS0;
+ fctx->reason = "disabling EDNS";
+ } else if ((triededns(fctx, &query->addrinfo->sockaddr) ||
+ fctx->timeouts >= MAX_EDNS0_TIMEOUTS) &&
+ (query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
+ query->options |= DNS_FETCHOPT_EDNS512;
+ fctx->reason = "reducing the advertised EDNS UDP packet "
+ "size to 512 octets";
+ }
+
+ if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
+ if ((query->addrinfo->flags & DNS_FETCHOPT_NOEDNS0) == 0) {
+ unsigned int version = 0; /* Default version. */
+ unsigned int flags;
+ isc_uint16_t udpsize = res->udpsize;
+ isc_boolean_t reqnsid = res->view->requestnsid;
+
+ flags = query->addrinfo->flags;
+ if ((flags & DNS_FETCHOPT_EDNSVERSIONSET) != 0) {
+ version = flags & DNS_FETCHOPT_EDNSVERSIONMASK;
+ version >>= DNS_FETCHOPT_EDNSVERSIONSHIFT;
+ }
+ if ((query->options & DNS_FETCHOPT_EDNS512) != 0)
+ udpsize = 512;
+ else if (peer != NULL)
+ (void)dns_peer_getudpsize(peer, &udpsize);
+
+ /* request NSID for current view or peer? */
+ if (peer != NULL)
+ (void) dns_peer_getrequestnsid(peer, &reqnsid);
+ result = fctx_addopt(fctx->qmessage, version,
+ udpsize, reqnsid);
+ if (reqnsid && result == ISC_R_SUCCESS) {
+ query->options |= DNS_FETCHOPT_WANTNSID;
+ } else if (result != ISC_R_SUCCESS) {
+ /*
+ * We couldn't add the OPT, but we'll press on.
+ * We're not using EDNS0, so set the NOEDNS0
+ * bit.
+ */
+ query->options |= DNS_FETCHOPT_NOEDNS0;
+ }
+ } else {
+ /*
+ * We know this server doesn't like EDNS0, so we
+ * won't use it. Set the NOEDNS0 bit since we're
+ * not using EDNS0.
+ */
+ query->options |= DNS_FETCHOPT_NOEDNS0;
+ }
+ }
+
+ /*
+ * If we need EDNS0 to do this query and aren't using it, we lose.
+ */
+ if (NEEDEDNS0(fctx) && (query->options & DNS_FETCHOPT_NOEDNS0) != 0) {
+ result = DNS_R_SERVFAIL;
+ goto cleanup_message;
+ }
+
+ if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0)
+ add_triededns(fctx, &query->addrinfo->sockaddr);
+
+ if ((query->options & DNS_FETCHOPT_EDNS512) != 0)
+ add_triededns512(fctx, &query->addrinfo->sockaddr);
+
+ /*
+ * Clear CD if EDNS is not in use.
+ */
+ if ((query->options & DNS_FETCHOPT_NOEDNS0) != 0)
+ fctx->qmessage->flags &= ~DNS_MESSAGEFLAG_CD;
+
+ /*
+ * Add TSIG record tailored to the current recipient.
+ */
+ result = dns_view_getpeertsig(fctx->res->view, &ipaddr, &tsigkey);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
+ goto cleanup_message;
+
+ if (tsigkey != NULL) {
+ result = dns_message_settsigkey(fctx->qmessage, tsigkey);
+ dns_tsigkey_detach(&tsigkey);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_message;
+ }
+
+ result = dns_message_rendersection(fctx->qmessage,
+ DNS_SECTION_ADDITIONAL, 0);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_message;
+
+ result = dns_message_renderend(fctx->qmessage);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_message;
+
+ dns_compress_invalidate(&cctx);
+ cleanup_cctx = ISC_FALSE;
+
+ if (dns_message_gettsigkey(fctx->qmessage) != NULL) {
+ dns_tsigkey_attach(dns_message_gettsigkey(fctx->qmessage),
+ &query->tsigkey);
+ result = dns_message_getquerytsig(fctx->qmessage,
+ fctx->res->mctx,
+ &query->tsig);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_message;
+ }
+
+ /*
+ * If using TCP, write the length of the message at the beginning
+ * of the buffer.
+ */
+ if ((query->options & DNS_FETCHOPT_TCP) != 0) {
+ isc_buffer_usedregion(&query->buffer, &r);
+ isc_buffer_putuint16(&tcpbuffer, (isc_uint16_t)r.length);
+ isc_buffer_add(&tcpbuffer, r.length);
+ }
+
+ /*
+ * We're now done with the query message.
+ */
+ dns_message_reset(fctx->qmessage, DNS_MESSAGE_INTENTRENDER);
+
+ if (query->exclusivesocket)
+ socket = dns_dispatch_getentrysocket(query->dispentry);
+ else
+ socket = dns_dispatch_getsocket(query->dispatch);
+ /*
+ * Send the query!
+ */
+ if ((query->options & DNS_FETCHOPT_TCP) == 0) {
+ address = &query->addrinfo->sockaddr;
+ if (query->exclusivesocket) {
+ result = isc_socket_connect(socket, address, task,
+ resquery_udpconnected,
+ query);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_message;
+ query->connects++;
+ }
+ }
+ isc_buffer_usedregion(buffer, &r);
+
+ /*
+ * XXXRTH Make sure we don't send to ourselves! We should probably
+ * prune out these addresses when we get them from the ADB.
+ */
+ result = isc_socket_sendto(socket, &r, task, resquery_senddone,
+ query, address, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_message;
+
+ query->sends++;
+
+ QTRACE("sent");
+
+ return (ISC_R_SUCCESS);
+
+ cleanup_message:
+ if (cleanup_cctx)
+ dns_compress_invalidate(&cctx);
+
+ dns_message_reset(fctx->qmessage, DNS_MESSAGE_INTENTRENDER);
+
+ /*
+ * Stop the dispatcher from listening.
+ */
+ dns_dispatch_removeresponse(&query->dispentry, NULL);
+
+ cleanup_temps:
+ if (qname != NULL)
+ dns_message_puttempname(fctx->qmessage, &qname);
+ if (qrdataset != NULL)
+ dns_message_puttemprdataset(fctx->qmessage, &qrdataset);
+
+ return (result);
+}
+
+static void
+resquery_connected(isc_task_t *task, isc_event_t *event) {
+ isc_socketevent_t *sevent = (isc_socketevent_t *)event;
+ resquery_t *query = event->ev_arg;
+ isc_boolean_t retry = ISC_FALSE;
+ isc_interval_t interval;
+ isc_result_t result;
+ unsigned int attrs;
+ fetchctx_t *fctx;
+
+ REQUIRE(event->ev_type == ISC_SOCKEVENT_CONNECT);
+ REQUIRE(VALID_QUERY(query));
+
+ QTRACE("connected");
+
+ UNUSED(task);
+
+ /*
+ * XXXRTH
+ *
+ * Currently we don't wait for the connect event before retrying
+ * a query. This means that if we get really behind, we may end
+ * up doing extra work!
+ */
+
+ query->connects--;
+ fctx = query->fctx;
+
+ if (RESQUERY_CANCELED(query)) {
+ /*
+ * This query was canceled while the connect() was in
+ * progress.
+ */
+ isc_socket_detach(&query->tcpsocket);
+ resquery_destroy(&query);
+ } else {
+ switch (sevent->result) {
+ case ISC_R_SUCCESS:
+
+ /*
+ * Extend the idle timer for TCP. 20 seconds
+ * should be long enough for a TCP connection to be
+ * established, a single DNS request to be sent,
+ * and the response received.
+ */
+ isc_interval_set(&interval, 20, 0);
+ result = fctx_startidletimer(query->fctx, &interval);
+ if (result != ISC_R_SUCCESS) {
+ fctx_cancelquery(&query, NULL, NULL, ISC_FALSE);
+ fctx_done(fctx, result);
+ break;
+ }
+ /*
+ * We are connected. Create a dispatcher and
+ * send the query.
+ */
+ attrs = 0;
+ attrs |= DNS_DISPATCHATTR_TCP;
+ attrs |= DNS_DISPATCHATTR_PRIVATE;
+ attrs |= DNS_DISPATCHATTR_CONNECTED;
+ if (isc_sockaddr_pf(&query->addrinfo->sockaddr) ==
+ AF_INET)
+ attrs |= DNS_DISPATCHATTR_IPV4;
+ else
+ attrs |= DNS_DISPATCHATTR_IPV6;
+ attrs |= DNS_DISPATCHATTR_MAKEQUERY;
+
+ result = dns_dispatch_createtcp(query->dispatchmgr,
+ query->tcpsocket,
+ query->fctx->res->taskmgr,
+ 4096, 2, 1, 1, 3, attrs,
+ &query->dispatch);
+
+ /*
+ * Regardless of whether dns_dispatch_create()
+ * succeeded or not, we don't need our reference
+ * to the socket anymore.
+ */
+ isc_socket_detach(&query->tcpsocket);
+
+ if (result == ISC_R_SUCCESS)
+ result = resquery_send(query);
+
+ if (result != ISC_R_SUCCESS) {
+ fctx_cancelquery(&query, NULL, NULL, ISC_FALSE);
+ fctx_done(fctx, result);
+ }
+ break;
+
+ case ISC_R_NETUNREACH:
+ case ISC_R_HOSTUNREACH:
+ case ISC_R_CONNREFUSED:
+ case ISC_R_NOPERM:
+ case ISC_R_ADDRNOTAVAIL:
+ case ISC_R_CONNECTIONRESET:
+ /*
+ * No route to remote.
+ */
+ isc_socket_detach(&query->tcpsocket);
+ fctx_cancelquery(&query, NULL, NULL, ISC_TRUE);
+ retry = ISC_TRUE;
+ break;
+
+ default:
+ isc_socket_detach(&query->tcpsocket);
+ fctx_cancelquery(&query, NULL, NULL, ISC_FALSE);
+ break;
+ }
+ }
+
+ isc_event_free(&event);
+
+ if (retry) {
+ /*
+ * Behave as if the idle timer has expired. For TCP
+ * connections this may not actually reflect the latest timer.
+ */
+ fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
+ result = fctx_stopidletimer(fctx);
+ if (result != ISC_R_SUCCESS)
+ fctx_done(fctx, result);
+ else
+ fctx_try(fctx, ISC_TRUE);
+ }
+}
+
+static void
+fctx_finddone(isc_task_t *task, isc_event_t *event) {
+ fetchctx_t *fctx;
+ dns_adbfind_t *find;
+ dns_resolver_t *res;
+ isc_boolean_t want_try = ISC_FALSE;
+ isc_boolean_t want_done = ISC_FALSE;
+ isc_boolean_t bucket_empty = ISC_FALSE;
+ unsigned int bucketnum;
+
+ find = event->ev_sender;
+ fctx = event->ev_arg;
+ REQUIRE(VALID_FCTX(fctx));
+ res = fctx->res;
+
+ UNUSED(task);
+
+ FCTXTRACE("finddone");
+
+ INSIST(fctx->pending > 0);
+ fctx->pending--;
+
+ if (ADDRWAIT(fctx)) {
+ /*
+ * The fetch is waiting for a name to be found.
+ */
+ INSIST(!SHUTTINGDOWN(fctx));
+ fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
+ if (event->ev_type == DNS_EVENT_ADBMOREADDRESSES)
+ want_try = ISC_TRUE;
+ else if (fctx->pending == 0) {
+ /*
+ * We've got nothing else to wait for and don't
+ * know the answer. There's nothing to do but
+ * fail the fctx.
+ */
+ want_done = ISC_TRUE;
+ }
+ } else if (SHUTTINGDOWN(fctx) && fctx->pending == 0 &&
+ fctx->nqueries == 0 && ISC_LIST_EMPTY(fctx->validators)) {
+ bucketnum = fctx->bucketnum;
+ LOCK(&res->buckets[bucketnum].lock);
+ /*
+ * Note that we had to wait until we had the lock before
+ * looking at fctx->references.
+ */
+ if (fctx->references == 0)
+ bucket_empty = fctx_destroy(fctx);
+ UNLOCK(&res->buckets[bucketnum].lock);
+ }
+
+ isc_event_free(&event);
+ dns_adb_destroyfind(&find);
+
+ if (want_try)
+ fctx_try(fctx, ISC_TRUE);
+ else if (want_done)
+ fctx_done(fctx, ISC_R_FAILURE);
+ else if (bucket_empty)
+ empty_bucket(res);
+}
+
+
+static inline isc_boolean_t
+bad_server(fetchctx_t *fctx, isc_sockaddr_t *address) {
+ isc_sockaddr_t *sa;
+
+ for (sa = ISC_LIST_HEAD(fctx->bad);
+ sa != NULL;
+ sa = ISC_LIST_NEXT(sa, link)) {
+ if (isc_sockaddr_equal(sa, address))
+ return (ISC_TRUE);
+ }
+
+ return (ISC_FALSE);
+}
+
+static inline isc_boolean_t
+mark_bad(fetchctx_t *fctx) {
+ dns_adbfind_t *curr;
+ dns_adbaddrinfo_t *addrinfo;
+ isc_boolean_t all_bad = ISC_TRUE;
+
+ /*
+ * Mark all known bad servers, so we don't try to talk to them
+ * again.
+ */
+
+ /*
+ * Mark any bad nameservers.
+ */
+ for (curr = ISC_LIST_HEAD(fctx->finds);
+ curr != NULL;
+ curr = ISC_LIST_NEXT(curr, publink)) {
+ for (addrinfo = ISC_LIST_HEAD(curr->list);
+ addrinfo != NULL;
+ addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
+ if (bad_server(fctx, &addrinfo->sockaddr))
+ addrinfo->flags |= FCTX_ADDRINFO_MARK;
+ else
+ all_bad = ISC_FALSE;
+ }
+ }
+
+ /*
+ * Mark any bad forwarders.
+ */
+ for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs);
+ addrinfo != NULL;
+ addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
+ if (bad_server(fctx, &addrinfo->sockaddr))
+ addrinfo->flags |= FCTX_ADDRINFO_MARK;
+ else
+ all_bad = ISC_FALSE;
+ }
+
+ /*
+ * Mark any bad alternates.
+ */
+ for (curr = ISC_LIST_HEAD(fctx->altfinds);
+ curr != NULL;
+ curr = ISC_LIST_NEXT(curr, publink)) {
+ for (addrinfo = ISC_LIST_HEAD(curr->list);
+ addrinfo != NULL;
+ addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
+ if (bad_server(fctx, &addrinfo->sockaddr))
+ addrinfo->flags |= FCTX_ADDRINFO_MARK;
+ else
+ all_bad = ISC_FALSE;
+ }
+ }
+
+ for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs);
+ addrinfo != NULL;
+ addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
+ if (bad_server(fctx, &addrinfo->sockaddr))
+ addrinfo->flags |= FCTX_ADDRINFO_MARK;
+ else
+ all_bad = ISC_FALSE;
+ }
+
+ return (all_bad);
+}
+
+static void
+add_bad(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, isc_result_t reason) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char addrbuf[ISC_SOCKADDR_FORMATSIZE];
+ char classbuf[64];
+ char typebuf[64];
+ char code[64];
+ isc_buffer_t b;
+ isc_sockaddr_t *sa;
+ const char *sep1, *sep2;
+ isc_sockaddr_t *address = &addrinfo->sockaddr;
+
+ if (bad_server(fctx, address)) {
+ /*
+ * We already know this server is bad.
+ */
+ return;
+ }
+
+ FCTXTRACE("add_bad");
+
+ sa = isc_mem_get(fctx->res->buckets[fctx->bucketnum].mctx,
+ sizeof(*sa));
+ if (sa == NULL)
+ return;
+ *sa = *address;
+ ISC_LIST_INITANDAPPEND(fctx->bad, sa, link);
+
+ if (reason == DNS_R_LAME) /* already logged */
+ return;
+
+ if (reason == DNS_R_UNEXPECTEDRCODE &&
+ fctx->rmessage->rcode == dns_rcode_servfail &&
+ ISFORWARDER(addrinfo))
+ return;
+
+ if (reason == DNS_R_UNEXPECTEDRCODE) {
+ isc_buffer_init(&b, code, sizeof(code) - 1);
+ dns_rcode_totext(fctx->rmessage->rcode, &b);
+ code[isc_buffer_usedlength(&b)] = '\0';
+ sep1 = "(";
+ sep2 = ") ";
+ } else if (reason == DNS_R_UNEXPECTEDOPCODE) {
+ isc_buffer_init(&b, code, sizeof(code) - 1);
+ dns_opcode_totext((dns_opcode_t)fctx->rmessage->opcode, &b);
+ code[isc_buffer_usedlength(&b)] = '\0';
+ sep1 = "(";
+ sep2 = ") ";
+ } else {
+ code[0] = '\0';
+ sep1 = "";
+ sep2 = "";
+ }
+ dns_name_format(&fctx->name, namebuf, sizeof(namebuf));
+ dns_rdatatype_format(fctx->type, typebuf, sizeof(typebuf));
+ dns_rdataclass_format(fctx->res->rdclass, classbuf, sizeof(classbuf));
+ isc_sockaddr_format(address, addrbuf, sizeof(addrbuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_LAME_SERVERS,
+ DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO,
+ "%s %s%s%sresolving '%s/%s/%s': %s",
+ dns_result_totext(reason), sep1, code, sep2,
+ namebuf, typebuf, classbuf, addrbuf);
+}
+
+/*
+ * Return 'bits' bits of random entropy from fctx->rand_buf,
+ * refreshing it by calling isc_random_get() whenever the requested
+ * number of bits is greater than the number in the buffer.
+ */
+static inline isc_uint32_t
+random_bits(fetchctx_t *fctx, isc_uint32_t bits) {
+ isc_uint32_t ret = 0;
+
+ REQUIRE(VALID_FCTX(fctx));
+ REQUIRE(bits <= 32);
+ if (bits == 0)
+ return (0);
+
+ if (bits >= fctx->rand_bits) {
+ /* if rand_bits == 0, this is unnecessary but harmless */
+ bits -= fctx->rand_bits;
+ ret = fctx->rand_buf << bits;
+
+ /* refresh random buffer now */
+ isc_random_get(&fctx->rand_buf);
+ fctx->rand_bits = sizeof(fctx->rand_buf) * CHAR_BIT;
+ }
+
+ if (bits > 0) {
+ isc_uint32_t mask = 0xffffffff;
+ if (bits < 32) {
+ mask = (1 << bits) - 1;
+ }
+
+ ret |= fctx->rand_buf & mask;
+ fctx->rand_buf >>= bits;
+ fctx->rand_bits -= bits;
+ }
+
+ return (ret);
+}
+
+/*
+ * Add some random jitter to a server's RTT value so that the
+ * order of queries will be unpredictable.
+ *
+ * RTT values of servers which have been tried are fuzzed by 128 ms.
+ * Servers that haven't been tried yet have their RTT set to a random
+ * value between 0 ms and 7 ms; they should get to go first, but in
+ * unpredictable order.
+ */
+static inline void
+randomize_srtt(fetchctx_t *fctx, dns_adbaddrinfo_t *ai) {
+ if (TRIED(ai)) {
+ ai->srtt >>= 10; /* convert to milliseconds, near enough */
+ ai->srtt |= (ai->srtt & 0x80) | random_bits(fctx, 7);
+ ai->srtt <<= 10; /* now back to microseconds */
+ } else
+ ai->srtt = random_bits(fctx, 3) << 10;
+}
+
+/*
+ * Sort addrinfo list by RTT (with random jitter)
+ */
+static void
+sort_adbfind(fetchctx_t *fctx, dns_adbfind_t *find) {
+ dns_adbaddrinfo_t *best, *curr;
+ dns_adbaddrinfolist_t sorted;
+
+ /* Add jitter to SRTT values */
+ curr = ISC_LIST_HEAD(find->list);
+ while (curr != NULL) {
+ randomize_srtt(fctx, curr);
+ curr = ISC_LIST_NEXT(curr, publink);
+ }
+
+ /* Lame N^2 bubble sort. */
+ ISC_LIST_INIT(sorted);
+ while (!ISC_LIST_EMPTY(find->list)) {
+ best = ISC_LIST_HEAD(find->list);
+ curr = ISC_LIST_NEXT(best, publink);
+ while (curr != NULL) {
+ if (curr->srtt < best->srtt)
+ best = curr;
+ curr = ISC_LIST_NEXT(curr, publink);
+ }
+ ISC_LIST_UNLINK(find->list, best, publink);
+ ISC_LIST_APPEND(sorted, best, publink);
+ }
+ find->list = sorted;
+}
+
+/*
+ * Sort a list of finds by server RTT (with random jitter)
+ */
+static void
+sort_finds(fetchctx_t *fctx, dns_adbfindlist_t *findlist) {
+ dns_adbfind_t *best, *curr;
+ dns_adbfindlist_t sorted;
+ dns_adbaddrinfo_t *addrinfo, *bestaddrinfo;
+
+ /* Sort each find's addrinfo list by SRTT (after adding jitter) */
+ for (curr = ISC_LIST_HEAD(*findlist);
+ curr != NULL;
+ curr = ISC_LIST_NEXT(curr, publink))
+ sort_adbfind(fctx, curr);
+
+ /* Lame N^2 bubble sort. */
+ ISC_LIST_INIT(sorted);
+ while (!ISC_LIST_EMPTY(*findlist)) {
+ best = ISC_LIST_HEAD(*findlist);
+ bestaddrinfo = ISC_LIST_HEAD(best->list);
+ INSIST(bestaddrinfo != NULL);
+ curr = ISC_LIST_NEXT(best, publink);
+ while (curr != NULL) {
+ addrinfo = ISC_LIST_HEAD(curr->list);
+ INSIST(addrinfo != NULL);
+ if (addrinfo->srtt < bestaddrinfo->srtt) {
+ best = curr;
+ bestaddrinfo = addrinfo;
+ }
+ curr = ISC_LIST_NEXT(curr, publink);
+ }
+ ISC_LIST_UNLINK(*findlist, best, publink);
+ ISC_LIST_APPEND(sorted, best, publink);
+ }
+ *findlist = sorted;
+}
+
+static void
+findname(fetchctx_t *fctx, dns_name_t *name, in_port_t port,
+ unsigned int options, unsigned int flags, isc_stdtime_t now,
+ isc_boolean_t *need_alternate)
+{
+ dns_adbaddrinfo_t *ai;
+ dns_adbfind_t *find;
+ dns_resolver_t *res;
+ isc_boolean_t unshared;
+ isc_result_t result;
+
+ res = fctx->res;
+ unshared = ISC_TF((fctx->options | DNS_FETCHOPT_UNSHARED) != 0);
+ /*
+ * If this name is a subdomain of the query domain, tell
+ * the ADB to start looking using zone/hint data. This keeps us
+ * from getting stuck if the nameserver is beneath the zone cut
+ * and we don't know its address (e.g. because the A record has
+ * expired).
+ */
+ if (dns_name_issubdomain(name, &fctx->domain))
+ options |= DNS_ADBFIND_STARTATZONE;
+ options |= DNS_ADBFIND_GLUEOK;
+ options |= DNS_ADBFIND_HINTOK;
+
+ /*
+ * See what we know about this address.
+ */
+ find = NULL;
+ result = dns_adb_createfind(fctx->adb,
+ res->buckets[fctx->bucketnum].task,
+ fctx_finddone, fctx, name,
+ &fctx->name, fctx->type,
+ options, now, NULL,
+ res->view->dstport, &find);
+ if (result != ISC_R_SUCCESS) {
+ if (result == DNS_R_ALIAS) {
+ /*
+ * XXXRTH Follow the CNAME/DNAME chain?
+ */
+ dns_adb_destroyfind(&find);
+ }
+ } else if (!ISC_LIST_EMPTY(find->list)) {
+ /*
+ * We have at least some of the addresses for the
+ * name.
+ */
+ INSIST((find->options & DNS_ADBFIND_WANTEVENT) == 0);
+ if (flags != 0 || port != 0) {
+ for (ai = ISC_LIST_HEAD(find->list);
+ ai != NULL;
+ ai = ISC_LIST_NEXT(ai, publink)) {
+ ai->flags |= flags;
+ if (port != 0)
+ isc_sockaddr_setport(&ai->sockaddr,
+ port);
+ }
+ }
+ if ((flags & FCTX_ADDRINFO_FORWARDER) != 0)
+ ISC_LIST_APPEND(fctx->altfinds, find, publink);
+ else
+ ISC_LIST_APPEND(fctx->finds, find, publink);
+ } else {
+ /*
+ * We don't know any of the addresses for this
+ * name.
+ */
+ if ((find->options & DNS_ADBFIND_WANTEVENT) != 0) {
+ /*
+ * We're looking for them and will get an
+ * event about it later.
+ */
+ fctx->pending++;
+ /*
+ * Bootstrap.
+ */
+ if (need_alternate != NULL &&
+ !*need_alternate && unshared &&
+ ((res->dispatchv4 == NULL &&
+ find->result_v6 != DNS_R_NXDOMAIN) ||
+ (res->dispatchv6 == NULL &&
+ find->result_v4 != DNS_R_NXDOMAIN)))
+ *need_alternate = ISC_TRUE;
+ } else {
+ /*
+ * If we know there are no addresses for
+ * the family we are using then try to add
+ * an alternative server.
+ */
+ if (need_alternate != NULL && !*need_alternate &&
+ ((res->dispatchv4 == NULL &&
+ find->result_v6 == DNS_R_NXRRSET) ||
+ (res->dispatchv6 == NULL &&
+ find->result_v4 == DNS_R_NXRRSET)))
+ *need_alternate = ISC_TRUE;
+ dns_adb_destroyfind(&find);
+ }
+ }
+}
+
+static isc_result_t
+fctx_getaddresses(fetchctx_t *fctx) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_result_t result;
+ dns_resolver_t *res;
+ isc_stdtime_t now;
+ unsigned int stdoptions;
+ isc_sockaddr_t *sa;
+ dns_adbaddrinfo_t *ai;
+ isc_boolean_t all_bad;
+ dns_rdata_ns_t ns;
+ isc_boolean_t need_alternate = ISC_FALSE;
+
+ FCTXTRACE("getaddresses");
+
+ /*
+ * Don't pound on remote servers. (Failsafe!)
+ */
+ fctx->restarts++;
+ if (fctx->restarts > 10) {
+ FCTXTRACE("too many restarts");
+ return (DNS_R_SERVFAIL);
+ }
+
+ res = fctx->res;
+ stdoptions = 0; /* Keep compiler happy. */
+
+ /*
+ * Forwarders.
+ */
+
+ INSIST(ISC_LIST_EMPTY(fctx->forwaddrs));
+ INSIST(ISC_LIST_EMPTY(fctx->altaddrs));
+
+ /*
+ * If this fctx has forwarders, use them; otherwise use any
+ * selective forwarders specified in the view; otherwise use the
+ * resolver's forwarders (if any).
+ */
+ sa = ISC_LIST_HEAD(fctx->forwarders);
+ if (sa == NULL) {
+ dns_forwarders_t *forwarders = NULL;
+ dns_name_t *name = &fctx->name;
+ dns_name_t suffix;
+ unsigned int labels;
+
+ /*
+ * DS records are found in the parent server.
+ * Strip label to get the correct forwarder (if any).
+ */
+ if (fctx->type == dns_rdatatype_ds &&
+ dns_name_countlabels(name) > 1) {
+ dns_name_init(&suffix, NULL);
+ labels = dns_name_countlabels(name);
+ dns_name_getlabelsequence(name, 1, labels - 1, &suffix);
+ name = &suffix;
+ }
+ result = dns_fwdtable_find(fctx->res->view->fwdtable, name,
+ &forwarders);
+ if (result == ISC_R_SUCCESS) {
+ sa = ISC_LIST_HEAD(forwarders->addrs);
+ fctx->fwdpolicy = forwarders->fwdpolicy;
+ }
+ }
+
+ while (sa != NULL) {
+ if ((isc_sockaddr_pf(sa) == AF_INET &&
+ fctx->res->dispatchv4 == NULL) ||
+ (isc_sockaddr_pf(sa) == AF_INET6 &&
+ fctx->res->dispatchv6 == NULL)) {
+ sa = ISC_LIST_NEXT(sa, link);
+ continue;
+ }
+ ai = NULL;
+ result = dns_adb_findaddrinfo(fctx->adb,
+ sa, &ai, 0); /* XXXMLG */
+ if (result == ISC_R_SUCCESS) {
+ dns_adbaddrinfo_t *cur;
+ ai->flags |= FCTX_ADDRINFO_FORWARDER;
+ cur = ISC_LIST_HEAD(fctx->forwaddrs);
+ while (cur != NULL && cur->srtt < ai->srtt)
+ cur = ISC_LIST_NEXT(cur, publink);
+ if (cur != NULL)
+ ISC_LIST_INSERTBEFORE(fctx->forwaddrs, cur,
+ ai, publink);
+ else
+ ISC_LIST_APPEND(fctx->forwaddrs, ai, publink);
+ }
+ sa = ISC_LIST_NEXT(sa, link);
+ }
+
+ /*
+ * If the forwarding policy is "only", we don't need the addresses
+ * of the nameservers.
+ */
+ if (fctx->fwdpolicy == dns_fwdpolicy_only)
+ goto out;
+
+ /*
+ * Normal nameservers.
+ */
+
+ stdoptions = DNS_ADBFIND_WANTEVENT | DNS_ADBFIND_EMPTYEVENT;
+ if (fctx->restarts == 1) {
+ /*
+ * To avoid sending out a flood of queries likely to
+ * result in NXRRSET, we suppress fetches for address
+ * families we don't have the first time through,
+ * provided that we have addresses in some family we
+ * can use.
+ *
+ * We don't want to set this option all the time, since
+ * if fctx->restarts > 1, we've clearly been having trouble
+ * with the addresses we had, so getting more could help.
+ */
+ stdoptions |= DNS_ADBFIND_AVOIDFETCHES;
+ }
+ if (res->dispatchv4 != NULL)
+ stdoptions |= DNS_ADBFIND_INET;
+ if (res->dispatchv6 != NULL)
+ stdoptions |= DNS_ADBFIND_INET6;
+ isc_stdtime_get(&now);
+
+ INSIST(ISC_LIST_EMPTY(fctx->finds));
+ INSIST(ISC_LIST_EMPTY(fctx->altfinds));
+
+ for (result = dns_rdataset_first(&fctx->nameservers);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&fctx->nameservers))
+ {
+ dns_rdataset_current(&fctx->nameservers, &rdata);
+ /*
+ * Extract the name from the NS record.
+ */
+ result = dns_rdata_tostruct(&rdata, &ns, NULL);
+ if (result != ISC_R_SUCCESS)
+ continue;
+
+ findname(fctx, &ns.name, 0, stdoptions, 0, now,
+ &need_alternate);
+ dns_rdata_reset(&rdata);
+ dns_rdata_freestruct(&ns);
+ }
+ if (result != ISC_R_NOMORE)
+ return (result);
+
+ /*
+ * Do we need to use 6 to 4?
+ */
+ if (need_alternate) {
+ int family;
+ alternate_t *a;
+ family = (res->dispatchv6 != NULL) ? AF_INET6 : AF_INET;
+ for (a = ISC_LIST_HEAD(fctx->res->alternates);
+ a != NULL;
+ a = ISC_LIST_NEXT(a, link)) {
+ if (!a->isaddress) {
+ findname(fctx, &a->_u._n.name, a->_u._n.port,
+ stdoptions, FCTX_ADDRINFO_FORWARDER,
+ now, NULL);
+ continue;
+ }
+ if (isc_sockaddr_pf(&a->_u.addr) != family)
+ continue;
+ ai = NULL;
+ result = dns_adb_findaddrinfo(fctx->adb, &a->_u.addr,
+ &ai, 0);
+ if (result == ISC_R_SUCCESS) {
+ dns_adbaddrinfo_t *cur;
+ ai->flags |= FCTX_ADDRINFO_FORWARDER;
+ cur = ISC_LIST_HEAD(fctx->altaddrs);
+ while (cur != NULL && cur->srtt < ai->srtt)
+ cur = ISC_LIST_NEXT(cur, publink);
+ if (cur != NULL)
+ ISC_LIST_INSERTBEFORE(fctx->altaddrs,
+ cur, ai, publink);
+ else
+ ISC_LIST_APPEND(fctx->altaddrs, ai,
+ publink);
+ }
+ }
+ }
+
+ out:
+ /*
+ * Mark all known bad servers.
+ */
+ all_bad = mark_bad(fctx);
+
+ /*
+ * How are we doing?
+ */
+ if (all_bad) {
+ /*
+ * We've got no addresses.
+ */
+ if (fctx->pending > 0) {
+ /*
+ * We're fetching the addresses, but don't have any
+ * yet. Tell the caller to wait for an answer.
+ */
+ result = DNS_R_WAIT;
+ } else {
+ /*
+ * We've lost completely. We don't know any
+ * addresses, and the ADB has told us it can't get
+ * them.
+ */
+ FCTXTRACE("no addresses");
+ result = ISC_R_FAILURE;
+ }
+ } else {
+ /*
+ * We've found some addresses. We might still be looking
+ * for more addresses.
+ */
+ sort_finds(fctx, &fctx->finds);
+ sort_finds(fctx, &fctx->altfinds);
+ result = ISC_R_SUCCESS;
+ }
+
+ return (result);
+}
+
+static inline void
+possibly_mark(fetchctx_t *fctx, dns_adbaddrinfo_t *addr)
+{
+ isc_netaddr_t na;
+ char buf[ISC_NETADDR_FORMATSIZE];
+ isc_sockaddr_t *sa;
+ isc_boolean_t aborted = ISC_FALSE;
+ isc_boolean_t bogus;
+ dns_acl_t *blackhole;
+ isc_netaddr_t ipaddr;
+ dns_peer_t *peer = NULL;
+ dns_resolver_t *res;
+ const char *msg = NULL;
+
+ sa = &addr->sockaddr;
+
+ res = fctx->res;
+ isc_netaddr_fromsockaddr(&ipaddr, sa);
+ blackhole = dns_dispatchmgr_getblackhole(res->dispatchmgr);
+ (void) dns_peerlist_peerbyaddr(res->view->peers, &ipaddr, &peer);
+
+ if (blackhole != NULL) {
+ int match;
+
+ if (dns_acl_match(&ipaddr, NULL, blackhole,
+ &res->view->aclenv,
+ &match, NULL) == ISC_R_SUCCESS &&
+ match > 0)
+ aborted = ISC_TRUE;
+ }
+
+ if (peer != NULL &&
+ dns_peer_getbogus(peer, &bogus) == ISC_R_SUCCESS &&
+ bogus)
+ aborted = ISC_TRUE;
+
+ if (aborted) {
+ addr->flags |= FCTX_ADDRINFO_MARK;
+ msg = "ignoring blackholed / bogus server: ";
+ } else if (isc_sockaddr_ismulticast(sa)) {
+ addr->flags |= FCTX_ADDRINFO_MARK;
+ msg = "ignoring multicast address: ";
+ } else if (isc_sockaddr_isexperimental(sa)) {
+ addr->flags |= FCTX_ADDRINFO_MARK;
+ msg = "ignoring experimental address: ";
+ } else if (sa->type.sa.sa_family != AF_INET6) {
+ return;
+ } else if (IN6_IS_ADDR_V4MAPPED(&sa->type.sin6.sin6_addr)) {
+ addr->flags |= FCTX_ADDRINFO_MARK;
+ msg = "ignoring IPv6 mapped IPV4 address: ";
+ } else if (IN6_IS_ADDR_V4COMPAT(&sa->type.sin6.sin6_addr)) {
+ addr->flags |= FCTX_ADDRINFO_MARK;
+ msg = "ignoring IPv6 compatibility IPV4 address: ";
+ } else
+ return;
+
+ if (!isc_log_wouldlog(dns_lctx, ISC_LOG_DEBUG(3)))
+ return;
+
+ isc_netaddr_fromsockaddr(&na, sa);
+ isc_netaddr_format(&na, buf, sizeof(buf));
+ FCTXTRACE2(msg, buf);
+}
+
+static inline dns_adbaddrinfo_t *
+fctx_nextaddress(fetchctx_t *fctx) {
+ dns_adbfind_t *find, *start;
+ dns_adbaddrinfo_t *addrinfo;
+ dns_adbaddrinfo_t *faddrinfo;
+
+ /*
+ * Return the next untried address, if any.
+ */
+
+ /*
+ * Find the first unmarked forwarder (if any).
+ */
+ for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs);
+ addrinfo != NULL;
+ addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
+ if (!UNMARKED(addrinfo))
+ continue;
+ possibly_mark(fctx, addrinfo);
+ if (UNMARKED(addrinfo)) {
+ addrinfo->flags |= FCTX_ADDRINFO_MARK;
+ fctx->find = NULL;
+ return (addrinfo);
+ }
+ }
+
+ /*
+ * No forwarders. Move to the next find.
+ */
+
+ fctx->attributes |= FCTX_ATTR_TRIEDFIND;
+
+ find = fctx->find;
+ if (find == NULL)
+ find = ISC_LIST_HEAD(fctx->finds);
+ else {
+ find = ISC_LIST_NEXT(find, publink);
+ if (find == NULL)
+ find = ISC_LIST_HEAD(fctx->finds);
+ }
+
+ /*
+ * Find the first unmarked addrinfo.
+ */
+ addrinfo = NULL;
+ if (find != NULL) {
+ start = find;
+ do {
+ for (addrinfo = ISC_LIST_HEAD(find->list);
+ addrinfo != NULL;
+ addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
+ if (!UNMARKED(addrinfo))
+ continue;
+ possibly_mark(fctx, addrinfo);
+ if (UNMARKED(addrinfo)) {
+ addrinfo->flags |= FCTX_ADDRINFO_MARK;
+ break;
+ }
+ }
+ if (addrinfo != NULL)
+ break;
+ find = ISC_LIST_NEXT(find, publink);
+ if (find == NULL)
+ find = ISC_LIST_HEAD(fctx->finds);
+ } while (find != start);
+ }
+
+ fctx->find = find;
+ if (addrinfo != NULL)
+ return (addrinfo);
+
+ /*
+ * No nameservers left. Try alternates.
+ */
+
+ fctx->attributes |= FCTX_ATTR_TRIEDALT;
+
+ find = fctx->altfind;
+ if (find == NULL)
+ find = ISC_LIST_HEAD(fctx->altfinds);
+ else {
+ find = ISC_LIST_NEXT(find, publink);
+ if (find == NULL)
+ find = ISC_LIST_HEAD(fctx->altfinds);
+ }
+
+ /*
+ * Find the first unmarked addrinfo.
+ */
+ addrinfo = NULL;
+ if (find != NULL) {
+ start = find;
+ do {
+ for (addrinfo = ISC_LIST_HEAD(find->list);
+ addrinfo != NULL;
+ addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
+ if (!UNMARKED(addrinfo))
+ continue;
+ possibly_mark(fctx, addrinfo);
+ if (UNMARKED(addrinfo)) {
+ addrinfo->flags |= FCTX_ADDRINFO_MARK;
+ break;
+ }
+ }
+ if (addrinfo != NULL)
+ break;
+ find = ISC_LIST_NEXT(find, publink);
+ if (find == NULL)
+ find = ISC_LIST_HEAD(fctx->altfinds);
+ } while (find != start);
+ }
+
+ faddrinfo = addrinfo;
+
+ /*
+ * See if we have a better alternate server by address.
+ */
+
+ for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs);
+ addrinfo != NULL;
+ addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
+ if (!UNMARKED(addrinfo))
+ continue;
+ possibly_mark(fctx, addrinfo);
+ if (UNMARKED(addrinfo) &&
+ (faddrinfo == NULL ||
+ addrinfo->srtt < faddrinfo->srtt)) {
+ if (faddrinfo != NULL)
+ faddrinfo->flags &= ~FCTX_ADDRINFO_MARK;
+ addrinfo->flags |= FCTX_ADDRINFO_MARK;
+ break;
+ }
+ }
+
+ if (addrinfo == NULL) {
+ addrinfo = faddrinfo;
+ fctx->altfind = find;
+ }
+
+ return (addrinfo);
+}
+
+static void
+fctx_try(fetchctx_t *fctx, isc_boolean_t retrying) {
+ isc_result_t result;
+ dns_adbaddrinfo_t *addrinfo;
+
+ FCTXTRACE("try");
+
+ REQUIRE(!ADDRWAIT(fctx));
+
+ addrinfo = fctx_nextaddress(fctx);
+ if (addrinfo == NULL) {
+ /*
+ * We have no more addresses. Start over.
+ */
+ fctx_cancelqueries(fctx, ISC_TRUE);
+ fctx_cleanupfinds(fctx);
+ fctx_cleanupaltfinds(fctx);
+ fctx_cleanupforwaddrs(fctx);
+ fctx_cleanupaltaddrs(fctx);
+ result = fctx_getaddresses(fctx);
+ if (result == DNS_R_WAIT) {
+ /*
+ * Sleep waiting for addresses.
+ */
+ FCTXTRACE("addrwait");
+ fctx->attributes |= FCTX_ATTR_ADDRWAIT;
+ return;
+ } else if (result != ISC_R_SUCCESS) {
+ /*
+ * Something bad happened.
+ */
+ fctx_done(fctx, result);
+ return;
+ }
+
+ addrinfo = fctx_nextaddress(fctx);
+ /*
+ * While we may have addresses from the ADB, they
+ * might be bad ones. In this case, return SERVFAIL.
+ */
+ if (addrinfo == NULL) {
+ fctx_done(fctx, DNS_R_SERVFAIL);
+ return;
+ }
+ }
+
+ result = fctx_query(fctx, addrinfo, fctx->options);
+ if (result != ISC_R_SUCCESS)
+ fctx_done(fctx, result);
+ else if (retrying)
+ inc_stats(fctx->res, dns_resstatscounter_retry);
+}
+
+static isc_boolean_t
+fctx_destroy(fetchctx_t *fctx) {
+ dns_resolver_t *res;
+ unsigned int bucketnum;
+ isc_sockaddr_t *sa, *next_sa;
+
+ /*
+ * Caller must be holding the bucket lock.
+ */
+
+ REQUIRE(VALID_FCTX(fctx));
+ REQUIRE(fctx->state == fetchstate_done ||
+ fctx->state == fetchstate_init);
+ REQUIRE(ISC_LIST_EMPTY(fctx->events));
+ REQUIRE(ISC_LIST_EMPTY(fctx->queries));
+ REQUIRE(ISC_LIST_EMPTY(fctx->finds));
+ REQUIRE(ISC_LIST_EMPTY(fctx->altfinds));
+ REQUIRE(fctx->pending == 0);
+ REQUIRE(fctx->references == 0);
+ REQUIRE(ISC_LIST_EMPTY(fctx->validators));
+
+ FCTXTRACE("destroy");
+
+ res = fctx->res;
+ bucketnum = fctx->bucketnum;
+
+ ISC_LIST_UNLINK(res->buckets[bucketnum].fctxs, fctx, link);
+
+ /*
+ * Free bad.
+ */
+ for (sa = ISC_LIST_HEAD(fctx->bad);
+ sa != NULL;
+ sa = next_sa) {
+ next_sa = ISC_LIST_NEXT(sa, link);
+ ISC_LIST_UNLINK(fctx->bad, sa, link);
+ isc_mem_put(res->buckets[bucketnum].mctx, sa, sizeof(*sa));
+ }
+
+ for (sa = ISC_LIST_HEAD(fctx->edns);
+ sa != NULL;
+ sa = next_sa) {
+ next_sa = ISC_LIST_NEXT(sa, link);
+ ISC_LIST_UNLINK(fctx->edns, sa, link);
+ isc_mem_put(res->buckets[bucketnum].mctx, sa, sizeof(*sa));
+ }
+
+ for (sa = ISC_LIST_HEAD(fctx->edns512);
+ sa != NULL;
+ sa = next_sa) {
+ next_sa = ISC_LIST_NEXT(sa, link);
+ ISC_LIST_UNLINK(fctx->edns512, sa, link);
+ isc_mem_put(res->buckets[bucketnum].mctx, sa, sizeof(*sa));
+ }
+
+ isc_timer_detach(&fctx->timer);
+ dns_message_destroy(&fctx->rmessage);
+ dns_message_destroy(&fctx->qmessage);
+ if (dns_name_countlabels(&fctx->domain) > 0)
+ dns_name_free(&fctx->domain, res->buckets[bucketnum].mctx);
+ if (dns_rdataset_isassociated(&fctx->nameservers))
+ dns_rdataset_disassociate(&fctx->nameservers);
+ dns_name_free(&fctx->name, res->buckets[bucketnum].mctx);
+ dns_db_detach(&fctx->cache);
+ dns_adb_detach(&fctx->adb);
+ isc_mem_free(res->buckets[bucketnum].mctx, fctx->info);
+ isc_mem_put(res->buckets[bucketnum].mctx, fctx, sizeof(*fctx));
+
+ LOCK(&res->nlock);
+ res->nfctx--;
+ UNLOCK(&res->nlock);
+
+ if (res->buckets[bucketnum].exiting &&
+ ISC_LIST_EMPTY(res->buckets[bucketnum].fctxs))
+ return (ISC_TRUE);
+
+ return (ISC_FALSE);
+}
+
+/*
+ * Fetch event handlers.
+ */
+
+static void
+fctx_timeout(isc_task_t *task, isc_event_t *event) {
+ fetchctx_t *fctx = event->ev_arg;
+ isc_timerevent_t *tevent = (isc_timerevent_t *)event;
+ resquery_t *query;
+
+ REQUIRE(VALID_FCTX(fctx));
+
+ UNUSED(task);
+
+ FCTXTRACE("timeout");
+
+ if (event->ev_type == ISC_TIMEREVENT_LIFE) {
+ fctx->reason = NULL;
+ fctx_done(fctx, ISC_R_TIMEDOUT);
+ } else {
+ isc_result_t result;
+
+ fctx->timeouts++;
+ /*
+ * We could cancel the running queries here, or we could let
+ * them keep going. Since we normally use separate sockets for
+ * different queries, we adopt the former approach to reduce
+ * the number of open sockets: cancel the oldest query if it
+ * expired after the query had started (this is usually the
+ * case but is not always so, depending on the task schedule
+ * timing).
+ */
+ query = ISC_LIST_HEAD(fctx->queries);
+ if (query != NULL &&
+ isc_time_compare(&tevent->due, &query->start) >= 0) {
+ fctx_cancelquery(&query, NULL, NULL, ISC_TRUE);
+ }
+ fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
+ /*
+ * Our timer has triggered. Reestablish the fctx lifetime
+ * timer.
+ */
+ result = fctx_starttimer(fctx);
+ if (result != ISC_R_SUCCESS)
+ fctx_done(fctx, result);
+ else
+ /*
+ * Keep trying.
+ */
+ fctx_try(fctx, ISC_TRUE);
+ }
+
+ isc_event_free(&event);
+}
+
+static void
+fctx_shutdown(fetchctx_t *fctx) {
+ isc_event_t *cevent;
+
+ /*
+ * Start the shutdown process for fctx, if it isn't already underway.
+ */
+
+ FCTXTRACE("shutdown");
+
+ /*
+ * The caller must be holding the appropriate bucket lock.
+ */
+
+ if (fctx->want_shutdown)
+ return;
+
+ fctx->want_shutdown = ISC_TRUE;
+
+ /*
+ * Unless we're still initializing (in which case the
+ * control event is still outstanding), we need to post
+ * the control event to tell the fetch we want it to
+ * exit.
+ */
+ if (fctx->state != fetchstate_init) {
+ cevent = &fctx->control_event;
+ isc_task_send(fctx->res->buckets[fctx->bucketnum].task,
+ &cevent);
+ }
+}
+
+static void
+fctx_doshutdown(isc_task_t *task, isc_event_t *event) {
+ fetchctx_t *fctx = event->ev_arg;
+ isc_boolean_t bucket_empty = ISC_FALSE;
+ dns_resolver_t *res;
+ unsigned int bucketnum;
+ dns_validator_t *validator;
+
+ REQUIRE(VALID_FCTX(fctx));
+
+ UNUSED(task);
+
+ res = fctx->res;
+ bucketnum = fctx->bucketnum;
+
+ FCTXTRACE("doshutdown");
+
+ /*
+ * An fctx that is shutting down is no longer in ADDRWAIT mode.
+ */
+ fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
+
+ /*
+ * Cancel all pending validators. Note that this must be done
+ * without the bucket lock held, since that could cause deadlock.
+ */
+ validator = ISC_LIST_HEAD(fctx->validators);
+ while (validator != NULL) {
+ dns_validator_cancel(validator);
+ validator = ISC_LIST_NEXT(validator, link);
+ }
+
+ if (fctx->nsfetch != NULL)
+ dns_resolver_cancelfetch(fctx->nsfetch);
+
+ /*
+ * Shut down anything that is still running on behalf of this
+ * fetch. To avoid deadlock with the ADB, we must do this
+ * before we lock the bucket lock.
+ */
+ fctx_stopeverything(fctx, ISC_FALSE);
+
+ LOCK(&res->buckets[bucketnum].lock);
+
+ fctx->attributes |= FCTX_ATTR_SHUTTINGDOWN;
+
+ INSIST(fctx->state == fetchstate_active ||
+ fctx->state == fetchstate_done);
+ INSIST(fctx->want_shutdown);
+
+ if (fctx->state != fetchstate_done) {
+ fctx->state = fetchstate_done;
+ fctx_sendevents(fctx, ISC_R_CANCELED);
+ }
+
+ if (fctx->references == 0 && fctx->pending == 0 &&
+ fctx->nqueries == 0 && ISC_LIST_EMPTY(fctx->validators))
+ bucket_empty = fctx_destroy(fctx);
+
+ UNLOCK(&res->buckets[bucketnum].lock);
+
+ if (bucket_empty)
+ empty_bucket(res);
+}
+
+static void
+fctx_start(isc_task_t *task, isc_event_t *event) {
+ fetchctx_t *fctx = event->ev_arg;
+ isc_boolean_t done = ISC_FALSE, bucket_empty = ISC_FALSE;
+ dns_resolver_t *res;
+ unsigned int bucketnum;
+
+ REQUIRE(VALID_FCTX(fctx));
+
+ UNUSED(task);
+
+ res = fctx->res;
+ bucketnum = fctx->bucketnum;
+
+ FCTXTRACE("start");
+
+ LOCK(&res->buckets[bucketnum].lock);
+
+ INSIST(fctx->state == fetchstate_init);
+ if (fctx->want_shutdown) {
+ /*
+ * We haven't started this fctx yet, and we've been requested
+ * to shut it down.
+ */
+ fctx->attributes |= FCTX_ATTR_SHUTTINGDOWN;
+ fctx->state = fetchstate_done;
+ fctx_sendevents(fctx, ISC_R_CANCELED);
+ /*
+ * Since we haven't started, we INSIST that we have no
+ * pending ADB finds and no pending validations.
+ */
+ INSIST(fctx->pending == 0);
+ INSIST(fctx->nqueries == 0);
+ INSIST(ISC_LIST_EMPTY(fctx->validators));
+ if (fctx->references == 0) {
+ /*
+ * It's now safe to destroy this fctx.
+ */
+ bucket_empty = fctx_destroy(fctx);
+ }
+ done = ISC_TRUE;
+ } else {
+ /*
+ * Normal fctx startup.
+ */
+ fctx->state = fetchstate_active;
+ /*
+ * Reset the control event for later use in shutting down
+ * the fctx.
+ */
+ ISC_EVENT_INIT(event, sizeof(*event), 0, NULL,
+ DNS_EVENT_FETCHCONTROL, fctx_doshutdown, fctx,
+ NULL, NULL, NULL);
+ }
+
+ UNLOCK(&res->buckets[bucketnum].lock);
+
+ if (!done) {
+ isc_result_t result;
+
+ /*
+ * All is well. Start working on the fetch.
+ */
+ result = fctx_starttimer(fctx);
+ if (result != ISC_R_SUCCESS)
+ fctx_done(fctx, result);
+ else
+ fctx_try(fctx, ISC_FALSE);
+ } else if (bucket_empty)
+ empty_bucket(res);
+}
+
+/*
+ * Fetch Creation, Joining, and Cancelation.
+ */
+
+static inline isc_result_t
+fctx_join(fetchctx_t *fctx, isc_task_t *task, isc_sockaddr_t *client,
+ dns_messageid_t id, isc_taskaction_t action, void *arg,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
+ dns_fetch_t *fetch)
+{
+ isc_task_t *clone;
+ dns_fetchevent_t *event;
+
+ FCTXTRACE("join");
+
+ /*
+ * We store the task we're going to send this event to in the
+ * sender field. We'll make the fetch the sender when we actually
+ * send the event.
+ */
+ clone = NULL;
+ isc_task_attach(task, &clone);
+ event = (dns_fetchevent_t *)
+ isc_event_allocate(fctx->res->mctx, clone, DNS_EVENT_FETCHDONE,
+ action, arg, sizeof(*event));
+ if (event == NULL) {
+ isc_task_detach(&clone);
+ return (ISC_R_NOMEMORY);
+ }
+ event->result = DNS_R_SERVFAIL;
+ event->qtype = fctx->type;
+ event->db = NULL;
+ event->node = NULL;
+ event->rdataset = rdataset;
+ event->sigrdataset = sigrdataset;
+ event->fetch = fetch;
+ event->client = client;
+ event->id = id;
+ dns_fixedname_init(&event->foundname);
+
+ /*
+ * Make sure that we can store the sigrdataset in the
+ * first event if it is needed by any of the events.
+ */
+ if (event->sigrdataset != NULL)
+ ISC_LIST_PREPEND(fctx->events, event, ev_link);
+ else
+ ISC_LIST_APPEND(fctx->events, event, ev_link);
+ fctx->references++;
+
+ fetch->magic = DNS_FETCH_MAGIC;
+ fetch->private = fctx;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
+ dns_name_t *domain, dns_rdataset_t *nameservers,
+ unsigned int options, unsigned int bucketnum, fetchctx_t **fctxp)
+{
+ fetchctx_t *fctx;
+ isc_result_t result;
+ isc_result_t iresult;
+ isc_interval_t interval;
+ dns_fixedname_t fixed;
+ unsigned int findoptions = 0;
+ char buf[DNS_NAME_FORMATSIZE + DNS_RDATATYPE_FORMATSIZE];
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
+ dns_name_t suffix;
+
+ /*
+ * Caller must be holding the lock for bucket number 'bucketnum'.
+ */
+ REQUIRE(fctxp != NULL && *fctxp == NULL);
+
+ fctx = isc_mem_get(res->buckets[bucketnum].mctx, sizeof(*fctx));
+ if (fctx == NULL)
+ return (ISC_R_NOMEMORY);
+ dns_name_format(name, buf, sizeof(buf));
+ dns_rdatatype_format(type, typebuf, sizeof(typebuf));
+ strcat(buf, "/"); /* checked */
+ strcat(buf, typebuf); /* checked */
+ fctx->info = isc_mem_strdup(res->buckets[bucketnum].mctx, buf);
+ if (fctx->info == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_fetch;
+ }
+ FCTXTRACE("create");
+ dns_name_init(&fctx->name, NULL);
+ result = dns_name_dup(name, res->buckets[bucketnum].mctx, &fctx->name);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_info;
+ dns_name_init(&fctx->domain, NULL);
+ dns_rdataset_init(&fctx->nameservers);
+
+ fctx->type = type;
+ fctx->options = options;
+ /*
+ * Note! We do not attach to the task. We are relying on the
+ * resolver to ensure that this task doesn't go away while we are
+ * using it.
+ */
+ fctx->res = res;
+ fctx->references = 0;
+ fctx->bucketnum = bucketnum;
+ fctx->state = fetchstate_init;
+ fctx->want_shutdown = ISC_FALSE;
+ fctx->cloned = ISC_FALSE;
+ ISC_LIST_INIT(fctx->queries);
+ ISC_LIST_INIT(fctx->finds);
+ ISC_LIST_INIT(fctx->altfinds);
+ ISC_LIST_INIT(fctx->forwaddrs);
+ ISC_LIST_INIT(fctx->altaddrs);
+ ISC_LIST_INIT(fctx->forwarders);
+ fctx->fwdpolicy = dns_fwdpolicy_none;
+ ISC_LIST_INIT(fctx->bad);
+ ISC_LIST_INIT(fctx->edns);
+ ISC_LIST_INIT(fctx->edns512);
+ ISC_LIST_INIT(fctx->validators);
+ fctx->validator = NULL;
+ fctx->find = NULL;
+ fctx->altfind = NULL;
+ fctx->pending = 0;
+ fctx->restarts = 0;
+ fctx->timeouts = 0;
+ fctx->attributes = 0;
+ fctx->spilled = ISC_FALSE;
+ fctx->nqueries = 0;
+ fctx->reason = NULL;
+ fctx->rand_buf = 0;
+ fctx->rand_bits = 0;
+
+ dns_name_init(&fctx->nsname, NULL);
+ fctx->nsfetch = NULL;
+ dns_rdataset_init(&fctx->nsrrset);
+
+ if (domain == NULL) {
+ dns_forwarders_t *forwarders = NULL;
+ unsigned int labels;
+
+ /*
+ * DS records are found in the parent server.
+ * Strip label to get the correct forwarder (if any).
+ */
+ if (fctx->type == dns_rdatatype_ds &&
+ dns_name_countlabels(name) > 1) {
+ dns_name_init(&suffix, NULL);
+ labels = dns_name_countlabels(name);
+ dns_name_getlabelsequence(name, 1, labels - 1, &suffix);
+ name = &suffix;
+ }
+ dns_fixedname_init(&fixed);
+ domain = dns_fixedname_name(&fixed);
+ result = dns_fwdtable_find2(fctx->res->view->fwdtable, name,
+ domain, &forwarders);
+ if (result == ISC_R_SUCCESS)
+ fctx->fwdpolicy = forwarders->fwdpolicy;
+
+ if (fctx->fwdpolicy != dns_fwdpolicy_only) {
+ /*
+ * The caller didn't supply a query domain and
+ * nameservers, and we're not in forward-only mode,
+ * so find the best nameservers to use.
+ */
+ if (dns_rdatatype_atparent(type))
+ findoptions |= DNS_DBFIND_NOEXACT;
+ result = dns_view_findzonecut(res->view, name, domain,
+ 0, findoptions, ISC_TRUE,
+ &fctx->nameservers,
+ NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_name;
+ result = dns_name_dup(domain,
+ res->buckets[bucketnum].mctx,
+ &fctx->domain);
+ if (result != ISC_R_SUCCESS) {
+ dns_rdataset_disassociate(&fctx->nameservers);
+ goto cleanup_name;
+ }
+ } else {
+ /*
+ * We're in forward-only mode. Set the query domain.
+ */
+ result = dns_name_dup(domain,
+ res->buckets[bucketnum].mctx,
+ &fctx->domain);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_name;
+ }
+ } else {
+ result = dns_name_dup(domain,
+ res->buckets[bucketnum].mctx,
+ &fctx->domain);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_name;
+ dns_rdataset_clone(nameservers, &fctx->nameservers);
+ }
+
+ INSIST(dns_name_issubdomain(&fctx->name, &fctx->domain));
+
+ fctx->qmessage = NULL;
+ result = dns_message_create(res->buckets[bucketnum].mctx,
+ DNS_MESSAGE_INTENTRENDER,
+ &fctx->qmessage);
+
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_domain;
+
+ fctx->rmessage = NULL;
+ result = dns_message_create(res->buckets[bucketnum].mctx,
+ DNS_MESSAGE_INTENTPARSE,
+ &fctx->rmessage);
+
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_qmessage;
+
+ /*
+ * Compute an expiration time for the entire fetch.
+ */
+ isc_interval_set(&interval, 30, 0); /* XXXRTH constant */
+ iresult = isc_time_nowplusinterval(&fctx->expires, &interval);
+ if (iresult != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_time_nowplusinterval: %s",
+ isc_result_totext(iresult));
+ result = ISC_R_UNEXPECTED;
+ goto cleanup_rmessage;
+ }
+
+ /*
+ * Default retry interval initialization. We set the interval now
+ * mostly so it won't be uninitialized. It will be set to the
+ * correct value before a query is issued.
+ */
+ isc_interval_set(&fctx->interval, 2, 0);
+
+ /*
+ * Create an inactive timer. It will be made active when the fetch
+ * is actually started.
+ */
+ fctx->timer = NULL;
+ iresult = isc_timer_create(res->timermgr, isc_timertype_inactive,
+ NULL, NULL,
+ res->buckets[bucketnum].task, fctx_timeout,
+ fctx, &fctx->timer);
+ if (iresult != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_timer_create: %s",
+ isc_result_totext(iresult));
+ result = ISC_R_UNEXPECTED;
+ goto cleanup_rmessage;
+ }
+
+ /*
+ * Attach to the view's cache and adb.
+ */
+ fctx->cache = NULL;
+ dns_db_attach(res->view->cachedb, &fctx->cache);
+ fctx->adb = NULL;
+ dns_adb_attach(res->view->adb, &fctx->adb);
+
+ ISC_LIST_INIT(fctx->events);
+ ISC_LINK_INIT(fctx, link);
+ fctx->magic = FCTX_MAGIC;
+
+ ISC_LIST_APPEND(res->buckets[bucketnum].fctxs, fctx, link);
+
+ LOCK(&res->nlock);
+ res->nfctx++;
+ UNLOCK(&res->nlock);
+
+ *fctxp = fctx;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup_rmessage:
+ dns_message_destroy(&fctx->rmessage);
+
+ cleanup_qmessage:
+ dns_message_destroy(&fctx->qmessage);
+
+ cleanup_domain:
+ if (dns_name_countlabels(&fctx->domain) > 0)
+ dns_name_free(&fctx->domain, res->buckets[bucketnum].mctx);
+ if (dns_rdataset_isassociated(&fctx->nameservers))
+ dns_rdataset_disassociate(&fctx->nameservers);
+
+ cleanup_name:
+ dns_name_free(&fctx->name, res->buckets[bucketnum].mctx);
+
+ cleanup_info:
+ isc_mem_free(res->buckets[bucketnum].mctx, fctx->info);
+
+ cleanup_fetch:
+ isc_mem_put(res->buckets[bucketnum].mctx, fctx, sizeof(*fctx));
+
+ return (result);
+}
+
+/*
+ * Handle Responses
+ */
+static inline isc_boolean_t
+is_lame(fetchctx_t *fctx) {
+ dns_message_t *message = fctx->rmessage;
+ dns_name_t *name;
+ dns_rdataset_t *rdataset;
+ isc_result_t result;
+
+ if (message->rcode != dns_rcode_noerror &&
+ message->rcode != dns_rcode_nxdomain)
+ return (ISC_FALSE);
+
+ if (message->counts[DNS_SECTION_ANSWER] != 0)
+ return (ISC_FALSE);
+
+ if (message->counts[DNS_SECTION_AUTHORITY] == 0)
+ return (ISC_FALSE);
+
+ result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
+ while (result == ISC_R_SUCCESS) {
+ name = NULL;
+ dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link)) {
+ dns_namereln_t namereln;
+ int order;
+ unsigned int labels;
+ if (rdataset->type != dns_rdatatype_ns)
+ continue;
+ namereln = dns_name_fullcompare(name, &fctx->domain,
+ &order, &labels);
+ if (namereln == dns_namereln_equal &&
+ (message->flags & DNS_MESSAGEFLAG_AA) != 0)
+ return (ISC_FALSE);
+ if (namereln == dns_namereln_subdomain)
+ return (ISC_FALSE);
+ return (ISC_TRUE);
+ }
+ result = dns_message_nextname(message, DNS_SECTION_AUTHORITY);
+ }
+
+ return (ISC_FALSE);
+}
+
+static inline void
+log_lame(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char domainbuf[DNS_NAME_FORMATSIZE];
+ char addrbuf[ISC_SOCKADDR_FORMATSIZE];
+
+ dns_name_format(&fctx->name, namebuf, sizeof(namebuf));
+ dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf));
+ isc_sockaddr_format(&addrinfo->sockaddr, addrbuf, sizeof(addrbuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_LAME_SERVERS,
+ DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO,
+ "lame server resolving '%s' (in '%s'?): %s",
+ namebuf, domainbuf, addrbuf);
+}
+
+static inline isc_result_t
+same_question(fetchctx_t *fctx) {
+ isc_result_t result;
+ dns_message_t *message = fctx->rmessage;
+ dns_name_t *name;
+ dns_rdataset_t *rdataset;
+
+ /*
+ * Caller must be holding the fctx lock.
+ */
+
+ /*
+ * XXXRTH Currently we support only one question.
+ */
+ if (message->counts[DNS_SECTION_QUESTION] != 1)
+ return (DNS_R_FORMERR);
+
+ result = dns_message_firstname(message, DNS_SECTION_QUESTION);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ name = NULL;
+ dns_message_currentname(message, DNS_SECTION_QUESTION, &name);
+ rdataset = ISC_LIST_HEAD(name->list);
+ INSIST(rdataset != NULL);
+ INSIST(ISC_LIST_NEXT(rdataset, link) == NULL);
+ if (fctx->type != rdataset->type ||
+ fctx->res->rdclass != rdataset->rdclass ||
+ !dns_name_equal(&fctx->name, name))
+ return (DNS_R_FORMERR);
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+clone_results(fetchctx_t *fctx) {
+ dns_fetchevent_t *event, *hevent;
+ isc_result_t result;
+ dns_name_t *name, *hname;
+
+ FCTXTRACE("clone_results");
+
+ /*
+ * Set up any other events to have the same data as the first
+ * event.
+ *
+ * Caller must be holding the appropriate lock.
+ */
+
+ fctx->cloned = ISC_TRUE;
+ hevent = ISC_LIST_HEAD(fctx->events);
+ if (hevent == NULL)
+ return;
+ hname = dns_fixedname_name(&hevent->foundname);
+ for (event = ISC_LIST_NEXT(hevent, ev_link);
+ event != NULL;
+ event = ISC_LIST_NEXT(event, ev_link)) {
+ name = dns_fixedname_name(&event->foundname);
+ result = dns_name_copy(hname, name, NULL);
+ if (result != ISC_R_SUCCESS)
+ event->result = result;
+ else
+ event->result = hevent->result;
+ dns_db_attach(hevent->db, &event->db);
+ dns_db_attachnode(hevent->db, hevent->node, &event->node);
+ INSIST(hevent->rdataset != NULL);
+ INSIST(event->rdataset != NULL);
+ if (dns_rdataset_isassociated(hevent->rdataset))
+ dns_rdataset_clone(hevent->rdataset, event->rdataset);
+ INSIST(! (hevent->sigrdataset == NULL &&
+ event->sigrdataset != NULL));
+ if (hevent->sigrdataset != NULL &&
+ dns_rdataset_isassociated(hevent->sigrdataset) &&
+ event->sigrdataset != NULL)
+ dns_rdataset_clone(hevent->sigrdataset,
+ event->sigrdataset);
+ }
+}
+
+#define CACHE(r) (((r)->attributes & DNS_RDATASETATTR_CACHE) != 0)
+#define ANSWER(r) (((r)->attributes & DNS_RDATASETATTR_ANSWER) != 0)
+#define ANSWERSIG(r) (((r)->attributes & DNS_RDATASETATTR_ANSWERSIG) != 0)
+#define EXTERNAL(r) (((r)->attributes & DNS_RDATASETATTR_EXTERNAL) != 0)
+#define CHAINING(r) (((r)->attributes & DNS_RDATASETATTR_CHAINING) != 0)
+#define CHASE(r) (((r)->attributes & DNS_RDATASETATTR_CHASE) != 0)
+#define CHECKNAMES(r) (((r)->attributes & DNS_RDATASETATTR_CHECKNAMES) != 0)
+
+
+/*
+ * Destroy '*fctx' if it is ready to be destroyed (i.e., if it has
+ * no references and is no longer waiting for any events). If this
+ * was the last fctx in the resolver, destroy the resolver.
+ *
+ * Requires:
+ * '*fctx' is shutting down.
+ */
+static void
+maybe_destroy(fetchctx_t *fctx) {
+ unsigned int bucketnum;
+ isc_boolean_t bucket_empty = ISC_FALSE;
+ dns_resolver_t *res = fctx->res;
+ dns_validator_t *validator, *next_validator;
+
+ REQUIRE(SHUTTINGDOWN(fctx));
+
+ if (fctx->pending != 0 || fctx->nqueries != 0)
+ return;
+
+ for (validator = ISC_LIST_HEAD(fctx->validators);
+ validator != NULL; validator = next_validator) {
+ next_validator = ISC_LIST_NEXT(validator, link);
+ dns_validator_cancel(validator);
+ /*
+ * If this is a active validator wait for the cancel
+ * to complete before calling dns_validator_destroy().
+ */
+ if (validator == fctx->validator)
+ continue;
+ ISC_LIST_UNLINK(fctx->validators, validator, link);
+ dns_validator_destroy(&validator);
+ }
+
+ bucketnum = fctx->bucketnum;
+ LOCK(&res->buckets[bucketnum].lock);
+ if (fctx->references == 0 && ISC_LIST_EMPTY(fctx->validators))
+ bucket_empty = fctx_destroy(fctx);
+ UNLOCK(&res->buckets[bucketnum].lock);
+
+ if (bucket_empty)
+ empty_bucket(res);
+}
+
+/*
+ * The validator has finished.
+ */
+static void
+validated(isc_task_t *task, isc_event_t *event) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_result_t eresult = ISC_R_SUCCESS;
+ isc_stdtime_t now;
+ fetchctx_t *fctx;
+ dns_validatorevent_t *vevent;
+ dns_fetchevent_t *hevent;
+ dns_rdataset_t *ardataset = NULL;
+ dns_rdataset_t *asigrdataset = NULL;
+ dns_dbnode_t *node = NULL;
+ isc_boolean_t negative;
+ isc_boolean_t chaining;
+ isc_boolean_t sentresponse;
+ isc_uint32_t ttl;
+ dns_dbnode_t *nsnode = NULL;
+ dns_name_t *name;
+ dns_rdataset_t *rdataset;
+ dns_rdataset_t *sigrdataset;
+ dns_valarg_t *valarg;
+ dns_adbaddrinfo_t *addrinfo;
+
+ UNUSED(task); /* for now */
+
+ REQUIRE(event->ev_type == DNS_EVENT_VALIDATORDONE);
+ valarg = event->ev_arg;
+ fctx = valarg->fctx;
+ addrinfo = valarg->addrinfo;
+ REQUIRE(VALID_FCTX(fctx));
+ REQUIRE(!ISC_LIST_EMPTY(fctx->validators));
+
+ vevent = (dns_validatorevent_t *)event;
+
+ FCTXTRACE("received validation completion event");
+
+ ISC_LIST_UNLINK(fctx->validators, vevent->validator, link);
+ fctx->validator = NULL;
+
+ /*
+ * Destroy the validator early so that we can
+ * destroy the fctx if necessary.
+ */
+ dns_validator_destroy(&vevent->validator);
+ isc_mem_put(fctx->res->buckets[fctx->bucketnum].mctx,
+ valarg, sizeof(*valarg));
+
+ negative = ISC_TF(vevent->rdataset == NULL);
+
+ sentresponse = ISC_TF((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0);
+
+ /*
+ * If shutting down, ignore the results. Check to see if we're
+ * done waiting for validator completions and ADB pending events; if
+ * so, destroy the fctx.
+ */
+ if (SHUTTINGDOWN(fctx) && !sentresponse) {
+ maybe_destroy(fctx); /* Locks bucket. */
+ goto cleanup_event;
+ }
+
+ LOCK(&fctx->res->buckets[fctx->bucketnum].lock);
+
+ /*
+ * If chaining, we need to make sure that the right result code is
+ * returned, and that the rdatasets are bound.
+ */
+ if (vevent->result == ISC_R_SUCCESS &&
+ !negative &&
+ vevent->rdataset != NULL &&
+ CHAINING(vevent->rdataset))
+ {
+ if (vevent->rdataset->type == dns_rdatatype_cname)
+ eresult = DNS_R_CNAME;
+ else {
+ INSIST(vevent->rdataset->type == dns_rdatatype_dname);
+ eresult = DNS_R_DNAME;
+ }
+ chaining = ISC_TRUE;
+ } else
+ chaining = ISC_FALSE;
+
+ /*
+ * Either we're not shutting down, or we are shutting down but want
+ * to cache the result anyway (if this was a validation started by
+ * a query with cd set)
+ */
+
+ hevent = ISC_LIST_HEAD(fctx->events);
+ if (hevent != NULL) {
+ if (!negative && !chaining &&
+ (fctx->type == dns_rdatatype_any ||
+ fctx->type == dns_rdatatype_rrsig ||
+ fctx->type == dns_rdatatype_sig)) {
+ /*
+ * Don't bind rdatasets; the caller
+ * will iterate the node.
+ */
+ } else {
+ ardataset = hevent->rdataset;
+ asigrdataset = hevent->sigrdataset;
+ }
+ }
+
+ if (vevent->result != ISC_R_SUCCESS) {
+ FCTXTRACE("validation failed");
+ inc_stats(fctx->res, dns_resstatscounter_valfail);
+ result = ISC_R_NOTFOUND;
+ if (vevent->rdataset != NULL)
+ result = dns_db_findnode(fctx->cache, vevent->name,
+ ISC_TRUE, &node);
+ if (result == ISC_R_SUCCESS)
+ (void)dns_db_deleterdataset(fctx->cache, node, NULL,
+ vevent->type, 0);
+ if (result == ISC_R_SUCCESS && vevent->sigrdataset != NULL)
+ (void)dns_db_deleterdataset(fctx->cache, node, NULL,
+ dns_rdatatype_rrsig,
+ vevent->type);
+ if (result == ISC_R_SUCCESS)
+ dns_db_detachnode(fctx->cache, &node);
+ result = vevent->result;
+ add_bad(fctx, addrinfo, result);
+ isc_event_free(&event);
+ UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock);
+ INSIST(fctx->validator == NULL);
+ fctx->validator = ISC_LIST_HEAD(fctx->validators);
+ if (fctx->validator != NULL) {
+ dns_validator_send(fctx->validator);
+ } else if (sentresponse)
+ fctx_done(fctx, result); /* Locks bucket. */
+ else
+ fctx_try(fctx, ISC_TRUE); /* Locks bucket. */
+ return;
+ }
+
+ isc_stdtime_get(&now);
+
+ if (negative) {
+ dns_rdatatype_t covers;
+ FCTXTRACE("nonexistence validation OK");
+
+ inc_stats(fctx->res, dns_resstatscounter_valnegsuccess);
+
+ if (fctx->rmessage->rcode == dns_rcode_nxdomain)
+ covers = dns_rdatatype_any;
+ else
+ covers = fctx->type;
+
+ result = dns_db_findnode(fctx->cache, vevent->name, ISC_TRUE,
+ &node);
+ if (result != ISC_R_SUCCESS)
+ goto noanswer_response;
+
+ /*
+ * If we are asking for a SOA record set the cache time
+ * to zero to facilitate locating the containing zone of
+ * a arbitary zone.
+ */
+ ttl = fctx->res->view->maxncachettl;
+ if (fctx->type == dns_rdatatype_soa &&
+ covers == dns_rdatatype_any &&
+ fctx->res->zero_no_soa_ttl)
+ ttl = 0;
+
+ result = ncache_adderesult(fctx->rmessage, fctx->cache, node,
+ covers, now, ttl, vevent->optout,
+ ardataset, &eresult);
+ if (result != ISC_R_SUCCESS)
+ goto noanswer_response;
+ goto answer_response;
+ } else
+ inc_stats(fctx->res, dns_resstatscounter_valsuccess);
+
+ FCTXTRACE("validation OK");
+
+ if (vevent->proofs[DNS_VALIDATOR_NOQNAMEPROOF] != NULL) {
+
+ result = dns_rdataset_addnoqname(vevent->rdataset,
+ vevent->proofs[DNS_VALIDATOR_NOQNAMEPROOF]);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ INSIST(vevent->sigrdataset != NULL);
+ vevent->sigrdataset->ttl = vevent->rdataset->ttl;
+ if (vevent->proofs[DNS_VALIDATOR_CLOSESTENCLOSER] != NULL) {
+ result = dns_rdataset_addclosest(vevent->rdataset,
+ vevent->proofs[DNS_VALIDATOR_CLOSESTENCLOSER]);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ }
+ }
+
+ /*
+ * The data was already cached as pending data.
+ * Re-cache it as secure and bind the cached
+ * rdatasets to the first event on the fetch
+ * event list.
+ */
+ result = dns_db_findnode(fctx->cache, vevent->name, ISC_TRUE, &node);
+ if (result != ISC_R_SUCCESS)
+ goto noanswer_response;
+
+ result = dns_db_addrdataset(fctx->cache, node, NULL, now,
+ vevent->rdataset, 0, ardataset);
+ if (result != ISC_R_SUCCESS &&
+ result != DNS_R_UNCHANGED)
+ goto noanswer_response;
+ if (ardataset != NULL && ardataset->type == 0) {
+ if (NXDOMAIN(ardataset))
+ eresult = DNS_R_NCACHENXDOMAIN;
+ else
+ eresult = DNS_R_NCACHENXRRSET;
+ } else if (vevent->sigrdataset != NULL) {
+ result = dns_db_addrdataset(fctx->cache, node, NULL, now,
+ vevent->sigrdataset, 0,
+ asigrdataset);
+ if (result != ISC_R_SUCCESS &&
+ result != DNS_R_UNCHANGED)
+ goto noanswer_response;
+ }
+
+ if (sentresponse) {
+ /*
+ * If we only deferred the destroy because we wanted to cache
+ * the data, destroy now.
+ */
+ dns_db_detachnode(fctx->cache, &node);
+ UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock);
+ if (SHUTTINGDOWN(fctx))
+ maybe_destroy(fctx); /* Locks bucket. */
+ goto cleanup_event;
+ }
+
+ if (!ISC_LIST_EMPTY(fctx->validators)) {
+ INSIST(!negative);
+ INSIST(fctx->type == dns_rdatatype_any ||
+ fctx->type == dns_rdatatype_rrsig ||
+ fctx->type == dns_rdatatype_sig);
+ /*
+ * Don't send a response yet - we have
+ * more rdatasets that still need to
+ * be validated.
+ */
+ dns_db_detachnode(fctx->cache, &node);
+ UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock);
+ dns_validator_send(ISC_LIST_HEAD(fctx->validators));
+ goto cleanup_event;
+ }
+
+ answer_response:
+ /*
+ * Cache any NS/NSEC records that happened to be validated.
+ */
+ result = dns_message_firstname(fctx->rmessage, DNS_SECTION_AUTHORITY);
+ while (result == ISC_R_SUCCESS) {
+ name = NULL;
+ dns_message_currentname(fctx->rmessage, DNS_SECTION_AUTHORITY,
+ &name);
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link)) {
+ if ((rdataset->type != dns_rdatatype_ns &&
+ rdataset->type != dns_rdatatype_nsec) ||
+ rdataset->trust != dns_trust_secure)
+ continue;
+ for (sigrdataset = ISC_LIST_HEAD(name->list);
+ sigrdataset != NULL;
+ sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) {
+ if (sigrdataset->type != dns_rdatatype_rrsig ||
+ sigrdataset->covers != rdataset->type)
+ continue;
+ break;
+ }
+ if (sigrdataset == NULL ||
+ sigrdataset->trust != dns_trust_secure)
+ continue;
+ result = dns_db_findnode(fctx->cache, name, ISC_TRUE,
+ &nsnode);
+ if (result != ISC_R_SUCCESS)
+ continue;
+
+ result = dns_db_addrdataset(fctx->cache, nsnode, NULL,
+ now, rdataset, 0, NULL);
+ if (result == ISC_R_SUCCESS)
+ result = dns_db_addrdataset(fctx->cache, nsnode,
+ NULL, now,
+ sigrdataset, 0,
+ NULL);
+ dns_db_detachnode(fctx->cache, &nsnode);
+ }
+ result = dns_message_nextname(fctx->rmessage,
+ DNS_SECTION_AUTHORITY);
+ }
+
+ result = ISC_R_SUCCESS;
+
+ /*
+ * Respond with an answer, positive or negative,
+ * as opposed to an error. 'node' must be non-NULL.
+ */
+
+ fctx->attributes |= FCTX_ATTR_HAVEANSWER;
+
+ if (hevent != NULL) {
+ hevent->result = eresult;
+ RUNTIME_CHECK(dns_name_copy(vevent->name,
+ dns_fixedname_name(&hevent->foundname), NULL)
+ == ISC_R_SUCCESS);
+ dns_db_attach(fctx->cache, &hevent->db);
+ dns_db_transfernode(fctx->cache, &node, &hevent->node);
+ clone_results(fctx);
+ }
+
+ noanswer_response:
+ if (node != NULL)
+ dns_db_detachnode(fctx->cache, &node);
+
+ UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock);
+
+ fctx_done(fctx, result); /* Locks bucket. */
+
+ cleanup_event:
+ INSIST(node == NULL);
+ isc_event_free(&event);
+}
+
+static inline isc_result_t
+cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo,
+ isc_stdtime_t now)
+{
+ dns_rdataset_t *rdataset, *sigrdataset;
+ dns_rdataset_t *addedrdataset, *ardataset, *asigrdataset;
+ dns_rdataset_t *valrdataset = NULL, *valsigrdataset = NULL;
+ dns_dbnode_t *node, **anodep;
+ dns_db_t **adbp;
+ dns_name_t *aname;
+ dns_resolver_t *res;
+ isc_boolean_t need_validation, secure_domain, have_answer;
+ isc_result_t result, eresult;
+ dns_fetchevent_t *event;
+ unsigned int options;
+ isc_task_t *task;
+ isc_boolean_t fail;
+ unsigned int valoptions = 0;
+
+ /*
+ * The appropriate bucket lock must be held.
+ */
+
+ res = fctx->res;
+ need_validation = ISC_FALSE;
+ secure_domain = ISC_FALSE;
+ have_answer = ISC_FALSE;
+ eresult = ISC_R_SUCCESS;
+ task = res->buckets[fctx->bucketnum].task;
+
+ /*
+ * Is DNSSEC validation required for this name?
+ */
+ if (res->view->enablevalidation) {
+ result = dns_keytable_issecuredomain(res->view->secroots, name,
+ &secure_domain);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (!secure_domain && res->view->dlv != NULL) {
+ valoptions = DNS_VALIDATOR_DLV;
+ secure_domain = ISC_TRUE;
+ }
+ }
+
+ if ((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0)
+ need_validation = ISC_FALSE;
+ else
+ need_validation = secure_domain;
+
+ adbp = NULL;
+ aname = NULL;
+ anodep = NULL;
+ ardataset = NULL;
+ asigrdataset = NULL;
+ event = NULL;
+ if ((name->attributes & DNS_NAMEATTR_ANSWER) != 0 &&
+ !need_validation) {
+ have_answer = ISC_TRUE;
+ event = ISC_LIST_HEAD(fctx->events);
+ if (event != NULL) {
+ adbp = &event->db;
+ aname = dns_fixedname_name(&event->foundname);
+ result = dns_name_copy(name, aname, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ anodep = &event->node;
+ /*
+ * If this is an ANY, SIG or RRSIG query, we're not
+ * going to return any rdatasets, unless we encountered
+ * a CNAME or DNAME as "the answer". In this case,
+ * we're going to return DNS_R_CNAME or DNS_R_DNAME
+ * and we must set up the rdatasets.
+ */
+ if ((fctx->type != dns_rdatatype_any &&
+ fctx->type != dns_rdatatype_rrsig &&
+ fctx->type != dns_rdatatype_sig) ||
+ (name->attributes & DNS_NAMEATTR_CHAINING) != 0) {
+ ardataset = event->rdataset;
+ asigrdataset = event->sigrdataset;
+ }
+ }
+ }
+
+ /*
+ * Find or create the cache node.
+ */
+ node = NULL;
+ result = dns_db_findnode(fctx->cache, name, ISC_TRUE, &node);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * Cache or validate each cacheable rdataset.
+ */
+ fail = ISC_TF((fctx->res->options & DNS_RESOLVER_CHECKNAMESFAIL) != 0);
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link)) {
+ if (!CACHE(rdataset))
+ continue;
+ if (CHECKNAMES(rdataset)) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
+ char classbuf[DNS_RDATATYPE_FORMATSIZE];
+
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ dns_rdatatype_format(rdataset->type, typebuf,
+ sizeof(typebuf));
+ dns_rdataclass_format(rdataset->rdclass, classbuf,
+ sizeof(classbuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+ DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE,
+ "check-names %s %s/%s/%s",
+ fail ? "failure" : "warning",
+ namebuf, typebuf, classbuf);
+ if (fail) {
+ if (ANSWER(rdataset)) {
+ dns_db_detachnode(fctx->cache, &node);
+ return (DNS_R_BADNAME);
+ }
+ continue;
+ }
+ }
+
+ /*
+ * Enforce the configure maximum cache TTL.
+ */
+ if (rdataset->ttl > res->view->maxcachettl)
+ rdataset->ttl = res->view->maxcachettl;
+
+ /*
+ * If this rrset is in a secure domain, do DNSSEC validation
+ * for it, unless it is glue.
+ */
+ if (secure_domain && rdataset->trust != dns_trust_glue) {
+ /*
+ * RRSIGs are validated as part of validating the
+ * type they cover.
+ */
+ if (rdataset->type == dns_rdatatype_rrsig)
+ continue;
+ /*
+ * Find the SIG for this rdataset, if we have it.
+ */
+ for (sigrdataset = ISC_LIST_HEAD(name->list);
+ sigrdataset != NULL;
+ sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) {
+ if (sigrdataset->type == dns_rdatatype_rrsig &&
+ sigrdataset->covers == rdataset->type)
+ break;
+ }
+ if (sigrdataset == NULL) {
+ if (!ANSWER(rdataset) && need_validation) {
+ /*
+ * Ignore non-answer rdatasets that
+ * are missing signatures.
+ */
+ continue;
+ }
+ }
+
+ /*
+ * Normalize the rdataset and sigrdataset TTLs.
+ */
+ if (sigrdataset != NULL) {
+ rdataset->ttl = ISC_MIN(rdataset->ttl,
+ sigrdataset->ttl);
+ sigrdataset->ttl = rdataset->ttl;
+ }
+
+ /*
+ * Cache this rdataset/sigrdataset pair as
+ * pending data.
+ */
+ rdataset->trust = dns_trust_pending;
+ if (sigrdataset != NULL)
+ sigrdataset->trust = dns_trust_pending;
+ if (!need_validation)
+ addedrdataset = ardataset;
+ else
+ addedrdataset = NULL;
+ result = dns_db_addrdataset(fctx->cache, node, NULL,
+ now, rdataset, 0,
+ addedrdataset);
+ if (result == DNS_R_UNCHANGED) {
+ result = ISC_R_SUCCESS;
+ if (!need_validation &&
+ ardataset != NULL &&
+ ardataset->type == 0) {
+ /*
+ * The answer in the cache is better
+ * than the answer we found, and is
+ * a negative cache entry, so we
+ * must set eresult appropriately.
+ */
+ if (NXDOMAIN(ardataset))
+ eresult = DNS_R_NCACHENXDOMAIN;
+ else
+ eresult = DNS_R_NCACHENXRRSET;
+ /*
+ * We have a negative response from
+ * the cache so don't attempt to
+ * add the RRSIG rrset.
+ */
+ continue;
+ }
+ }
+ if (result != ISC_R_SUCCESS)
+ break;
+ if (sigrdataset != NULL) {
+ if (!need_validation)
+ addedrdataset = asigrdataset;
+ else
+ addedrdataset = NULL;
+ result = dns_db_addrdataset(fctx->cache,
+ node, NULL, now,
+ sigrdataset, 0,
+ addedrdataset);
+ if (result == DNS_R_UNCHANGED)
+ result = ISC_R_SUCCESS;
+ if (result != ISC_R_SUCCESS)
+ break;
+ } else if (!ANSWER(rdataset))
+ continue;
+
+ if (ANSWER(rdataset) && need_validation) {
+ if (fctx->type != dns_rdatatype_any &&
+ fctx->type != dns_rdatatype_rrsig &&
+ fctx->type != dns_rdatatype_sig) {
+ /*
+ * This is The Answer. We will
+ * validate it, but first we cache
+ * the rest of the response - it may
+ * contain useful keys.
+ */
+ INSIST(valrdataset == NULL &&
+ valsigrdataset == NULL);
+ valrdataset = rdataset;
+ valsigrdataset = sigrdataset;
+ } else {
+ /*
+ * This is one of (potentially)
+ * multiple answers to an ANY
+ * or SIG query. To keep things
+ * simple, we just start the
+ * validator right away rather
+ * than caching first and
+ * having to remember which
+ * rdatasets needed validation.
+ */
+ result = valcreate(fctx, addrinfo,
+ name, rdataset->type,
+ rdataset,
+ sigrdataset,
+ valoptions, task);
+ /*
+ * Defer any further validations.
+ * This prevents multiple validators
+ * from manipulating fctx->rmessage
+ * simultaniously.
+ */
+ valoptions |= DNS_VALIDATOR_DEFER;
+ }
+ } else if (CHAINING(rdataset)) {
+ if (rdataset->type == dns_rdatatype_cname)
+ eresult = DNS_R_CNAME;
+ else {
+ INSIST(rdataset->type ==
+ dns_rdatatype_dname);
+ eresult = DNS_R_DNAME;
+ }
+ }
+ } else if (!EXTERNAL(rdataset)) {
+ /*
+ * It's OK to cache this rdataset now.
+ */
+ if (ANSWER(rdataset))
+ addedrdataset = ardataset;
+ else if (ANSWERSIG(rdataset))
+ addedrdataset = asigrdataset;
+ else
+ addedrdataset = NULL;
+ if (CHAINING(rdataset)) {
+ if (rdataset->type == dns_rdatatype_cname)
+ eresult = DNS_R_CNAME;
+ else {
+ INSIST(rdataset->type ==
+ dns_rdatatype_dname);
+ eresult = DNS_R_DNAME;
+ }
+ }
+ if (rdataset->trust == dns_trust_glue &&
+ (rdataset->type == dns_rdatatype_ns ||
+ (rdataset->type == dns_rdatatype_rrsig &&
+ rdataset->covers == dns_rdatatype_ns))) {
+ /*
+ * If the trust level is 'dns_trust_glue'
+ * then we are adding data from a referral
+ * we got while executing the search algorithm.
+ * New referral data always takes precedence
+ * over the existing cache contents.
+ */
+ options = DNS_DBADD_FORCE;
+ } else
+ options = 0;
+ /*
+ * Now we can add the rdataset.
+ */
+ result = dns_db_addrdataset(fctx->cache,
+ node, NULL, now,
+ rdataset,
+ options,
+ addedrdataset);
+ if (result == DNS_R_UNCHANGED) {
+ if (ANSWER(rdataset) &&
+ ardataset != NULL &&
+ ardataset->type == 0) {
+ /*
+ * The answer in the cache is better
+ * than the answer we found, and is
+ * a negative cache entry, so we
+ * must set eresult appropriately.
+ */
+ if (NXDOMAIN(ardataset))
+ eresult = DNS_R_NCACHENXDOMAIN;
+ else
+ eresult = DNS_R_NCACHENXRRSET;
+ }
+ result = ISC_R_SUCCESS;
+ } else if (result != ISC_R_SUCCESS)
+ break;
+ }
+ }
+
+ if (valrdataset != NULL)
+ result = valcreate(fctx, addrinfo, name, fctx->type,
+ valrdataset, valsigrdataset, valoptions,
+ task);
+
+ if (result == ISC_R_SUCCESS && have_answer) {
+ fctx->attributes |= FCTX_ATTR_HAVEANSWER;
+ if (event != NULL) {
+ /*
+ * Negative results must be indicated in event->result.
+ */
+ if (dns_rdataset_isassociated(event->rdataset) &&
+ event->rdataset->type == dns_rdatatype_none) {
+ INSIST(eresult == DNS_R_NCACHENXDOMAIN ||
+ eresult == DNS_R_NCACHENXRRSET);
+ }
+ event->result = eresult;
+ dns_db_attach(fctx->cache, adbp);
+ dns_db_transfernode(fctx->cache, &node, anodep);
+ clone_results(fctx);
+ }
+ }
+
+ if (node != NULL)
+ dns_db_detachnode(fctx->cache, &node);
+
+ return (result);
+}
+
+static inline isc_result_t
+cache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, isc_stdtime_t now)
+{
+ isc_result_t result;
+ dns_section_t section;
+ dns_name_t *name;
+
+ FCTXTRACE("cache_message");
+
+ fctx->attributes &= ~FCTX_ATTR_WANTCACHE;
+
+ LOCK(&fctx->res->buckets[fctx->bucketnum].lock);
+
+ for (section = DNS_SECTION_ANSWER;
+ section <= DNS_SECTION_ADDITIONAL;
+ section++) {
+ result = dns_message_firstname(fctx->rmessage, section);
+ while (result == ISC_R_SUCCESS) {
+ name = NULL;
+ dns_message_currentname(fctx->rmessage, section,
+ &name);
+ if ((name->attributes & DNS_NAMEATTR_CACHE) != 0) {
+ result = cache_name(fctx, name, addrinfo, now);
+ if (result != ISC_R_SUCCESS)
+ break;
+ }
+ result = dns_message_nextname(fctx->rmessage, section);
+ }
+ if (result != ISC_R_NOMORE)
+ break;
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+
+ UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock);
+
+ return (result);
+}
+
+/*
+ * Do what dns_ncache_addoptout() does, and then compute an appropriate eresult.
+ */
+static isc_result_t
+ncache_adderesult(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
+ dns_rdatatype_t covers, isc_stdtime_t now, dns_ttl_t maxttl,
+ isc_boolean_t optout, dns_rdataset_t *ardataset,
+ isc_result_t *eresultp)
+{
+ isc_result_t result;
+ dns_rdataset_t rdataset;
+
+ if (ardataset == NULL) {
+ dns_rdataset_init(&rdataset);
+ ardataset = &rdataset;
+ }
+ result = dns_ncache_addoptout(message, cache, node, covers, now,
+ maxttl, optout, ardataset);
+ if (result == DNS_R_UNCHANGED || result == ISC_R_SUCCESS) {
+ /*
+ * If the cache now contains a negative entry and we
+ * care about whether it is DNS_R_NCACHENXDOMAIN or
+ * DNS_R_NCACHENXRRSET then extract it.
+ */
+ if (ardataset->type == 0) {
+ /*
+ * The cache data is a negative cache entry.
+ */
+ if (NXDOMAIN(ardataset))
+ *eresultp = DNS_R_NCACHENXDOMAIN;
+ else
+ *eresultp = DNS_R_NCACHENXRRSET;
+ } else {
+ /*
+ * Either we don't care about the nature of the
+ * cache rdataset (because no fetch is interested
+ * in the outcome), or the cache rdataset is not
+ * a negative cache entry. Whichever case it is,
+ * we can return success.
+ *
+ * XXXRTH There's a CNAME/DNAME problem here.
+ */
+ *eresultp = ISC_R_SUCCESS;
+ }
+ result = ISC_R_SUCCESS;
+ }
+ if (ardataset == &rdataset && dns_rdataset_isassociated(ardataset))
+ dns_rdataset_disassociate(ardataset);
+
+ return (result);
+}
+
+static inline isc_result_t
+ncache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
+ dns_rdatatype_t covers, isc_stdtime_t now)
+{
+ isc_result_t result, eresult;
+ dns_name_t *name;
+ dns_resolver_t *res;
+ dns_db_t **adbp;
+ dns_dbnode_t *node, **anodep;
+ dns_rdataset_t *ardataset;
+ isc_boolean_t need_validation, secure_domain;
+ dns_name_t *aname;
+ dns_fetchevent_t *event;
+ isc_uint32_t ttl;
+ unsigned int valoptions = 0;
+
+ FCTXTRACE("ncache_message");
+
+ fctx->attributes &= ~FCTX_ATTR_WANTNCACHE;
+
+ res = fctx->res;
+ need_validation = ISC_FALSE;
+ secure_domain = ISC_FALSE;
+ eresult = ISC_R_SUCCESS;
+ name = &fctx->name;
+ node = NULL;
+
+ /*
+ * XXXMPA remove when we follow cnames and adjust the setting
+ * of FCTX_ATTR_WANTNCACHE in noanswer_response().
+ */
+ INSIST(fctx->rmessage->counts[DNS_SECTION_ANSWER] == 0);
+
+ /*
+ * Is DNSSEC validation required for this name?
+ */
+ if (fctx->res->view->enablevalidation) {
+ result = dns_keytable_issecuredomain(res->view->secroots, name,
+ &secure_domain);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (!secure_domain && res->view->dlv != NULL) {
+ valoptions = DNS_VALIDATOR_DLV;
+ secure_domain = ISC_TRUE;
+ }
+ }
+
+ if ((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0)
+ need_validation = ISC_FALSE;
+ else
+ need_validation = secure_domain;
+
+ if (secure_domain) {
+ /*
+ * Mark all rdatasets as pending.
+ */
+ dns_rdataset_t *trdataset;
+ dns_name_t *tname;
+
+ result = dns_message_firstname(fctx->rmessage,
+ DNS_SECTION_AUTHORITY);
+ while (result == ISC_R_SUCCESS) {
+ tname = NULL;
+ dns_message_currentname(fctx->rmessage,
+ DNS_SECTION_AUTHORITY,
+ &tname);
+ for (trdataset = ISC_LIST_HEAD(tname->list);
+ trdataset != NULL;
+ trdataset = ISC_LIST_NEXT(trdataset, link))
+ trdataset->trust = dns_trust_pending;
+ result = dns_message_nextname(fctx->rmessage,
+ DNS_SECTION_AUTHORITY);
+ }
+ if (result != ISC_R_NOMORE)
+ return (result);
+
+ }
+
+ if (need_validation) {
+ /*
+ * Do negative response validation.
+ */
+ result = valcreate(fctx, addrinfo, name, fctx->type,
+ NULL, NULL, valoptions,
+ res->buckets[fctx->bucketnum].task);
+ /*
+ * If validation is necessary, return now. Otherwise continue
+ * to process the message, letting the validation complete
+ * in its own good time.
+ */
+ return (result);
+ }
+
+ LOCK(&res->buckets[fctx->bucketnum].lock);
+
+ adbp = NULL;
+ aname = NULL;
+ anodep = NULL;
+ ardataset = NULL;
+ if (!HAVE_ANSWER(fctx)) {
+ event = ISC_LIST_HEAD(fctx->events);
+ if (event != NULL) {
+ adbp = &event->db;
+ aname = dns_fixedname_name(&event->foundname);
+ result = dns_name_copy(name, aname, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto unlock;
+ anodep = &event->node;
+ ardataset = event->rdataset;
+ }
+ } else
+ event = NULL;
+
+ result = dns_db_findnode(fctx->cache, name, ISC_TRUE, &node);
+ if (result != ISC_R_SUCCESS)
+ goto unlock;
+
+ /*
+ * If we are asking for a SOA record set the cache time
+ * to zero to facilitate locating the containing zone of
+ * a arbitary zone.
+ */
+ ttl = fctx->res->view->maxncachettl;
+ if (fctx->type == dns_rdatatype_soa &&
+ covers == dns_rdatatype_any)
+ ttl = 0;
+
+ result = ncache_adderesult(fctx->rmessage, fctx->cache, node,
+ covers, now, ttl, ISC_FALSE,
+ ardataset, &eresult);
+ if (result != ISC_R_SUCCESS)
+ goto unlock;
+
+ if (!HAVE_ANSWER(fctx)) {
+ fctx->attributes |= FCTX_ATTR_HAVEANSWER;
+ if (event != NULL) {
+ event->result = eresult;
+ dns_db_attach(fctx->cache, adbp);
+ dns_db_transfernode(fctx->cache, &node, anodep);
+ clone_results(fctx);
+ }
+ }
+
+ unlock:
+ UNLOCK(&res->buckets[fctx->bucketnum].lock);
+
+ if (node != NULL)
+ dns_db_detachnode(fctx->cache, &node);
+
+ return (result);
+}
+
+static inline void
+mark_related(dns_name_t *name, dns_rdataset_t *rdataset,
+ isc_boolean_t external, isc_boolean_t gluing)
+{
+ name->attributes |= DNS_NAMEATTR_CACHE;
+ if (gluing) {
+ rdataset->trust = dns_trust_glue;
+ /*
+ * Glue with 0 TTL causes problems. We force the TTL to
+ * 1 second to prevent this.
+ */
+ if (rdataset->ttl == 0)
+ rdataset->ttl = 1;
+ } else
+ rdataset->trust = dns_trust_additional;
+ /*
+ * Avoid infinite loops by only marking new rdatasets.
+ */
+ if (!CACHE(rdataset)) {
+ name->attributes |= DNS_NAMEATTR_CHASE;
+ rdataset->attributes |= DNS_RDATASETATTR_CHASE;
+ }
+ rdataset->attributes |= DNS_RDATASETATTR_CACHE;
+ if (external)
+ rdataset->attributes |= DNS_RDATASETATTR_EXTERNAL;
+}
+
+static isc_result_t
+check_related(void *arg, dns_name_t *addname, dns_rdatatype_t type) {
+ fetchctx_t *fctx = arg;
+ isc_result_t result;
+ dns_name_t *name;
+ dns_rdataset_t *rdataset;
+ isc_boolean_t external;
+ dns_rdatatype_t rtype;
+ isc_boolean_t gluing;
+
+ REQUIRE(VALID_FCTX(fctx));
+
+ if (GLUING(fctx))
+ gluing = ISC_TRUE;
+ else
+ gluing = ISC_FALSE;
+ name = NULL;
+ rdataset = NULL;
+ result = dns_message_findname(fctx->rmessage, DNS_SECTION_ADDITIONAL,
+ addname, dns_rdatatype_any, 0, &name,
+ NULL);
+ if (result == ISC_R_SUCCESS) {
+ external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain));
+ if (type == dns_rdatatype_a) {
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link)) {
+ if (rdataset->type == dns_rdatatype_rrsig)
+ rtype = rdataset->covers;
+ else
+ rtype = rdataset->type;
+ if (rtype == dns_rdatatype_a ||
+ rtype == dns_rdatatype_aaaa)
+ mark_related(name, rdataset, external,
+ gluing);
+ }
+ } else {
+ result = dns_message_findtype(name, type, 0,
+ &rdataset);
+ if (result == ISC_R_SUCCESS) {
+ mark_related(name, rdataset, external, gluing);
+ /*
+ * Do we have its SIG too?
+ */
+ rdataset = NULL;
+ result = dns_message_findtype(name,
+ dns_rdatatype_rrsig,
+ type, &rdataset);
+ if (result == ISC_R_SUCCESS)
+ mark_related(name, rdataset, external,
+ gluing);
+ }
+ }
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+chase_additional(fetchctx_t *fctx) {
+ isc_boolean_t rescan;
+ dns_section_t section = DNS_SECTION_ADDITIONAL;
+ isc_result_t result;
+
+ again:
+ rescan = ISC_FALSE;
+
+ for (result = dns_message_firstname(fctx->rmessage, section);
+ result == ISC_R_SUCCESS;
+ result = dns_message_nextname(fctx->rmessage, section)) {
+ dns_name_t *name = NULL;
+ dns_rdataset_t *rdataset;
+ dns_message_currentname(fctx->rmessage, DNS_SECTION_ADDITIONAL,
+ &name);
+ if ((name->attributes & DNS_NAMEATTR_CHASE) == 0)
+ continue;
+ name->attributes &= ~DNS_NAMEATTR_CHASE;
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link)) {
+ if (CHASE(rdataset)) {
+ rdataset->attributes &= ~DNS_RDATASETATTR_CHASE;
+ (void)dns_rdataset_additionaldata(rdataset,
+ check_related,
+ fctx);
+ rescan = ISC_TRUE;
+ }
+ }
+ }
+ if (rescan)
+ goto again;
+}
+
+static inline isc_result_t
+cname_target(dns_rdataset_t *rdataset, dns_name_t *tname) {
+ isc_result_t result;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_cname_t cname;
+
+ result = dns_rdataset_first(rdataset);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ dns_rdataset_current(rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &cname, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ dns_name_init(tname, NULL);
+ dns_name_clone(&cname.cname, tname);
+ dns_rdata_freestruct(&cname);
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+dname_target(dns_rdataset_t *rdataset, dns_name_t *qname, dns_name_t *oname,
+ dns_fixedname_t *fixeddname)
+{
+ isc_result_t result;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ unsigned int nlabels;
+ int order;
+ dns_namereln_t namereln;
+ dns_rdata_dname_t dname;
+ dns_fixedname_t prefix;
+
+ /*
+ * Get the target name of the DNAME.
+ */
+
+ result = dns_rdataset_first(rdataset);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ dns_rdataset_current(rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &dname, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * Get the prefix of qname.
+ */
+ namereln = dns_name_fullcompare(qname, oname, &order, &nlabels);
+ if (namereln != dns_namereln_subdomain) {
+ dns_rdata_freestruct(&dname);
+ return (DNS_R_FORMERR);
+ }
+ dns_fixedname_init(&prefix);
+ dns_name_split(qname, nlabels, dns_fixedname_name(&prefix), NULL);
+ dns_fixedname_init(fixeddname);
+ result = dns_name_concatenate(dns_fixedname_name(&prefix),
+ &dname.dname,
+ dns_fixedname_name(fixeddname), NULL);
+ dns_rdata_freestruct(&dname);
+ return (result);
+}
+
+/*
+ * Handle a no-answer response (NXDOMAIN, NXRRSET, or referral).
+ * If bind8_ns_resp is ISC_TRUE, this is a suspected BIND 8
+ * response to an NS query that should be treated as a referral
+ * even though the NS records occur in the answer section
+ * rather than the authority section.
+ */
+static isc_result_t
+noanswer_response(fetchctx_t *fctx, dns_name_t *oqname,
+ isc_boolean_t bind8_ns_resp)
+{
+ isc_result_t result;
+ dns_message_t *message;
+ dns_name_t *name, *qname, *ns_name, *soa_name, *ds_name;
+ dns_rdataset_t *rdataset, *ns_rdataset;
+ isc_boolean_t aa, negative_response;
+ dns_rdatatype_t type;
+ dns_section_t section =
+ bind8_ns_resp ? DNS_SECTION_ANSWER : DNS_SECTION_AUTHORITY;
+
+ FCTXTRACE("noanswer_response");
+
+ message = fctx->rmessage;
+
+ /*
+ * Setup qname.
+ */
+ if (oqname == NULL) {
+ /*
+ * We have a normal, non-chained negative response or
+ * referral.
+ */
+ if ((message->flags & DNS_MESSAGEFLAG_AA) != 0)
+ aa = ISC_TRUE;
+ else
+ aa = ISC_FALSE;
+ qname = &fctx->name;
+ } else {
+ /*
+ * We're being invoked by answer_response() after it has
+ * followed a CNAME/DNAME chain.
+ */
+ qname = oqname;
+ aa = ISC_FALSE;
+ /*
+ * If the current qname is not a subdomain of the query
+ * domain, there's no point in looking at the authority
+ * section without doing DNSSEC validation.
+ *
+ * Until we do that validation, we'll just return success
+ * in this case.
+ */
+ if (!dns_name_issubdomain(qname, &fctx->domain))
+ return (ISC_R_SUCCESS);
+ }
+
+ /*
+ * We have to figure out if this is a negative response, or a
+ * referral.
+ */
+
+ /*
+ * Sometimes we can tell if its a negative response by looking at
+ * the message header.
+ */
+ negative_response = ISC_FALSE;
+ if (message->rcode == dns_rcode_nxdomain ||
+ (message->counts[DNS_SECTION_ANSWER] == 0 &&
+ message->counts[DNS_SECTION_AUTHORITY] == 0))
+ negative_response = ISC_TRUE;
+
+ /*
+ * Process the authority section.
+ */
+ ns_name = NULL;
+ ns_rdataset = NULL;
+ soa_name = NULL;
+ ds_name = NULL;
+ result = dns_message_firstname(message, section);
+ while (result == ISC_R_SUCCESS) {
+ name = NULL;
+ dns_message_currentname(message, section, &name);
+ if (dns_name_issubdomain(name, &fctx->domain)) {
+ /*
+ * Look for NS/SOA RRsets first.
+ */
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link)) {
+ type = rdataset->type;
+ if (type == dns_rdatatype_rrsig)
+ type = rdataset->covers;
+ if (((type == dns_rdatatype_ns ||
+ type == dns_rdatatype_soa) &&
+ !dns_name_issubdomain(qname, name)))
+ return (DNS_R_FORMERR);
+ if (type == dns_rdatatype_ns) {
+ /*
+ * NS or RRSIG NS.
+ *
+ * Only one set of NS RRs is allowed.
+ */
+ if (rdataset->type ==
+ dns_rdatatype_ns) {
+ if (ns_name != NULL &&
+ name != ns_name)
+ return (DNS_R_FORMERR);
+ ns_name = name;
+ ns_rdataset = rdataset;
+ }
+ name->attributes |=
+ DNS_NAMEATTR_CACHE;
+ rdataset->attributes |=
+ DNS_RDATASETATTR_CACHE;
+ rdataset->trust = dns_trust_glue;
+ }
+ if (type == dns_rdatatype_soa) {
+ /*
+ * SOA, or RRSIG SOA.
+ *
+ * Only one SOA is allowed.
+ */
+ if (rdataset->type ==
+ dns_rdatatype_soa) {
+ if (soa_name != NULL &&
+ name != soa_name)
+ return (DNS_R_FORMERR);
+ soa_name = name;
+ }
+ name->attributes |=
+ DNS_NAMEATTR_NCACHE;
+ rdataset->attributes |=
+ DNS_RDATASETATTR_NCACHE;
+ if (aa)
+ rdataset->trust =
+ dns_trust_authauthority;
+ else
+ rdataset->trust =
+ dns_trust_additional;
+ }
+ }
+ }
+ result = dns_message_nextname(message, section);
+ if (result == ISC_R_NOMORE)
+ break;
+ else if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ /*
+ * A negative response has a SOA record (Type 2)
+ * and a optional NS RRset (Type 1) or it has neither
+ * a SOA or a NS RRset (Type 3, handled above) or
+ * rcode is NXDOMAIN (handled above) in which case
+ * the NS RRset is allowed (Type 4).
+ */
+ if (soa_name != NULL)
+ negative_response = ISC_TRUE;
+
+ result = dns_message_firstname(message, section);
+ while (result == ISC_R_SUCCESS) {
+ name = NULL;
+ dns_message_currentname(message, section, &name);
+ if (dns_name_issubdomain(name, &fctx->domain)) {
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link)) {
+ type = rdataset->type;
+ if (type == dns_rdatatype_rrsig)
+ type = rdataset->covers;
+ if (type == dns_rdatatype_nsec ||
+ type == dns_rdatatype_nsec3) {
+ /*
+ * NSEC or RRSIG NSEC.
+ */
+ if (negative_response) {
+ name->attributes |=
+ DNS_NAMEATTR_NCACHE;
+ rdataset->attributes |=
+ DNS_RDATASETATTR_NCACHE;
+ } else if (type == dns_rdatatype_nsec) {
+ name->attributes |=
+ DNS_NAMEATTR_CACHE;
+ rdataset->attributes |=
+ DNS_RDATASETATTR_CACHE;
+ }
+ if (aa)
+ rdataset->trust =
+ dns_trust_authauthority;
+ else
+ rdataset->trust =
+ dns_trust_additional;
+ /*
+ * No additional data needs to be
+ * marked.
+ */
+ } else if (type == dns_rdatatype_ds) {
+ /*
+ * DS or SIG DS.
+ *
+ * These should only be here if
+ * this is a referral, and there
+ * should only be one DS.
+ */
+ if (ns_name == NULL)
+ return (DNS_R_FORMERR);
+ if (rdataset->type ==
+ dns_rdatatype_ds) {
+ if (ds_name != NULL &&
+ name != ds_name)
+ return (DNS_R_FORMERR);
+ ds_name = name;
+ }
+ name->attributes |=
+ DNS_NAMEATTR_CACHE;
+ rdataset->attributes |=
+ DNS_RDATASETATTR_CACHE;
+ if (aa)
+ rdataset->trust =
+ dns_trust_authauthority;
+ else
+ rdataset->trust =
+ dns_trust_additional;
+ }
+ }
+ }
+ result = dns_message_nextname(message, section);
+ if (result == ISC_R_NOMORE)
+ break;
+ else if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ /*
+ * Trigger lookups for DNS nameservers.
+ */
+ if (negative_response && message->rcode == dns_rcode_noerror &&
+ fctx->type == dns_rdatatype_ds && soa_name != NULL &&
+ dns_name_equal(soa_name, qname) &&
+ !dns_name_equal(qname, dns_rootname))
+ return (DNS_R_CHASEDSSERVERS);
+
+ /*
+ * Did we find anything?
+ */
+ if (!negative_response && ns_name == NULL) {
+ /*
+ * Nope.
+ */
+ if (oqname != NULL) {
+ /*
+ * We've already got a partial CNAME/DNAME chain,
+ * and haven't found else anything useful here, but
+ * no error has occurred since we have an answer.
+ */
+ return (ISC_R_SUCCESS);
+ } else {
+ /*
+ * The responder is insane.
+ */
+ return (DNS_R_FORMERR);
+ }
+ }
+
+ /*
+ * If we found both NS and SOA, they should be the same name.
+ */
+ if (ns_name != NULL && soa_name != NULL && ns_name != soa_name)
+ return (DNS_R_FORMERR);
+
+ /*
+ * Do we have a referral? (We only want to follow a referral if
+ * we're not following a chain.)
+ */
+ if (!negative_response && ns_name != NULL && oqname == NULL) {
+ /*
+ * We already know ns_name is a subdomain of fctx->domain.
+ * If ns_name is equal to fctx->domain, we're not making
+ * progress. We return DNS_R_FORMERR so that we'll keep
+ * trying other servers.
+ */
+ if (dns_name_equal(ns_name, &fctx->domain))
+ return (DNS_R_FORMERR);
+
+ /*
+ * If the referral name is not a parent of the query
+ * name, consider the responder insane.
+ */
+ if (! dns_name_issubdomain(&fctx->name, ns_name)) {
+ FCTXTRACE("referral to non-parent");
+ return (DNS_R_FORMERR);
+ }
+
+ /*
+ * Mark any additional data related to this rdataset.
+ * It's important that we do this before we change the
+ * query domain.
+ */
+ INSIST(ns_rdataset != NULL);
+ fctx->attributes |= FCTX_ATTR_GLUING;
+ (void)dns_rdataset_additionaldata(ns_rdataset, check_related,
+ fctx);
+ fctx->attributes &= ~FCTX_ATTR_GLUING;
+ /*
+ * NS rdatasets with 0 TTL cause problems.
+ * dns_view_findzonecut() will not find them when we
+ * try to follow the referral, and we'll SERVFAIL
+ * because the best nameservers are now above QDOMAIN.
+ * We force the TTL to 1 second to prevent this.
+ */
+ if (ns_rdataset->ttl == 0)
+ ns_rdataset->ttl = 1;
+ /*
+ * Set the current query domain to the referral name.
+ *
+ * XXXRTH We should check if we're in forward-only mode, and
+ * if so we should bail out.
+ */
+ INSIST(dns_name_countlabels(&fctx->domain) > 0);
+ dns_name_free(&fctx->domain,
+ fctx->res->buckets[fctx->bucketnum].mctx);
+ if (dns_rdataset_isassociated(&fctx->nameservers))
+ dns_rdataset_disassociate(&fctx->nameservers);
+ dns_name_init(&fctx->domain, NULL);
+ result = dns_name_dup(ns_name,
+ fctx->res->buckets[fctx->bucketnum].mctx,
+ &fctx->domain);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ fctx->attributes |= FCTX_ATTR_WANTCACHE;
+ return (DNS_R_DELEGATION);
+ }
+
+ /*
+ * Since we're not doing a referral, we don't want to cache any
+ * NS RRs we may have found.
+ */
+ if (ns_name != NULL)
+ ns_name->attributes &= ~DNS_NAMEATTR_CACHE;
+
+ if (negative_response && oqname == NULL)
+ fctx->attributes |= FCTX_ATTR_WANTNCACHE;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+answer_response(fetchctx_t *fctx) {
+ isc_result_t result;
+ dns_message_t *message;
+ dns_name_t *name, *qname, tname;
+ dns_rdataset_t *rdataset;
+ isc_boolean_t done, external, chaining, aa, found, want_chaining;
+ isc_boolean_t have_answer, found_cname, found_type, wanted_chaining;
+ unsigned int aflag;
+ dns_rdatatype_t type;
+ dns_fixedname_t dname, fqname;
+
+ FCTXTRACE("answer_response");
+
+ message = fctx->rmessage;
+
+ /*
+ * Examine the answer section, marking those rdatasets which are
+ * part of the answer and should be cached.
+ */
+
+ done = ISC_FALSE;
+ found_cname = ISC_FALSE;
+ found_type = ISC_FALSE;
+ chaining = ISC_FALSE;
+ have_answer = ISC_FALSE;
+ want_chaining = ISC_FALSE;
+ if ((message->flags & DNS_MESSAGEFLAG_AA) != 0)
+ aa = ISC_TRUE;
+ else
+ aa = ISC_FALSE;
+ qname = &fctx->name;
+ type = fctx->type;
+ result = dns_message_firstname(message, DNS_SECTION_ANSWER);
+ while (!done && result == ISC_R_SUCCESS) {
+ name = NULL;
+ dns_message_currentname(message, DNS_SECTION_ANSWER, &name);
+ external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain));
+ if (dns_name_equal(name, qname)) {
+ wanted_chaining = ISC_FALSE;
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link)) {
+ found = ISC_FALSE;
+ want_chaining = ISC_FALSE;
+ aflag = 0;
+ if (rdataset->type == dns_rdatatype_nsec3) {
+ /*
+ * NSEC3 records are not allowed to
+ * appear in the answer section.
+ */
+ return (DNS_R_FORMERR);
+ }
+ if (rdataset->type == type && !found_cname) {
+ /*
+ * We've found an ordinary answer.
+ */
+ found = ISC_TRUE;
+ found_type = ISC_TRUE;
+ done = ISC_TRUE;
+ aflag = DNS_RDATASETATTR_ANSWER;
+ } else if (type == dns_rdatatype_any) {
+ /*
+ * We've found an answer matching
+ * an ANY query. There may be
+ * more.
+ */
+ found = ISC_TRUE;
+ aflag = DNS_RDATASETATTR_ANSWER;
+ } else if (rdataset->type == dns_rdatatype_rrsig
+ && rdataset->covers == type
+ && !found_cname) {
+ /*
+ * We've found a signature that
+ * covers the type we're looking for.
+ */
+ found = ISC_TRUE;
+ found_type = ISC_TRUE;
+ aflag = DNS_RDATASETATTR_ANSWERSIG;
+ } else if (rdataset->type ==
+ dns_rdatatype_cname
+ && !found_type) {
+ /*
+ * We're looking for something else,
+ * but we found a CNAME.
+ *
+ * Getting a CNAME response for some
+ * query types is an error.
+ */
+ if (type == dns_rdatatype_rrsig ||
+ type == dns_rdatatype_dnskey ||
+ type == dns_rdatatype_nsec)
+ return (DNS_R_FORMERR);
+ found = ISC_TRUE;
+ found_cname = ISC_TRUE;
+ want_chaining = ISC_TRUE;
+ aflag = DNS_RDATASETATTR_ANSWER;
+ result = cname_target(rdataset,
+ &tname);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ } else if (rdataset->type == dns_rdatatype_rrsig
+ && rdataset->covers ==
+ dns_rdatatype_cname
+ && !found_type) {
+ /*
+ * We're looking for something else,
+ * but we found a SIG CNAME.
+ */
+ found = ISC_TRUE;
+ found_cname = ISC_TRUE;
+ aflag = DNS_RDATASETATTR_ANSWERSIG;
+ }
+
+ if (found) {
+ /*
+ * We've found an answer to our
+ * question.
+ */
+ name->attributes |=
+ DNS_NAMEATTR_CACHE;
+ rdataset->attributes |=
+ DNS_RDATASETATTR_CACHE;
+ rdataset->trust = dns_trust_answer;
+ if (!chaining) {
+ /*
+ * This data is "the" answer
+ * to our question only if
+ * we're not chaining (i.e.
+ * if we haven't followed
+ * a CNAME or DNAME).
+ */
+ INSIST(!external);
+ if (aflag ==
+ DNS_RDATASETATTR_ANSWER)
+ have_answer = ISC_TRUE;
+ name->attributes |=
+ DNS_NAMEATTR_ANSWER;
+ rdataset->attributes |= aflag;
+ if (aa)
+ rdataset->trust =
+ dns_trust_authanswer;
+ } else if (external) {
+ /*
+ * This data is outside of
+ * our query domain, and
+ * may only be cached if it
+ * comes from a secure zone
+ * and validates.
+ */
+ rdataset->attributes |=
+ DNS_RDATASETATTR_EXTERNAL;
+ }
+
+ /*
+ * Mark any additional data related
+ * to this rdataset.
+ */
+ (void)dns_rdataset_additionaldata(
+ rdataset,
+ check_related,
+ fctx);
+
+ /*
+ * CNAME chaining.
+ */
+ if (want_chaining) {
+ wanted_chaining = ISC_TRUE;
+ name->attributes |=
+ DNS_NAMEATTR_CHAINING;
+ rdataset->attributes |=
+ DNS_RDATASETATTR_CHAINING;
+ qname = &tname;
+ }
+ }
+ /*
+ * We could add an "else" clause here and
+ * log that we're ignoring this rdataset.
+ */
+ }
+ /*
+ * If wanted_chaining is true, we've done
+ * some chaining as the result of processing
+ * this node, and thus we need to set
+ * chaining to true.
+ *
+ * We don't set chaining inside of the
+ * rdataset loop because doing that would
+ * cause us to ignore the signatures of
+ * CNAMEs.
+ */
+ if (wanted_chaining)
+ chaining = ISC_TRUE;
+ } else {
+ /*
+ * Look for a DNAME (or its SIG). Anything else is
+ * ignored.
+ */
+ wanted_chaining = ISC_FALSE;
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link)) {
+ isc_boolean_t found_dname = ISC_FALSE;
+ found = ISC_FALSE;
+ aflag = 0;
+ if (rdataset->type == dns_rdatatype_dname) {
+ /*
+ * We're looking for something else,
+ * but we found a DNAME.
+ *
+ * If we're not chaining, then the
+ * DNAME should not be external.
+ */
+ if (!chaining && external)
+ return (DNS_R_FORMERR);
+ found = ISC_TRUE;
+ want_chaining = ISC_TRUE;
+ aflag = DNS_RDATASETATTR_ANSWER;
+ result = dname_target(rdataset,
+ qname, name,
+ &dname);
+ if (result == ISC_R_NOSPACE) {
+ /*
+ * We can't construct the
+ * DNAME target. Do not
+ * try to continue.
+ */
+ want_chaining = ISC_FALSE;
+ } else if (result != ISC_R_SUCCESS)
+ return (result);
+ else
+ found_dname = ISC_TRUE;
+ } else if (rdataset->type == dns_rdatatype_rrsig
+ && rdataset->covers ==
+ dns_rdatatype_dname) {
+ /*
+ * We've found a signature that
+ * covers the DNAME.
+ */
+ found = ISC_TRUE;
+ aflag = DNS_RDATASETATTR_ANSWERSIG;
+ }
+
+ if (found) {
+ /*
+ * We've found an answer to our
+ * question.
+ */
+ name->attributes |=
+ DNS_NAMEATTR_CACHE;
+ rdataset->attributes |=
+ DNS_RDATASETATTR_CACHE;
+ rdataset->trust = dns_trust_answer;
+ if (!chaining) {
+ /*
+ * This data is "the" answer
+ * to our question only if
+ * we're not chaining.
+ */
+ INSIST(!external);
+ if (aflag ==
+ DNS_RDATASETATTR_ANSWER)
+ have_answer = ISC_TRUE;
+ name->attributes |=
+ DNS_NAMEATTR_ANSWER;
+ rdataset->attributes |= aflag;
+ if (aa)
+ rdataset->trust =
+ dns_trust_authanswer;
+ } else if (external) {
+ rdataset->attributes |=
+ DNS_RDATASETATTR_EXTERNAL;
+ }
+
+ /*
+ * DNAME chaining.
+ */
+ if (found_dname) {
+ /*
+ * Copy the the dname into the
+ * qname fixed name.
+ *
+ * Although we check for
+ * failure of the copy
+ * operation, in practice it
+ * should never fail since
+ * we already know that the
+ * result fits in a fixedname.
+ */
+ dns_fixedname_init(&fqname);
+ result = dns_name_copy(
+ dns_fixedname_name(&dname),
+ dns_fixedname_name(&fqname),
+ NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ wanted_chaining = ISC_TRUE;
+ name->attributes |=
+ DNS_NAMEATTR_CHAINING;
+ rdataset->attributes |=
+ DNS_RDATASETATTR_CHAINING;
+ qname = dns_fixedname_name(
+ &fqname);
+ }
+ }
+ }
+ if (wanted_chaining)
+ chaining = ISC_TRUE;
+ }
+ result = dns_message_nextname(message, DNS_SECTION_ANSWER);
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * We should have found an answer.
+ */
+ if (!have_answer)
+ return (DNS_R_FORMERR);
+
+ /*
+ * This response is now potentially cacheable.
+ */
+ fctx->attributes |= FCTX_ATTR_WANTCACHE;
+
+ /*
+ * Did chaining end before we got the final answer?
+ */
+ if (chaining) {
+ /*
+ * Yes. This may be a negative reply, so hand off
+ * authority section processing to the noanswer code.
+ * If it isn't a noanswer response, no harm will be
+ * done.
+ */
+ return (noanswer_response(fctx, qname, ISC_FALSE));
+ }
+
+ /*
+ * We didn't end with an incomplete chain, so the rcode should be
+ * "no error".
+ */
+ if (message->rcode != dns_rcode_noerror)
+ return (DNS_R_FORMERR);
+
+ /*
+ * Examine the authority section (if there is one).
+ *
+ * We expect there to be only one owner name for all the rdatasets
+ * in this section, and we expect that it is not external.
+ */
+ done = ISC_FALSE;
+ result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
+ while (!done && result == ISC_R_SUCCESS) {
+ name = NULL;
+ dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
+ external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain));
+ if (!external) {
+ /*
+ * We expect to find NS or SIG NS rdatasets, and
+ * nothing else.
+ */
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link)) {
+ if (rdataset->type == dns_rdatatype_ns ||
+ (rdataset->type == dns_rdatatype_rrsig &&
+ rdataset->covers == dns_rdatatype_ns)) {
+ name->attributes |=
+ DNS_NAMEATTR_CACHE;
+ rdataset->attributes |=
+ DNS_RDATASETATTR_CACHE;
+ if (aa && !chaining)
+ rdataset->trust =
+ dns_trust_authauthority;
+ else
+ rdataset->trust =
+ dns_trust_additional;
+
+ /*
+ * Mark any additional data related
+ * to this rdataset.
+ */
+ (void)dns_rdataset_additionaldata(
+ rdataset,
+ check_related,
+ fctx);
+ done = ISC_TRUE;
+ }
+ }
+ }
+ result = dns_message_nextname(message, DNS_SECTION_AUTHORITY);
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+
+ return (result);
+}
+
+static void
+resume_dslookup(isc_task_t *task, isc_event_t *event) {
+ dns_fetchevent_t *fevent;
+ dns_resolver_t *res;
+ fetchctx_t *fctx;
+ isc_result_t result;
+ isc_boolean_t bucket_empty = ISC_FALSE;
+ isc_boolean_t locked = ISC_FALSE;
+ unsigned int bucketnum;
+ dns_rdataset_t nameservers;
+ dns_fixedname_t fixed;
+ dns_name_t *domain;
+
+ REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE);
+ fevent = (dns_fetchevent_t *)event;
+ fctx = event->ev_arg;
+ REQUIRE(VALID_FCTX(fctx));
+ res = fctx->res;
+
+ UNUSED(task);
+ FCTXTRACE("resume_dslookup");
+
+ if (fevent->node != NULL)
+ dns_db_detachnode(fevent->db, &fevent->node);
+ if (fevent->db != NULL)
+ dns_db_detach(&fevent->db);
+
+ dns_rdataset_init(&nameservers);
+
+ bucketnum = fctx->bucketnum;
+ if (fevent->result == ISC_R_CANCELED) {
+ dns_resolver_destroyfetch(&fctx->nsfetch);
+ fctx_done(fctx, ISC_R_CANCELED);
+ } else if (fevent->result == ISC_R_SUCCESS) {
+
+ FCTXTRACE("resuming DS lookup");
+
+ dns_resolver_destroyfetch(&fctx->nsfetch);
+ if (dns_rdataset_isassociated(&fctx->nameservers))
+ dns_rdataset_disassociate(&fctx->nameservers);
+ dns_rdataset_clone(fevent->rdataset, &fctx->nameservers);
+ dns_name_free(&fctx->domain,
+ fctx->res->buckets[bucketnum].mctx);
+ dns_name_init(&fctx->domain, NULL);
+ result = dns_name_dup(&fctx->nsname,
+ fctx->res->buckets[bucketnum].mctx,
+ &fctx->domain);
+ if (result != ISC_R_SUCCESS) {
+ fctx_done(fctx, DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ /*
+ * Try again.
+ */
+ fctx_try(fctx, ISC_TRUE);
+ } else {
+ unsigned int n;
+ dns_rdataset_t *nsrdataset = NULL;
+
+ /*
+ * Retrieve state from fctx->nsfetch before we destroy it.
+ */
+ dns_fixedname_init(&fixed);
+ domain = dns_fixedname_name(&fixed);
+ dns_name_copy(&fctx->nsfetch->private->domain, domain, NULL);
+ if (dns_name_equal(&fctx->nsname, domain)) {
+ fctx_done(fctx, DNS_R_SERVFAIL);
+ dns_resolver_destroyfetch(&fctx->nsfetch);
+ goto cleanup;
+ }
+ if (dns_rdataset_isassociated(
+ &fctx->nsfetch->private->nameservers)) {
+ dns_rdataset_clone(
+ &fctx->nsfetch->private->nameservers,
+ &nameservers);
+ nsrdataset = &nameservers;
+ } else
+ domain = NULL;
+ dns_resolver_destroyfetch(&fctx->nsfetch);
+ n = dns_name_countlabels(&fctx->nsname);
+ dns_name_getlabelsequence(&fctx->nsname, 1, n - 1,
+ &fctx->nsname);
+
+ if (dns_rdataset_isassociated(fevent->rdataset))
+ dns_rdataset_disassociate(fevent->rdataset);
+ FCTXTRACE("continuing to look for parent's NS records");
+ result = dns_resolver_createfetch(fctx->res, &fctx->nsname,
+ dns_rdatatype_ns, domain,
+ nsrdataset, NULL, 0, task,
+ resume_dslookup, fctx,
+ &fctx->nsrrset, NULL,
+ &fctx->nsfetch);
+ if (result != ISC_R_SUCCESS)
+ fctx_done(fctx, result);
+ else {
+ LOCK(&res->buckets[bucketnum].lock);
+ locked = ISC_TRUE;
+ fctx->references++;
+ }
+ }
+
+ cleanup:
+ if (dns_rdataset_isassociated(&nameservers))
+ dns_rdataset_disassociate(&nameservers);
+ if (dns_rdataset_isassociated(fevent->rdataset))
+ dns_rdataset_disassociate(fevent->rdataset);
+ INSIST(fevent->sigrdataset == NULL);
+ isc_event_free(&event);
+ if (!locked)
+ LOCK(&res->buckets[bucketnum].lock);
+ fctx->references--;
+ if (fctx->references == 0)
+ bucket_empty = fctx_destroy(fctx);
+ UNLOCK(&res->buckets[bucketnum].lock);
+ if (bucket_empty)
+ empty_bucket(res);
+}
+
+static inline void
+checknamessection(dns_message_t *message, dns_section_t section) {
+ isc_result_t result;
+ dns_name_t *name;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_t *rdataset;
+
+ for (result = dns_message_firstname(message, section);
+ result == ISC_R_SUCCESS;
+ result = dns_message_nextname(message, section))
+ {
+ name = NULL;
+ dns_message_currentname(message, section, &name);
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link)) {
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset)) {
+ dns_rdataset_current(rdataset, &rdata);
+ if (!dns_rdata_checkowner(name, rdata.rdclass,
+ rdata.type,
+ ISC_FALSE) ||
+ !dns_rdata_checknames(&rdata, name, NULL))
+ {
+ rdataset->attributes |=
+ DNS_RDATASETATTR_CHECKNAMES;
+ }
+ dns_rdata_reset(&rdata);
+ }
+ }
+ }
+}
+
+static void
+checknames(dns_message_t *message) {
+
+ checknamessection(message, DNS_SECTION_ANSWER);
+ checknamessection(message, DNS_SECTION_AUTHORITY);
+ checknamessection(message, DNS_SECTION_ADDITIONAL);
+}
+
+/*
+ * Log server NSID at log level 'level'
+ */
+static isc_result_t
+log_nsid(dns_rdataset_t *opt, resquery_t *query, int level, isc_mem_t *mctx)
+{
+ static const char hex[17] = "0123456789abcdef";
+ char addrbuf[ISC_SOCKADDR_FORMATSIZE];
+ isc_uint16_t optcode, nsid_len, buflen, i;
+ isc_result_t result;
+ isc_buffer_t nsidbuf;
+ dns_rdata_t rdata;
+ unsigned char *p, *buf, *nsid;
+
+ /* Extract rdata from OPT rdataset */
+ result = dns_rdataset_first(opt);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_FAILURE);
+
+ dns_rdata_init(&rdata);
+ dns_rdataset_current(opt, &rdata);
+ if (rdata.length < 4)
+ return (ISC_R_FAILURE);
+
+ /* Check for NSID */
+ isc_buffer_init(&nsidbuf, rdata.data, rdata.length);
+ isc_buffer_add(&nsidbuf, rdata.length);
+ optcode = isc_buffer_getuint16(&nsidbuf);
+ nsid_len = isc_buffer_getuint16(&nsidbuf);
+ if (optcode != DNS_OPT_NSID || nsid_len == 0)
+ return (ISC_R_FAILURE);
+
+ /* Allocate buffer for storing hex version of the NSID */
+ buflen = nsid_len * 2 + 1;
+ buf = isc_mem_get(mctx, buflen);
+ if (buf == NULL)
+ return (ISC_R_NOSPACE);
+
+ /* Convert to hex */
+ p = buf;
+ nsid = rdata.data + 4;
+ for (i = 0; i < nsid_len; i++) {
+ *p++ = hex[(nsid[0] >> 4) & 0xf];
+ *p++ = hex[nsid[0] & 0xf];
+ nsid++;
+ }
+ *p = '\0';
+
+ isc_sockaddr_format(&query->addrinfo->sockaddr, addrbuf,
+ sizeof(addrbuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+ DNS_LOGMODULE_RESOLVER, level,
+ "received NSID '%s' from %s", buf, addrbuf);
+
+ /* Clean up */
+ isc_mem_put(mctx, buf, buflen);
+ return (ISC_R_SUCCESS);
+}
+
+static void
+log_packet(dns_message_t *message, int level, isc_mem_t *mctx) {
+ isc_buffer_t buffer;
+ char *buf = NULL;
+ int len = 1024;
+ isc_result_t result;
+
+ if (! isc_log_wouldlog(dns_lctx, level))
+ return;
+
+ /*
+ * Note that these are multiline debug messages. We want a newline
+ * to appear in the log after each message.
+ */
+
+ do {
+ buf = isc_mem_get(mctx, len);
+ if (buf == NULL)
+ break;
+ isc_buffer_init(&buffer, buf, len);
+ result = dns_message_totext(message, &dns_master_style_debug,
+ 0, &buffer);
+ if (result == ISC_R_NOSPACE) {
+ isc_mem_put(mctx, buf, len);
+ len += 1024;
+ } else if (result == ISC_R_SUCCESS)
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+ DNS_LOGMODULE_RESOLVER, level,
+ "received packet:\n%.*s",
+ (int)isc_buffer_usedlength(&buffer),
+ buf);
+ } while (result == ISC_R_NOSPACE);
+
+ if (buf != NULL)
+ isc_mem_put(mctx, buf, len);
+}
+
+static void
+resquery_response(isc_task_t *task, isc_event_t *event) {
+ isc_result_t result = ISC_R_SUCCESS;
+ resquery_t *query = event->ev_arg;
+ dns_dispatchevent_t *devent = (dns_dispatchevent_t *)event;
+ isc_boolean_t keep_trying, get_nameservers, resend;
+ isc_boolean_t truncated;
+ dns_message_t *message;
+ dns_rdataset_t *opt;
+ fetchctx_t *fctx;
+ dns_name_t *fname;
+ dns_fixedname_t foundname;
+ isc_stdtime_t now;
+ isc_time_t tnow, *finish;
+ dns_adbaddrinfo_t *addrinfo;
+ unsigned int options;
+ unsigned int findoptions;
+ isc_result_t broken_server;
+
+ REQUIRE(VALID_QUERY(query));
+ fctx = query->fctx;
+ options = query->options;
+ REQUIRE(VALID_FCTX(fctx));
+ REQUIRE(event->ev_type == DNS_EVENT_DISPATCH);
+
+ QTRACE("response");
+
+ if (isc_sockaddr_pf(&query->addrinfo->sockaddr) == PF_INET)
+ inc_stats(fctx->res, dns_resstatscounter_responsev4);
+ else
+ inc_stats(fctx->res, dns_resstatscounter_responsev6);
+
+ (void)isc_timer_touch(fctx->timer);
+
+ keep_trying = ISC_FALSE;
+ broken_server = ISC_R_SUCCESS;
+ get_nameservers = ISC_FALSE;
+ resend = ISC_FALSE;
+ truncated = ISC_FALSE;
+ finish = NULL;
+
+ if (fctx->res->exiting) {
+ result = ISC_R_SHUTTINGDOWN;
+ goto done;
+ }
+
+ fctx->timeouts = 0;
+
+ /*
+ * XXXRTH We should really get the current time just once. We
+ * need a routine to convert from an isc_time_t to an
+ * isc_stdtime_t.
+ */
+ TIME_NOW(&tnow);
+ finish = &tnow;
+ isc_stdtime_get(&now);
+
+ /*
+ * Did the dispatcher have a problem?
+ */
+ if (devent->result != ISC_R_SUCCESS) {
+ if (devent->result == ISC_R_EOF &&
+ (query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
+ /*
+ * The problem might be that they
+ * don't understand EDNS0. Turn it
+ * off and try again.
+ */
+ options |= DNS_FETCHOPT_NOEDNS0;
+ resend = ISC_TRUE;
+ /*
+ * Remember that they don't like EDNS0.
+ */
+ dns_adb_changeflags(fctx->adb,
+ query->addrinfo,
+ DNS_FETCHOPT_NOEDNS0,
+ DNS_FETCHOPT_NOEDNS0);
+ } else {
+ /*
+ * There's no hope for this query.
+ */
+ keep_trying = ISC_TRUE;
+
+ /*
+ * If this is a network error on an exclusive query
+ * socket, mark the server as bad so that we won't try
+ * it for this fetch again.
+ */
+ if (query->exclusivesocket &&
+ (devent->result == ISC_R_HOSTUNREACH ||
+ devent->result == ISC_R_NETUNREACH ||
+ devent->result == ISC_R_CONNREFUSED ||
+ devent->result == ISC_R_CANCELED)) {
+ broken_server = devent->result;
+ }
+ }
+ goto done;
+ }
+
+ message = fctx->rmessage;
+
+ if (query->tsig != NULL) {
+ result = dns_message_setquerytsig(message, query->tsig);
+ if (result != ISC_R_SUCCESS)
+ goto done;
+ }
+
+ if (query->tsigkey) {
+ result = dns_message_settsigkey(message, query->tsigkey);
+ if (result != ISC_R_SUCCESS)
+ goto done;
+ }
+
+ result = dns_message_parse(message, &devent->buffer, 0);
+ if (result != ISC_R_SUCCESS) {
+ switch (result) {
+ case ISC_R_UNEXPECTEDEND:
+ if (!message->question_ok ||
+ (message->flags & DNS_MESSAGEFLAG_TC) == 0 ||
+ (options & DNS_FETCHOPT_TCP) != 0) {
+ /*
+ * Either the message ended prematurely,
+ * and/or wasn't marked as being truncated,
+ * and/or this is a response to a query we
+ * sent over TCP. In all of these cases,
+ * something is wrong with the remote
+ * server and we don't want to retry using
+ * TCP.
+ */
+ if ((query->options & DNS_FETCHOPT_NOEDNS0)
+ == 0) {
+ /*
+ * The problem might be that they
+ * don't understand EDNS0. Turn it
+ * off and try again.
+ */
+ options |= DNS_FETCHOPT_NOEDNS0;
+ resend = ISC_TRUE;
+ /*
+ * Remember that they don't like EDNS0.
+ */
+ dns_adb_changeflags(
+ fctx->adb,
+ query->addrinfo,
+ DNS_FETCHOPT_NOEDNS0,
+ DNS_FETCHOPT_NOEDNS0);
+ inc_stats(fctx->res,
+ dns_resstatscounter_edns0fail);
+ } else {
+ broken_server = result;
+ keep_trying = ISC_TRUE;
+ }
+ goto done;
+ }
+ /*
+ * We defer retrying via TCP for a bit so we can
+ * check out this message further.
+ */
+ truncated = ISC_TRUE;
+ break;
+ case DNS_R_FORMERR:
+ if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
+ /*
+ * The problem might be that they
+ * don't understand EDNS0. Turn it
+ * off and try again.
+ */
+ options |= DNS_FETCHOPT_NOEDNS0;
+ resend = ISC_TRUE;
+ /*
+ * Remember that they don't like EDNS0.
+ */
+ dns_adb_changeflags(fctx->adb,
+ query->addrinfo,
+ DNS_FETCHOPT_NOEDNS0,
+ DNS_FETCHOPT_NOEDNS0);
+ inc_stats(fctx->res,
+ dns_resstatscounter_edns0fail);
+ } else {
+ broken_server = DNS_R_UNEXPECTEDRCODE;
+ keep_trying = ISC_TRUE;
+ }
+ goto done;
+ default:
+ /*
+ * Something bad has happened.
+ */
+ goto done;
+ }
+ }
+
+
+ /*
+ * Log the incoming packet.
+ */
+ log_packet(message, ISC_LOG_DEBUG(10), fctx->res->mctx);
+
+ /*
+ * Did we request NSID? If so, and if the response contains
+ * NSID data, log it at INFO level.
+ */
+ opt = dns_message_getopt(message);
+ if (opt != NULL && (query->options & DNS_FETCHOPT_WANTNSID) != 0)
+ log_nsid(opt, query, ISC_LOG_INFO, fctx->res->mctx);
+
+ /*
+ * If the message is signed, check the signature. If not, this
+ * returns success anyway.
+ */
+ result = dns_message_checksig(message, fctx->res->view);
+ if (result != ISC_R_SUCCESS)
+ goto done;
+
+ /*
+ * The dispatcher should ensure we only get responses with QR set.
+ */
+ INSIST((message->flags & DNS_MESSAGEFLAG_QR) != 0);
+ /*
+ * INSIST() that the message comes from the place we sent it to,
+ * since the dispatch code should ensure this.
+ *
+ * INSIST() that the message id is correct (this should also be
+ * ensured by the dispatch code).
+ */
+
+
+ /*
+ * Deal with truncated responses by retrying using TCP.
+ */
+ if ((message->flags & DNS_MESSAGEFLAG_TC) != 0)
+ truncated = ISC_TRUE;
+
+ if (truncated) {
+ inc_stats(fctx->res, dns_resstatscounter_truncated);
+ if ((options & DNS_FETCHOPT_TCP) != 0) {
+ broken_server = DNS_R_TRUNCATEDTCP;
+ keep_trying = ISC_TRUE;
+ } else {
+ options |= DNS_FETCHOPT_TCP;
+ resend = ISC_TRUE;
+ }
+ goto done;
+ }
+
+ /*
+ * Is it a query response?
+ */
+ if (message->opcode != dns_opcode_query) {
+ /* XXXRTH Log */
+ broken_server = DNS_R_UNEXPECTEDOPCODE;
+ keep_trying = ISC_TRUE;
+ goto done;
+ }
+
+ /*
+ * Update statistics about erroneous responses.
+ */
+ if (message->rcode != dns_rcode_noerror) {
+ switch (message->rcode) {
+ case dns_rcode_nxdomain:
+ inc_stats(fctx->res, dns_resstatscounter_nxdomain);
+ break;
+ case dns_rcode_servfail:
+ inc_stats(fctx->res, dns_resstatscounter_servfail);
+ break;
+ case dns_rcode_formerr:
+ inc_stats(fctx->res, dns_resstatscounter_formerr);
+ break;
+ default:
+ inc_stats(fctx->res, dns_resstatscounter_othererror);
+ break;
+ }
+ }
+
+ /*
+ * Is the remote server broken, or does it dislike us?
+ */
+ if (message->rcode != dns_rcode_noerror &&
+ message->rcode != dns_rcode_nxdomain) {
+ if (((message->rcode == dns_rcode_formerr ||
+ message->rcode == dns_rcode_notimp) ||
+ (message->rcode == dns_rcode_servfail &&
+ dns_message_getopt(message) == NULL)) &&
+ (query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
+ /*
+ * It's very likely they don't like EDNS0.
+ * If the response code is SERVFAIL, also check if the
+ * response contains an OPT RR and don't cache the
+ * failure since it can be returned for various other
+ * reasons.
+ *
+ * XXXRTH We should check if the question
+ * we're asking requires EDNS0, and
+ * if so, we should bail out.
+ */
+ options |= DNS_FETCHOPT_NOEDNS0;
+ resend = ISC_TRUE;
+ /*
+ * Remember that they don't like EDNS0.
+ */
+ if (message->rcode != dns_rcode_servfail)
+ dns_adb_changeflags(fctx->adb, query->addrinfo,
+ DNS_FETCHOPT_NOEDNS0,
+ DNS_FETCHOPT_NOEDNS0);
+ inc_stats(fctx->res, dns_resstatscounter_edns0fail);
+ } else if (message->rcode == dns_rcode_formerr) {
+ if (ISFORWARDER(query->addrinfo)) {
+ /*
+ * This forwarder doesn't understand us,
+ * but other forwarders might. Keep trying.
+ */
+ broken_server = DNS_R_REMOTEFORMERR;
+ keep_trying = ISC_TRUE;
+ } else {
+ /*
+ * The server doesn't understand us. Since
+ * all servers for a zone need similar
+ * capabilities, we assume that we will get
+ * FORMERR from all servers, and thus we
+ * cannot make any more progress with this
+ * fetch.
+ */
+ result = DNS_R_FORMERR;
+ }
+ } else if (message->rcode == dns_rcode_yxdomain) {
+ /*
+ * DNAME mapping failed because the new name
+ * was too long. There's no chance of success
+ * for this fetch.
+ */
+ result = DNS_R_YXDOMAIN;
+ } else if (message->rcode == dns_rcode_badvers) {
+ unsigned int flags, mask;
+ unsigned int version;
+
+ resend = ISC_TRUE;
+ version = (opt->ttl >> 16) & 0xff;
+ flags = (version << DNS_FETCHOPT_EDNSVERSIONSHIFT) |
+ DNS_FETCHOPT_EDNSVERSIONSET;
+ mask = DNS_FETCHOPT_EDNSVERSIONMASK |
+ DNS_FETCHOPT_EDNSVERSIONSET;
+ switch (version) {
+ case 0:
+ dns_adb_changeflags(fctx->adb, query->addrinfo,
+ flags, mask);
+ break;
+ default:
+ broken_server = DNS_R_BADVERS;
+ keep_trying = ISC_TRUE;
+ break;
+ }
+ } else {
+ /*
+ * XXXRTH log.
+ */
+ broken_server = DNS_R_UNEXPECTEDRCODE;
+ INSIST(broken_server != ISC_R_SUCCESS);
+ keep_trying = ISC_TRUE;
+ }
+ goto done;
+ }
+
+ /*
+ * Is the question the same as the one we asked?
+ */
+ result = same_question(fctx);
+ if (result != ISC_R_SUCCESS) {
+ /* XXXRTH Log */
+ if (result == DNS_R_FORMERR)
+ keep_trying = ISC_TRUE;
+ goto done;
+ }
+
+ /*
+ * Is the server lame?
+ */
+ if (fctx->res->lame_ttl != 0 && !ISFORWARDER(query->addrinfo) &&
+ is_lame(fctx)) {
+ inc_stats(fctx->res, dns_resstatscounter_lame);
+ log_lame(fctx, query->addrinfo);
+ result = dns_adb_marklame(fctx->adb, query->addrinfo,
+ &fctx->name, fctx->type,
+ now + fctx->res->lame_ttl);
+ if (result != ISC_R_SUCCESS)
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+ DNS_LOGMODULE_RESOLVER, ISC_LOG_ERROR,
+ "could not mark server as lame: %s",
+ isc_result_totext(result));
+ broken_server = DNS_R_LAME;
+ keep_trying = ISC_TRUE;
+ goto done;
+ }
+
+ /*
+ * Enforce delegations only zones like NET and COM.
+ */
+ if (!ISFORWARDER(query->addrinfo) &&
+ dns_view_isdelegationonly(fctx->res->view, &fctx->domain) &&
+ !dns_name_equal(&fctx->domain, &fctx->name) &&
+ fix_mustbedelegationornxdomain(message, fctx)) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char domainbuf[DNS_NAME_FORMATSIZE];
+ char addrbuf[ISC_SOCKADDR_FORMATSIZE];
+ char classbuf[64];
+ char typebuf[64];
+
+ dns_name_format(&fctx->name, namebuf, sizeof(namebuf));
+ dns_name_format(&fctx->domain, domainbuf, sizeof(domainbuf));
+ dns_rdatatype_format(fctx->type, typebuf, sizeof(typebuf));
+ dns_rdataclass_format(fctx->res->rdclass, classbuf,
+ sizeof(classbuf));
+ isc_sockaddr_format(&query->addrinfo->sockaddr, addrbuf,
+ sizeof(addrbuf));
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DELEGATION_ONLY,
+ DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE,
+ "enforced delegation-only for '%s' (%s/%s/%s) "
+ "from %s",
+ domainbuf, namebuf, typebuf, classbuf, addrbuf);
+ }
+
+ if ((fctx->res->options & DNS_RESOLVER_CHECKNAMES) != 0)
+ checknames(message);
+
+ /*
+ * Clear cache bits.
+ */
+ fctx->attributes &= ~(FCTX_ATTR_WANTNCACHE | FCTX_ATTR_WANTCACHE);
+
+ /*
+ * Did we get any answers?
+ */
+ if (message->counts[DNS_SECTION_ANSWER] > 0 &&
+ (message->rcode == dns_rcode_noerror ||
+ message->rcode == dns_rcode_nxdomain)) {
+ /*
+ * We've got answers. However, if we sent
+ * a BIND 8 server an NS query, it may have
+ * incorrectly responded with a non-authoritative
+ * answer instead of a referral. Since this
+ * answer lacks the SIGs necessary to do DNSSEC
+ * validation, we must invoke the following special
+ * kludge to treat it as a referral.
+ */
+ if (fctx->type == dns_rdatatype_ns &&
+ (message->flags & DNS_MESSAGEFLAG_AA) == 0 &&
+ !ISFORWARDER(query->addrinfo))
+ {
+ result = noanswer_response(fctx, NULL, ISC_TRUE);
+ if (result != DNS_R_DELEGATION) {
+ /*
+ * The answer section must have contained
+ * something other than the NS records
+ * we asked for. Since AA is not set
+ * and the server is not a forwarder,
+ * it is technically lame and it's easier
+ * to treat it as such than to figure out
+ * some more elaborate course of action.
+ */
+ broken_server = DNS_R_LAME;
+ keep_trying = ISC_TRUE;
+ goto done;
+ }
+ goto force_referral;
+ }
+ result = answer_response(fctx);
+ if (result != ISC_R_SUCCESS) {
+ if (result == DNS_R_FORMERR)
+ keep_trying = ISC_TRUE;
+ goto done;
+ }
+ } else if (message->counts[DNS_SECTION_AUTHORITY] > 0 ||
+ message->rcode == dns_rcode_noerror ||
+ message->rcode == dns_rcode_nxdomain) {
+ /*
+ * NXDOMAIN, NXRDATASET, or referral.
+ */
+ result = noanswer_response(fctx, NULL, ISC_FALSE);
+ if (result == DNS_R_CHASEDSSERVERS) {
+ } else if (result == DNS_R_DELEGATION) {
+ force_referral:
+ /*
+ * We don't have the answer, but we know a better
+ * place to look.
+ */
+ get_nameservers = ISC_TRUE;
+ keep_trying = ISC_TRUE;
+ /*
+ * We have a new set of name servers, and it
+ * has not experienced any restarts yet.
+ */
+ fctx->restarts = 0;
+ result = ISC_R_SUCCESS;
+ } else if (result != ISC_R_SUCCESS) {
+ /*
+ * Something has gone wrong.
+ */
+ if (result == DNS_R_FORMERR)
+ keep_trying = ISC_TRUE;
+ goto done;
+ }
+ } else {
+ /*
+ * The server is insane.
+ */
+ /* XXXRTH Log */
+ broken_server = DNS_R_UNEXPECTEDRCODE;
+ keep_trying = ISC_TRUE;
+ goto done;
+ }
+
+ /*
+ * Follow additional section data chains.
+ */
+ chase_additional(fctx);
+
+ /*
+ * Cache the cacheable parts of the message. This may also cause
+ * work to be queued to the DNSSEC validator.
+ */
+ if (WANTCACHE(fctx)) {
+ result = cache_message(fctx, query->addrinfo, now);
+ if (result != ISC_R_SUCCESS)
+ goto done;
+ }
+
+ /*
+ * Ncache the negatively cacheable parts of the message. This may
+ * also cause work to be queued to the DNSSEC validator.
+ */
+ if (WANTNCACHE(fctx)) {
+ dns_rdatatype_t covers;
+ if (message->rcode == dns_rcode_nxdomain)
+ covers = dns_rdatatype_any;
+ else
+ covers = fctx->type;
+
+ /*
+ * Cache any negative cache entries in the message.
+ */
+ result = ncache_message(fctx, query->addrinfo, covers, now);
+ }
+
+ done:
+ /*
+ * Remember the query's addrinfo, in case we need to mark the
+ * server as broken.
+ */
+ addrinfo = query->addrinfo;
+
+ /*
+ * Cancel the query.
+ *
+ * XXXRTH Don't cancel the query if waiting for validation?
+ */
+ fctx_cancelquery(&query, &devent, finish, ISC_FALSE);
+
+ if (keep_trying) {
+ if (result == DNS_R_FORMERR)
+ broken_server = DNS_R_FORMERR;
+ if (broken_server != ISC_R_SUCCESS) {
+ /*
+ * Add this server to the list of bad servers for
+ * this fctx.
+ */
+ add_bad(fctx, addrinfo, broken_server);
+ }
+
+ if (get_nameservers) {
+ dns_name_t *name;
+ dns_fixedname_init(&foundname);
+ fname = dns_fixedname_name(&foundname);
+ if (result != ISC_R_SUCCESS) {
+ fctx_done(fctx, DNS_R_SERVFAIL);
+ return;
+ }
+ findoptions = 0;
+ if (dns_rdatatype_atparent(fctx->type))
+ findoptions |= DNS_DBFIND_NOEXACT;
+ if ((options & DNS_FETCHOPT_UNSHARED) == 0)
+ name = &fctx->name;
+ else
+ name = &fctx->domain;
+ result = dns_view_findzonecut(fctx->res->view,
+ name, fname,
+ now, findoptions,
+ ISC_TRUE,
+ &fctx->nameservers,
+ NULL);
+ if (result != ISC_R_SUCCESS) {
+ FCTXTRACE("couldn't find a zonecut");
+ fctx_done(fctx, DNS_R_SERVFAIL);
+ return;
+ }
+ if (!dns_name_issubdomain(fname, &fctx->domain)) {
+ /*
+ * The best nameservers are now above our
+ * QDOMAIN.
+ */
+ FCTXTRACE("nameservers now above QDOMAIN");
+ fctx_done(fctx, DNS_R_SERVFAIL);
+ return;
+ }
+ dns_name_free(&fctx->domain,
+ fctx->res->buckets[fctx->bucketnum].mctx);
+ dns_name_init(&fctx->domain, NULL);
+ result = dns_name_dup(fname,
+ fctx->res->buckets[fctx->bucketnum].mctx,
+ &fctx->domain);
+ if (result != ISC_R_SUCCESS) {
+ fctx_done(fctx, DNS_R_SERVFAIL);
+ return;
+ }
+ fctx_cancelqueries(fctx, ISC_TRUE);
+ fctx_cleanupfinds(fctx);
+ fctx_cleanupaltfinds(fctx);
+ fctx_cleanupforwaddrs(fctx);
+ fctx_cleanupaltaddrs(fctx);
+ }
+ /*
+ * Try again.
+ */
+ fctx_try(fctx, !get_nameservers);
+ } else if (resend) {
+ /*
+ * Resend (probably with changed options).
+ */
+ FCTXTRACE("resend");
+ inc_stats(fctx->res, dns_resstatscounter_retry);
+ result = fctx_query(fctx, addrinfo, options);
+ if (result != ISC_R_SUCCESS)
+ fctx_done(fctx, result);
+ } else if (result == ISC_R_SUCCESS && !HAVE_ANSWER(fctx)) {
+ /*
+ * All has gone well so far, but we are waiting for the
+ * DNSSEC validator to validate the answer.
+ */
+ FCTXTRACE("wait for validator");
+ fctx_cancelqueries(fctx, ISC_TRUE);
+ /*
+ * We must not retransmit while the validator is working;
+ * it has references to the current rmessage.
+ */
+ result = fctx_stopidletimer(fctx);
+ if (result != ISC_R_SUCCESS)
+ fctx_done(fctx, result);
+ } else if (result == DNS_R_CHASEDSSERVERS) {
+ unsigned int n;
+ add_bad(fctx, addrinfo, result);
+ fctx_cancelqueries(fctx, ISC_TRUE);
+ fctx_cleanupfinds(fctx);
+ fctx_cleanupforwaddrs(fctx);
+
+ n = dns_name_countlabels(&fctx->name);
+ dns_name_getlabelsequence(&fctx->name, 1, n - 1, &fctx->nsname);
+
+ FCTXTRACE("suspending DS lookup to find parent's NS records");
+
+ result = dns_resolver_createfetch(fctx->res, &fctx->nsname,
+ dns_rdatatype_ns,
+ NULL, NULL, NULL, 0, task,
+ resume_dslookup, fctx,
+ &fctx->nsrrset, NULL,
+ &fctx->nsfetch);
+ if (result != ISC_R_SUCCESS)
+ fctx_done(fctx, result);
+ LOCK(&fctx->res->buckets[fctx->bucketnum].lock);
+ fctx->references++;
+ UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock);
+ result = fctx_stopidletimer(fctx);
+ if (result != ISC_R_SUCCESS)
+ fctx_done(fctx, result);
+ } else {
+ /*
+ * We're done.
+ */
+ fctx_done(fctx, result);
+ }
+}
+
+
+/***
+ *** Resolver Methods
+ ***/
+
+static void
+destroy(dns_resolver_t *res) {
+ unsigned int i;
+ alternate_t *a;
+
+ REQUIRE(res->references == 0);
+ REQUIRE(!res->priming);
+ REQUIRE(res->primefetch == NULL);
+
+ RTRACE("destroy");
+
+ INSIST(res->nfctx == 0);
+
+ DESTROYLOCK(&res->primelock);
+ DESTROYLOCK(&res->nlock);
+ DESTROYLOCK(&res->lock);
+ for (i = 0; i < res->nbuckets; i++) {
+ INSIST(ISC_LIST_EMPTY(res->buckets[i].fctxs));
+ isc_task_shutdown(res->buckets[i].task);
+ isc_task_detach(&res->buckets[i].task);
+ DESTROYLOCK(&res->buckets[i].lock);
+ isc_mem_detach(&res->buckets[i].mctx);
+ }
+ isc_mem_put(res->mctx, res->buckets,
+ res->nbuckets * sizeof(fctxbucket_t));
+ if (res->dispatchv4 != NULL)
+ dns_dispatch_detach(&res->dispatchv4);
+ if (res->dispatchv6 != NULL)
+ dns_dispatch_detach(&res->dispatchv6);
+ while ((a = ISC_LIST_HEAD(res->alternates)) != NULL) {
+ ISC_LIST_UNLINK(res->alternates, a, link);
+ if (!a->isaddress)
+ dns_name_free(&a->_u._n.name, res->mctx);
+ isc_mem_put(res->mctx, a, sizeof(*a));
+ }
+ dns_resolver_reset_algorithms(res);
+ dns_resolver_resetmustbesecure(res);
+#if USE_ALGLOCK
+ isc_rwlock_destroy(&res->alglock);
+#endif
+#if USE_MBSLOCK
+ isc_rwlock_destroy(&res->mbslock);
+#endif
+ isc_timer_detach(&res->spillattimer);
+ res->magic = 0;
+ isc_mem_put(res->mctx, res, sizeof(*res));
+}
+
+static void
+send_shutdown_events(dns_resolver_t *res) {
+ isc_event_t *event, *next_event;
+ isc_task_t *etask;
+
+ /*
+ * Caller must be holding the resolver lock.
+ */
+
+ for (event = ISC_LIST_HEAD(res->whenshutdown);
+ event != NULL;
+ event = next_event) {
+ next_event = ISC_LIST_NEXT(event, ev_link);
+ ISC_LIST_UNLINK(res->whenshutdown, event, ev_link);
+ etask = event->ev_sender;
+ event->ev_sender = res;
+ isc_task_sendanddetach(&etask, &event);
+ }
+}
+
+static void
+empty_bucket(dns_resolver_t *res) {
+ RTRACE("empty_bucket");
+
+ LOCK(&res->lock);
+
+ INSIST(res->activebuckets > 0);
+ res->activebuckets--;
+ if (res->activebuckets == 0)
+ send_shutdown_events(res);
+
+ UNLOCK(&res->lock);
+}
+
+static void
+spillattimer_countdown(isc_task_t *task, isc_event_t *event) {
+ dns_resolver_t *res = event->ev_arg;
+ isc_result_t result;
+ unsigned int count;
+ isc_boolean_t logit = ISC_FALSE;
+
+ REQUIRE(VALID_RESOLVER(res));
+
+ UNUSED(task);
+
+ LOCK(&res->lock);
+ INSIST(!res->exiting);
+ if (res->spillat > res->spillatmin) {
+ res->spillat--;
+ logit = ISC_TRUE;
+ }
+ if (res->spillat <= res->spillatmin) {
+ result = isc_timer_reset(res->spillattimer,
+ isc_timertype_inactive, NULL,
+ NULL, ISC_TRUE);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ }
+ count = res->spillat;
+ UNLOCK(&res->lock);
+ if (logit)
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+ DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE,
+ "clients-per-query decreased to %u", count);
+
+ isc_event_free(&event);
+}
+
+isc_result_t
+dns_resolver_create(dns_view_t *view,
+ isc_taskmgr_t *taskmgr, unsigned int ntasks,
+ isc_socketmgr_t *socketmgr,
+ isc_timermgr_t *timermgr,
+ unsigned int options,
+ dns_dispatchmgr_t *dispatchmgr,
+ dns_dispatch_t *dispatchv4,
+ dns_dispatch_t *dispatchv6,
+ dns_resolver_t **resp)
+{
+ dns_resolver_t *res;
+ isc_result_t result = ISC_R_SUCCESS;
+ unsigned int i, buckets_created = 0;
+ isc_task_t *task = NULL;
+ char name[16];
+ unsigned dispattr;
+
+ /*
+ * Create a resolver.
+ */
+
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(ntasks > 0);
+ REQUIRE(resp != NULL && *resp == NULL);
+ REQUIRE(dispatchmgr != NULL);
+ REQUIRE(dispatchv4 != NULL || dispatchv6 != NULL);
+
+ res = isc_mem_get(view->mctx, sizeof(*res));
+ if (res == NULL)
+ return (ISC_R_NOMEMORY);
+ RTRACE("create");
+ res->mctx = view->mctx;
+ res->rdclass = view->rdclass;
+ res->socketmgr = socketmgr;
+ res->timermgr = timermgr;
+ res->taskmgr = taskmgr;
+ res->dispatchmgr = dispatchmgr;
+ res->view = view;
+ res->options = options;
+ res->lame_ttl = 0;
+ ISC_LIST_INIT(res->alternates);
+ res->udpsize = RECV_BUFFER_SIZE;
+ res->algorithms = NULL;
+ res->mustbesecure = NULL;
+ res->spillatmin = res->spillat = 10;
+ res->spillatmax = 100;
+ res->spillattimer = NULL;
+ res->zero_no_soa_ttl = ISC_FALSE;
+ res->ndisps = 0;
+ res->nextdisp = 0; /* meaningless at this point, but init it */
+ res->nbuckets = ntasks;
+ res->activebuckets = ntasks;
+ res->buckets = isc_mem_get(view->mctx,
+ ntasks * sizeof(fctxbucket_t));
+ if (res->buckets == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_res;
+ }
+ for (i = 0; i < ntasks; i++) {
+ result = isc_mutex_init(&res->buckets[i].lock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_buckets;
+ res->buckets[i].task = NULL;
+ result = isc_task_create(taskmgr, 0, &res->buckets[i].task);
+ if (result != ISC_R_SUCCESS) {
+ DESTROYLOCK(&res->buckets[i].lock);
+ goto cleanup_buckets;
+ }
+ res->buckets[i].mctx = NULL;
+ snprintf(name, sizeof(name), "res%u", i);
+#ifdef ISC_PLATFORM_USETHREADS
+ /*
+ * Use a separate memory context for each bucket to reduce
+ * contention among multiple threads. Do this only when
+ * enabling threads because it will be require more memory.
+ */
+ result = isc_mem_create(0, 0, &res->buckets[i].mctx);
+ if (result != ISC_R_SUCCESS) {
+ isc_task_detach(&res->buckets[i].task);
+ DESTROYLOCK(&res->buckets[i].lock);
+ goto cleanup_buckets;
+ }
+ isc_mem_setname(res->buckets[i].mctx, name, NULL);
+#else
+ isc_mem_attach(view->mctx, &res->buckets[i].mctx);
+#endif
+ isc_task_setname(res->buckets[i].task, name, res);
+ ISC_LIST_INIT(res->buckets[i].fctxs);
+ res->buckets[i].exiting = ISC_FALSE;
+ buckets_created++;
+ }
+
+ res->dispatchv4 = NULL;
+ if (dispatchv4 != NULL) {
+ dns_dispatch_attach(dispatchv4, &res->dispatchv4);
+ dispattr = dns_dispatch_getattributes(dispatchv4);
+ res->exclusivev4 =
+ ISC_TF((dispattr & DNS_DISPATCHATTR_EXCLUSIVE) != 0);
+ }
+
+ res->dispatchv6 = NULL;
+ if (dispatchv6 != NULL) {
+ dns_dispatch_attach(dispatchv6, &res->dispatchv6);
+ dispattr = dns_dispatch_getattributes(dispatchv6);
+ res->exclusivev6 =
+ ISC_TF((dispattr & DNS_DISPATCHATTR_EXCLUSIVE) != 0);
+ }
+
+ res->references = 1;
+ res->exiting = ISC_FALSE;
+ res->frozen = ISC_FALSE;
+ ISC_LIST_INIT(res->whenshutdown);
+ res->priming = ISC_FALSE;
+ res->primefetch = NULL;
+ res->nfctx = 0;
+
+ result = isc_mutex_init(&res->lock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_dispatches;
+
+ result = isc_mutex_init(&res->nlock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_lock;
+
+ result = isc_mutex_init(&res->primelock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_nlock;
+
+ task = NULL;
+ result = isc_task_create(taskmgr, 0, &task);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_primelock;
+
+ result = isc_timer_create(timermgr, isc_timertype_inactive, NULL, NULL,
+ task, spillattimer_countdown, res,
+ &res->spillattimer);
+ isc_task_detach(&task);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_primelock;
+
+#if USE_ALGLOCK
+ result = isc_rwlock_init(&res->alglock, 0, 0);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_spillattimer;
+#endif
+#if USE_MBSLOCK
+ result = isc_rwlock_init(&res->mbslock, 0, 0);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_alglock;
+#endif
+
+ res->magic = RES_MAGIC;
+
+ *resp = res;
+
+ return (ISC_R_SUCCESS);
+
+#if USE_MBSLOCK
+ cleanup_alglock:
+#if USE_ALGLOCK
+ isc_rwlock_destroy(&res->alglock);
+#endif
+#endif
+#if USE_ALGLOCK || USE_MBSLOCK
+ cleanup_spillattimer:
+ isc_timer_detach(&res->spillattimer);
+#endif
+
+ cleanup_primelock:
+ DESTROYLOCK(&res->primelock);
+
+ cleanup_nlock:
+ DESTROYLOCK(&res->nlock);
+
+ cleanup_lock:
+ DESTROYLOCK(&res->lock);
+
+ cleanup_dispatches:
+ if (res->dispatchv6 != NULL)
+ dns_dispatch_detach(&res->dispatchv6);
+ if (res->dispatchv4 != NULL)
+ dns_dispatch_detach(&res->dispatchv4);
+
+ cleanup_buckets:
+ for (i = 0; i < buckets_created; i++) {
+ isc_mem_detach(&res->buckets[i].mctx);
+ DESTROYLOCK(&res->buckets[i].lock);
+ isc_task_shutdown(res->buckets[i].task);
+ isc_task_detach(&res->buckets[i].task);
+ }
+ isc_mem_put(view->mctx, res->buckets,
+ res->nbuckets * sizeof(fctxbucket_t));
+
+ cleanup_res:
+ isc_mem_put(view->mctx, res, sizeof(*res));
+
+ return (result);
+}
+
+static void
+prime_done(isc_task_t *task, isc_event_t *event) {
+ dns_resolver_t *res;
+ dns_fetchevent_t *fevent;
+ dns_fetch_t *fetch;
+ dns_db_t *db = NULL;
+
+ REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE);
+ fevent = (dns_fetchevent_t *)event;
+ res = event->ev_arg;
+ REQUIRE(VALID_RESOLVER(res));
+
+ UNUSED(task);
+
+ LOCK(&res->lock);
+
+ INSIST(res->priming);
+ res->priming = ISC_FALSE;
+ LOCK(&res->primelock);
+ fetch = res->primefetch;
+ res->primefetch = NULL;
+ UNLOCK(&res->primelock);
+
+ UNLOCK(&res->lock);
+
+ if (fevent->result == ISC_R_SUCCESS &&
+ res->view->cache != NULL && res->view->hints != NULL) {
+ dns_cache_attachdb(res->view->cache, &db);
+ dns_root_checkhints(res->view, res->view->hints, db);
+ dns_db_detach(&db);
+ }
+
+ if (fevent->node != NULL)
+ dns_db_detachnode(fevent->db, &fevent->node);
+ if (fevent->db != NULL)
+ dns_db_detach(&fevent->db);
+ if (dns_rdataset_isassociated(fevent->rdataset))
+ dns_rdataset_disassociate(fevent->rdataset);
+ INSIST(fevent->sigrdataset == NULL);
+
+ isc_mem_put(res->mctx, fevent->rdataset, sizeof(*fevent->rdataset));
+
+ isc_event_free(&event);
+ dns_resolver_destroyfetch(&fetch);
+}
+
+void
+dns_resolver_prime(dns_resolver_t *res) {
+ isc_boolean_t want_priming = ISC_FALSE;
+ dns_rdataset_t *rdataset;
+ isc_result_t result;
+
+ REQUIRE(VALID_RESOLVER(res));
+ REQUIRE(res->frozen);
+
+ RTRACE("dns_resolver_prime");
+
+ LOCK(&res->lock);
+
+ if (!res->exiting && !res->priming) {
+ INSIST(res->primefetch == NULL);
+ res->priming = ISC_TRUE;
+ want_priming = ISC_TRUE;
+ }
+
+ UNLOCK(&res->lock);
+
+ if (want_priming) {
+ /*
+ * To avoid any possible recursive locking problems, we
+ * start the priming fetch like any other fetch, and holding
+ * no resolver locks. No one else will try to start it
+ * because we're the ones who set res->priming to true.
+ * Any other callers of dns_resolver_prime() while we're
+ * running will see that res->priming is already true and
+ * do nothing.
+ */
+ RTRACE("priming");
+ rdataset = isc_mem_get(res->mctx, sizeof(*rdataset));
+ if (rdataset == NULL) {
+ LOCK(&res->lock);
+ INSIST(res->priming);
+ INSIST(res->primefetch == NULL);
+ res->priming = ISC_FALSE;
+ UNLOCK(&res->lock);
+ return;
+ }
+ dns_rdataset_init(rdataset);
+ LOCK(&res->primelock);
+ result = dns_resolver_createfetch(res, dns_rootname,
+ dns_rdatatype_ns,
+ NULL, NULL, NULL, 0,
+ res->buckets[0].task,
+ prime_done,
+ res, rdataset, NULL,
+ &res->primefetch);
+ UNLOCK(&res->primelock);
+ if (result != ISC_R_SUCCESS) {
+ LOCK(&res->lock);
+ INSIST(res->priming);
+ res->priming = ISC_FALSE;
+ UNLOCK(&res->lock);
+ }
+ }
+}
+
+void
+dns_resolver_freeze(dns_resolver_t *res) {
+
+ /*
+ * Freeze resolver.
+ */
+
+ REQUIRE(VALID_RESOLVER(res));
+ REQUIRE(!res->frozen);
+
+ res->frozen = ISC_TRUE;
+}
+
+void
+dns_resolver_attach(dns_resolver_t *source, dns_resolver_t **targetp) {
+ REQUIRE(VALID_RESOLVER(source));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ RRTRACE(source, "attach");
+ LOCK(&source->lock);
+ REQUIRE(!source->exiting);
+
+ INSIST(source->references > 0);
+ source->references++;
+ INSIST(source->references != 0);
+ UNLOCK(&source->lock);
+
+ *targetp = source;
+}
+
+void
+dns_resolver_whenshutdown(dns_resolver_t *res, isc_task_t *task,
+ isc_event_t **eventp)
+{
+ isc_task_t *clone;
+ isc_event_t *event;
+
+ REQUIRE(VALID_RESOLVER(res));
+ REQUIRE(eventp != NULL);
+
+ event = *eventp;
+ *eventp = NULL;
+
+ LOCK(&res->lock);
+
+ if (res->exiting && res->activebuckets == 0) {
+ /*
+ * We're already shutdown. Send the event.
+ */
+ event->ev_sender = res;
+ isc_task_send(task, &event);
+ } else {
+ clone = NULL;
+ isc_task_attach(task, &clone);
+ event->ev_sender = clone;
+ ISC_LIST_APPEND(res->whenshutdown, event, ev_link);
+ }
+
+ UNLOCK(&res->lock);
+}
+
+void
+dns_resolver_shutdown(dns_resolver_t *res) {
+ unsigned int i;
+ fetchctx_t *fctx;
+ isc_socket_t *sock;
+ isc_result_t result;
+
+ REQUIRE(VALID_RESOLVER(res));
+
+ RTRACE("shutdown");
+
+ LOCK(&res->lock);
+
+ if (!res->exiting) {
+ RTRACE("exiting");
+ res->exiting = ISC_TRUE;
+
+ for (i = 0; i < res->nbuckets; i++) {
+ LOCK(&res->buckets[i].lock);
+ for (fctx = ISC_LIST_HEAD(res->buckets[i].fctxs);
+ fctx != NULL;
+ fctx = ISC_LIST_NEXT(fctx, link))
+ fctx_shutdown(fctx);
+ if (res->dispatchv4 != NULL && !res->exclusivev4) {
+ sock = dns_dispatch_getsocket(res->dispatchv4);
+ isc_socket_cancel(sock, res->buckets[i].task,
+ ISC_SOCKCANCEL_ALL);
+ }
+ if (res->dispatchv6 != NULL && !res->exclusivev6) {
+ sock = dns_dispatch_getsocket(res->dispatchv6);
+ isc_socket_cancel(sock, res->buckets[i].task,
+ ISC_SOCKCANCEL_ALL);
+ }
+ res->buckets[i].exiting = ISC_TRUE;
+ if (ISC_LIST_EMPTY(res->buckets[i].fctxs)) {
+ INSIST(res->activebuckets > 0);
+ res->activebuckets--;
+ }
+ UNLOCK(&res->buckets[i].lock);
+ }
+ if (res->activebuckets == 0)
+ send_shutdown_events(res);
+ result = isc_timer_reset(res->spillattimer,
+ isc_timertype_inactive, NULL,
+ NULL, ISC_TRUE);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ }
+
+ UNLOCK(&res->lock);
+}
+
+void
+dns_resolver_detach(dns_resolver_t **resp) {
+ dns_resolver_t *res;
+ isc_boolean_t need_destroy = ISC_FALSE;
+
+ REQUIRE(resp != NULL);
+ res = *resp;
+ REQUIRE(VALID_RESOLVER(res));
+
+ RTRACE("detach");
+
+ LOCK(&res->lock);
+
+ INSIST(res->references > 0);
+ res->references--;
+ if (res->references == 0) {
+ INSIST(res->exiting && res->activebuckets == 0);
+ need_destroy = ISC_TRUE;
+ }
+
+ UNLOCK(&res->lock);
+
+ if (need_destroy)
+ destroy(res);
+
+ *resp = NULL;
+}
+
+static inline isc_boolean_t
+fctx_match(fetchctx_t *fctx, dns_name_t *name, dns_rdatatype_t type,
+ unsigned int options)
+{
+ if (fctx->type != type || fctx->options != options)
+ return (ISC_FALSE);
+ return (dns_name_equal(&fctx->name, name));
+}
+
+static inline void
+log_fetch(dns_name_t *name, dns_rdatatype_t type) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
+ int level = ISC_LOG_DEBUG(1);
+
+ if (! isc_log_wouldlog(dns_lctx, level))
+ return;
+
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ dns_rdatatype_format(type, typebuf, sizeof(typebuf));
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+ DNS_LOGMODULE_RESOLVER, level,
+ "createfetch: %s %s", namebuf, typebuf);
+}
+
+isc_result_t
+dns_resolver_createfetch(dns_resolver_t *res, dns_name_t *name,
+ dns_rdatatype_t type,
+ dns_name_t *domain, dns_rdataset_t *nameservers,
+ dns_forwarders_t *forwarders,
+ unsigned int options, isc_task_t *task,
+ isc_taskaction_t action, void *arg,
+ dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset,
+ dns_fetch_t **fetchp)
+{
+ return (dns_resolver_createfetch2(res, name, type, domain,
+ nameservers, forwarders, NULL, 0,
+ options, task, action, arg,
+ rdataset, sigrdataset, fetchp));
+}
+
+isc_result_t
+dns_resolver_createfetch2(dns_resolver_t *res, dns_name_t *name,
+ dns_rdatatype_t type,
+ dns_name_t *domain, dns_rdataset_t *nameservers,
+ dns_forwarders_t *forwarders,
+ isc_sockaddr_t *client, dns_messageid_t id,
+ unsigned int options, isc_task_t *task,
+ isc_taskaction_t action, void *arg,
+ dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset,
+ dns_fetch_t **fetchp)
+{
+ dns_fetch_t *fetch;
+ fetchctx_t *fctx = NULL;
+ isc_result_t result = ISC_R_SUCCESS;
+ unsigned int bucketnum;
+ isc_boolean_t new_fctx = ISC_FALSE;
+ isc_event_t *event;
+ unsigned int count = 0;
+ unsigned int spillat;
+ unsigned int spillatmin;
+
+ UNUSED(forwarders);
+
+ REQUIRE(VALID_RESOLVER(res));
+ REQUIRE(res->frozen);
+ /* XXXRTH Check for meta type */
+ if (domain != NULL) {
+ REQUIRE(DNS_RDATASET_VALID(nameservers));
+ REQUIRE(nameservers->type == dns_rdatatype_ns);
+ } else
+ REQUIRE(nameservers == NULL);
+ REQUIRE(forwarders == NULL);
+ REQUIRE(!dns_rdataset_isassociated(rdataset));
+ REQUIRE(sigrdataset == NULL ||
+ !dns_rdataset_isassociated(sigrdataset));
+ REQUIRE(fetchp != NULL && *fetchp == NULL);
+
+ log_fetch(name, type);
+
+ /*
+ * XXXRTH use a mempool?
+ */
+ fetch = isc_mem_get(res->mctx, sizeof(*fetch));
+ if (fetch == NULL)
+ return (ISC_R_NOMEMORY);
+
+ bucketnum = dns_name_fullhash(name, ISC_FALSE) % res->nbuckets;
+
+ LOCK(&res->lock);
+ spillat = res->spillat;
+ spillatmin = res->spillatmin;
+ UNLOCK(&res->lock);
+ LOCK(&res->buckets[bucketnum].lock);
+
+ if (res->buckets[bucketnum].exiting) {
+ result = ISC_R_SHUTTINGDOWN;
+ goto unlock;
+ }
+
+ if ((options & DNS_FETCHOPT_UNSHARED) == 0) {
+ for (fctx = ISC_LIST_HEAD(res->buckets[bucketnum].fctxs);
+ fctx != NULL;
+ fctx = ISC_LIST_NEXT(fctx, link)) {
+ if (fctx_match(fctx, name, type, options))
+ break;
+ }
+ }
+
+ /*
+ * Is this a duplicate?
+ */
+ if (fctx != NULL && client != NULL) {
+ dns_fetchevent_t *fevent;
+ for (fevent = ISC_LIST_HEAD(fctx->events);
+ fevent != NULL;
+ fevent = ISC_LIST_NEXT(fevent, ev_link)) {
+ if (fevent->client != NULL && fevent->id == id &&
+ isc_sockaddr_equal(fevent->client, client)) {
+ result = DNS_R_DUPLICATE;
+ goto unlock;
+ }
+ count++;
+ }
+ }
+ if (count >= spillatmin && spillatmin != 0) {
+ INSIST(fctx != NULL);
+ if (count >= spillat)
+ fctx->spilled = ISC_TRUE;
+ if (fctx->spilled) {
+ result = DNS_R_DROP;
+ goto unlock;
+ }
+ }
+
+ /*
+ * If we didn't have a fetch, would attach to a done fetch, this
+ * fetch has already cloned its results, or if the fetch has gone
+ * "idle" (no one was interested in it), we need to start a new
+ * fetch instead of joining with the existing one.
+ */
+ if (fctx == NULL ||
+ fctx->state == fetchstate_done ||
+ fctx->cloned ||
+ ISC_LIST_EMPTY(fctx->events)) {
+ fctx = NULL;
+ result = fctx_create(res, name, type, domain, nameservers,
+ options, bucketnum, &fctx);
+ if (result != ISC_R_SUCCESS)
+ goto unlock;
+ new_fctx = ISC_TRUE;
+ }
+
+ result = fctx_join(fctx, task, client, id, action, arg,
+ rdataset, sigrdataset, fetch);
+ if (new_fctx) {
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * Launch this fctx.
+ */
+ event = &fctx->control_event;
+ ISC_EVENT_INIT(event, sizeof(*event), 0, NULL,
+ DNS_EVENT_FETCHCONTROL,
+ fctx_start, fctx, NULL,
+ NULL, NULL);
+ isc_task_send(res->buckets[bucketnum].task, &event);
+ } else {
+ /*
+ * We don't care about the result of fctx_destroy()
+ * since we know we're not exiting.
+ */
+ (void)fctx_destroy(fctx);
+ }
+ }
+
+ unlock:
+ UNLOCK(&res->buckets[bucketnum].lock);
+
+ if (result == ISC_R_SUCCESS) {
+ FTRACE("created");
+ *fetchp = fetch;
+ } else
+ isc_mem_put(res->mctx, fetch, sizeof(*fetch));
+
+ return (result);
+}
+
+void
+dns_resolver_cancelfetch(dns_fetch_t *fetch) {
+ fetchctx_t *fctx;
+ dns_resolver_t *res;
+ dns_fetchevent_t *event, *next_event;
+ isc_task_t *etask;
+
+ REQUIRE(DNS_FETCH_VALID(fetch));
+ fctx = fetch->private;
+ REQUIRE(VALID_FCTX(fctx));
+ res = fctx->res;
+
+ FTRACE("cancelfetch");
+
+ LOCK(&res->buckets[fctx->bucketnum].lock);
+
+ /*
+ * Find the completion event for this fetch (as opposed
+ * to those for other fetches that have joined the same
+ * fctx) and send it with result = ISC_R_CANCELED.
+ */
+ event = NULL;
+ if (fctx->state != fetchstate_done) {
+ for (event = ISC_LIST_HEAD(fctx->events);
+ event != NULL;
+ event = next_event) {
+ next_event = ISC_LIST_NEXT(event, ev_link);
+ if (event->fetch == fetch) {
+ ISC_LIST_UNLINK(fctx->events, event, ev_link);
+ break;
+ }
+ }
+ }
+ if (event != NULL) {
+ etask = event->ev_sender;
+ event->ev_sender = fctx;
+ event->result = ISC_R_CANCELED;
+ isc_task_sendanddetach(&etask, ISC_EVENT_PTR(&event));
+ }
+ /*
+ * The fctx continues running even if no fetches remain;
+ * the answer is still cached.
+ */
+
+ UNLOCK(&res->buckets[fctx->bucketnum].lock);
+}
+
+void
+dns_resolver_destroyfetch(dns_fetch_t **fetchp) {
+ dns_fetch_t *fetch;
+ dns_resolver_t *res;
+ dns_fetchevent_t *event, *next_event;
+ fetchctx_t *fctx;
+ unsigned int bucketnum;
+ isc_boolean_t bucket_empty = ISC_FALSE;
+
+ REQUIRE(fetchp != NULL);
+ fetch = *fetchp;
+ REQUIRE(DNS_FETCH_VALID(fetch));
+ fctx = fetch->private;
+ REQUIRE(VALID_FCTX(fctx));
+ res = fctx->res;
+
+ FTRACE("destroyfetch");
+
+ bucketnum = fctx->bucketnum;
+ LOCK(&res->buckets[bucketnum].lock);
+
+ /*
+ * Sanity check: the caller should have gotten its event before
+ * trying to destroy the fetch.
+ */
+ event = NULL;
+ if (fctx->state != fetchstate_done) {
+ for (event = ISC_LIST_HEAD(fctx->events);
+ event != NULL;
+ event = next_event) {
+ next_event = ISC_LIST_NEXT(event, ev_link);
+ RUNTIME_CHECK(event->fetch != fetch);
+ }
+ }
+
+ INSIST(fctx->references > 0);
+ fctx->references--;
+ if (fctx->references == 0) {
+ /*
+ * No one cares about the result of this fetch anymore.
+ */
+ if (fctx->pending == 0 && fctx->nqueries == 0 &&
+ ISC_LIST_EMPTY(fctx->validators) &&
+ SHUTTINGDOWN(fctx)) {
+ /*
+ * This fctx is already shutdown; we were just
+ * waiting for the last reference to go away.
+ */
+ bucket_empty = fctx_destroy(fctx);
+ } else {
+ /*
+ * Initiate shutdown.
+ */
+ fctx_shutdown(fctx);
+ }
+ }
+
+ UNLOCK(&res->buckets[bucketnum].lock);
+
+ isc_mem_put(res->mctx, fetch, sizeof(*fetch));
+ *fetchp = NULL;
+
+ if (bucket_empty)
+ empty_bucket(res);
+}
+
+dns_dispatchmgr_t *
+dns_resolver_dispatchmgr(dns_resolver_t *resolver) {
+ REQUIRE(VALID_RESOLVER(resolver));
+ return (resolver->dispatchmgr);
+}
+
+dns_dispatch_t *
+dns_resolver_dispatchv4(dns_resolver_t *resolver) {
+ REQUIRE(VALID_RESOLVER(resolver));
+ return (resolver->dispatchv4);
+}
+
+dns_dispatch_t *
+dns_resolver_dispatchv6(dns_resolver_t *resolver) {
+ REQUIRE(VALID_RESOLVER(resolver));
+ return (resolver->dispatchv6);
+}
+
+isc_socketmgr_t *
+dns_resolver_socketmgr(dns_resolver_t *resolver) {
+ REQUIRE(VALID_RESOLVER(resolver));
+ return (resolver->socketmgr);
+}
+
+isc_taskmgr_t *
+dns_resolver_taskmgr(dns_resolver_t *resolver) {
+ REQUIRE(VALID_RESOLVER(resolver));
+ return (resolver->taskmgr);
+}
+
+isc_uint32_t
+dns_resolver_getlamettl(dns_resolver_t *resolver) {
+ REQUIRE(VALID_RESOLVER(resolver));
+ return (resolver->lame_ttl);
+}
+
+void
+dns_resolver_setlamettl(dns_resolver_t *resolver, isc_uint32_t lame_ttl) {
+ REQUIRE(VALID_RESOLVER(resolver));
+ resolver->lame_ttl = lame_ttl;
+}
+
+unsigned int
+dns_resolver_nrunning(dns_resolver_t *resolver) {
+ unsigned int n;
+ LOCK(&resolver->nlock);
+ n = resolver->nfctx;
+ UNLOCK(&resolver->nlock);
+ return (n);
+}
+
+isc_result_t
+dns_resolver_addalternate(dns_resolver_t *resolver, isc_sockaddr_t *alt,
+ dns_name_t *name, in_port_t port) {
+ alternate_t *a;
+ isc_result_t result;
+
+ REQUIRE(VALID_RESOLVER(resolver));
+ REQUIRE(!resolver->frozen);
+ REQUIRE((alt == NULL) ^ (name == NULL));
+
+ a = isc_mem_get(resolver->mctx, sizeof(*a));
+ if (a == NULL)
+ return (ISC_R_NOMEMORY);
+ if (alt != NULL) {
+ a->isaddress = ISC_TRUE;
+ a->_u.addr = *alt;
+ } else {
+ a->isaddress = ISC_FALSE;
+ a->_u._n.port = port;
+ dns_name_init(&a->_u._n.name, NULL);
+ result = dns_name_dup(name, resolver->mctx, &a->_u._n.name);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(resolver->mctx, a, sizeof(*a));
+ return (result);
+ }
+ }
+ ISC_LINK_INIT(a, link);
+ ISC_LIST_APPEND(resolver->alternates, a, link);
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_resolver_setudpsize(dns_resolver_t *resolver, isc_uint16_t udpsize) {
+ REQUIRE(VALID_RESOLVER(resolver));
+ resolver->udpsize = udpsize;
+}
+
+isc_uint16_t
+dns_resolver_getudpsize(dns_resolver_t *resolver) {
+ REQUIRE(VALID_RESOLVER(resolver));
+ return (resolver->udpsize);
+}
+
+static void
+free_algorithm(void *node, void *arg) {
+ unsigned char *algorithms = node;
+ isc_mem_t *mctx = arg;
+
+ isc_mem_put(mctx, algorithms, *algorithms);
+}
+
+void
+dns_resolver_reset_algorithms(dns_resolver_t *resolver) {
+
+ REQUIRE(VALID_RESOLVER(resolver));
+
+#if USE_ALGLOCK
+ RWLOCK(&resolver->alglock, isc_rwlocktype_write);
+#endif
+ if (resolver->algorithms != NULL)
+ dns_rbt_destroy(&resolver->algorithms);
+#if USE_ALGLOCK
+ RWUNLOCK(&resolver->alglock, isc_rwlocktype_write);
+#endif
+}
+
+isc_result_t
+dns_resolver_disable_algorithm(dns_resolver_t *resolver, dns_name_t *name,
+ unsigned int alg)
+{
+ unsigned int len, mask;
+ unsigned char *new;
+ unsigned char *algorithms;
+ isc_result_t result;
+ dns_rbtnode_t *node = NULL;
+
+ REQUIRE(VALID_RESOLVER(resolver));
+ if (alg > 255)
+ return (ISC_R_RANGE);
+
+#if USE_ALGLOCK
+ RWLOCK(&resolver->alglock, isc_rwlocktype_write);
+#endif
+ if (resolver->algorithms == NULL) {
+ result = dns_rbt_create(resolver->mctx, free_algorithm,
+ resolver->mctx, &resolver->algorithms);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+
+ len = alg/8 + 2;
+ mask = 1 << (alg%8);
+
+ result = dns_rbt_addnode(resolver->algorithms, name, &node);
+
+ if (result == ISC_R_SUCCESS || result == ISC_R_EXISTS) {
+ algorithms = node->data;
+ if (algorithms == NULL || len > *algorithms) {
+ new = isc_mem_get(resolver->mctx, len);
+ if (new == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ memset(new, 0, len);
+ if (algorithms != NULL)
+ memcpy(new, algorithms, *algorithms);
+ new[len-1] |= mask;
+ *new = len;
+ node->data = new;
+ if (algorithms != NULL)
+ isc_mem_put(resolver->mctx, algorithms,
+ *algorithms);
+ } else
+ algorithms[len-1] |= mask;
+ }
+ result = ISC_R_SUCCESS;
+ cleanup:
+#if USE_ALGLOCK
+ RWUNLOCK(&resolver->alglock, isc_rwlocktype_write);
+#endif
+ return (result);
+}
+
+isc_boolean_t
+dns_resolver_algorithm_supported(dns_resolver_t *resolver, dns_name_t *name,
+ unsigned int alg)
+{
+ unsigned int len, mask;
+ unsigned char *algorithms;
+ void *data = NULL;
+ isc_result_t result;
+ isc_boolean_t found = ISC_FALSE;
+
+ REQUIRE(VALID_RESOLVER(resolver));
+
+#if USE_ALGLOCK
+ RWLOCK(&resolver->alglock, isc_rwlocktype_read);
+#endif
+ if (resolver->algorithms == NULL)
+ goto unlock;
+ result = dns_rbt_findname(resolver->algorithms, name, 0, NULL, &data);
+ if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
+ len = alg/8 + 2;
+ mask = 1 << (alg%8);
+ algorithms = data;
+ if (len <= *algorithms && (algorithms[len-1] & mask) != 0)
+ found = ISC_TRUE;
+ }
+ unlock:
+#if USE_ALGLOCK
+ RWUNLOCK(&resolver->alglock, isc_rwlocktype_read);
+#endif
+ if (found)
+ return (ISC_FALSE);
+ return (dst_algorithm_supported(alg));
+}
+
+isc_boolean_t
+dns_resolver_digest_supported(dns_resolver_t *resolver, unsigned int digest) {
+
+ UNUSED(resolver);
+ return (dns_ds_digest_supported(digest));
+}
+
+void
+dns_resolver_resetmustbesecure(dns_resolver_t *resolver) {
+
+ REQUIRE(VALID_RESOLVER(resolver));
+
+#if USE_MBSLOCK
+ RWLOCK(&resolver->mbslock, isc_rwlocktype_write);
+#endif
+ if (resolver->mustbesecure != NULL)
+ dns_rbt_destroy(&resolver->mustbesecure);
+#if USE_MBSLOCK
+ RWUNLOCK(&resolver->mbslock, isc_rwlocktype_write);
+#endif
+}
+
+static isc_boolean_t yes = ISC_TRUE, no = ISC_FALSE;
+
+isc_result_t
+dns_resolver_setmustbesecure(dns_resolver_t *resolver, dns_name_t *name,
+ isc_boolean_t value)
+{
+ isc_result_t result;
+
+ REQUIRE(VALID_RESOLVER(resolver));
+
+#if USE_MBSLOCK
+ RWLOCK(&resolver->mbslock, isc_rwlocktype_write);
+#endif
+ if (resolver->mustbesecure == NULL) {
+ result = dns_rbt_create(resolver->mctx, NULL, NULL,
+ &resolver->mustbesecure);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+ result = dns_rbt_addname(resolver->mustbesecure, name,
+ value ? &yes : &no);
+ cleanup:
+#if USE_MBSLOCK
+ RWUNLOCK(&resolver->mbslock, isc_rwlocktype_write);
+#endif
+ return (result);
+}
+
+isc_boolean_t
+dns_resolver_getmustbesecure(dns_resolver_t *resolver, dns_name_t *name) {
+ void *data = NULL;
+ isc_boolean_t value = ISC_FALSE;
+ isc_result_t result;
+
+ REQUIRE(VALID_RESOLVER(resolver));
+
+#if USE_MBSLOCK
+ RWLOCK(&resolver->mbslock, isc_rwlocktype_read);
+#endif
+ if (resolver->mustbesecure == NULL)
+ goto unlock;
+ result = dns_rbt_findname(resolver->mustbesecure, name, 0, NULL, &data);
+ if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
+ value = *(isc_boolean_t*)data;
+ unlock:
+#if USE_MBSLOCK
+ RWUNLOCK(&resolver->mbslock, isc_rwlocktype_read);
+#endif
+ return (value);
+}
+
+void
+dns_resolver_getclientsperquery(dns_resolver_t *resolver, isc_uint32_t *cur,
+ isc_uint32_t *min, isc_uint32_t *max)
+{
+ REQUIRE(VALID_RESOLVER(resolver));
+
+ LOCK(&resolver->lock);
+ if (cur != NULL)
+ *cur = resolver->spillat;
+ if (min != NULL)
+ *min = resolver->spillatmin;
+ if (max != NULL)
+ *max = resolver->spillatmax;
+ UNLOCK(&resolver->lock);
+}
+
+void
+dns_resolver_setclientsperquery(dns_resolver_t *resolver, isc_uint32_t min,
+ isc_uint32_t max)
+{
+ REQUIRE(VALID_RESOLVER(resolver));
+
+ LOCK(&resolver->lock);
+ resolver->spillatmin = resolver->spillat = min;
+ resolver->spillatmax = max;
+ UNLOCK(&resolver->lock);
+}
+
+isc_boolean_t
+dns_resolver_getzeronosoattl(dns_resolver_t *resolver) {
+ REQUIRE(VALID_RESOLVER(resolver));
+
+ return (resolver->zero_no_soa_ttl);
+}
+
+void
+dns_resolver_setzeronosoattl(dns_resolver_t *resolver, isc_boolean_t state) {
+ REQUIRE(VALID_RESOLVER(resolver));
+
+ resolver->zero_no_soa_ttl = state;
+}
+
+unsigned int
+dns_resolver_getoptions(dns_resolver_t *resolver) {
+ REQUIRE(VALID_RESOLVER(resolver));
+
+ return (resolver->options);
+}
diff --git a/lib/dns/result.c b/lib/dns/result.c
new file mode 100644
index 0000000..54c70e0
--- /dev/null
+++ b/lib/dns/result.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: result.c,v 1.125 2008/09/25 04:02:38 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/once.h>
+#include <isc/util.h>
+
+#include <dns/result.h>
+#include <dns/lib.h>
+
+static const char *text[DNS_R_NRESULTS] = {
+ "label too long", /*%< 0 DNS_R_LABELTOOLONG */
+ "bad escape", /*%< 1 DNS_R_BADESCAPE */
+ /*!
+ * Note that DNS_R_BADBITSTRING and DNS_R_BITSTRINGTOOLONG are
+ * deprecated.
+ */
+ "bad bitstring", /*%< 2 DNS_R_BADBITSTRING */
+ "bitstring too long", /*%< 3 DNS_R_BITSTRINGTOOLONG */
+ "empty label", /*%< 4 DNS_R_EMPTYLABEL */
+
+ "bad dotted quad", /*%< 5 DNS_R_BADDOTTEDQUAD */
+ "invalid NS owner name (wildcard)", /*%< 6 DNS_R_INVALIDNS */
+ "unknown class/type", /*%< 7 DNS_R_UNKNOWN */
+ "bad label type", /*%< 8 DNS_R_BADLABELTYPE */
+ "bad compression pointer", /*%< 9 DNS_R_BADPOINTER */
+
+ "too many hops", /*%< 10 DNS_R_TOOMANYHOPS */
+ "disallowed (by application policy)", /*%< 11 DNS_R_DISALLOWED */
+ "extra input text", /*%< 12 DNS_R_EXTRATOKEN */
+ "extra input data", /*%< 13 DNS_R_EXTRADATA */
+ "text too long", /*%< 14 DNS_R_TEXTTOOLONG */
+
+ "not at top of zone", /*%< 15 DNS_R_NOTZONETOP */
+ "syntax error", /*%< 16 DNS_R_SYNTAX */
+ "bad checksum", /*%< 17 DNS_R_BADCKSUM */
+ "bad IPv6 address", /*%< 18 DNS_R_BADAAAA */
+ "no owner", /*%< 19 DNS_R_NOOWNER */
+
+ "no ttl", /*%< 20 DNS_R_NOTTL */
+ "bad class", /*%< 21 DNS_R_BADCLASS */
+ "name too long", /*%< 22 DNS_R_NAMETOOLONG */
+ "partial match", /*%< 23 DNS_R_PARTIALMATCH */
+ "new origin", /*%< 24 DNS_R_NEWORIGIN */
+
+ "unchanged", /*%< 25 DNS_R_UNCHANGED */
+ "bad ttl", /*%< 26 DNS_R_BADTTL */
+ "more data needed/to be rendered", /*%< 27 DNS_R_NOREDATA */
+ "continue", /*%< 28 DNS_R_CONTINUE */
+ "delegation", /*%< 29 DNS_R_DELEGATION */
+
+ "glue", /*%< 30 DNS_R_GLUE */
+ "dname", /*%< 31 DNS_R_DNAME */
+ "cname", /*%< 32 DNS_R_CNAME */
+ "bad database", /*%< 33 DNS_R_BADDB */
+ "zonecut", /*%< 34 DNS_R_ZONECUT */
+
+ "bad zone", /*%< 35 DNS_R_BADZONE */
+ "more data", /*%< 36 DNS_R_MOREDATA */
+ "up to date", /*%< 37 DNS_R_UPTODATE */
+ "tsig verify failure", /*%< 38 DNS_R_TSIGVERIFYFAILURE */
+ "tsig indicates error", /*%< 39 DNS_R_TSIGERRORSET */
+
+ "RRSIG failed to verify", /*%< 40 DNS_R_SIGINVALID */
+ "RRSIG has expired", /*%< 41 DNS_R_SIGEXPIRED */
+ "RRSIG validity period has not begun", /*%< 42 DNS_R_SIGFUTURE */
+ "key is unauthorized to sign data", /*%< 43 DNS_R_KEYUNAUTHORIZED */
+ "invalid time", /*%< 44 DNS_R_INVALIDTIME */
+
+ "expected a TSIG or SIG(0)", /*%< 45 DNS_R_EXPECTEDTSIG */
+ "did not expect a TSIG or SIG(0)", /*%< 46 DNS_R_UNEXPECTEDTSIG */
+ "TKEY is unacceptable", /*%< 47 DNS_R_INVALIDTKEY */
+ "hint", /*%< 48 DNS_R_HINT */
+ "drop", /*%< 49 DNS_R_DROP */
+
+ "zone not loaded", /*%< 50 DNS_R_NOTLOADED */
+ "ncache nxdomain", /*%< 51 DNS_R_NCACHENXDOMAIN */
+ "ncache nxrrset", /*%< 52 DNS_R_NCACHENXRRSET */
+ "wait", /*%< 53 DNS_R_WAIT */
+ "not verified yet", /*%< 54 DNS_R_NOTVERIFIEDYET */
+
+ "no identity", /*%< 55 DNS_R_NOIDENTITY */
+ "no journal", /*%< 56 DNS_R_NOJOURNAL */
+ "alias", /*%< 57 DNS_R_ALIAS */
+ "use TCP", /*%< 58 DNS_R_USETCP */
+ "no valid RRSIG", /*%< 59 DNS_R_NOVALIDSIG */
+
+ "no valid NSEC", /*%< 60 DNS_R_NOVALIDNSEC */
+ "not insecure", /*%< 61 DNS_R_NOTINSECURE */
+ "unknown service", /*%< 62 DNS_R_UNKNOWNSERVICE */
+ "recoverable error occurred", /*%< 63 DNS_R_RECOVERABLE */
+ "unknown opt attribute record", /*%< 64 DNS_R_UNKNOWNOPT */
+
+ "unexpected message id", /*%< 65 DNS_R_UNEXPECTEDID */
+ "seen include file", /*%< 66 DNS_R_SEENINCLUDE */
+ "not exact", /*%< 67 DNS_R_NOTEXACT */
+ "address blackholed", /*%< 68 DNS_R_BLACKHOLED */
+ "bad algorithm", /*%< 69 DNS_R_BADALG */
+
+ "invalid use of a meta type", /*%< 70 DNS_R_METATYPE */
+ "CNAME and other data", /*%< 71 DNS_R_CNAMEANDOTHER */
+ "multiple RRs of singleton type", /*%< 72 DNS_R_SINGLETON */
+ "hint nxrrset", /*%< 73 DNS_R_HINTNXRRSET */
+ "no master file configured", /*%< 74 DNS_R_NOMASTERFILE */
+
+ "unknown protocol", /*%< 75 DNS_R_UNKNOWNPROTO */
+ "clocks are unsynchronized", /*%< 76 DNS_R_CLOCKSKEW */
+ "IXFR failed", /*%< 77 DNS_R_BADIXFR */
+ "not authoritative", /*%< 78 DNS_R_NOTAUTHORITATIVE */
+ "no valid KEY", /*%< 79 DNS_R_NOVALIDKEY */
+
+ "obsolete", /*%< 80 DNS_R_OBSOLETE */
+ "already frozen", /*%< 81 DNS_R_FROZEN */
+ "unknown flag", /*%< 82 DNS_R_UNKNOWNFLAG */
+ "expected a response", /*%< 83 DNS_R_EXPECTEDRESPONSE */
+ "no valid DS", /*%< 84 DNS_R_NOVALIDDS */
+
+ "NS is an address", /*%< 85 DNS_R_NSISADDRESS */
+ "received FORMERR", /*%< 86 DNS_R_REMOTEFORMERR */
+ "truncated TCP response", /*%< 87 DNS_R_TRUNCATEDTCP */
+ "lame server detected", /*%< 88 DNS_R_LAME */
+ "unexpected RCODE", /*%< 89 DNS_R_UNEXPECTEDRCODE */
+
+ "unexpected OPCODE", /*%< 90 DNS_R_UNEXPECTEDOPCODE */
+ "chase DS servers", /*%< 91 DNS_R_CHASEDSSERVERS */
+ "empty name", /*%< 92 DNS_R_EMPTYNAME */
+ "empty wild", /*%< 93 DNS_R_EMPTYWILD */
+ "bad bitmap", /*%< 94 DNS_R_BADBITMAP */
+
+ "from wildcard", /*%< 95 DNS_R_FROMWILDCARD */
+ "bad owner name (check-names)", /*%< 96 DNS_R_BADOWNERNAME */
+ "bad name (check-names)", /*%< 97 DNS_R_BADNAME */
+ "dynamic zone", /*%< 98 DNS_R_DYNAMIC */
+ "unknown command", /*%< 99 DNS_R_UNKNOWNCOMMAND */
+
+ "must-be-secure", /*%< 100 DNS_R_MUSTBESECURE */
+ "covering NSEC record returned", /*%< 101 DNS_R_COVERINGNSEC */
+ "MX is an address", /*%< 102 DNS_R_MXISADDRESS */
+ "duplicate query", /*%< 103 DNS_R_DUPLICATE */
+ "invalid NSEC3 owner name (wildcard)", /*%< 104 DNS_R_INVALIDNSEC3 */
+};
+
+static const char *rcode_text[DNS_R_NRCODERESULTS] = {
+ "NOERROR", /*%< 0 DNS_R_NOEROR */
+ "FORMERR", /*%< 1 DNS_R_FORMERR */
+ "SERVFAIL", /*%< 2 DNS_R_SERVFAIL */
+ "NXDOMAIN", /*%< 3 DNS_R_NXDOMAIN */
+ "NOTIMP", /*%< 4 DNS_R_NOTIMP */
+
+ "REFUSED", /*%< 5 DNS_R_REFUSED */
+ "YXDOMAIN", /*%< 6 DNS_R_YXDOMAIN */
+ "YXRRSET", /*%< 7 DNS_R_YXRRSET */
+ "NXRRSET", /*%< 8 DNS_R_NXRRSET */
+ "NOTAUTH", /*%< 9 DNS_R_NOTAUTH */
+
+ "NOTZONE", /*%< 10 DNS_R_NOTZONE */
+ "<rcode 11>", /*%< 11 has no macro */
+ "<rcode 12>", /*%< 12 has no macro */
+ "<rcode 13>", /*%< 13 has no macro */
+ "<rcode 14>", /*%< 14 has no macro */
+
+ "<rcode 15>", /*%< 15 has no macro */
+ "BADVERS", /*%< 16 DNS_R_BADVERS */
+};
+
+#define DNS_RESULT_RESULTSET 2
+#define DNS_RESULT_RCODERESULTSET 3
+
+static isc_once_t once = ISC_ONCE_INIT;
+
+static void
+initialize_action(void) {
+ isc_result_t result;
+
+ result = isc_result_register(ISC_RESULTCLASS_DNS, DNS_R_NRESULTS,
+ text, dns_msgcat, DNS_RESULT_RESULTSET);
+ if (result == ISC_R_SUCCESS)
+ result = isc_result_register(ISC_RESULTCLASS_DNSRCODE,
+ DNS_R_NRCODERESULTS,
+ rcode_text, dns_msgcat,
+ DNS_RESULT_RCODERESULTSET);
+ if (result != ISC_R_SUCCESS)
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_result_register() failed: %u", result);
+}
+
+static void
+initialize(void) {
+ dns_lib_initmsgcat();
+ RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
+}
+
+const char *
+dns_result_totext(isc_result_t result) {
+ initialize();
+
+ return (isc_result_totext(result));
+}
+
+void
+dns_result_register(void) {
+ initialize();
+}
+
+dns_rcode_t
+dns_result_torcode(isc_result_t result) {
+ dns_rcode_t rcode = dns_rcode_servfail;
+
+ if (DNS_RESULT_ISRCODE(result)) {
+ /*
+ * Rcodes can't be bigger than 12 bits, which is why we
+ * AND with 0xFFF instead of 0xFFFF.
+ */
+ return ((dns_rcode_t)((result) & 0xFFF));
+ }
+ /*
+ * Try to supply an appropriate rcode.
+ */
+ switch (result) {
+ case ISC_R_SUCCESS:
+ rcode = dns_rcode_noerror;
+ break;
+ case ISC_R_BADBASE64:
+ case ISC_R_NOSPACE:
+ case ISC_R_RANGE:
+ case ISC_R_UNEXPECTEDEND:
+ case DNS_R_BADAAAA:
+ /* case DNS_R_BADBITSTRING: deprecated */
+ case DNS_R_BADCKSUM:
+ case DNS_R_BADCLASS:
+ case DNS_R_BADLABELTYPE:
+ case DNS_R_BADPOINTER:
+ case DNS_R_BADTTL:
+ case DNS_R_BADZONE:
+ /* case DNS_R_BITSTRINGTOOLONG: deprecated */
+ case DNS_R_EXTRADATA:
+ case DNS_R_LABELTOOLONG:
+ case DNS_R_NOREDATA:
+ case DNS_R_SYNTAX:
+ case DNS_R_TEXTTOOLONG:
+ case DNS_R_TOOMANYHOPS:
+ case DNS_R_TSIGERRORSET:
+ case DNS_R_UNKNOWN:
+ rcode = dns_rcode_formerr;
+ break;
+ case DNS_R_DISALLOWED:
+ rcode = dns_rcode_refused;
+ break;
+ case DNS_R_TSIGVERIFYFAILURE:
+ case DNS_R_CLOCKSKEW:
+ rcode = dns_rcode_notauth;
+ break;
+ default:
+ rcode = dns_rcode_servfail;
+ }
+
+ return (rcode);
+}
diff --git a/lib/dns/rootns.c b/lib/dns/rootns.c
new file mode 100644
index 0000000..3c50a18
--- /dev/null
+++ b/lib/dns/rootns.c
@@ -0,0 +1,525 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: rootns.c,v 1.36 2008/09/24 02:46:22 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/buffer.h>
+#include <isc/string.h> /* Required for HP/UX (and others?) */
+#include <isc/util.h>
+
+#include <dns/callbacks.h>
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/fixedname.h>
+#include <dns/log.h>
+#include <dns/master.h>
+#include <dns/rdata.h>
+#include <dns/rdata.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/rdatastruct.h>
+#include <dns/rdatatype.h>
+#include <dns/result.h>
+#include <dns/rootns.h>
+#include <dns/view.h>
+
+static char root_ns[] =
+";\n"
+"; Internet Root Nameservers\n"
+";\n"
+"$TTL 518400\n"
+". 518400 IN NS A.ROOT-SERVERS.NET.\n"
+". 518400 IN NS B.ROOT-SERVERS.NET.\n"
+". 518400 IN NS C.ROOT-SERVERS.NET.\n"
+". 518400 IN NS D.ROOT-SERVERS.NET.\n"
+". 518400 IN NS E.ROOT-SERVERS.NET.\n"
+". 518400 IN NS F.ROOT-SERVERS.NET.\n"
+". 518400 IN NS G.ROOT-SERVERS.NET.\n"
+". 518400 IN NS H.ROOT-SERVERS.NET.\n"
+". 518400 IN NS I.ROOT-SERVERS.NET.\n"
+". 518400 IN NS J.ROOT-SERVERS.NET.\n"
+". 518400 IN NS K.ROOT-SERVERS.NET.\n"
+". 518400 IN NS L.ROOT-SERVERS.NET.\n"
+". 518400 IN NS M.ROOT-SERVERS.NET.\n"
+"A.ROOT-SERVERS.NET. 3600000 IN A 198.41.0.4\n"
+"A.ROOT-SERVERS.NET. 3600000 IN AAAA 2001:503:BA3E::2:30\n"
+"B.ROOT-SERVERS.NET. 3600000 IN A 192.228.79.201\n"
+"C.ROOT-SERVERS.NET. 3600000 IN A 192.33.4.12\n"
+"D.ROOT-SERVERS.NET. 3600000 IN A 128.8.10.90\n"
+"E.ROOT-SERVERS.NET. 3600000 IN A 192.203.230.10\n"
+"F.ROOT-SERVERS.NET. 3600000 IN A 192.5.5.241\n"
+"F.ROOT-SERVERS.NET. 3600000 IN AAAA 2001:500:2F::F\n"
+"G.ROOT-SERVERS.NET. 3600000 IN A 192.112.36.4\n"
+"H.ROOT-SERVERS.NET. 3600000 IN A 128.63.2.53\n"
+"H.ROOT-SERVERS.NET. 3600000 IN AAAA 2001:500:1::803F:235\n"
+"I.ROOT-SERVERS.NET. 3600000 IN A 192.36.148.17\n"
+"J.ROOT-SERVERS.NET. 3600000 IN A 192.58.128.30\n"
+"J.ROOT-SERVERS.NET. 3600000 IN AAAA 2001:503:C27::2:30\n"
+"K.ROOT-SERVERS.NET. 3600000 IN A 193.0.14.129\n"
+"K.ROOT-SERVERS.NET. 3600000 IN AAAA 2001:7FD::1\n"
+"L.ROOT-SERVERS.NET. 3600000 IN A 199.7.83.42\n"
+"M.ROOT-SERVERS.NET. 3600000 IN A 202.12.27.33\n"
+"M.ROOT-SERVERS.NET. 3600000 IN AAAA 2001:DC3::35\n";
+
+static isc_result_t
+in_rootns(dns_rdataset_t *rootns, dns_name_t *name) {
+ isc_result_t result;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_ns_t ns;
+
+ if (!dns_rdataset_isassociated(rootns))
+ return (ISC_R_NOTFOUND);
+
+ result = dns_rdataset_first(rootns);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdataset_current(rootns, &rdata);
+ result = dns_rdata_tostruct(&rdata, &ns, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (dns_name_compare(name, &ns.name) == 0)
+ return (ISC_R_SUCCESS);
+ result = dns_rdataset_next(rootns);
+ dns_rdata_reset(&rdata);
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_NOTFOUND;
+ return (result);
+}
+
+static isc_result_t
+check_node(dns_rdataset_t *rootns, dns_name_t *name,
+ dns_rdatasetiter_t *rdsiter) {
+ isc_result_t result;
+ dns_rdataset_t rdataset;
+
+ dns_rdataset_init(&rdataset);
+ result = dns_rdatasetiter_first(rdsiter);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdatasetiter_current(rdsiter, &rdataset);
+ switch (rdataset.type) {
+ case dns_rdatatype_a:
+ case dns_rdatatype_aaaa:
+ result = in_rootns(rootns, name);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ break;
+ case dns_rdatatype_ns:
+ if (dns_name_compare(name, dns_rootname) == 0)
+ break;
+ /*FALLTHROUGH*/
+ default:
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ dns_rdataset_disassociate(&rdataset);
+ result = dns_rdatasetiter_next(rdsiter);
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ cleanup:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ return (result);
+}
+
+static isc_result_t
+check_hints(dns_db_t *db) {
+ isc_result_t result;
+ dns_rdataset_t rootns;
+ dns_dbiterator_t *dbiter = NULL;
+ dns_dbnode_t *node = NULL;
+ isc_stdtime_t now;
+ dns_fixedname_t fixname;
+ dns_name_t *name;
+ dns_rdatasetiter_t *rdsiter = NULL;
+
+ isc_stdtime_get(&now);
+
+ dns_fixedname_init(&fixname);
+ name = dns_fixedname_name(&fixname);
+
+ dns_rdataset_init(&rootns);
+ (void)dns_db_find(db, dns_rootname, NULL, dns_rdatatype_ns, 0,
+ now, NULL, name, &rootns, NULL);
+ result = dns_db_createiterator(db, 0, &dbiter);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_dbiterator_first(dbiter);
+ while (result == ISC_R_SUCCESS) {
+ result = dns_dbiterator_current(dbiter, &node, name);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_db_allrdatasets(db, node, NULL, now, &rdsiter);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = check_node(&rootns, name, rdsiter);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ dns_rdatasetiter_destroy(&rdsiter);
+ dns_db_detachnode(db, &node);
+ result = dns_dbiterator_next(dbiter);
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ if (dns_rdataset_isassociated(&rootns))
+ dns_rdataset_disassociate(&rootns);
+ if (rdsiter != NULL)
+ dns_rdatasetiter_destroy(&rdsiter);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (dbiter != NULL)
+ dns_dbiterator_destroy(&dbiter);
+ return (result);
+}
+
+isc_result_t
+dns_rootns_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
+ const char *filename, dns_db_t **target)
+{
+ isc_result_t result, eresult;
+ isc_buffer_t source;
+ size_t len;
+ dns_rdatacallbacks_t callbacks;
+ dns_db_t *db = NULL;
+
+ REQUIRE(target != NULL && *target == NULL);
+
+ result = dns_db_create(mctx, "rbt", dns_rootname, dns_dbtype_zone,
+ rdclass, 0, NULL, &db);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ dns_rdatacallbacks_init(&callbacks);
+
+ len = strlen(root_ns);
+ isc_buffer_init(&source, root_ns, len);
+ isc_buffer_add(&source, len);
+
+ result = dns_db_beginload(db, &callbacks.add,
+ &callbacks.add_private);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (filename != NULL) {
+ /*
+ * Load the hints from the specified filename.
+ */
+ result = dns_master_loadfile(filename, &db->origin,
+ &db->origin, db->rdclass,
+ DNS_MASTER_HINT,
+ &callbacks, db->mctx);
+ } else if (rdclass == dns_rdataclass_in) {
+ /*
+ * Default to using the Internet root servers.
+ */
+ result = dns_master_loadbuffer(&source, &db->origin,
+ &db->origin, db->rdclass,
+ DNS_MASTER_HINT,
+ &callbacks, db->mctx);
+ } else
+ result = ISC_R_NOTFOUND;
+ eresult = dns_db_endload(db, &callbacks.add_private);
+ if (result == ISC_R_SUCCESS || result == DNS_R_SEENINCLUDE)
+ result = eresult;
+ if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE)
+ goto db_detach;
+ if (check_hints(db) != ISC_R_SUCCESS)
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
+ "extra data in root hints '%s'",
+ (filename != NULL) ? filename : "<BUILT-IN>");
+ *target = db;
+ return (ISC_R_SUCCESS);
+
+ db_detach:
+ dns_db_detach(&db);
+
+ return (result);
+}
+
+static void
+report(dns_view_t *view, dns_name_t *name, isc_boolean_t missing,
+ dns_rdata_t *rdata)
+{
+ const char *viewname = "", *sep = "";
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
+ char databuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:123.123.123.123")];
+ isc_buffer_t buffer;
+ isc_result_t result;
+
+ if (strcmp(view->name, "_bind") != 0 &&
+ strcmp(view->name, "_default") != 0) {
+ viewname = view->name;
+ sep = ": view ";
+ }
+
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ dns_rdatatype_format(rdata->type, typebuf, sizeof(typebuf));
+ isc_buffer_init(&buffer, databuf, sizeof(databuf) - 1);
+ result = dns_rdata_totext(rdata, NULL, &buffer);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ databuf[isc_buffer_usedlength(&buffer)] = '\0';
+
+ if (missing)
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
+ "checkhints%s%s: %s/%s (%s) missing from hints",
+ sep, viewname, namebuf, typebuf, databuf);
+ else
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
+ "checkhints%s%s: %s/%s (%s) extra record "
+ "in hints", sep, viewname, namebuf, typebuf,
+ databuf);
+}
+
+static isc_boolean_t
+inrrset(dns_rdataset_t *rrset, dns_rdata_t *rdata) {
+ isc_result_t result;
+ dns_rdata_t current = DNS_RDATA_INIT;
+
+ result = dns_rdataset_first(rrset);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdataset_current(rrset, &current);
+ if (dns_rdata_compare(rdata, &current) == 0)
+ return (ISC_TRUE);
+ dns_rdata_reset(&current);
+ result = dns_rdataset_next(rrset);
+ }
+ return (ISC_FALSE);
+}
+
+/*
+ * Check that the address RRsets match.
+ *
+ * Note we don't complain about missing glue records.
+ */
+
+static void
+check_address_records(dns_view_t *view, dns_db_t *hints, dns_db_t *db,
+ dns_name_t *name, isc_stdtime_t now)
+{
+ isc_result_t hresult, rresult, result;
+ dns_rdataset_t hintrrset, rootrrset;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_name_t *foundname;
+ dns_fixedname_t fixed;
+
+ dns_rdataset_init(&hintrrset);
+ dns_rdataset_init(&rootrrset);
+ dns_fixedname_init(&fixed);
+ foundname = dns_fixedname_name(&fixed);
+
+ hresult = dns_db_find(hints, name, NULL, dns_rdatatype_a, 0,
+ now, NULL, foundname, &hintrrset, NULL);
+ rresult = dns_db_find(db, name, NULL, dns_rdatatype_a,
+ DNS_DBFIND_GLUEOK, now, NULL, foundname,
+ &rootrrset, NULL);
+ if (hresult == ISC_R_SUCCESS &&
+ (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE)) {
+ result = dns_rdataset_first(&rootrrset);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(&rootrrset, &rdata);
+ if (!inrrset(&hintrrset, &rdata))
+ report(view, name, ISC_TRUE, &rdata);
+ result = dns_rdataset_next(&rootrrset);
+ }
+ result = dns_rdataset_first(&hintrrset);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(&hintrrset, &rdata);
+ if (!inrrset(&rootrrset, &rdata))
+ report(view, name, ISC_FALSE, &rdata);
+ result = dns_rdataset_next(&hintrrset);
+ }
+ }
+ if (hresult == ISC_R_NOTFOUND &&
+ (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE)) {
+ result = dns_rdataset_first(&rootrrset);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(&rootrrset, &rdata);
+ report(view, name, ISC_TRUE, &rdata);
+ result = dns_rdataset_next(&rootrrset);
+ }
+ }
+ if (dns_rdataset_isassociated(&rootrrset))
+ dns_rdataset_disassociate(&rootrrset);
+ if (dns_rdataset_isassociated(&hintrrset))
+ dns_rdataset_disassociate(&hintrrset);
+
+ /*
+ * Check AAAA records.
+ */
+ hresult = dns_db_find(hints, name, NULL, dns_rdatatype_aaaa, 0,
+ now, NULL, foundname, &hintrrset, NULL);
+ rresult = dns_db_find(db, name, NULL, dns_rdatatype_aaaa,
+ DNS_DBFIND_GLUEOK, now, NULL, foundname,
+ &rootrrset, NULL);
+ if (hresult == ISC_R_SUCCESS &&
+ (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE)) {
+ result = dns_rdataset_first(&rootrrset);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(&rootrrset, &rdata);
+ if (!inrrset(&hintrrset, &rdata))
+ report(view, name, ISC_TRUE, &rdata);
+ dns_rdata_reset(&rdata);
+ result = dns_rdataset_next(&rootrrset);
+ }
+ result = dns_rdataset_first(&hintrrset);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(&hintrrset, &rdata);
+ if (!inrrset(&rootrrset, &rdata))
+ report(view, name, ISC_FALSE, &rdata);
+ dns_rdata_reset(&rdata);
+ result = dns_rdataset_next(&hintrrset);
+ }
+ }
+ if (hresult == ISC_R_NOTFOUND &&
+ (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE)) {
+ result = dns_rdataset_first(&rootrrset);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(&rootrrset, &rdata);
+ report(view, name, ISC_TRUE, &rdata);
+ dns_rdata_reset(&rdata);
+ result = dns_rdataset_next(&rootrrset);
+ }
+ }
+ if (dns_rdataset_isassociated(&rootrrset))
+ dns_rdataset_disassociate(&rootrrset);
+ if (dns_rdataset_isassociated(&hintrrset))
+ dns_rdataset_disassociate(&hintrrset);
+}
+
+void
+dns_root_checkhints(dns_view_t *view, dns_db_t *hints, dns_db_t *db) {
+ isc_result_t result;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_ns_t ns;
+ dns_rdataset_t hintns, rootns;
+ const char *viewname = "", *sep = "";
+ isc_stdtime_t now;
+ dns_name_t *name;
+ dns_fixedname_t fixed;
+
+ REQUIRE(hints != NULL);
+ REQUIRE(db != NULL);
+ REQUIRE(view != NULL);
+
+ isc_stdtime_get(&now);
+
+ if (strcmp(view->name, "_bind") != 0 &&
+ strcmp(view->name, "_default") != 0) {
+ viewname = view->name;
+ sep = ": view ";
+ }
+
+ dns_rdataset_init(&hintns);
+ dns_rdataset_init(&rootns);
+ dns_fixedname_init(&fixed);
+ name = dns_fixedname_name(&fixed);
+
+ result = dns_db_find(hints, dns_rootname, NULL, dns_rdatatype_ns, 0,
+ now, NULL, name, &hintns, NULL);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
+ "checkhints%s%s: unable to get root NS rrset "
+ "from hints: %s", sep, viewname,
+ dns_result_totext(result));
+ goto cleanup;
+ }
+
+ result = dns_db_find(db, dns_rootname, NULL, dns_rdatatype_ns, 0,
+ now, NULL, name, &rootns, NULL);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
+ "checkhints%s%s: unable to get root NS rrset "
+ "from cache: %s", sep, viewname,
+ dns_result_totext(result));
+ goto cleanup;
+ }
+
+ /*
+ * Look for missing root NS names.
+ */
+ result = dns_rdataset_first(&rootns);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdataset_current(&rootns, &rdata);
+ result = dns_rdata_tostruct(&rdata, &ns, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ result = in_rootns(&hintns, &ns.name);
+ if (result != ISC_R_SUCCESS) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ /* missing from hints */
+ dns_name_format(&ns.name, namebuf, sizeof(namebuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
+ "checkhints%s%s: unable to find root "
+ "NS '%s' in hints", sep, viewname,
+ namebuf);
+ } else
+ check_address_records(view, hints, db, &ns.name, now);
+ dns_rdata_reset(&rdata);
+ result = dns_rdataset_next(&rootns);
+ }
+ if (result != ISC_R_NOMORE) {
+ goto cleanup;
+ }
+
+ /*
+ * Look for extra root NS names.
+ */
+ result = dns_rdataset_first(&hintns);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdataset_current(&hintns, &rdata);
+ result = dns_rdata_tostruct(&rdata, &ns, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ result = in_rootns(&rootns, &ns.name);
+ if (result != ISC_R_SUCCESS) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ /* extra entry in hints */
+ dns_name_format(&ns.name, namebuf, sizeof(namebuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
+ "checkhints%s%s: extra NS '%s' in hints",
+ sep, viewname, namebuf);
+ }
+ dns_rdata_reset(&rdata);
+ result = dns_rdataset_next(&hintns);
+ }
+ if (result != ISC_R_NOMORE) {
+ goto cleanup;
+ }
+
+ cleanup:
+ if (dns_rdataset_isassociated(&rootns))
+ dns_rdataset_disassociate(&rootns);
+ if (dns_rdataset_isassociated(&hintns))
+ dns_rdataset_disassociate(&hintns);
+}
diff --git a/lib/dns/sdb.c b/lib/dns/sdb.c
new file mode 100644
index 0000000..f141a10
--- /dev/null
+++ b/lib/dns/sdb.c
@@ -0,0 +1,1551 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001, 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: sdb.c,v 1.66 2008/09/24 03:16:58 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <isc/buffer.h>
+#include <isc/lex.h>
+#include <isc/log.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/once.h>
+#include <isc/print.h>
+#include <isc/region.h>
+#include <isc/util.h>
+
+#include <dns/callbacks.h>
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/fixedname.h>
+#include <dns/log.h>
+#include <dns/rdata.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/rdatatype.h>
+#include <dns/result.h>
+#include <dns/sdb.h>
+#include <dns/types.h>
+
+#include "rdatalist_p.h"
+
+struct dns_sdbimplementation {
+ const dns_sdbmethods_t *methods;
+ void *driverdata;
+ unsigned int flags;
+ isc_mem_t *mctx;
+ isc_mutex_t driverlock;
+ dns_dbimplementation_t *dbimp;
+};
+
+struct dns_sdb {
+ /* Unlocked */
+ dns_db_t common;
+ char *zone;
+ dns_sdbimplementation_t *implementation;
+ void *dbdata;
+ isc_mutex_t lock;
+ /* Locked */
+ unsigned int references;
+};
+
+struct dns_sdblookup {
+ /* Unlocked */
+ unsigned int magic;
+ dns_sdb_t *sdb;
+ ISC_LIST(dns_rdatalist_t) lists;
+ ISC_LIST(isc_buffer_t) buffers;
+ dns_name_t *name;
+ ISC_LINK(dns_sdblookup_t) link;
+ isc_mutex_t lock;
+ dns_rdatacallbacks_t callbacks;
+ /* Locked */
+ unsigned int references;
+};
+
+typedef struct dns_sdblookup dns_sdbnode_t;
+
+struct dns_sdballnodes {
+ dns_dbiterator_t common;
+ ISC_LIST(dns_sdbnode_t) nodelist;
+ dns_sdbnode_t *current;
+ dns_sdbnode_t *origin;
+};
+
+typedef dns_sdballnodes_t sdb_dbiterator_t;
+
+typedef struct sdb_rdatasetiter {
+ dns_rdatasetiter_t common;
+ dns_rdatalist_t *current;
+} sdb_rdatasetiter_t;
+
+#define SDB_MAGIC ISC_MAGIC('S', 'D', 'B', '-')
+
+/*%
+ * Note that "impmagic" is not the first four bytes of the struct, so
+ * ISC_MAGIC_VALID cannot be used.
+ */
+#define VALID_SDB(sdb) ((sdb) != NULL && \
+ (sdb)->common.impmagic == SDB_MAGIC)
+
+#define SDBLOOKUP_MAGIC ISC_MAGIC('S','D','B','L')
+#define VALID_SDBLOOKUP(sdbl) ISC_MAGIC_VALID(sdbl, SDBLOOKUP_MAGIC)
+#define VALID_SDBNODE(sdbn) VALID_SDBLOOKUP(sdbn)
+
+/* These values are taken from RFC1537 */
+#define SDB_DEFAULT_REFRESH (60 * 60 * 8)
+#define SDB_DEFAULT_RETRY (60 * 60 * 2)
+#define SDB_DEFAULT_EXPIRE (60 * 60 * 24 * 7)
+#define SDB_DEFAULT_MINIMUM (60 * 60 * 24)
+
+/* This is a reasonable value */
+#define SDB_DEFAULT_TTL (60 * 60 * 24)
+
+#ifdef __COVERITY__
+#define MAYBE_LOCK(sdb) LOCK(&sdb->implementation->driverlock)
+#define MAYBE_UNLOCK(sdb) UNLOCK(&sdb->implementation->driverlock)
+#else
+#define MAYBE_LOCK(sdb) \
+ do { \
+ unsigned int flags = sdb->implementation->flags; \
+ if ((flags & DNS_SDBFLAG_THREADSAFE) == 0) \
+ LOCK(&sdb->implementation->driverlock); \
+ } while (0)
+
+#define MAYBE_UNLOCK(sdb) \
+ do { \
+ unsigned int flags = sdb->implementation->flags; \
+ if ((flags & DNS_SDBFLAG_THREADSAFE) == 0) \
+ UNLOCK(&sdb->implementation->driverlock); \
+ } while (0)
+#endif
+
+static int dummy;
+
+static isc_result_t dns_sdb_create(isc_mem_t *mctx, dns_name_t *origin,
+ dns_dbtype_t type, dns_rdataclass_t rdclass,
+ unsigned int argc, char *argv[],
+ void *driverarg, dns_db_t **dbp);
+
+static isc_result_t findrdataset(dns_db_t *db, dns_dbnode_t *node,
+ dns_dbversion_t *version,
+ dns_rdatatype_t type, dns_rdatatype_t covers,
+ isc_stdtime_t now, dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset);
+
+static isc_result_t createnode(dns_sdb_t *sdb, dns_sdbnode_t **nodep);
+
+static void destroynode(dns_sdbnode_t *node);
+
+static void detachnode(dns_db_t *db, dns_dbnode_t **targetp);
+
+
+static void list_tordataset(dns_rdatalist_t *rdatalist,
+ dns_db_t *db, dns_dbnode_t *node,
+ dns_rdataset_t *rdataset);
+
+static void dbiterator_destroy(dns_dbiterator_t **iteratorp);
+static isc_result_t dbiterator_first(dns_dbiterator_t *iterator);
+static isc_result_t dbiterator_last(dns_dbiterator_t *iterator);
+static isc_result_t dbiterator_seek(dns_dbiterator_t *iterator,
+ dns_name_t *name);
+static isc_result_t dbiterator_prev(dns_dbiterator_t *iterator);
+static isc_result_t dbiterator_next(dns_dbiterator_t *iterator);
+static isc_result_t dbiterator_current(dns_dbiterator_t *iterator,
+ dns_dbnode_t **nodep,
+ dns_name_t *name);
+static isc_result_t dbiterator_pause(dns_dbiterator_t *iterator);
+static isc_result_t dbiterator_origin(dns_dbiterator_t *iterator,
+ dns_name_t *name);
+
+static dns_dbiteratormethods_t dbiterator_methods = {
+ dbiterator_destroy,
+ dbiterator_first,
+ dbiterator_last,
+ dbiterator_seek,
+ dbiterator_prev,
+ dbiterator_next,
+ dbiterator_current,
+ dbiterator_pause,
+ dbiterator_origin
+};
+
+static void rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp);
+static isc_result_t rdatasetiter_first(dns_rdatasetiter_t *iterator);
+static isc_result_t rdatasetiter_next(dns_rdatasetiter_t *iterator);
+static void rdatasetiter_current(dns_rdatasetiter_t *iterator,
+ dns_rdataset_t *rdataset);
+
+static dns_rdatasetitermethods_t rdatasetiter_methods = {
+ rdatasetiter_destroy,
+ rdatasetiter_first,
+ rdatasetiter_next,
+ rdatasetiter_current
+};
+
+/*
+ * Functions used by implementors of simple databases
+ */
+isc_result_t
+dns_sdb_register(const char *drivername, const dns_sdbmethods_t *methods,
+ void *driverdata, unsigned int flags, isc_mem_t *mctx,
+ dns_sdbimplementation_t **sdbimp)
+{
+ dns_sdbimplementation_t *imp;
+ isc_result_t result;
+
+ REQUIRE(drivername != NULL);
+ REQUIRE(methods != NULL);
+ REQUIRE(methods->lookup != NULL);
+ REQUIRE(mctx != NULL);
+ REQUIRE(sdbimp != NULL && *sdbimp == NULL);
+ REQUIRE((flags & ~(DNS_SDBFLAG_RELATIVEOWNER |
+ DNS_SDBFLAG_RELATIVERDATA |
+ DNS_SDBFLAG_THREADSAFE)) == 0);
+
+ imp = isc_mem_get(mctx, sizeof(dns_sdbimplementation_t));
+ if (imp == NULL)
+ return (ISC_R_NOMEMORY);
+ imp->methods = methods;
+ imp->driverdata = driverdata;
+ imp->flags = flags;
+ imp->mctx = NULL;
+ isc_mem_attach(mctx, &imp->mctx);
+ result = isc_mutex_init(&imp->driverlock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_mctx;
+
+ imp->dbimp = NULL;
+ result = dns_db_register(drivername, dns_sdb_create, imp, mctx,
+ &imp->dbimp);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_mutex;
+ *sdbimp = imp;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup_mutex:
+ DESTROYLOCK(&imp->driverlock);
+ cleanup_mctx:
+ isc_mem_put(mctx, imp, sizeof(dns_sdbimplementation_t));
+ return (result);
+}
+
+void
+dns_sdb_unregister(dns_sdbimplementation_t **sdbimp) {
+ dns_sdbimplementation_t *imp;
+ isc_mem_t *mctx;
+
+ REQUIRE(sdbimp != NULL && *sdbimp != NULL);
+
+ imp = *sdbimp;
+ dns_db_unregister(&imp->dbimp);
+ DESTROYLOCK(&imp->driverlock);
+
+ mctx = imp->mctx;
+ isc_mem_put(mctx, imp, sizeof(dns_sdbimplementation_t));
+ isc_mem_detach(&mctx);
+
+ *sdbimp = NULL;
+}
+
+static inline unsigned int
+initial_size(unsigned int len) {
+ unsigned int size;
+
+ for (size = 1024; size < (64 * 1024); size *= 2)
+ if (len < size)
+ return (size);
+ return (65535);
+}
+
+isc_result_t
+dns_sdb_putrdata(dns_sdblookup_t *lookup, dns_rdatatype_t typeval, dns_ttl_t ttl,
+ const unsigned char *rdatap, unsigned int rdlen)
+{
+ dns_rdatalist_t *rdatalist;
+ dns_rdata_t *rdata;
+ isc_buffer_t *rdatabuf = NULL;
+ isc_result_t result;
+ isc_mem_t *mctx;
+ isc_region_t region;
+
+ mctx = lookup->sdb->common.mctx;
+
+ rdatalist = ISC_LIST_HEAD(lookup->lists);
+ while (rdatalist != NULL) {
+ if (rdatalist->type == typeval)
+ break;
+ rdatalist = ISC_LIST_NEXT(rdatalist, link);
+ }
+
+ if (rdatalist == NULL) {
+ rdatalist = isc_mem_get(mctx, sizeof(dns_rdatalist_t));
+ if (rdatalist == NULL)
+ return (ISC_R_NOMEMORY);
+ rdatalist->rdclass = lookup->sdb->common.rdclass;
+ rdatalist->type = typeval;
+ rdatalist->covers = 0;
+ rdatalist->ttl = ttl;
+ ISC_LIST_INIT(rdatalist->rdata);
+ ISC_LINK_INIT(rdatalist, link);
+ ISC_LIST_APPEND(lookup->lists, rdatalist, link);
+ } else
+ if (rdatalist->ttl != ttl)
+ return (DNS_R_BADTTL);
+
+ rdata = isc_mem_get(mctx, sizeof(dns_rdata_t));
+ if (rdata == NULL)
+ return (ISC_R_NOMEMORY);
+
+ result = isc_buffer_allocate(mctx, &rdatabuf, rdlen);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ DE_CONST(rdatap, region.base);
+ region.length = rdlen;
+ isc_buffer_copyregion(rdatabuf, &region);
+ isc_buffer_usedregion(rdatabuf, &region);
+ dns_rdata_init(rdata);
+ dns_rdata_fromregion(rdata, rdatalist->rdclass, rdatalist->type,
+ &region);
+ ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
+ ISC_LIST_APPEND(lookup->buffers, rdatabuf, link);
+ rdata = NULL;
+
+ failure:
+ if (rdata != NULL)
+ isc_mem_put(mctx, rdata, sizeof(dns_rdata_t));
+ return (result);
+}
+
+
+isc_result_t
+dns_sdb_putrr(dns_sdblookup_t *lookup, const char *type, dns_ttl_t ttl,
+ const char *data)
+{
+ unsigned int datalen;
+ dns_rdatatype_t typeval;
+ isc_textregion_t r;
+ isc_lex_t *lex = NULL;
+ isc_result_t result;
+ unsigned char *p = NULL;
+ unsigned int size = 0; /* Init to suppress compiler warning */
+ isc_mem_t *mctx;
+ dns_sdbimplementation_t *imp;
+ dns_name_t *origin;
+ isc_buffer_t b;
+ isc_buffer_t rb;
+
+ REQUIRE(VALID_SDBLOOKUP(lookup));
+ REQUIRE(type != NULL);
+ REQUIRE(data != NULL);
+
+ mctx = lookup->sdb->common.mctx;
+
+ DE_CONST(type, r.base);
+ r.length = strlen(type);
+ result = dns_rdatatype_fromtext(&typeval, &r);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ imp = lookup->sdb->implementation;
+ if ((imp->flags & DNS_SDBFLAG_RELATIVERDATA) != 0)
+ origin = &lookup->sdb->common.origin;
+ else
+ origin = dns_rootname;
+
+ result = isc_lex_create(mctx, 64, &lex);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ datalen = strlen(data);
+ size = initial_size(datalen);
+ do {
+ isc_buffer_init(&b, data, datalen);
+ isc_buffer_add(&b, datalen);
+ result = isc_lex_openbuffer(lex, &b);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ if (size >= 65535)
+ size = 65535;
+ p = isc_mem_get(mctx, size);
+ if (p == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto failure;
+ }
+ isc_buffer_init(&rb, p, size);
+ result = dns_rdata_fromtext(NULL,
+ lookup->sdb->common.rdclass,
+ typeval, lex,
+ origin, 0,
+ mctx, &rb,
+ &lookup->callbacks);
+ if (result != ISC_R_NOSPACE)
+ break;
+
+ /*
+ * Is the RR too big?
+ */
+ if (size >= 65535)
+ break;
+ isc_mem_put(mctx, p, size);
+ p = NULL;
+ size *= 2;
+ } while (result == ISC_R_NOSPACE);
+
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ result = dns_sdb_putrdata(lookup, typeval, ttl,
+ isc_buffer_base(&rb),
+ isc_buffer_usedlength(&rb));
+ failure:
+ if (p != NULL)
+ isc_mem_put(mctx, p, size);
+ if (lex != NULL)
+ isc_lex_destroy(&lex);
+
+ return (result);
+}
+
+static isc_result_t
+getnode(dns_sdballnodes_t *allnodes, const char *name, dns_sdbnode_t **nodep) {
+ dns_name_t *newname, *origin;
+ dns_fixedname_t fnewname;
+ dns_sdb_t *sdb = (dns_sdb_t *)allnodes->common.db;
+ dns_sdbimplementation_t *imp = sdb->implementation;
+ dns_sdbnode_t *sdbnode;
+ isc_mem_t *mctx = sdb->common.mctx;
+ isc_buffer_t b;
+ isc_result_t result;
+
+ dns_fixedname_init(&fnewname);
+ newname = dns_fixedname_name(&fnewname);
+
+ if ((imp->flags & DNS_SDBFLAG_RELATIVERDATA) != 0)
+ origin = &sdb->common.origin;
+ else
+ origin = dns_rootname;
+ isc_buffer_init(&b, name, strlen(name));
+ isc_buffer_add(&b, strlen(name));
+
+ result = dns_name_fromtext(newname, &b, origin, ISC_FALSE, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (allnodes->common.relative_names) {
+ /* All names are relative to the root */
+ unsigned int nlabels = dns_name_countlabels(newname);
+ dns_name_getlabelsequence(newname, 0, nlabels - 1, newname);
+ }
+
+ sdbnode = ISC_LIST_HEAD(allnodes->nodelist);
+ if (sdbnode == NULL || !dns_name_equal(sdbnode->name, newname)) {
+ sdbnode = NULL;
+ result = createnode(sdb, &sdbnode);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ sdbnode->name = isc_mem_get(mctx, sizeof(dns_name_t));
+ if (sdbnode->name == NULL) {
+ destroynode(sdbnode);
+ return (ISC_R_NOMEMORY);
+ }
+ dns_name_init(sdbnode->name, NULL);
+ result = dns_name_dup(newname, mctx, sdbnode->name);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, sdbnode->name, sizeof(dns_name_t));
+ destroynode(sdbnode);
+ return (result);
+ }
+ ISC_LIST_PREPEND(allnodes->nodelist, sdbnode, link);
+ if (allnodes->origin == NULL &&
+ dns_name_equal(newname, &sdb->common.origin))
+ allnodes->origin = sdbnode;
+ }
+ *nodep = sdbnode;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_sdb_putnamedrr(dns_sdballnodes_t *allnodes, const char *name,
+ const char *type, dns_ttl_t ttl, const char *data)
+{
+ isc_result_t result;
+ dns_sdbnode_t *sdbnode = NULL;
+ result = getnode(allnodes, name, &sdbnode);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ return (dns_sdb_putrr(sdbnode, type, ttl, data));
+}
+
+isc_result_t
+dns_sdb_putnamedrdata(dns_sdballnodes_t *allnodes, const char *name,
+ dns_rdatatype_t type, dns_ttl_t ttl,
+ const void *rdata, unsigned int rdlen)
+{
+ isc_result_t result;
+ dns_sdbnode_t *sdbnode = NULL;
+ result = getnode(allnodes, name, &sdbnode);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ return (dns_sdb_putrdata(sdbnode, type, ttl, rdata, rdlen));
+}
+
+isc_result_t
+dns_sdb_putsoa(dns_sdblookup_t *lookup, const char *mname, const char *rname,
+ isc_uint32_t serial)
+{
+ char str[2 * DNS_NAME_MAXTEXT + 5 * (sizeof("2147483647")) + 7];
+ int n;
+
+ REQUIRE(mname != NULL);
+ REQUIRE(rname != NULL);
+
+ n = snprintf(str, sizeof(str), "%s %s %u %u %u %u %u",
+ mname, rname, serial,
+ SDB_DEFAULT_REFRESH, SDB_DEFAULT_RETRY,
+ SDB_DEFAULT_EXPIRE, SDB_DEFAULT_MINIMUM);
+ if (n >= (int)sizeof(str) || n < 0)
+ return (ISC_R_NOSPACE);
+ return (dns_sdb_putrr(lookup, "SOA", SDB_DEFAULT_TTL, str));
+}
+
+/*
+ * DB routines
+ */
+
+static void
+attach(dns_db_t *source, dns_db_t **targetp) {
+ dns_sdb_t *sdb = (dns_sdb_t *) source;
+
+ REQUIRE(VALID_SDB(sdb));
+
+ LOCK(&sdb->lock);
+ REQUIRE(sdb->references > 0);
+ sdb->references++;
+ UNLOCK(&sdb->lock);
+
+ *targetp = source;
+}
+
+static void
+destroy(dns_sdb_t *sdb) {
+ isc_mem_t *mctx;
+ dns_sdbimplementation_t *imp = sdb->implementation;
+
+ mctx = sdb->common.mctx;
+
+ if (imp->methods->destroy != NULL) {
+ MAYBE_LOCK(sdb);
+ imp->methods->destroy(sdb->zone, imp->driverdata,
+ &sdb->dbdata);
+ MAYBE_UNLOCK(sdb);
+ }
+
+ isc_mem_free(mctx, sdb->zone);
+ DESTROYLOCK(&sdb->lock);
+
+ sdb->common.magic = 0;
+ sdb->common.impmagic = 0;
+
+ dns_name_free(&sdb->common.origin, mctx);
+
+ isc_mem_put(mctx, sdb, sizeof(dns_sdb_t));
+ isc_mem_detach(&mctx);
+}
+
+static void
+detach(dns_db_t **dbp) {
+ dns_sdb_t *sdb = (dns_sdb_t *)(*dbp);
+ isc_boolean_t need_destroy = ISC_FALSE;
+
+ REQUIRE(VALID_SDB(sdb));
+ LOCK(&sdb->lock);
+ REQUIRE(sdb->references > 0);
+ sdb->references--;
+ if (sdb->references == 0)
+ need_destroy = ISC_TRUE;
+ UNLOCK(&sdb->lock);
+
+ if (need_destroy)
+ destroy(sdb);
+
+ *dbp = NULL;
+}
+
+static isc_result_t
+beginload(dns_db_t *db, dns_addrdatasetfunc_t *addp, dns_dbload_t **dbloadp) {
+ UNUSED(db);
+ UNUSED(addp);
+ UNUSED(dbloadp);
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static isc_result_t
+endload(dns_db_t *db, dns_dbload_t **dbloadp) {
+ UNUSED(db);
+ UNUSED(dbloadp);
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static isc_result_t
+dump(dns_db_t *db, dns_dbversion_t *version, const char *filename,
+ dns_masterformat_t masterformat) {
+ UNUSED(db);
+ UNUSED(version);
+ UNUSED(filename);
+ UNUSED(masterformat);
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static void
+currentversion(dns_db_t *db, dns_dbversion_t **versionp) {
+ REQUIRE(versionp != NULL && *versionp == NULL);
+
+ UNUSED(db);
+
+ *versionp = (void *) &dummy;
+ return;
+}
+
+static isc_result_t
+newversion(dns_db_t *db, dns_dbversion_t **versionp) {
+ UNUSED(db);
+ UNUSED(versionp);
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static void
+attachversion(dns_db_t *db, dns_dbversion_t *source,
+ dns_dbversion_t **targetp)
+{
+ REQUIRE(source != NULL && source == (void *) &dummy);
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ UNUSED(db);
+ *targetp = source;
+ return;
+}
+
+static void
+closeversion(dns_db_t *db, dns_dbversion_t **versionp, isc_boolean_t commit) {
+ REQUIRE(versionp != NULL && *versionp == (void *) &dummy);
+ REQUIRE(commit == ISC_FALSE);
+
+ UNUSED(db);
+ UNUSED(commit);
+
+ *versionp = NULL;
+}
+
+static isc_result_t
+createnode(dns_sdb_t *sdb, dns_sdbnode_t **nodep) {
+ dns_sdbnode_t *node;
+ isc_result_t result;
+
+ node = isc_mem_get(sdb->common.mctx, sizeof(dns_sdbnode_t));
+ if (node == NULL)
+ return (ISC_R_NOMEMORY);
+
+ node->sdb = NULL;
+ attach((dns_db_t *)sdb, (dns_db_t **)&node->sdb);
+ ISC_LIST_INIT(node->lists);
+ ISC_LIST_INIT(node->buffers);
+ ISC_LINK_INIT(node, link);
+ node->name = NULL;
+ result = isc_mutex_init(&node->lock);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(sdb->common.mctx, node, sizeof(dns_sdbnode_t));
+ return (result);
+ }
+ dns_rdatacallbacks_init(&node->callbacks);
+ node->references = 1;
+ node->magic = SDBLOOKUP_MAGIC;
+
+ *nodep = node;
+ return (ISC_R_SUCCESS);
+}
+
+static void
+destroynode(dns_sdbnode_t *node) {
+ dns_rdatalist_t *list;
+ dns_rdata_t *rdata;
+ isc_buffer_t *b;
+ dns_sdb_t *sdb;
+ isc_mem_t *mctx;
+
+ sdb = node->sdb;
+ mctx = sdb->common.mctx;
+
+ while (!ISC_LIST_EMPTY(node->lists)) {
+ list = ISC_LIST_HEAD(node->lists);
+ while (!ISC_LIST_EMPTY(list->rdata)) {
+ rdata = ISC_LIST_HEAD(list->rdata);
+ ISC_LIST_UNLINK(list->rdata, rdata, link);
+ isc_mem_put(mctx, rdata, sizeof(dns_rdata_t));
+ }
+ ISC_LIST_UNLINK(node->lists, list, link);
+ isc_mem_put(mctx, list, sizeof(dns_rdatalist_t));
+ }
+
+ while (!ISC_LIST_EMPTY(node->buffers)) {
+ b = ISC_LIST_HEAD(node->buffers);
+ ISC_LIST_UNLINK(node->buffers, b, link);
+ isc_buffer_free(&b);
+ }
+
+ if (node->name != NULL) {
+ dns_name_free(node->name, mctx);
+ isc_mem_put(mctx, node->name, sizeof(dns_name_t));
+ }
+ DESTROYLOCK(&node->lock);
+ node->magic = 0;
+ isc_mem_put(mctx, node, sizeof(dns_sdbnode_t));
+ detach((dns_db_t **) (void *)&sdb);
+}
+
+static isc_result_t
+findnode(dns_db_t *db, dns_name_t *name, isc_boolean_t create,
+ dns_dbnode_t **nodep)
+{
+ dns_sdb_t *sdb = (dns_sdb_t *)db;
+ dns_sdbnode_t *node = NULL;
+ isc_result_t result;
+ isc_buffer_t b;
+ char namestr[DNS_NAME_MAXTEXT + 1];
+ isc_boolean_t isorigin;
+ dns_sdbimplementation_t *imp;
+
+ REQUIRE(VALID_SDB(sdb));
+ REQUIRE(create == ISC_FALSE);
+ REQUIRE(nodep != NULL && *nodep == NULL);
+
+ UNUSED(name);
+ UNUSED(create);
+
+ imp = sdb->implementation;
+
+ isc_buffer_init(&b, namestr, sizeof(namestr));
+ if ((imp->flags & DNS_SDBFLAG_RELATIVEOWNER) != 0) {
+ dns_name_t relname;
+ unsigned int labels;
+
+ labels = dns_name_countlabels(name) -
+ dns_name_countlabels(&db->origin);
+ dns_name_init(&relname, NULL);
+ dns_name_getlabelsequence(name, 0, labels, &relname);
+ result = dns_name_totext(&relname, ISC_TRUE, &b);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ } else {
+ result = dns_name_totext(name, ISC_TRUE, &b);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ isc_buffer_putuint8(&b, 0);
+
+ result = createnode(sdb, &node);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ isorigin = dns_name_equal(name, &sdb->common.origin);
+
+ MAYBE_LOCK(sdb);
+ result = imp->methods->lookup(sdb->zone, namestr, sdb->dbdata, node);
+ MAYBE_UNLOCK(sdb);
+ if (result != ISC_R_SUCCESS &&
+ !(result == ISC_R_NOTFOUND &&
+ isorigin && imp->methods->authority != NULL))
+ {
+ destroynode(node);
+ return (result);
+ }
+
+ if (isorigin && imp->methods->authority != NULL) {
+ MAYBE_LOCK(sdb);
+ result = imp->methods->authority(sdb->zone, sdb->dbdata, node);
+ MAYBE_UNLOCK(sdb);
+ if (result != ISC_R_SUCCESS) {
+ destroynode(node);
+ return (result);
+ }
+ }
+
+ *nodep = node;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
+ dns_rdatatype_t type, unsigned int options, isc_stdtime_t now,
+ dns_dbnode_t **nodep, dns_name_t *foundname,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+ dns_sdb_t *sdb = (dns_sdb_t *)db;
+ dns_dbnode_t *node = NULL;
+ dns_fixedname_t fname;
+ dns_rdataset_t xrdataset;
+ dns_name_t *xname;
+ unsigned int nlabels, olabels;
+ isc_result_t result;
+ unsigned int i;
+
+ REQUIRE(VALID_SDB(sdb));
+ REQUIRE(nodep == NULL || *nodep == NULL);
+ REQUIRE(version == NULL || version == (void *) &dummy);
+
+ UNUSED(options);
+ UNUSED(sdb);
+
+ if (!dns_name_issubdomain(name, &db->origin))
+ return (DNS_R_NXDOMAIN);
+
+ olabels = dns_name_countlabels(&db->origin);
+ nlabels = dns_name_countlabels(name);
+
+ dns_fixedname_init(&fname);
+ xname = dns_fixedname_name(&fname);
+
+ if (rdataset == NULL) {
+ dns_rdataset_init(&xrdataset);
+ rdataset = &xrdataset;
+ }
+
+ result = DNS_R_NXDOMAIN;
+
+ for (i = olabels; i <= nlabels; i++) {
+ /*
+ * Unless this is an explicit lookup at the origin, don't
+ * look at the origin.
+ */
+ if (i == olabels && i != nlabels)
+ continue;
+
+ /*
+ * Look up the next label.
+ */
+ dns_name_getlabelsequence(name, nlabels - i, i, xname);
+ result = findnode(db, xname, ISC_FALSE, &node);
+ if (result != ISC_R_SUCCESS) {
+ result = DNS_R_NXDOMAIN;
+ continue;
+ }
+
+ /*
+ * Look for a DNAME at the current label, unless this is
+ * the qname.
+ */
+ if (i < nlabels) {
+ result = findrdataset(db, node, version,
+ dns_rdatatype_dname,
+ 0, now, rdataset, sigrdataset);
+ if (result == ISC_R_SUCCESS) {
+ result = DNS_R_DNAME;
+ break;
+ }
+ }
+
+ /*
+ * Look for an NS at the current label, unless this is the
+ * origin or glue is ok.
+ */
+ if (i != olabels && (options & DNS_DBFIND_GLUEOK) == 0) {
+ result = findrdataset(db, node, version,
+ dns_rdatatype_ns,
+ 0, now, rdataset, sigrdataset);
+ if (result == ISC_R_SUCCESS) {
+ if (i == nlabels && type == dns_rdatatype_any)
+ {
+ result = DNS_R_ZONECUT;
+ dns_rdataset_disassociate(rdataset);
+ if (sigrdataset != NULL)
+ dns_rdataset_disassociate
+ (sigrdataset);
+ } else
+ result = DNS_R_DELEGATION;
+ break;
+ }
+ }
+
+ /*
+ * If the current name is not the qname, add another label
+ * and try again.
+ */
+ if (i < nlabels) {
+ destroynode(node);
+ node = NULL;
+ continue;
+ }
+
+ /*
+ * If we're looking for ANY, we're done.
+ */
+ if (type == dns_rdatatype_any) {
+ result = ISC_R_SUCCESS;
+ break;
+ }
+
+ /*
+ * Look for the qtype.
+ */
+ result = findrdataset(db, node, version, type,
+ 0, now, rdataset, sigrdataset);
+ if (result == ISC_R_SUCCESS)
+ break;
+
+ /*
+ * Look for a CNAME
+ */
+ if (type != dns_rdatatype_cname) {
+ result = findrdataset(db, node, version,
+ dns_rdatatype_cname,
+ 0, now, rdataset, sigrdataset);
+ if (result == ISC_R_SUCCESS) {
+ result = DNS_R_CNAME;
+ break;
+ }
+ }
+
+ result = DNS_R_NXRRSET;
+ break;
+ }
+
+ if (rdataset == &xrdataset && dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+
+ if (foundname != NULL) {
+ isc_result_t xresult;
+
+ xresult = dns_name_copy(xname, foundname, NULL);
+ if (xresult != ISC_R_SUCCESS) {
+ if (node != NULL)
+ destroynode(node);
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ return (DNS_R_BADDB);
+ }
+ }
+
+ if (nodep != NULL)
+ *nodep = node;
+ else if (node != NULL)
+ detachnode(db, &node);
+
+ return (result);
+}
+
+static isc_result_t
+findzonecut(dns_db_t *db, dns_name_t *name, unsigned int options,
+ isc_stdtime_t now, dns_dbnode_t **nodep, dns_name_t *foundname,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+ UNUSED(db);
+ UNUSED(name);
+ UNUSED(options);
+ UNUSED(now);
+ UNUSED(nodep);
+ UNUSED(foundname);
+ UNUSED(rdataset);
+ UNUSED(sigrdataset);
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static void
+attachnode(dns_db_t *db, dns_dbnode_t *source, dns_dbnode_t **targetp) {
+ dns_sdb_t *sdb = (dns_sdb_t *)db;
+ dns_sdbnode_t *node = (dns_sdbnode_t *)source;
+
+ REQUIRE(VALID_SDB(sdb));
+
+ UNUSED(sdb);
+
+ LOCK(&node->lock);
+ INSIST(node->references > 0);
+ node->references++;
+ INSIST(node->references != 0); /* Catch overflow. */
+ UNLOCK(&node->lock);
+
+ *targetp = source;
+}
+
+static void
+detachnode(dns_db_t *db, dns_dbnode_t **targetp) {
+ dns_sdb_t *sdb = (dns_sdb_t *)db;
+ dns_sdbnode_t *node;
+ isc_boolean_t need_destroy = ISC_FALSE;
+
+ REQUIRE(VALID_SDB(sdb));
+ REQUIRE(targetp != NULL && *targetp != NULL);
+
+ UNUSED(sdb);
+
+ node = (dns_sdbnode_t *)(*targetp);
+
+ LOCK(&node->lock);
+ INSIST(node->references > 0);
+ node->references--;
+ if (node->references == 0)
+ need_destroy = ISC_TRUE;
+ UNLOCK(&node->lock);
+
+ if (need_destroy)
+ destroynode(node);
+
+ *targetp = NULL;
+}
+
+static isc_result_t
+expirenode(dns_db_t *db, dns_dbnode_t *node, isc_stdtime_t now) {
+ UNUSED(db);
+ UNUSED(node);
+ UNUSED(now);
+ INSIST(0);
+ return (ISC_R_UNEXPECTED);
+}
+
+static void
+printnode(dns_db_t *db, dns_dbnode_t *node, FILE *out) {
+ UNUSED(db);
+ UNUSED(node);
+ UNUSED(out);
+ return;
+}
+
+static isc_result_t
+createiterator(dns_db_t *db, unsigned int options, dns_dbiterator_t **iteratorp)
+{
+ dns_sdb_t *sdb = (dns_sdb_t *)db;
+ sdb_dbiterator_t *sdbiter;
+ dns_sdbimplementation_t *imp = sdb->implementation;
+ isc_result_t result;
+
+ REQUIRE(VALID_SDB(sdb));
+
+ if (imp->methods->allnodes == NULL)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ if ((options & DNS_DB_NSEC3ONLY) != 0 ||
+ (options & DNS_DB_NONSEC3) != 0)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ sdbiter = isc_mem_get(sdb->common.mctx, sizeof(sdb_dbiterator_t));
+ if (sdbiter == NULL)
+ return (ISC_R_NOMEMORY);
+
+ sdbiter->common.methods = &dbiterator_methods;
+ sdbiter->common.db = NULL;
+ dns_db_attach(db, &sdbiter->common.db);
+ sdbiter->common.relative_names = ISC_TF(options & DNS_DB_RELATIVENAMES);
+ sdbiter->common.magic = DNS_DBITERATOR_MAGIC;
+ ISC_LIST_INIT(sdbiter->nodelist);
+ sdbiter->current = NULL;
+ sdbiter->origin = NULL;
+
+ MAYBE_LOCK(sdb);
+ result = imp->methods->allnodes(sdb->zone, sdb->dbdata, sdbiter);
+ MAYBE_UNLOCK(sdb);
+ if (result != ISC_R_SUCCESS) {
+ dbiterator_destroy((dns_dbiterator_t **) (void *)&sdbiter);
+ return (result);
+ }
+
+ if (sdbiter->origin != NULL) {
+ ISC_LIST_UNLINK(sdbiter->nodelist, sdbiter->origin, link);
+ ISC_LIST_PREPEND(sdbiter->nodelist, sdbiter->origin, link);
+ }
+
+ *iteratorp = (dns_dbiterator_t *)sdbiter;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ dns_rdatatype_t type, dns_rdatatype_t covers,
+ isc_stdtime_t now, dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset)
+{
+ dns_rdatalist_t *list;
+ dns_sdbnode_t *sdbnode = (dns_sdbnode_t *)node;
+
+ REQUIRE(VALID_SDBNODE(node));
+
+ UNUSED(db);
+ UNUSED(version);
+ UNUSED(covers);
+ UNUSED(now);
+ UNUSED(sigrdataset);
+
+ if (type == dns_rdatatype_rrsig)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ list = ISC_LIST_HEAD(sdbnode->lists);
+ while (list != NULL) {
+ if (list->type == type)
+ break;
+ list = ISC_LIST_NEXT(list, link);
+ }
+ if (list == NULL)
+ return (ISC_R_NOTFOUND);
+
+ list_tordataset(list, db, node, rdataset);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ isc_stdtime_t now, dns_rdatasetiter_t **iteratorp)
+{
+ sdb_rdatasetiter_t *iterator;
+
+ REQUIRE(version == NULL || version == &dummy);
+
+ UNUSED(version);
+ UNUSED(now);
+
+ iterator = isc_mem_get(db->mctx, sizeof(sdb_rdatasetiter_t));
+ if (iterator == NULL)
+ return (ISC_R_NOMEMORY);
+
+ iterator->common.magic = DNS_RDATASETITER_MAGIC;
+ iterator->common.methods = &rdatasetiter_methods;
+ iterator->common.db = db;
+ iterator->common.node = NULL;
+ attachnode(db, node, &iterator->common.node);
+ iterator->common.version = version;
+ iterator->common.now = now;
+
+ *iteratorp = (dns_rdatasetiter_t *)iterator;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ isc_stdtime_t now, dns_rdataset_t *rdataset, unsigned int options,
+ dns_rdataset_t *addedrdataset)
+{
+ UNUSED(db);
+ UNUSED(node);
+ UNUSED(version);
+ UNUSED(now);
+ UNUSED(rdataset);
+ UNUSED(options);
+ UNUSED(addedrdataset);
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static isc_result_t
+subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ dns_rdataset_t *rdataset, unsigned int options,
+ dns_rdataset_t *newrdataset)
+{
+ UNUSED(db);
+ UNUSED(node);
+ UNUSED(version);
+ UNUSED(rdataset);
+ UNUSED(options);
+ UNUSED(newrdataset);
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static isc_result_t
+deleterdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ dns_rdatatype_t type, dns_rdatatype_t covers)
+{
+ UNUSED(db);
+ UNUSED(node);
+ UNUSED(version);
+ UNUSED(type);
+ UNUSED(covers);
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static isc_boolean_t
+issecure(dns_db_t *db) {
+ UNUSED(db);
+
+ return (ISC_FALSE);
+}
+
+static unsigned int
+nodecount(dns_db_t *db) {
+ UNUSED(db);
+
+ return (0);
+}
+
+static isc_boolean_t
+ispersistent(dns_db_t *db) {
+ UNUSED(db);
+ return (ISC_TRUE);
+}
+
+static void
+overmem(dns_db_t *db, isc_boolean_t overmem) {
+ UNUSED(db);
+ UNUSED(overmem);
+}
+
+static void
+settask(dns_db_t *db, isc_task_t *task) {
+ UNUSED(db);
+ UNUSED(task);
+}
+
+
+static dns_dbmethods_t sdb_methods = {
+ attach,
+ detach,
+ beginload,
+ endload,
+ dump,
+ currentversion,
+ newversion,
+ attachversion,
+ closeversion,
+ findnode,
+ find,
+ findzonecut,
+ attachnode,
+ detachnode,
+ expirenode,
+ printnode,
+ createiterator,
+ findrdataset,
+ allrdatasets,
+ addrdataset,
+ subtractrdataset,
+ deleterdataset,
+ issecure,
+ nodecount,
+ ispersistent,
+ overmem,
+ settask,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static isc_result_t
+dns_sdb_create(isc_mem_t *mctx, dns_name_t *origin, dns_dbtype_t type,
+ dns_rdataclass_t rdclass, unsigned int argc, char *argv[],
+ void *driverarg, dns_db_t **dbp)
+{
+ dns_sdb_t *sdb;
+ isc_result_t result;
+ char zonestr[DNS_NAME_MAXTEXT + 1];
+ isc_buffer_t b;
+ dns_sdbimplementation_t *imp;
+
+ REQUIRE(driverarg != NULL);
+
+ imp = driverarg;
+
+ if (type != dns_dbtype_zone)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ sdb = isc_mem_get(mctx, sizeof(dns_sdb_t));
+ if (sdb == NULL)
+ return (ISC_R_NOMEMORY);
+ memset(sdb, 0, sizeof(dns_sdb_t));
+
+ dns_name_init(&sdb->common.origin, NULL);
+ sdb->common.attributes = 0;
+ sdb->common.methods = &sdb_methods;
+ sdb->common.rdclass = rdclass;
+ sdb->common.mctx = NULL;
+ sdb->implementation = imp;
+
+ isc_mem_attach(mctx, &sdb->common.mctx);
+
+ result = isc_mutex_init(&sdb->lock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_mctx;
+
+ result = dns_name_dupwithoffsets(origin, mctx, &sdb->common.origin);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_lock;
+
+ isc_buffer_init(&b, zonestr, sizeof(zonestr));
+ result = dns_name_totext(origin, ISC_TRUE, &b);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_origin;
+ isc_buffer_putuint8(&b, 0);
+
+ sdb->zone = isc_mem_strdup(mctx, zonestr);
+ if (sdb->zone == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_origin;
+ }
+
+ sdb->dbdata = NULL;
+ if (imp->methods->create != NULL) {
+ MAYBE_LOCK(sdb);
+ result = imp->methods->create(sdb->zone, argc, argv,
+ imp->driverdata, &sdb->dbdata);
+ MAYBE_UNLOCK(sdb);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_zonestr;
+ }
+
+ sdb->references = 1;
+
+ sdb->common.magic = DNS_DB_MAGIC;
+ sdb->common.impmagic = SDB_MAGIC;
+
+ *dbp = (dns_db_t *)sdb;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup_zonestr:
+ isc_mem_free(mctx, sdb->zone);
+ cleanup_origin:
+ dns_name_free(&sdb->common.origin, mctx);
+ cleanup_lock:
+ isc_mutex_destroy(&sdb->lock);
+ cleanup_mctx:
+ isc_mem_put(mctx, sdb, sizeof(dns_sdb_t));
+ isc_mem_detach(&mctx);
+
+ return (result);
+}
+
+
+/*
+ * Rdataset Methods
+ */
+
+static void
+disassociate(dns_rdataset_t *rdataset) {
+ dns_dbnode_t *node = rdataset->private5;
+ dns_sdbnode_t *sdbnode = (dns_sdbnode_t *) node;
+ dns_db_t *db = (dns_db_t *) sdbnode->sdb;
+
+ detachnode(db, &node);
+ isc__rdatalist_disassociate(rdataset);
+}
+
+static void
+rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target) {
+ dns_dbnode_t *node = source->private5;
+ dns_sdbnode_t *sdbnode = (dns_sdbnode_t *) node;
+ dns_db_t *db = (dns_db_t *) sdbnode->sdb;
+ dns_dbnode_t *tempdb = NULL;
+
+ isc__rdatalist_clone(source, target);
+ attachnode(db, node, &tempdb);
+ source->private5 = tempdb;
+}
+
+static dns_rdatasetmethods_t methods = {
+ disassociate,
+ isc__rdatalist_first,
+ isc__rdatalist_next,
+ isc__rdatalist_current,
+ rdataset_clone,
+ isc__rdatalist_count,
+ isc__rdatalist_addnoqname,
+ isc__rdatalist_getnoqname,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static void
+list_tordataset(dns_rdatalist_t *rdatalist,
+ dns_db_t *db, dns_dbnode_t *node,
+ dns_rdataset_t *rdataset)
+{
+ /*
+ * The sdb rdataset is an rdatalist with some additions.
+ * - private1 & private2 are used by the rdatalist.
+ * - private3 & private 4 are unused.
+ * - private5 is the node.
+ */
+
+ /* This should never fail. */
+ RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, rdataset) ==
+ ISC_R_SUCCESS);
+
+ rdataset->methods = &methods;
+ dns_db_attachnode(db, node, &rdataset->private5);
+}
+
+/*
+ * Database Iterator Methods
+ */
+static void
+dbiterator_destroy(dns_dbiterator_t **iteratorp) {
+ sdb_dbiterator_t *sdbiter = (sdb_dbiterator_t *)(*iteratorp);
+ dns_sdb_t *sdb = (dns_sdb_t *)sdbiter->common.db;
+
+ while (!ISC_LIST_EMPTY(sdbiter->nodelist)) {
+ dns_sdbnode_t *node;
+ node = ISC_LIST_HEAD(sdbiter->nodelist);
+ ISC_LIST_UNLINK(sdbiter->nodelist, node, link);
+ destroynode(node);
+ }
+
+ dns_db_detach(&sdbiter->common.db);
+ isc_mem_put(sdb->common.mctx, sdbiter, sizeof(sdb_dbiterator_t));
+
+ *iteratorp = NULL;
+}
+
+static isc_result_t
+dbiterator_first(dns_dbiterator_t *iterator) {
+ sdb_dbiterator_t *sdbiter = (sdb_dbiterator_t *)iterator;
+
+ sdbiter->current = ISC_LIST_HEAD(sdbiter->nodelist);
+ if (sdbiter->current == NULL)
+ return (ISC_R_NOMORE);
+ else
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+dbiterator_last(dns_dbiterator_t *iterator) {
+ sdb_dbiterator_t *sdbiter = (sdb_dbiterator_t *)iterator;
+
+ sdbiter->current = ISC_LIST_TAIL(sdbiter->nodelist);
+ if (sdbiter->current == NULL)
+ return (ISC_R_NOMORE);
+ else
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+dbiterator_seek(dns_dbiterator_t *iterator, dns_name_t *name) {
+ sdb_dbiterator_t *sdbiter = (sdb_dbiterator_t *)iterator;
+
+ sdbiter->current = ISC_LIST_HEAD(sdbiter->nodelist);
+ while (sdbiter->current != NULL)
+ if (dns_name_equal(sdbiter->current->name, name))
+ return (ISC_R_SUCCESS);
+ return (ISC_R_NOTFOUND);
+}
+
+static isc_result_t
+dbiterator_prev(dns_dbiterator_t *iterator) {
+ sdb_dbiterator_t *sdbiter = (sdb_dbiterator_t *)iterator;
+
+ sdbiter->current = ISC_LIST_PREV(sdbiter->current, link);
+ if (sdbiter->current == NULL)
+ return (ISC_R_NOMORE);
+ else
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+dbiterator_next(dns_dbiterator_t *iterator) {
+ sdb_dbiterator_t *sdbiter = (sdb_dbiterator_t *)iterator;
+
+ sdbiter->current = ISC_LIST_NEXT(sdbiter->current, link);
+ if (sdbiter->current == NULL)
+ return (ISC_R_NOMORE);
+ else
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+dbiterator_current(dns_dbiterator_t *iterator, dns_dbnode_t **nodep,
+ dns_name_t *name)
+{
+ sdb_dbiterator_t *sdbiter = (sdb_dbiterator_t *)iterator;
+
+ attachnode(iterator->db, sdbiter->current, nodep);
+ if (name != NULL)
+ return (dns_name_copy(sdbiter->current->name, name, NULL));
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+dbiterator_pause(dns_dbiterator_t *iterator) {
+ UNUSED(iterator);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name) {
+ UNUSED(iterator);
+ return (dns_name_copy(dns_rootname, name, NULL));
+}
+
+/*
+ * Rdataset Iterator Methods
+ */
+
+static void
+rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp) {
+ sdb_rdatasetiter_t *sdbiterator = (sdb_rdatasetiter_t *)(*iteratorp);
+ detachnode(sdbiterator->common.db, &sdbiterator->common.node);
+ isc_mem_put(sdbiterator->common.db->mctx, sdbiterator,
+ sizeof(sdb_rdatasetiter_t));
+ *iteratorp = NULL;
+}
+
+static isc_result_t
+rdatasetiter_first(dns_rdatasetiter_t *iterator) {
+ sdb_rdatasetiter_t *sdbiterator = (sdb_rdatasetiter_t *)iterator;
+ dns_sdbnode_t *sdbnode = (dns_sdbnode_t *)iterator->node;
+
+ if (ISC_LIST_EMPTY(sdbnode->lists))
+ return (ISC_R_NOMORE);
+ sdbiterator->current = ISC_LIST_HEAD(sdbnode->lists);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+rdatasetiter_next(dns_rdatasetiter_t *iterator) {
+ sdb_rdatasetiter_t *sdbiterator = (sdb_rdatasetiter_t *)iterator;
+
+ sdbiterator->current = ISC_LIST_NEXT(sdbiterator->current, link);
+ if (sdbiterator->current == NULL)
+ return (ISC_R_NOMORE);
+ else
+ return (ISC_R_SUCCESS);
+}
+
+static void
+rdatasetiter_current(dns_rdatasetiter_t *iterator, dns_rdataset_t *rdataset) {
+ sdb_rdatasetiter_t *sdbiterator = (sdb_rdatasetiter_t *)iterator;
+
+ list_tordataset(sdbiterator->current, iterator->db, iterator->node,
+ rdataset);
+}
diff --git a/lib/dns/sdlz.c b/lib/dns/sdlz.c
new file mode 100644
index 0000000..ec82c3e
--- /dev/null
+++ b/lib/dns/sdlz.c
@@ -0,0 +1,1799 @@
+/*
+ * Portions Copyright (C) 2005-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ *
+ * 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 STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET 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.
+ *
+ * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was
+ * conceived and contributed by Rob Butler.
+ *
+ * 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 ROB BUTLER
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * ROB BUTLER 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.
+ */
+
+/* $Id: sdlz.c,v 1.18 2008/09/24 02:46:22 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+#include <string.h>
+
+#include <isc/buffer.h>
+#include <isc/lex.h>
+#include <isc/log.h>
+#include <isc/rwlock.h>
+#include <isc/string.h>
+#include <isc/util.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/once.h>
+#include <isc/print.h>
+#include <isc/region.h>
+
+#include <dns/callbacks.h>
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/dlz.h>
+#include <dns/fixedname.h>
+#include <dns/log.h>
+#include <dns/rdata.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/rdatatype.h>
+#include <dns/result.h>
+#include <dns/master.h>
+#include <dns/sdlz.h>
+#include <dns/types.h>
+
+#include "rdatalist_p.h"
+
+/*
+ * Private Types
+ */
+
+struct dns_sdlzimplementation {
+ const dns_sdlzmethods_t *methods;
+ isc_mem_t *mctx;
+ void *driverarg;
+ unsigned int flags;
+ isc_mutex_t driverlock;
+ dns_dlzimplementation_t *dlz_imp;
+};
+
+struct dns_sdlz_db {
+ /* Unlocked */
+ dns_db_t common;
+ void *dbdata;
+ dns_sdlzimplementation_t *dlzimp;
+ isc_mutex_t refcnt_lock;
+ /* Locked */
+ unsigned int references;
+};
+
+struct dns_sdlzlookup {
+ /* Unlocked */
+ unsigned int magic;
+ dns_sdlz_db_t *sdlz;
+ ISC_LIST(dns_rdatalist_t) lists;
+ ISC_LIST(isc_buffer_t) buffers;
+ dns_name_t *name;
+ ISC_LINK(dns_sdlzlookup_t) link;
+ isc_mutex_t lock;
+ dns_rdatacallbacks_t callbacks;
+ /* Locked */
+ unsigned int references;
+};
+
+typedef struct dns_sdlzlookup dns_sdlznode_t;
+
+struct dns_sdlzallnodes {
+ dns_dbiterator_t common;
+ ISC_LIST(dns_sdlznode_t) nodelist;
+ dns_sdlznode_t *current;
+ dns_sdlznode_t *origin;
+};
+
+typedef dns_sdlzallnodes_t sdlz_dbiterator_t;
+
+typedef struct sdlz_rdatasetiter {
+ dns_rdatasetiter_t common;
+ dns_rdatalist_t *current;
+} sdlz_rdatasetiter_t;
+
+
+#define SDLZDB_MAGIC ISC_MAGIC('D', 'L', 'Z', 'S')
+
+/*
+ * Note that "impmagic" is not the first four bytes of the struct, so
+ * ISC_MAGIC_VALID cannot be used.
+ */
+
+#define VALID_SDLZDB(sdlzdb) ((sdlzdb) != NULL && \
+ (sdlzdb)->common.impmagic == SDLZDB_MAGIC)
+
+#define SDLZLOOKUP_MAGIC ISC_MAGIC('D','L','Z','L')
+#define VALID_SDLZLOOKUP(sdlzl) ISC_MAGIC_VALID(sdlzl, SDLZLOOKUP_MAGIC)
+#define VALID_SDLZNODE(sdlzn) VALID_SDLZLOOKUP(sdlzn)
+
+/* These values are taken from RFC 1537 */
+#define SDLZ_DEFAULT_REFRESH (60 * 60 * 8)
+#define SDLZ_DEFAULT_RETRY (60 * 60 * 2)
+#define SDLZ_DEFAULT_EXPIRE (60 * 60 * 24 * 7)
+#define SDLZ_DEFAULT_MINIMUM (60 * 60 * 24)
+
+/* This is a reasonable value */
+#define SDLZ_DEFAULT_TTL (60 * 60 * 24)
+
+static int dummy;
+
+#ifdef __COVERITY__
+#define MAYBE_LOCK(imp) LOCK(&imp->driverlock)
+#define MAYBE_UNLOCK(imp) UNLOCK(&imp->driverlock)
+#else
+#define MAYBE_LOCK(imp) \
+ do { \
+ unsigned int flags = imp->flags; \
+ if ((flags & DNS_SDLZFLAG_THREADSAFE) == 0) \
+ LOCK(&imp->driverlock); \
+ } while (0)
+
+#define MAYBE_UNLOCK(imp) \
+ do { \
+ unsigned int flags = imp->flags; \
+ if ((flags & DNS_SDLZFLAG_THREADSAFE) == 0) \
+ UNLOCK(&imp->driverlock); \
+ } while (0)
+#endif
+
+/*
+ * Forward references. Try to keep these to a minimum.
+ */
+
+static void list_tordataset(dns_rdatalist_t *rdatalist,
+ dns_db_t *db, dns_dbnode_t *node,
+ dns_rdataset_t *rdataset);
+
+static void detachnode(dns_db_t *db, dns_dbnode_t **targetp);
+
+static void dbiterator_destroy(dns_dbiterator_t **iteratorp);
+static isc_result_t dbiterator_first(dns_dbiterator_t *iterator);
+static isc_result_t dbiterator_last(dns_dbiterator_t *iterator);
+static isc_result_t dbiterator_seek(dns_dbiterator_t *iterator,
+ dns_name_t *name);
+static isc_result_t dbiterator_prev(dns_dbiterator_t *iterator);
+static isc_result_t dbiterator_next(dns_dbiterator_t *iterator);
+static isc_result_t dbiterator_current(dns_dbiterator_t *iterator,
+ dns_dbnode_t **nodep,
+ dns_name_t *name);
+static isc_result_t dbiterator_pause(dns_dbiterator_t *iterator);
+static isc_result_t dbiterator_origin(dns_dbiterator_t *iterator,
+ dns_name_t *name);
+
+static dns_dbiteratormethods_t dbiterator_methods = {
+ dbiterator_destroy,
+ dbiterator_first,
+ dbiterator_last,
+ dbiterator_seek,
+ dbiterator_prev,
+ dbiterator_next,
+ dbiterator_current,
+ dbiterator_pause,
+ dbiterator_origin
+};
+
+/*
+ * Utility functions
+ */
+
+/*% Converts the input string to lowercase, in place. */
+
+static void
+dns_sdlz_tolower(char *str) {
+
+ unsigned int len = strlen(str);
+ unsigned int i;
+
+ for (i = 0; i < len; i++) {
+ if (str[i] >= 'A' && str[i] <= 'Z')
+ str[i] += 32;
+ }
+
+}
+
+static inline unsigned int
+initial_size(const char *data) {
+ unsigned int len = (strlen(data) / 64) + 1;
+ return (len * 64 + 64);
+}
+
+/*
+ * Rdataset Iterator Methods. These methods were "borrowed" from the SDB
+ * driver interface. See the SDB driver interface documentation for more info.
+ */
+
+static void
+rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp) {
+ sdlz_rdatasetiter_t *sdlziterator =
+ (sdlz_rdatasetiter_t *)(*iteratorp);
+
+ detachnode(sdlziterator->common.db, &sdlziterator->common.node);
+ isc_mem_put(sdlziterator->common.db->mctx, sdlziterator,
+ sizeof(sdlz_rdatasetiter_t));
+ *iteratorp = NULL;
+}
+
+static isc_result_t
+rdatasetiter_first(dns_rdatasetiter_t *iterator) {
+ sdlz_rdatasetiter_t *sdlziterator = (sdlz_rdatasetiter_t *)iterator;
+ dns_sdlznode_t *sdlznode = (dns_sdlznode_t *)iterator->node;
+
+ if (ISC_LIST_EMPTY(sdlznode->lists))
+ return (ISC_R_NOMORE);
+ sdlziterator->current = ISC_LIST_HEAD(sdlznode->lists);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+rdatasetiter_next(dns_rdatasetiter_t *iterator) {
+ sdlz_rdatasetiter_t *sdlziterator = (sdlz_rdatasetiter_t *)iterator;
+
+ sdlziterator->current = ISC_LIST_NEXT(sdlziterator->current, link);
+ if (sdlziterator->current == NULL)
+ return (ISC_R_NOMORE);
+ else
+ return (ISC_R_SUCCESS);
+}
+
+static void
+rdatasetiter_current(dns_rdatasetiter_t *iterator, dns_rdataset_t *rdataset) {
+ sdlz_rdatasetiter_t *sdlziterator = (sdlz_rdatasetiter_t *)iterator;
+
+ list_tordataset(sdlziterator->current, iterator->db, iterator->node,
+ rdataset);
+}
+
+static dns_rdatasetitermethods_t rdatasetiter_methods = {
+ rdatasetiter_destroy,
+ rdatasetiter_first,
+ rdatasetiter_next,
+ rdatasetiter_current
+};
+
+/*
+ * DB routines. These methods were "borrowed" from the SDB driver interface.
+ * See the SDB driver interface documentation for more info.
+ */
+
+static void
+attach(dns_db_t *source, dns_db_t **targetp) {
+ dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *) source;
+
+ REQUIRE(VALID_SDLZDB(sdlz));
+
+ LOCK(&sdlz->refcnt_lock);
+ REQUIRE(sdlz->references > 0);
+ sdlz->references++;
+ UNLOCK(&sdlz->refcnt_lock);
+
+ *targetp = source;
+}
+
+static void
+destroy(dns_sdlz_db_t *sdlz) {
+ isc_mem_t *mctx;
+ mctx = sdlz->common.mctx;
+
+ sdlz->common.magic = 0;
+ sdlz->common.impmagic = 0;
+
+ isc_mutex_destroy(&sdlz->refcnt_lock);
+
+ dns_name_free(&sdlz->common.origin, mctx);
+
+ isc_mem_put(mctx, sdlz, sizeof(dns_sdlz_db_t));
+ isc_mem_detach(&mctx);
+}
+
+static void
+detach(dns_db_t **dbp) {
+ dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)(*dbp);
+ isc_boolean_t need_destroy = ISC_FALSE;
+
+ REQUIRE(VALID_SDLZDB(sdlz));
+ LOCK(&sdlz->refcnt_lock);
+ REQUIRE(sdlz->references > 0);
+ sdlz->references--;
+ if (sdlz->references == 0)
+ need_destroy = ISC_TRUE;
+ UNLOCK(&sdlz->refcnt_lock);
+
+ if (need_destroy)
+ destroy(sdlz);
+
+ *dbp = NULL;
+}
+
+static isc_result_t
+beginload(dns_db_t *db, dns_addrdatasetfunc_t *addp, dns_dbload_t **dbloadp) {
+ UNUSED(db);
+ UNUSED(addp);
+ UNUSED(dbloadp);
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static isc_result_t
+endload(dns_db_t *db, dns_dbload_t **dbloadp) {
+ UNUSED(db);
+ UNUSED(dbloadp);
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static isc_result_t
+dump(dns_db_t *db, dns_dbversion_t *version, const char *filename,
+ dns_masterformat_t masterformat)
+{
+ UNUSED(db);
+ UNUSED(version);
+ UNUSED(filename);
+ UNUSED(masterformat);
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static void
+currentversion(dns_db_t *db, dns_dbversion_t **versionp) {
+ REQUIRE(versionp != NULL && *versionp == NULL);
+
+ UNUSED(db);
+
+ *versionp = (void *) &dummy;
+ return;
+}
+
+static isc_result_t
+newversion(dns_db_t *db, dns_dbversion_t **versionp) {
+ UNUSED(db);
+ UNUSED(versionp);
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static void
+attachversion(dns_db_t *db, dns_dbversion_t *source,
+ dns_dbversion_t **targetp)
+{
+ REQUIRE(source != NULL && source == (void *) &dummy);
+
+ UNUSED(db);
+ UNUSED(source);
+ UNUSED(targetp);
+ *targetp = source;
+}
+
+static void
+closeversion(dns_db_t *db, dns_dbversion_t **versionp, isc_boolean_t commit) {
+ REQUIRE(versionp != NULL && *versionp == (void *) &dummy);
+ REQUIRE(commit == ISC_FALSE);
+
+ UNUSED(db);
+ UNUSED(commit);
+
+ *versionp = NULL;
+}
+
+static isc_result_t
+createnode(dns_sdlz_db_t *sdlz, dns_sdlznode_t **nodep) {
+ dns_sdlznode_t *node;
+ isc_result_t result;
+
+ node = isc_mem_get(sdlz->common.mctx, sizeof(dns_sdlznode_t));
+ if (node == NULL)
+ return (ISC_R_NOMEMORY);
+
+ node->sdlz = NULL;
+ attach((dns_db_t *)sdlz, (dns_db_t **)&node->sdlz);
+ ISC_LIST_INIT(node->lists);
+ ISC_LIST_INIT(node->buffers);
+ ISC_LINK_INIT(node, link);
+ node->name = NULL;
+ result = isc_mutex_init(&node->lock);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_mutex_init() failed: %s",
+ isc_result_totext(result));
+ isc_mem_put(sdlz->common.mctx, node, sizeof(dns_sdlznode_t));
+ return (ISC_R_UNEXPECTED);
+ }
+ dns_rdatacallbacks_init(&node->callbacks);
+ node->references = 1;
+ node->magic = SDLZLOOKUP_MAGIC;
+
+ *nodep = node;
+ return (ISC_R_SUCCESS);
+}
+
+static void
+destroynode(dns_sdlznode_t *node) {
+ dns_rdatalist_t *list;
+ dns_rdata_t *rdata;
+ isc_buffer_t *b;
+ dns_sdlz_db_t *sdlz;
+ dns_db_t *db;
+ isc_mem_t *mctx;
+
+ sdlz = node->sdlz;
+ mctx = sdlz->common.mctx;
+
+ while (!ISC_LIST_EMPTY(node->lists)) {
+ list = ISC_LIST_HEAD(node->lists);
+ while (!ISC_LIST_EMPTY(list->rdata)) {
+ rdata = ISC_LIST_HEAD(list->rdata);
+ ISC_LIST_UNLINK(list->rdata, rdata, link);
+ isc_mem_put(mctx, rdata, sizeof(dns_rdata_t));
+ }
+ ISC_LIST_UNLINK(node->lists, list, link);
+ isc_mem_put(mctx, list, sizeof(dns_rdatalist_t));
+ }
+
+ while (!ISC_LIST_EMPTY(node->buffers)) {
+ b = ISC_LIST_HEAD(node->buffers);
+ ISC_LIST_UNLINK(node->buffers, b, link);
+ isc_buffer_free(&b);
+ }
+
+ if (node->name != NULL) {
+ dns_name_free(node->name, mctx);
+ isc_mem_put(mctx, node->name, sizeof(dns_name_t));
+ }
+ DESTROYLOCK(&node->lock);
+ node->magic = 0;
+ isc_mem_put(mctx, node, sizeof(dns_sdlznode_t));
+ db = &sdlz->common;
+ detach(&db);
+}
+
+static isc_result_t
+findnode(dns_db_t *db, dns_name_t *name, isc_boolean_t create,
+ dns_dbnode_t **nodep)
+{
+ dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db;
+ dns_sdlznode_t *node = NULL;
+ isc_result_t result;
+ isc_buffer_t b;
+ char namestr[DNS_NAME_MAXTEXT + 1];
+ isc_buffer_t b2;
+ char zonestr[DNS_NAME_MAXTEXT + 1];
+ isc_boolean_t isorigin;
+ dns_sdlzauthorityfunc_t authority;
+
+ REQUIRE(VALID_SDLZDB(sdlz));
+ REQUIRE(create == ISC_FALSE);
+ REQUIRE(nodep != NULL && *nodep == NULL);
+
+ UNUSED(name);
+ UNUSED(create);
+
+ isc_buffer_init(&b, namestr, sizeof(namestr));
+ if ((sdlz->dlzimp->flags & DNS_SDLZFLAG_RELATIVEOWNER) != 0) {
+ dns_name_t relname;
+ unsigned int labels;
+
+ labels = dns_name_countlabels(name) -
+ dns_name_countlabels(&db->origin);
+ dns_name_init(&relname, NULL);
+ dns_name_getlabelsequence(name, 0, labels, &relname);
+ result = dns_name_totext(&relname, ISC_TRUE, &b);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ } else {
+ result = dns_name_totext(name, ISC_TRUE, &b);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ isc_buffer_putuint8(&b, 0);
+
+ isc_buffer_init(&b2, zonestr, sizeof(zonestr));
+ result = dns_name_totext(&sdlz->common.origin, ISC_TRUE, &b2);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ isc_buffer_putuint8(&b2, 0);
+
+ result = createnode(sdlz, &node);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ isorigin = dns_name_equal(name, &sdlz->common.origin);
+
+ /* make sure strings are always lowercase */
+ dns_sdlz_tolower(zonestr);
+ dns_sdlz_tolower(namestr);
+
+ MAYBE_LOCK(sdlz->dlzimp);
+
+ /* try to lookup the host (namestr) */
+ result = sdlz->dlzimp->methods->lookup(zonestr, namestr,
+ sdlz->dlzimp->driverarg,
+ sdlz->dbdata, node);
+
+ /*
+ * if the host (namestr) was not found, try to lookup a
+ * "wildcard" host.
+ */
+ if (result != ISC_R_SUCCESS) {
+ result = sdlz->dlzimp->methods->lookup(zonestr, "*",
+ sdlz->dlzimp->driverarg,
+ sdlz->dbdata, node);
+ }
+
+ MAYBE_UNLOCK(sdlz->dlzimp);
+
+ if (result != ISC_R_SUCCESS && !isorigin) {
+ destroynode(node);
+ return (result);
+ }
+
+ if (isorigin && sdlz->dlzimp->methods->authority != NULL) {
+ MAYBE_LOCK(sdlz->dlzimp);
+ authority = sdlz->dlzimp->methods->authority;
+ result = (*authority)(zonestr, sdlz->dlzimp->driverarg,
+ sdlz->dbdata, node);
+ MAYBE_UNLOCK(sdlz->dlzimp);
+ if (result != ISC_R_SUCCESS &&
+ result != ISC_R_NOTIMPLEMENTED) {
+ destroynode(node);
+ return (result);
+ }
+ }
+
+ *nodep = node;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+findzonecut(dns_db_t *db, dns_name_t *name, unsigned int options,
+ isc_stdtime_t now, dns_dbnode_t **nodep, dns_name_t *foundname,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+ UNUSED(db);
+ UNUSED(name);
+ UNUSED(options);
+ UNUSED(now);
+ UNUSED(nodep);
+ UNUSED(foundname);
+ UNUSED(rdataset);
+ UNUSED(sigrdataset);
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static void
+attachnode(dns_db_t *db, dns_dbnode_t *source, dns_dbnode_t **targetp) {
+ dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db;
+ dns_sdlznode_t *node = (dns_sdlznode_t *)source;
+
+ REQUIRE(VALID_SDLZDB(sdlz));
+
+ UNUSED(sdlz);
+
+ LOCK(&node->lock);
+ INSIST(node->references > 0);
+ node->references++;
+ INSIST(node->references != 0); /* Catch overflow. */
+ UNLOCK(&node->lock);
+
+ *targetp = source;
+}
+
+static void
+detachnode(dns_db_t *db, dns_dbnode_t **targetp) {
+ dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db;
+ dns_sdlznode_t *node;
+ isc_boolean_t need_destroy = ISC_FALSE;
+
+ REQUIRE(VALID_SDLZDB(sdlz));
+ REQUIRE(targetp != NULL && *targetp != NULL);
+
+ UNUSED(sdlz);
+
+ node = (dns_sdlznode_t *)(*targetp);
+
+ LOCK(&node->lock);
+ INSIST(node->references > 0);
+ node->references--;
+ if (node->references == 0)
+ need_destroy = ISC_TRUE;
+ UNLOCK(&node->lock);
+
+ if (need_destroy)
+ destroynode(node);
+
+ *targetp = NULL;
+}
+
+static isc_result_t
+expirenode(dns_db_t *db, dns_dbnode_t *node, isc_stdtime_t now) {
+ UNUSED(db);
+ UNUSED(node);
+ UNUSED(now);
+ INSIST(0);
+ return (ISC_R_UNEXPECTED);
+}
+
+static void
+printnode(dns_db_t *db, dns_dbnode_t *node, FILE *out) {
+ UNUSED(db);
+ UNUSED(node);
+ UNUSED(out);
+ return;
+}
+
+static isc_result_t
+createiterator(dns_db_t *db, unsigned int options, dns_dbiterator_t **iteratorp)
+{
+ dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db;
+ sdlz_dbiterator_t *sdlziter;
+ isc_result_t result;
+ isc_buffer_t b;
+ char zonestr[DNS_NAME_MAXTEXT + 1];
+
+ REQUIRE(VALID_SDLZDB(sdlz));
+
+ if (sdlz->dlzimp->methods->allnodes == NULL)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ if ((options & DNS_DB_NSEC3ONLY) != 0 ||
+ (options & DNS_DB_NONSEC3) != 0)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ isc_buffer_init(&b, zonestr, sizeof(zonestr));
+ result = dns_name_totext(&sdlz->common.origin, ISC_TRUE, &b);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ isc_buffer_putuint8(&b, 0);
+
+ sdlziter = isc_mem_get(sdlz->common.mctx, sizeof(sdlz_dbiterator_t));
+ if (sdlziter == NULL)
+ return (ISC_R_NOMEMORY);
+
+ sdlziter->common.methods = &dbiterator_methods;
+ sdlziter->common.db = NULL;
+ dns_db_attach(db, &sdlziter->common.db);
+ sdlziter->common.relative_names = ISC_TF(options & DNS_DB_RELATIVENAMES);
+ sdlziter->common.magic = DNS_DBITERATOR_MAGIC;
+ ISC_LIST_INIT(sdlziter->nodelist);
+ sdlziter->current = NULL;
+ sdlziter->origin = NULL;
+
+ /* make sure strings are always lowercase */
+ dns_sdlz_tolower(zonestr);
+
+ MAYBE_LOCK(sdlz->dlzimp);
+ result = sdlz->dlzimp->methods->allnodes(zonestr,
+ sdlz->dlzimp->driverarg,
+ sdlz->dbdata, sdlziter);
+ MAYBE_UNLOCK(sdlz->dlzimp);
+ if (result != ISC_R_SUCCESS) {
+ dns_dbiterator_t *iter = &sdlziter->common;
+ dbiterator_destroy(&iter);
+ return (result);
+ }
+
+ if (sdlziter->origin != NULL) {
+ ISC_LIST_UNLINK(sdlziter->nodelist, sdlziter->origin, link);
+ ISC_LIST_PREPEND(sdlziter->nodelist, sdlziter->origin, link);
+ }
+
+ *iteratorp = (dns_dbiterator_t *)sdlziter;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ dns_rdatatype_t type, dns_rdatatype_t covers,
+ isc_stdtime_t now, dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset)
+{
+ dns_rdatalist_t *list;
+ dns_sdlznode_t *sdlznode = (dns_sdlznode_t *)node;
+
+ REQUIRE(VALID_SDLZNODE(node));
+
+ UNUSED(db);
+ UNUSED(version);
+ UNUSED(covers);
+ UNUSED(now);
+ UNUSED(sigrdataset);
+
+ if (type == dns_rdatatype_sig || type == dns_rdatatype_rrsig)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ list = ISC_LIST_HEAD(sdlznode->lists);
+ while (list != NULL) {
+ if (list->type == type)
+ break;
+ list = ISC_LIST_NEXT(list, link);
+ }
+ if (list == NULL)
+ return (ISC_R_NOTFOUND);
+
+ list_tordataset(list, db, node, rdataset);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
+ dns_rdatatype_t type, unsigned int options, isc_stdtime_t now,
+ dns_dbnode_t **nodep, dns_name_t *foundname,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+ dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db;
+ dns_dbnode_t *node = NULL;
+ dns_fixedname_t fname;
+ dns_rdataset_t xrdataset;
+ dns_name_t *xname;
+ unsigned int nlabels, olabels;
+ isc_result_t result;
+ unsigned int i;
+
+ REQUIRE(VALID_SDLZDB(sdlz));
+ REQUIRE(nodep == NULL || *nodep == NULL);
+ REQUIRE(version == NULL || version == (void *) &dummy);
+
+ UNUSED(options);
+ UNUSED(sdlz);
+
+ if (!dns_name_issubdomain(name, &db->origin))
+ return (DNS_R_NXDOMAIN);
+
+ olabels = dns_name_countlabels(&db->origin);
+ nlabels = dns_name_countlabels(name);
+
+ dns_fixedname_init(&fname);
+ xname = dns_fixedname_name(&fname);
+
+ if (rdataset == NULL) {
+ dns_rdataset_init(&xrdataset);
+ rdataset = &xrdataset;
+ }
+
+ result = DNS_R_NXDOMAIN;
+
+ for (i = olabels; i <= nlabels; i++) {
+ /*
+ * Unless this is an explicit lookup at the origin, don't
+ * look at the origin.
+ */
+ if (i == olabels && i != nlabels)
+ continue;
+
+ /*
+ * Look up the next label.
+ */
+ dns_name_getlabelsequence(name, nlabels - i, i, xname);
+ result = findnode(db, xname, ISC_FALSE, &node);
+ if (result != ISC_R_SUCCESS) {
+ result = DNS_R_NXDOMAIN;
+ continue;
+ }
+
+ /*
+ * Look for a DNAME at the current label, unless this is
+ * the qname.
+ */
+ if (i < nlabels) {
+ result = findrdataset(db, node, version,
+ dns_rdatatype_dname,
+ 0, now, rdataset, sigrdataset);
+ if (result == ISC_R_SUCCESS) {
+ result = DNS_R_DNAME;
+ break;
+ }
+ }
+
+ /*
+ * Look for an NS at the current label, unless this is the
+ * origin or glue is ok.
+ */
+ if (i != olabels && (options & DNS_DBFIND_GLUEOK) == 0) {
+ result = findrdataset(db, node, version,
+ dns_rdatatype_ns,
+ 0, now, rdataset, sigrdataset);
+ if (result == ISC_R_SUCCESS) {
+ if (i == nlabels && type == dns_rdatatype_any)
+ {
+ result = DNS_R_ZONECUT;
+ dns_rdataset_disassociate(rdataset);
+ if (sigrdataset != NULL)
+ dns_rdataset_disassociate
+ (sigrdataset);
+ } else
+ result = DNS_R_DELEGATION;
+ break;
+ }
+ }
+
+ /*
+ * If the current name is not the qname, add another label
+ * and try again.
+ */
+ if (i < nlabels) {
+ destroynode(node);
+ node = NULL;
+ continue;
+ }
+
+ /*
+ * If we're looking for ANY, we're done.
+ */
+ if (type == dns_rdatatype_any) {
+ result = ISC_R_SUCCESS;
+ break;
+ }
+
+ /*
+ * Look for the qtype.
+ */
+ result = findrdataset(db, node, version, type,
+ 0, now, rdataset, sigrdataset);
+ if (result == ISC_R_SUCCESS)
+ break;
+
+ /*
+ * Look for a CNAME
+ */
+ if (type != dns_rdatatype_cname) {
+ result = findrdataset(db, node, version,
+ dns_rdatatype_cname,
+ 0, now, rdataset, sigrdataset);
+ if (result == ISC_R_SUCCESS) {
+ result = DNS_R_CNAME;
+ break;
+ }
+ }
+
+ result = DNS_R_NXRRSET;
+ break;
+ }
+
+ if (rdataset == &xrdataset && dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+
+ if (foundname != NULL) {
+ isc_result_t xresult;
+
+ xresult = dns_name_copy(xname, foundname, NULL);
+ if (xresult != ISC_R_SUCCESS) {
+ if (node != NULL)
+ destroynode(node);
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ return (DNS_R_BADDB);
+ }
+ }
+
+ if (nodep != NULL)
+ *nodep = node;
+ else if (node != NULL)
+ detachnode(db, &node);
+
+ return (result);
+}
+
+static isc_result_t
+allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ isc_stdtime_t now, dns_rdatasetiter_t **iteratorp)
+{
+ sdlz_rdatasetiter_t *iterator;
+
+ REQUIRE(version == NULL || version == &dummy);
+
+ UNUSED(version);
+ UNUSED(now);
+
+ iterator = isc_mem_get(db->mctx, sizeof(sdlz_rdatasetiter_t));
+ if (iterator == NULL)
+ return (ISC_R_NOMEMORY);
+
+ iterator->common.magic = DNS_RDATASETITER_MAGIC;
+ iterator->common.methods = &rdatasetiter_methods;
+ iterator->common.db = db;
+ iterator->common.node = NULL;
+ attachnode(db, node, &iterator->common.node);
+ iterator->common.version = version;
+ iterator->common.now = now;
+
+ *iteratorp = (dns_rdatasetiter_t *)iterator;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ isc_stdtime_t now, dns_rdataset_t *rdataset, unsigned int options,
+ dns_rdataset_t *addedrdataset)
+{
+ UNUSED(db);
+ UNUSED(node);
+ UNUSED(version);
+ UNUSED(now);
+ UNUSED(rdataset);
+ UNUSED(options);
+ UNUSED(addedrdataset);
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static isc_result_t
+subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ dns_rdataset_t *rdataset, unsigned int options,
+ dns_rdataset_t *newrdataset)
+{
+ UNUSED(db);
+ UNUSED(node);
+ UNUSED(version);
+ UNUSED(rdataset);
+ UNUSED(options);
+ UNUSED(newrdataset);
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static isc_result_t
+deleterdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ dns_rdatatype_t type, dns_rdatatype_t covers)
+{
+ UNUSED(db);
+ UNUSED(node);
+ UNUSED(version);
+ UNUSED(type);
+ UNUSED(covers);
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static isc_boolean_t
+issecure(dns_db_t *db) {
+ UNUSED(db);
+
+ return (ISC_FALSE);
+}
+
+static unsigned int
+nodecount(dns_db_t *db) {
+ UNUSED(db);
+
+ return (0);
+}
+
+static isc_boolean_t
+ispersistent(dns_db_t *db) {
+ UNUSED(db);
+ return (ISC_TRUE);
+}
+
+static void
+overmem(dns_db_t *db, isc_boolean_t overmem) {
+ UNUSED(db);
+ UNUSED(overmem);
+}
+
+static void
+settask(dns_db_t *db, isc_task_t *task) {
+ UNUSED(db);
+ UNUSED(task);
+}
+
+
+static dns_dbmethods_t sdlzdb_methods = {
+ attach,
+ detach,
+ beginload,
+ endload,
+ dump,
+ currentversion,
+ newversion,
+ attachversion,
+ closeversion,
+ findnode,
+ find,
+ findzonecut,
+ attachnode,
+ detachnode,
+ expirenode,
+ printnode,
+ createiterator,
+ findrdataset,
+ allrdatasets,
+ addrdataset,
+ subtractrdataset,
+ deleterdataset,
+ issecure,
+ nodecount,
+ ispersistent,
+ overmem,
+ settask,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+/*
+ * Database Iterator Methods. These methods were "borrowed" from the SDB
+ * driver interface. See the SDB driver interface documentation for more info.
+ */
+
+static void
+dbiterator_destroy(dns_dbiterator_t **iteratorp) {
+ sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)(*iteratorp);
+ dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)sdlziter->common.db;
+
+ while (!ISC_LIST_EMPTY(sdlziter->nodelist)) {
+ dns_sdlznode_t *node;
+ node = ISC_LIST_HEAD(sdlziter->nodelist);
+ ISC_LIST_UNLINK(sdlziter->nodelist, node, link);
+ destroynode(node);
+ }
+
+ dns_db_detach(&sdlziter->common.db);
+ isc_mem_put(sdlz->common.mctx, sdlziter, sizeof(sdlz_dbiterator_t));
+
+ *iteratorp = NULL;
+}
+
+static isc_result_t
+dbiterator_first(dns_dbiterator_t *iterator) {
+ sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator;
+
+ sdlziter->current = ISC_LIST_HEAD(sdlziter->nodelist);
+ if (sdlziter->current == NULL)
+ return (ISC_R_NOMORE);
+ else
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+dbiterator_last(dns_dbiterator_t *iterator) {
+ sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator;
+
+ sdlziter->current = ISC_LIST_TAIL(sdlziter->nodelist);
+ if (sdlziter->current == NULL)
+ return (ISC_R_NOMORE);
+ else
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+dbiterator_seek(dns_dbiterator_t *iterator, dns_name_t *name) {
+ sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator;
+
+ sdlziter->current = ISC_LIST_HEAD(sdlziter->nodelist);
+ while (sdlziter->current != NULL)
+ if (dns_name_equal(sdlziter->current->name, name))
+ return (ISC_R_SUCCESS);
+ return (ISC_R_NOTFOUND);
+}
+
+static isc_result_t
+dbiterator_prev(dns_dbiterator_t *iterator) {
+ sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator;
+
+ sdlziter->current = ISC_LIST_PREV(sdlziter->current, link);
+ if (sdlziter->current == NULL)
+ return (ISC_R_NOMORE);
+ else
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+dbiterator_next(dns_dbiterator_t *iterator) {
+ sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator;
+
+ sdlziter->current = ISC_LIST_NEXT(sdlziter->current, link);
+ if (sdlziter->current == NULL)
+ return (ISC_R_NOMORE);
+ else
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+dbiterator_current(dns_dbiterator_t *iterator, dns_dbnode_t **nodep,
+ dns_name_t *name)
+{
+ sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator;
+
+ attachnode(iterator->db, sdlziter->current, nodep);
+ if (name != NULL)
+ return (dns_name_copy(sdlziter->current->name, name, NULL));
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+dbiterator_pause(dns_dbiterator_t *iterator) {
+ UNUSED(iterator);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name) {
+ UNUSED(iterator);
+ return (dns_name_copy(dns_rootname, name, NULL));
+}
+
+/*
+ * Rdataset Methods. These methods were "borrowed" from the SDB driver
+ * interface. See the SDB driver interface documentation for more info.
+ */
+
+static void
+disassociate(dns_rdataset_t *rdataset) {
+ dns_dbnode_t *node = rdataset->private5;
+ dns_sdlznode_t *sdlznode = (dns_sdlznode_t *) node;
+ dns_db_t *db = (dns_db_t *) sdlznode->sdlz;
+
+ detachnode(db, &node);
+ isc__rdatalist_disassociate(rdataset);
+}
+
+static void
+rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target) {
+ dns_dbnode_t *node = source->private5;
+ dns_sdlznode_t *sdlznode = (dns_sdlznode_t *) node;
+ dns_db_t *db = (dns_db_t *) sdlznode->sdlz;
+ dns_dbnode_t *tempdb = NULL;
+
+ isc__rdatalist_clone(source, target);
+ attachnode(db, node, &tempdb);
+ source->private5 = tempdb;
+}
+
+static dns_rdatasetmethods_t rdataset_methods = {
+ disassociate,
+ isc__rdatalist_first,
+ isc__rdatalist_next,
+ isc__rdatalist_current,
+ rdataset_clone,
+ isc__rdatalist_count,
+ isc__rdatalist_addnoqname,
+ isc__rdatalist_getnoqname,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static void
+list_tordataset(dns_rdatalist_t *rdatalist,
+ dns_db_t *db, dns_dbnode_t *node,
+ dns_rdataset_t *rdataset)
+{
+ /*
+ * The sdlz rdataset is an rdatalist with some additions.
+ * - private1 & private2 are used by the rdatalist.
+ * - private3 & private 4 are unused.
+ * - private5 is the node.
+ */
+
+ /* This should never fail. */
+ RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, rdataset) ==
+ ISC_R_SUCCESS);
+
+ rdataset->methods = &rdataset_methods;
+ dns_db_attachnode(db, node, &rdataset->private5);
+}
+
+/*
+ * SDLZ core methods. This is the core of the new DLZ functionality.
+ */
+
+/*%
+ * Build a 'bind' database driver structure to be returned by
+ * either the find zone or the allow zone transfer method.
+ * This method is only available in this source file, it is
+ * not made available anywhere else.
+ */
+
+static isc_result_t
+dns_sdlzcreateDBP(isc_mem_t *mctx, void *driverarg, void *dbdata,
+ dns_name_t *name, dns_rdataclass_t rdclass, dns_db_t **dbp)
+{
+ isc_result_t result;
+ dns_sdlz_db_t *sdlzdb;
+ dns_sdlzimplementation_t *imp;
+
+ /* check that things are as we expect */
+ REQUIRE(dbp != NULL && *dbp == NULL);
+ REQUIRE(name != NULL);
+
+ imp = (dns_sdlzimplementation_t *) driverarg;
+
+ /* allocate and zero memory for driver structure */
+ sdlzdb = isc_mem_get(mctx, sizeof(dns_sdlz_db_t));
+ if (sdlzdb == NULL)
+ return (ISC_R_NOMEMORY);
+ memset(sdlzdb, 0, sizeof(dns_sdlz_db_t));
+
+ /* initialize and set origin */
+ dns_name_init(&sdlzdb->common.origin, NULL);
+ result = dns_name_dupwithoffsets(name, mctx, &sdlzdb->common.origin);
+ if (result != ISC_R_SUCCESS)
+ goto mem_cleanup;
+
+ /* initialize the reference count mutex */
+ result = isc_mutex_init(&sdlzdb->refcnt_lock);
+ if (result != ISC_R_SUCCESS)
+ goto name_cleanup;
+
+ /* set the rest of the database structure attributes */
+ sdlzdb->dlzimp = imp;
+ sdlzdb->common.methods = &sdlzdb_methods;
+ sdlzdb->common.attributes = 0;
+ sdlzdb->common.rdclass = rdclass;
+ sdlzdb->common.mctx = NULL;
+ sdlzdb->dbdata = dbdata;
+ sdlzdb->references = 1;
+
+ /* attach to the memory context */
+ isc_mem_attach(mctx, &sdlzdb->common.mctx);
+
+ /* mark structure as valid */
+ sdlzdb->common.magic = DNS_DB_MAGIC;
+ sdlzdb->common.impmagic = SDLZDB_MAGIC;
+ *dbp = (dns_db_t *) sdlzdb;
+
+ return (result);
+
+ /*
+ * reference count mutex could not be initialized, clean up
+ * name memory
+ */
+ name_cleanup:
+ dns_name_free(&sdlzdb->common.origin, mctx);
+ mem_cleanup:
+ isc_mem_put(mctx, sdlzdb, sizeof(dns_sdlz_db_t));
+ return (result);
+}
+
+static isc_result_t
+dns_sdlzallowzonexfr(void *driverarg, void *dbdata, isc_mem_t *mctx,
+ dns_rdataclass_t rdclass, dns_name_t *name,
+ isc_sockaddr_t *clientaddr, dns_db_t **dbp)
+{
+ isc_buffer_t b;
+ isc_buffer_t b2;
+ char namestr[DNS_NAME_MAXTEXT + 1];
+ char clientstr[(sizeof "xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")
+ + 1];
+ isc_netaddr_t netaddr;
+ isc_result_t result;
+ dns_sdlzimplementation_t *imp;
+
+ /*
+ * Perform checks to make sure data is as we expect it to be.
+ */
+ REQUIRE(driverarg != NULL);
+ REQUIRE(name != NULL);
+ REQUIRE(clientaddr != NULL);
+ REQUIRE(dbp != NULL && *dbp == NULL);
+
+ imp = (dns_sdlzimplementation_t *) driverarg;
+
+ /* Convert DNS name to ascii text */
+ isc_buffer_init(&b, namestr, sizeof(namestr));
+ result = dns_name_totext(name, ISC_TRUE, &b);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ isc_buffer_putuint8(&b, 0);
+
+ /* convert client address to ascii text */
+ isc_buffer_init(&b2, clientstr, sizeof(clientstr));
+ isc_netaddr_fromsockaddr(&netaddr, clientaddr);
+ result = isc_netaddr_totext(&netaddr, &b2);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ isc_buffer_putuint8(&b2, 0);
+
+ /* make sure strings are always lowercase */
+ dns_sdlz_tolower(namestr);
+ dns_sdlz_tolower(clientstr);
+
+ /* Call SDLZ driver's find zone method */
+ if (imp->methods->allowzonexfr != NULL) {
+ MAYBE_LOCK(imp);
+ result = imp->methods->allowzonexfr(imp->driverarg, dbdata,
+ namestr, clientstr);
+ MAYBE_UNLOCK(imp);
+ /*
+ * if zone is supported and transfers allowed build a 'bind'
+ * database driver
+ */
+ if (result == ISC_R_SUCCESS)
+ result = dns_sdlzcreateDBP(mctx, driverarg, dbdata,
+ name, rdclass, dbp);
+ return (result);
+ }
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static isc_result_t
+dns_sdlzcreate(isc_mem_t *mctx, const char *dlzname, unsigned int argc,
+ char *argv[], void *driverarg, void **dbdata)
+{
+ dns_sdlzimplementation_t *imp;
+ isc_result_t result = ISC_R_NOTFOUND;
+
+ /* Write debugging message to log */
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+ "Loading SDLZ driver.");
+
+ /*
+ * Performs checks to make sure data is as we expect it to be.
+ */
+ REQUIRE(driverarg != NULL);
+ REQUIRE(dlzname != NULL);
+ REQUIRE(dbdata != NULL);
+ UNUSED(mctx);
+
+ imp = driverarg;
+
+ /* If the create method exists, call it. */
+ if (imp->methods->create != NULL) {
+ MAYBE_LOCK(imp);
+ result = imp->methods->create(dlzname, argc, argv,
+ imp->driverarg, dbdata);
+ MAYBE_UNLOCK(imp);
+ }
+
+ /* Write debugging message to log */
+ if (result == ISC_R_SUCCESS) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+ "SDLZ driver loaded successfully.");
+ } else {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+ "SDLZ driver failed to load.");
+ }
+
+ return (result);
+}
+
+static void
+dns_sdlzdestroy(void *driverdata, void **dbdata)
+{
+
+ dns_sdlzimplementation_t *imp;
+
+ /* Write debugging message to log */
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+ "Unloading SDLZ driver.");
+
+ imp = driverdata;
+
+ /* If the destroy method exists, call it. */
+ if (imp->methods->destroy != NULL) {
+ MAYBE_LOCK(imp);
+ imp->methods->destroy(imp->driverarg, dbdata);
+ MAYBE_UNLOCK(imp);
+ }
+}
+
+static isc_result_t
+dns_sdlzfindzone(void *driverarg, void *dbdata, isc_mem_t *mctx,
+ dns_rdataclass_t rdclass, dns_name_t *name, dns_db_t **dbp)
+{
+ isc_buffer_t b;
+ char namestr[DNS_NAME_MAXTEXT + 1];
+ isc_result_t result;
+ dns_sdlzimplementation_t *imp;
+
+ /*
+ * Perform checks to make sure data is as we expect it to be.
+ */
+ REQUIRE(driverarg != NULL);
+ REQUIRE(name != NULL);
+ REQUIRE(dbp != NULL && *dbp == NULL);
+
+ imp = (dns_sdlzimplementation_t *) driverarg;
+
+ /* Convert DNS name to ascii text */
+ isc_buffer_init(&b, namestr, sizeof(namestr));
+ result = dns_name_totext(name, ISC_TRUE, &b);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ isc_buffer_putuint8(&b, 0);
+
+ /* make sure strings are always lowercase */
+ dns_sdlz_tolower(namestr);
+
+ /* Call SDLZ driver's find zone method */
+ MAYBE_LOCK(imp);
+ result = imp->methods->findzone(imp->driverarg, dbdata, namestr);
+ MAYBE_UNLOCK(imp);
+
+ /*
+ * if zone is supported build a 'bind' database driver
+ * structure to return
+ */
+ if (result == ISC_R_SUCCESS)
+ result = dns_sdlzcreateDBP(mctx, driverarg, dbdata, name,
+ rdclass, dbp);
+
+ return (result);
+}
+
+static dns_dlzmethods_t sdlzmethods = {
+ dns_sdlzcreate,
+ dns_sdlzdestroy,
+ dns_sdlzfindzone,
+ dns_sdlzallowzonexfr
+};
+
+/*
+ * Public functions.
+ */
+
+isc_result_t
+dns_sdlz_putrr(dns_sdlzlookup_t *lookup, const char *type, dns_ttl_t ttl,
+ const char *data)
+{
+ dns_rdatalist_t *rdatalist;
+ dns_rdata_t *rdata;
+ dns_rdatatype_t typeval;
+ isc_consttextregion_t r;
+ isc_buffer_t b;
+ isc_buffer_t *rdatabuf = NULL;
+ isc_lex_t *lex;
+ isc_result_t result;
+ unsigned int size;
+ isc_mem_t *mctx;
+ dns_name_t *origin;
+
+ REQUIRE(VALID_SDLZLOOKUP(lookup));
+ REQUIRE(type != NULL);
+ REQUIRE(data != NULL);
+
+ mctx = lookup->sdlz->common.mctx;
+
+ r.base = type;
+ r.length = strlen(type);
+ result = dns_rdatatype_fromtext(&typeval, (void *) &r);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ rdatalist = ISC_LIST_HEAD(lookup->lists);
+ while (rdatalist != NULL) {
+ if (rdatalist->type == typeval)
+ break;
+ rdatalist = ISC_LIST_NEXT(rdatalist, link);
+ }
+
+ if (rdatalist == NULL) {
+ rdatalist = isc_mem_get(mctx, sizeof(dns_rdatalist_t));
+ if (rdatalist == NULL)
+ return (ISC_R_NOMEMORY);
+ rdatalist->rdclass = lookup->sdlz->common.rdclass;
+ rdatalist->type = typeval;
+ rdatalist->covers = 0;
+ rdatalist->ttl = ttl;
+ ISC_LIST_INIT(rdatalist->rdata);
+ ISC_LINK_INIT(rdatalist, link);
+ ISC_LIST_APPEND(lookup->lists, rdatalist, link);
+ } else
+ if (rdatalist->ttl != ttl)
+ return (DNS_R_BADTTL);
+
+ rdata = isc_mem_get(mctx, sizeof(dns_rdata_t));
+ if (rdata == NULL)
+ return (ISC_R_NOMEMORY);
+ dns_rdata_init(rdata);
+
+ if ((lookup->sdlz->dlzimp->flags & DNS_SDLZFLAG_RELATIVERDATA) != 0)
+ origin = &lookup->sdlz->common.origin;
+ else
+ origin = dns_rootname;
+
+ lex = NULL;
+ result = isc_lex_create(mctx, 64, &lex);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ size = initial_size(data);
+ do {
+ isc_buffer_init(&b, data, strlen(data));
+ isc_buffer_add(&b, strlen(data));
+
+ result = isc_lex_openbuffer(lex, &b);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ rdatabuf = NULL;
+ result = isc_buffer_allocate(mctx, &rdatabuf, size);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ result = dns_rdata_fromtext(rdata, rdatalist->rdclass,
+ rdatalist->type, lex,
+ origin, ISC_FALSE,
+ mctx, rdatabuf,
+ &lookup->callbacks);
+ if (result != ISC_R_SUCCESS)
+ isc_buffer_free(&rdatabuf);
+ size *= 2;
+ } while (result == ISC_R_NOSPACE);
+
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
+ ISC_LIST_APPEND(lookup->buffers, rdatabuf, link);
+
+ if (lex != NULL)
+ isc_lex_destroy(&lex);
+
+ return (ISC_R_SUCCESS);
+
+ failure:
+ if (rdatabuf != NULL)
+ isc_buffer_free(&rdatabuf);
+ if (lex != NULL)
+ isc_lex_destroy(&lex);
+ isc_mem_put(mctx, rdata, sizeof(dns_rdata_t));
+
+ return (result);
+}
+
+isc_result_t
+dns_sdlz_putnamedrr(dns_sdlzallnodes_t *allnodes, const char *name,
+ const char *type, dns_ttl_t ttl, const char *data)
+{
+ dns_name_t *newname, *origin;
+ dns_fixedname_t fnewname;
+ dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)allnodes->common.db;
+ dns_sdlznode_t *sdlznode;
+ isc_mem_t *mctx = sdlz->common.mctx;
+ isc_buffer_t b;
+ isc_result_t result;
+
+ dns_fixedname_init(&fnewname);
+ newname = dns_fixedname_name(&fnewname);
+
+ if ((sdlz->dlzimp->flags & DNS_SDLZFLAG_RELATIVERDATA) != 0)
+ origin = &sdlz->common.origin;
+ else
+ origin = dns_rootname;
+ isc_buffer_init(&b, name, strlen(name));
+ isc_buffer_add(&b, strlen(name));
+
+ result = dns_name_fromtext(newname, &b, origin, ISC_FALSE, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (allnodes->common.relative_names) {
+ /* All names are relative to the root */
+ unsigned int nlabels = dns_name_countlabels(newname);
+ dns_name_getlabelsequence(newname, 0, nlabels - 1, newname);
+ }
+
+ sdlznode = ISC_LIST_HEAD(allnodes->nodelist);
+ if (sdlznode == NULL || !dns_name_equal(sdlznode->name, newname)) {
+ sdlznode = NULL;
+ result = createnode(sdlz, &sdlznode);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ sdlznode->name = isc_mem_get(mctx, sizeof(dns_name_t));
+ if (sdlznode->name == NULL) {
+ destroynode(sdlznode);
+ return (ISC_R_NOMEMORY);
+ }
+ dns_name_init(sdlznode->name, NULL);
+ result = dns_name_dup(newname, mctx, sdlznode->name);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, sdlznode->name, sizeof(dns_name_t));
+ destroynode(sdlznode);
+ return (result);
+ }
+ ISC_LIST_PREPEND(allnodes->nodelist, sdlznode, link);
+ if (allnodes->origin == NULL &&
+ dns_name_equal(newname, &sdlz->common.origin))
+ allnodes->origin = sdlznode;
+ }
+ return (dns_sdlz_putrr(sdlznode, type, ttl, data));
+
+}
+
+isc_result_t
+dns_sdlz_putsoa(dns_sdlzlookup_t *lookup, const char *mname, const char *rname,
+ isc_uint32_t serial)
+{
+ char str[2 * DNS_NAME_MAXTEXT + 5 * (sizeof("2147483647")) + 7];
+ int n;
+
+ REQUIRE(mname != NULL);
+ REQUIRE(rname != NULL);
+
+ n = snprintf(str, sizeof str, "%s %s %u %u %u %u %u",
+ mname, rname, serial,
+ SDLZ_DEFAULT_REFRESH, SDLZ_DEFAULT_RETRY,
+ SDLZ_DEFAULT_EXPIRE, SDLZ_DEFAULT_MINIMUM);
+ if (n >= (int)sizeof(str) || n < 0)
+ return (ISC_R_NOSPACE);
+ return (dns_sdlz_putrr(lookup, "SOA", SDLZ_DEFAULT_TTL, str));
+}
+
+isc_result_t
+dns_sdlzregister(const char *drivername, const dns_sdlzmethods_t *methods,
+ void *driverarg, unsigned int flags, isc_mem_t *mctx,
+ dns_sdlzimplementation_t **sdlzimp)
+{
+
+ dns_sdlzimplementation_t *imp;
+ isc_result_t result;
+
+ /*
+ * Performs checks to make sure data is as we expect it to be.
+ */
+ REQUIRE(drivername != NULL);
+ REQUIRE(methods != NULL);
+ REQUIRE(methods->findzone != NULL);
+ REQUIRE(methods->lookup != NULL);
+ REQUIRE(mctx != NULL);
+ REQUIRE(sdlzimp != NULL && *sdlzimp == NULL);
+ REQUIRE((flags & ~(DNS_SDLZFLAG_RELATIVEOWNER |
+ DNS_SDLZFLAG_RELATIVERDATA |
+ DNS_SDLZFLAG_THREADSAFE)) == 0);
+
+ /* Write debugging message to log */
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+ "Registering SDLZ driver '%s'", drivername);
+
+ /*
+ * Allocate memory for a sdlz_implementation object. Error if
+ * we cannot.
+ */
+ imp = isc_mem_get(mctx, sizeof(dns_sdlzimplementation_t));
+ if (imp == NULL)
+ return (ISC_R_NOMEMORY);
+
+ /* Make sure memory region is set to all 0's */
+ memset(imp, 0, sizeof(dns_sdlzimplementation_t));
+
+ /* Store the data passed into this method */
+ imp->methods = methods;
+ imp->driverarg = driverarg;
+ imp->flags = flags;
+ imp->mctx = NULL;
+
+ /* attach the new sdlz_implementation object to a memory context */
+ isc_mem_attach(mctx, &imp->mctx);
+
+ /*
+ * initialize the driver lock, error if we cannot
+ * (used if a driver does not support multiple threads)
+ */
+ result = isc_mutex_init(&imp->driverlock);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_mutex_init() failed: %s",
+ isc_result_totext(result));
+ goto cleanup_mctx;
+ }
+
+ imp->dlz_imp = NULL;
+
+ /*
+ * register the DLZ driver. Pass in our "extra" sdlz information as
+ * a driverarg. (that's why we stored the passed in driver arg in our
+ * sdlz_implementation structure) Also, store the dlz_implementation
+ * structure in our sdlz_implementation.
+ */
+ result = dns_dlzregister(drivername, &sdlzmethods, imp, mctx,
+ &imp->dlz_imp);
+
+ /* if registration fails, cleanup and get outta here. */
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_mutex;
+
+ *sdlzimp = imp;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup_mutex:
+ /* destroy the driver lock, we don't need it anymore */
+ DESTROYLOCK(&imp->driverlock);
+
+ cleanup_mctx:
+ /*
+ * return the memory back to the available memory pool and
+ * remove it from the memory context.
+ */
+ isc_mem_put(mctx, imp, sizeof(dns_sdlzimplementation_t));
+ isc_mem_detach(&mctx);
+ return (result);
+}
+
+void
+dns_sdlzunregister(dns_sdlzimplementation_t **sdlzimp) {
+ dns_sdlzimplementation_t *imp;
+ isc_mem_t *mctx;
+
+ /* Write debugging message to log */
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+ "Unregistering SDLZ driver.");
+
+ /*
+ * Performs checks to make sure data is as we expect it to be.
+ */
+ REQUIRE(sdlzimp != NULL && *sdlzimp != NULL);
+
+ imp = *sdlzimp;
+
+ /* Unregister the DLZ driver implementation */
+ dns_dlzunregister(&imp->dlz_imp);
+
+ /* destroy the driver lock, we don't need it anymore */
+ DESTROYLOCK(&imp->driverlock);
+
+ mctx = imp->mctx;
+
+ /*
+ * return the memory back to the available memory pool and
+ * remove it from the memory context.
+ */
+ isc_mem_put(mctx, imp, sizeof(dns_sdlzimplementation_t));
+ isc_mem_detach(&mctx);
+
+ *sdlzimp = NULL;
+}
diff --git a/lib/dns/soa.c b/lib/dns/soa.c
new file mode 100644
index 0000000..83a1c17
--- /dev/null
+++ b/lib/dns/soa.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: soa.c,v 1.8 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/util.h>
+
+#include <dns/rdata.h>
+#include <dns/soa.h>
+
+static inline isc_uint32_t
+decode_uint32(unsigned char *p) {
+ return ((p[0] << 24) +
+ (p[1] << 16) +
+ (p[2] << 8) +
+ (p[3] << 0));
+}
+
+static inline void
+encode_uint32(isc_uint32_t val, unsigned char *p) {
+ p[0] = (isc_uint8_t)(val >> 24);
+ p[1] = (isc_uint8_t)(val >> 16);
+ p[2] = (isc_uint8_t)(val >> 8);
+ p[3] = (isc_uint8_t)(val >> 0);
+}
+
+static isc_uint32_t
+soa_get(dns_rdata_t *rdata, int offset) {
+ INSIST(rdata->type == dns_rdatatype_soa);
+ /*
+ * Locate the field within the SOA RDATA based
+ * on its position relative to the end of the data.
+ *
+ * This is a bit of a kludge, but the alternative approach of
+ * using dns_rdata_tostruct() and dns_rdata_fromstruct() would
+ * involve a lot of unnecessary work (like building domain
+ * names and allocating temporary memory) when all we really
+ * want to do is to get 32 bits of fixed-sized data.
+ */
+ INSIST(rdata->length >= 20);
+ INSIST(offset >= 0 && offset <= 16);
+ return (decode_uint32(rdata->data + rdata->length - 20 + offset));
+}
+
+isc_uint32_t
+dns_soa_getserial(dns_rdata_t *rdata) {
+ return soa_get(rdata, 0);
+}
+isc_uint32_t
+dns_soa_getrefresh(dns_rdata_t *rdata) {
+ return soa_get(rdata, 4);
+}
+isc_uint32_t
+dns_soa_getretry(dns_rdata_t *rdata) {
+ return soa_get(rdata, 8);
+}
+isc_uint32_t
+dns_soa_getexpire(dns_rdata_t *rdata) {
+ return soa_get(rdata, 12);
+}
+isc_uint32_t
+dns_soa_getminimum(dns_rdata_t *rdata) {
+ return soa_get(rdata, 16);
+}
+
+static void
+soa_set(dns_rdata_t *rdata, isc_uint32_t val, int offset) {
+ INSIST(rdata->type == dns_rdatatype_soa);
+ INSIST(rdata->length >= 20);
+ INSIST(offset >= 0 && offset <= 16);
+ encode_uint32(val, rdata->data + rdata->length - 20 + offset);
+}
+
+void
+dns_soa_setserial(isc_uint32_t val, dns_rdata_t *rdata) {
+ soa_set(rdata, val, 0);
+}
+void
+dns_soa_setrefresh(isc_uint32_t val, dns_rdata_t *rdata) {
+ soa_set(rdata, val, 4);
+}
+void
+dns_soa_setretry(isc_uint32_t val, dns_rdata_t *rdata) {
+ soa_set(rdata, val, 8);
+}
+void
+dns_soa_setexpire(isc_uint32_t val, dns_rdata_t *rdata) {
+ soa_set(rdata, val, 12);
+}
+void
+dns_soa_setminimum(isc_uint32_t val, dns_rdata_t *rdata) {
+ soa_set(rdata, val, 16);
+}
diff --git a/lib/dns/spnego.asn1 b/lib/dns/spnego.asn1
new file mode 100644
index 0000000..43d152b
--- /dev/null
+++ b/lib/dns/spnego.asn1
@@ -0,0 +1,52 @@
+-- Copyright (C) The Internet Society 2005. This version of
+-- this module is part of RFC 4178; see the RFC itself for
+-- full legal notices.
+
+-- (The above copyright notice is per RFC 3978 5.6 (a), q.v.)
+
+-- $Id: spnego.asn1,v 1.2 2006/12/04 01:52:46 marka Exp $
+
+-- This is the SPNEGO ASN.1 module from RFC 4178, tweaked
+-- to get the Heimdal ASN.1 compiler to accept it.
+
+SPNEGOASNOneSpec DEFINITIONS ::= BEGIN
+
+MechType ::= OBJECT IDENTIFIER
+
+MechTypeList ::= SEQUENCE OF MechType
+
+ContextFlags ::= BIT STRING {
+ delegFlag (0),
+ mutualFlag (1),
+ replayFlag (2),
+ sequenceFlag (3),
+ anonFlag (4),
+ confFlag (5),
+ integFlag (6)
+}
+
+NegTokenInit ::= SEQUENCE {
+ mechTypes [0] MechTypeList,
+ reqFlags [1] ContextFlags OPTIONAL,
+ mechToken [2] OCTET STRING OPTIONAL,
+ mechListMIC [3] OCTET STRING OPTIONAL
+}
+
+NegTokenResp ::= SEQUENCE {
+ negState [0] ENUMERATED {
+ accept-completed (0),
+ accept-incomplete (1),
+ reject (2),
+ request-mic (3)
+ } OPTIONAL,
+ supportedMech [1] MechType OPTIONAL,
+ responseToken [2] OCTET STRING OPTIONAL,
+ mechListMIC [3] OCTET STRING OPTIONAL
+}
+
+NegotiationToken ::= CHOICE {
+ negTokenInit [0] NegTokenInit,
+ negTokenResp [1] NegTokenResp
+}
+
+END
diff --git a/lib/dns/spnego.c b/lib/dns/spnego.c
new file mode 100644
index 0000000..a9dbbbd
--- /dev/null
+++ b/lib/dns/spnego.c
@@ -0,0 +1,1792 @@
+/*
+ * Copyright (C) 2006-2008 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 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.
+ */
+
+/* $Id: spnego.c,v 1.8 2008/04/03 06:09:04 tbox Exp $ */
+
+/*! \file
+ * \brief
+ * Portable SPNEGO implementation.
+ *
+ * This is part of a portable implementation of the SPNEGO protocol
+ * (RFCs 2478 and 4178). This implementation uses the RFC 4178 ASN.1
+ * module but is not a full implementation of the RFC 4178 protocol;
+ * at the moment, we only support GSS-TSIG with Kerberos
+ * authentication, so we only need enough of the SPNEGO protocol to
+ * support that.
+ *
+ * The files that make up this portable SPNEGO implementation are:
+ * \li spnego.c (this file)
+ * \li spnego.h (API SPNEGO exports to the rest of lib/dns)
+ * \li spnego.asn1 (SPNEGO ASN.1 module)
+ * \li spnego_asn1.c (routines generated from spngo.asn1)
+ * \li spnego_asn1.pl (perl script to generate spnego_asn1.c)
+ *
+ * Everything but the functions exported in spnego.h is static, to
+ * avoid possible conflicts with other libraries (particularly Heimdal,
+ * since much of this code comes from Heimdal by way of mod_auth_kerb).
+ *
+ * spnego_asn1.c is shipped as part of lib/dns because generating it
+ * requires both Perl and the Heimdal ASN.1 compiler. See
+ * spnego_asn1.pl for further details. We've tried to eliminate all
+ * compiler warnings from the generated code, but you may see a few
+ * when using a compiler version we haven't tested yet.
+ */
+
+/*
+ * Portions of this code were derived from mod_auth_kerb and Heimdal.
+ * These packages are available from:
+ *
+ * http://modauthkerb.sourceforge.net/
+ * http://www.pdc.kth.se/heimdal/
+ *
+ * and were released under the following licenses:
+ *
+ * ----------------------------------------------------------------
+ *
+ * Copyright (c) 2004 Masarykova universita
+ * (Masaryk University, Brno, Czech Republic)
+ * 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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.
+ *
+ * ----------------------------------------------------------------
+ *
+ * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * 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 Institute 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 INSTITUTE 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 INSTITUTE 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.
+ */
+
+/*
+ * XXXSRA We should omit this file entirely in Makefile.in via autoconf,
+ * but this will keep it from generating errors until that's written.
+ */
+
+#ifdef GSSAPI
+
+/*
+ * XXXSRA Some of the following files are almost certainly unnecessary,
+ * but using this list (borrowed from gssapictx.c) gets rid of some
+ * whacky compilation errors when building with MSVC and should be
+ * harmless in any case.
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include <isc/buffer.h>
+#include <isc/dir.h>
+#include <isc/entropy.h>
+#include <isc/lex.h>
+#include <isc/mem.h>
+#include <isc/once.h>
+#include <isc/random.h>
+#include <isc/string.h>
+#include <isc/time.h>
+#include <isc/util.h>
+
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/result.h>
+#include <dns/types.h>
+#include <dns/keyvalues.h>
+#include <dns/log.h>
+
+#include <dst/gssapi.h>
+#include <dst/result.h>
+
+#include "dst_internal.h"
+
+/*
+ * The API we export
+ */
+#include "spnego.h"
+
+/* asn1_err.h */
+/* Generated from ../../../lib/asn1/asn1_err.et */
+
+typedef enum asn1_error_number {
+ ASN1_BAD_TIMEFORMAT = 1859794432,
+ ASN1_MISSING_FIELD = 1859794433,
+ ASN1_MISPLACED_FIELD = 1859794434,
+ ASN1_TYPE_MISMATCH = 1859794435,
+ ASN1_OVERFLOW = 1859794436,
+ ASN1_OVERRUN = 1859794437,
+ ASN1_BAD_ID = 1859794438,
+ ASN1_BAD_LENGTH = 1859794439,
+ ASN1_BAD_FORMAT = 1859794440,
+ ASN1_PARSE_ERROR = 1859794441
+} asn1_error_number;
+
+#define ERROR_TABLE_BASE_asn1 1859794432
+
+#define __asn1_common_definitions__
+
+typedef struct octet_string {
+ size_t length;
+ void *data;
+} octet_string;
+
+typedef char *general_string;
+
+typedef char *utf8_string;
+
+typedef struct oid {
+ size_t length;
+ unsigned *components;
+} oid;
+
+/* der.h */
+
+typedef enum {
+ ASN1_C_UNIV = 0, ASN1_C_APPL = 1,
+ ASN1_C_CONTEXT = 2, ASN1_C_PRIVATE = 3
+} Der_class;
+
+typedef enum {
+ PRIM = 0, CONS = 1
+} Der_type;
+
+/* Universal tags */
+
+enum {
+ UT_Boolean = 1,
+ UT_Integer = 2,
+ UT_BitString = 3,
+ UT_OctetString = 4,
+ UT_Null = 5,
+ UT_OID = 6,
+ UT_Enumerated = 10,
+ UT_Sequence = 16,
+ UT_Set = 17,
+ UT_PrintableString = 19,
+ UT_IA5String = 22,
+ UT_UTCTime = 23,
+ UT_GeneralizedTime = 24,
+ UT_VisibleString = 26,
+ UT_GeneralString = 27
+};
+
+#define ASN1_INDEFINITE 0xdce0deed
+
+static int
+der_get_length(const unsigned char *p, size_t len,
+ size_t * val, size_t * size);
+
+static int
+der_get_octet_string(const unsigned char *p, size_t len,
+ octet_string * data, size_t * size);
+static int
+der_get_oid(const unsigned char *p, size_t len,
+ oid * data, size_t * size);
+static int
+der_get_tag(const unsigned char *p, size_t len,
+ Der_class * class, Der_type * type,
+ int *tag, size_t * size);
+
+static int
+der_match_tag(const unsigned char *p, size_t len,
+ Der_class class, Der_type type,
+ int tag, size_t * size);
+static int
+der_match_tag_and_length(const unsigned char *p, size_t len,
+ Der_class class, Der_type type, int tag,
+ size_t * length_ret, size_t * size);
+
+static int
+decode_oid(const unsigned char *p, size_t len,
+ oid * k, size_t * size);
+
+static int
+decode_enumerated(const unsigned char *p, size_t len,
+ unsigned *num, size_t *size);
+
+static int
+decode_octet_string(const unsigned char *, size_t, octet_string *, size_t *);
+
+static int
+der_put_int(unsigned char *p, size_t len, int val, size_t *);
+
+static int
+der_put_length(unsigned char *p, size_t len, size_t val, size_t *);
+
+static int
+der_put_octet_string(unsigned char *p, size_t len,
+ const octet_string * data, size_t *);
+static int
+der_put_oid(unsigned char *p, size_t len,
+ const oid * data, size_t * size);
+static int
+der_put_tag(unsigned char *p, size_t len, Der_class class, Der_type type,
+ int tag, size_t *);
+static int
+der_put_length_and_tag(unsigned char *, size_t, size_t,
+ Der_class, Der_type, int, size_t *);
+
+static int
+encode_enumerated(unsigned char *p, size_t len,
+ const unsigned *data, size_t *);
+
+static int
+encode_octet_string(unsigned char *p, size_t len,
+ const octet_string * k, size_t *);
+static int
+encode_oid(unsigned char *p, size_t len,
+ const oid * k, size_t *);
+
+static void
+free_octet_string(octet_string * k);
+
+static void
+free_oid (oid * k);
+
+static size_t
+length_len(size_t len);
+
+static int
+fix_dce(size_t reallen, size_t * len);
+
+/*
+ * Include stuff generated by the ASN.1 compiler.
+ */
+
+#include "spnego_asn1.c"
+
+static unsigned char gss_krb5_mech_oid_bytes[] = {
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02
+};
+
+static gss_OID_desc gss_krb5_mech_oid_desc = {
+ sizeof(gss_krb5_mech_oid_bytes),
+ gss_krb5_mech_oid_bytes
+};
+
+static gss_OID GSS_KRB5_MECH = &gss_krb5_mech_oid_desc;
+
+static unsigned char gss_mskrb5_mech_oid_bytes[] = {
+ 0x2a, 0x86, 0x48, 0x82, 0xf7, 0x12, 0x01, 0x02, 0x02
+};
+
+static gss_OID_desc gss_mskrb5_mech_oid_desc = {
+ sizeof(gss_mskrb5_mech_oid_bytes),
+ gss_mskrb5_mech_oid_bytes
+};
+
+static gss_OID GSS_MSKRB5_MECH = &gss_mskrb5_mech_oid_desc;
+
+static unsigned char gss_spnego_mech_oid_bytes[] = {
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x02
+};
+
+static gss_OID_desc gss_spnego_mech_oid_desc = {
+ sizeof(gss_spnego_mech_oid_bytes),
+ gss_spnego_mech_oid_bytes
+};
+
+static gss_OID GSS_SPNEGO_MECH = &gss_spnego_mech_oid_desc;
+
+/* spnegokrb5_locl.h */
+
+static OM_uint32
+gssapi_spnego_encapsulate(OM_uint32 *,
+ unsigned char *,
+ size_t,
+ gss_buffer_t,
+ const gss_OID);
+
+static OM_uint32
+gssapi_spnego_decapsulate(OM_uint32 *,
+ gss_buffer_t,
+ unsigned char **,
+ size_t *,
+ const gss_OID);
+
+/* mod_auth_kerb.c */
+
+static int
+cmp_gss_type(gss_buffer_t token, gss_OID oid)
+{
+ unsigned char *p;
+ size_t len;
+
+ if (token->length == 0)
+ return (GSS_S_DEFECTIVE_TOKEN);
+
+ p = token->value;
+ if (*p++ != 0x60)
+ return (GSS_S_DEFECTIVE_TOKEN);
+ len = *p++;
+ if (len & 0x80) {
+ if ((len & 0x7f) > 4)
+ return (GSS_S_DEFECTIVE_TOKEN);
+ p += len & 0x7f;
+ }
+ if (*p++ != 0x06)
+ return (GSS_S_DEFECTIVE_TOKEN);
+
+ if (((OM_uint32) *p++) != oid->length)
+ return (GSS_S_DEFECTIVE_TOKEN);
+
+ return (memcmp(p, oid->elements, oid->length));
+}
+
+/* accept_sec_context.c */
+/*
+ * SPNEGO wrapper for Kerberos5 GSS-API kouril@ics.muni.cz, 2003 (mostly
+ * based on Heimdal code)
+ */
+
+static OM_uint32
+code_NegTokenArg(OM_uint32 * minor_status,
+ const NegTokenResp * resp,
+ unsigned char **outbuf,
+ size_t * outbuf_size)
+{
+ OM_uint32 ret;
+ u_char *buf;
+ size_t buf_size, buf_len;
+
+ buf_size = 1024;
+ buf = malloc(buf_size);
+ if (buf == NULL) {
+ *minor_status = ENOMEM;
+ return (GSS_S_FAILURE);
+ }
+ do {
+ ret = encode_NegTokenResp(buf + buf_size - 1,
+ buf_size,
+ resp, &buf_len);
+ if (ret == 0) {
+ size_t tmp;
+
+ ret = der_put_length_and_tag(buf + buf_size - buf_len - 1,
+ buf_size - buf_len,
+ buf_len,
+ ASN1_C_CONTEXT,
+ CONS,
+ 1,
+ &tmp);
+ if (ret == 0)
+ buf_len += tmp;
+ }
+ if (ret) {
+ if (ret == ASN1_OVERFLOW) {
+ u_char *tmp;
+
+ buf_size *= 2;
+ tmp = realloc(buf, buf_size);
+ if (tmp == NULL) {
+ *minor_status = ENOMEM;
+ free(buf);
+ return (GSS_S_FAILURE);
+ }
+ buf = tmp;
+ } else {
+ *minor_status = ret;
+ free(buf);
+ return (GSS_S_FAILURE);
+ }
+ }
+ } while (ret == ASN1_OVERFLOW);
+
+ *outbuf = malloc(buf_len);
+ if (*outbuf == NULL) {
+ *minor_status = ENOMEM;
+ free(buf);
+ return (GSS_S_FAILURE);
+ }
+ memcpy(*outbuf, buf + buf_size - buf_len, buf_len);
+ *outbuf_size = buf_len;
+
+ free(buf);
+
+ return (GSS_S_COMPLETE);
+}
+
+static OM_uint32
+send_reject(OM_uint32 * minor_status,
+ gss_buffer_t output_token)
+{
+ NegTokenResp resp;
+ OM_uint32 ret;
+
+ resp.negState = malloc(sizeof(*resp.negState));
+ if (resp.negState == NULL) {
+ *minor_status = ENOMEM;
+ return (GSS_S_FAILURE);
+ }
+ *(resp.negState) = reject;
+
+ resp.supportedMech = NULL;
+ resp.responseToken = NULL;
+ resp.mechListMIC = NULL;
+
+ ret = code_NegTokenArg(minor_status, &resp,
+ (unsigned char **)&output_token->value,
+ &output_token->length);
+ free_NegTokenResp(&resp);
+ if (ret)
+ return (ret);
+
+ return (GSS_S_BAD_MECH);
+}
+
+static OM_uint32
+send_accept(OM_uint32 * minor_status,
+ gss_buffer_t output_token,
+ gss_buffer_t mech_token,
+ const gss_OID pref)
+{
+ NegTokenResp resp;
+ OM_uint32 ret;
+
+ memset(&resp, 0, sizeof(resp));
+ resp.negState = malloc(sizeof(*resp.negState));
+ if (resp.negState == NULL) {
+ *minor_status = ENOMEM;
+ return (GSS_S_FAILURE);
+ }
+ *(resp.negState) = accept_completed;
+
+ resp.supportedMech = malloc(sizeof(*resp.supportedMech));
+ if (resp.supportedMech == NULL) {
+ free_NegTokenResp(&resp);
+ *minor_status = ENOMEM;
+ return (GSS_S_FAILURE);
+ }
+ ret = der_get_oid(pref->elements,
+ pref->length,
+ resp.supportedMech,
+ NULL);
+ if (ret) {
+ free_NegTokenResp(&resp);
+ *minor_status = ENOMEM;
+ return (GSS_S_FAILURE);
+ }
+ if (mech_token != NULL && mech_token->length != 0) {
+ resp.responseToken = malloc(sizeof(*resp.responseToken));
+ if (resp.responseToken == NULL) {
+ free_NegTokenResp(&resp);
+ *minor_status = ENOMEM;
+ return (GSS_S_FAILURE);
+ }
+ resp.responseToken->length = mech_token->length;
+ resp.responseToken->data = mech_token->value;
+ }
+
+ ret = code_NegTokenArg(minor_status, &resp,
+ (unsigned char **)&output_token->value,
+ &output_token->length);
+ if (resp.responseToken != NULL) {
+ free(resp.responseToken);
+ resp.responseToken = NULL;
+ }
+ free_NegTokenResp(&resp);
+ if (ret)
+ return (ret);
+
+ return (GSS_S_COMPLETE);
+}
+
+OM_uint32
+gss_accept_sec_context_spnego(OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ const gss_cred_id_t acceptor_cred_handle,
+ const gss_buffer_t input_token_buffer,
+ const gss_channel_bindings_t input_chan_bindings,
+ gss_name_t *src_name,
+ gss_OID *mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 *ret_flags,
+ OM_uint32 *time_rec,
+ gss_cred_id_t *delegated_cred_handle)
+{
+ NegTokenInit init_token;
+ OM_uint32 major_status;
+ OM_uint32 minor_status2;
+ gss_buffer_desc ibuf, obuf;
+ gss_buffer_t ot = NULL;
+ gss_OID pref = GSS_KRB5_MECH;
+ unsigned char *buf;
+ size_t buf_size;
+ size_t len, taglen, ni_len;
+ int found = 0;
+ int ret;
+ unsigned i;
+
+ /*
+ * Before doing anything else, see whether this is a SPNEGO
+ * PDU. If not, dispatch to the GSSAPI library and get out.
+ */
+
+ if (cmp_gss_type(input_token_buffer, GSS_SPNEGO_MECH))
+ return (gss_accept_sec_context(minor_status,
+ context_handle,
+ acceptor_cred_handle,
+ input_token_buffer,
+ input_chan_bindings,
+ src_name,
+ mech_type,
+ output_token,
+ ret_flags,
+ time_rec,
+ delegated_cred_handle));
+
+ /*
+ * If we get here, it's SPNEGO.
+ */
+
+ memset(&init_token, 0, sizeof(init_token));
+
+ ret = gssapi_spnego_decapsulate(minor_status, input_token_buffer,
+ &buf, &buf_size, GSS_SPNEGO_MECH);
+ if (ret)
+ return (ret);
+
+ ret = der_match_tag_and_length(buf, buf_size, ASN1_C_CONTEXT, CONS,
+ 0, &len, &taglen);
+ if (ret)
+ return (ret);
+
+ ret = decode_NegTokenInit(buf + taglen, len, &init_token, &ni_len);
+ if (ret) {
+ *minor_status = EINVAL; /* XXX */
+ return (GSS_S_DEFECTIVE_TOKEN);
+ }
+
+ for (i = 0; !found && i < init_token.mechTypes.len; ++i) {
+ char mechbuf[17];
+ size_t mech_len;
+
+ ret = der_put_oid(mechbuf + sizeof(mechbuf) - 1,
+ sizeof(mechbuf),
+ &init_token.mechTypes.val[i],
+ &mech_len);
+ if (ret)
+ return (GSS_S_DEFECTIVE_TOKEN);
+ if (mech_len == GSS_KRB5_MECH->length &&
+ memcmp(GSS_KRB5_MECH->elements,
+ mechbuf + sizeof(mechbuf) - mech_len,
+ mech_len) == 0) {
+ found = 1;
+ break;
+ }
+ if (mech_len == GSS_MSKRB5_MECH->length &&
+ memcmp(GSS_MSKRB5_MECH->elements,
+ mechbuf + sizeof(mechbuf) - mech_len,
+ mech_len) == 0) {
+ found = 1;
+ if (i == 0)
+ pref = GSS_MSKRB5_MECH;
+ break;
+ }
+ }
+
+ if (!found)
+ return (send_reject(minor_status, output_token));
+
+ if (i == 0 && init_token.mechToken != NULL) {
+ ibuf.length = init_token.mechToken->length;
+ ibuf.value = init_token.mechToken->data;
+
+ major_status = gss_accept_sec_context(minor_status,
+ context_handle,
+ acceptor_cred_handle,
+ &ibuf,
+ input_chan_bindings,
+ src_name,
+ mech_type,
+ &obuf,
+ ret_flags,
+ time_rec,
+ delegated_cred_handle);
+ if (GSS_ERROR(major_status)) {
+ send_reject(&minor_status2, output_token);
+ return (major_status);
+ }
+ ot = &obuf;
+ }
+ ret = send_accept(&minor_status2, output_token, ot, pref);
+ if (ot != NULL && ot->length != 0)
+ gss_release_buffer(&minor_status2, ot);
+
+ return (ret);
+}
+
+/* decapsulate.c */
+
+static OM_uint32
+gssapi_verify_mech_header(u_char ** str,
+ size_t total_len,
+ const gss_OID mech)
+{
+ size_t len, len_len, mech_len, foo;
+ int e;
+ u_char *p = *str;
+
+ if (total_len < 1)
+ return (GSS_S_DEFECTIVE_TOKEN);
+ if (*p++ != 0x60)
+ return (GSS_S_DEFECTIVE_TOKEN);
+ e = der_get_length(p, total_len - 1, &len, &len_len);
+ if (e || 1 + len_len + len != total_len)
+ return (GSS_S_DEFECTIVE_TOKEN);
+ p += len_len;
+ if (*p++ != 0x06)
+ return (GSS_S_DEFECTIVE_TOKEN);
+ e = der_get_length(p, total_len - 1 - len_len - 1,
+ &mech_len, &foo);
+ if (e)
+ return (GSS_S_DEFECTIVE_TOKEN);
+ p += foo;
+ if (mech_len != mech->length)
+ return (GSS_S_BAD_MECH);
+ if (memcmp(p, mech->elements, mech->length) != 0)
+ return (GSS_S_BAD_MECH);
+ p += mech_len;
+ *str = p;
+ return (GSS_S_COMPLETE);
+}
+
+/*
+ * Remove the GSS-API wrapping from `in_token' giving `buf and buf_size' Does
+ * not copy data, so just free `in_token'.
+ */
+
+static OM_uint32
+gssapi_spnego_decapsulate(OM_uint32 *minor_status,
+ gss_buffer_t input_token_buffer,
+ unsigned char **buf,
+ size_t *buf_len,
+ const gss_OID mech)
+{
+ u_char *p;
+ OM_uint32 ret;
+
+ p = input_token_buffer->value;
+ ret = gssapi_verify_mech_header(&p,
+ input_token_buffer->length,
+ mech);
+ if (ret) {
+ *minor_status = ret;
+ return (GSS_S_FAILURE);
+ }
+ *buf_len = input_token_buffer->length -
+ (p - (u_char *) input_token_buffer->value);
+ *buf = p;
+ return (GSS_S_COMPLETE);
+}
+
+/* der_free.c */
+
+static void
+free_octet_string(octet_string *k)
+{
+ free(k->data);
+ k->data = NULL;
+}
+
+static void
+free_oid(oid *k)
+{
+ free(k->components);
+ k->components = NULL;
+}
+
+/* der_get.c */
+
+/*
+ * All decoding functions take a pointer `p' to first position in which to
+ * read, from the left, `len' which means the maximum number of characters we
+ * are able to read, `ret' were the value will be returned and `size' where
+ * the number of used bytes is stored. Either 0 or an error code is returned.
+ */
+
+static int
+der_get_unsigned(const unsigned char *p, size_t len,
+ unsigned *ret, size_t *size)
+{
+ unsigned val = 0;
+ size_t oldlen = len;
+
+ while (len--)
+ val = val * 256 + *p++;
+ *ret = val;
+ if (size)
+ *size = oldlen;
+ return (0);
+}
+
+static int
+der_get_int(const unsigned char *p, size_t len,
+ int *ret, size_t *size)
+{
+ int val = 0;
+ size_t oldlen = len;
+
+ if (len > 0) {
+ val = (signed char)*p++;
+ while (--len)
+ val = val * 256 + *p++;
+ }
+ *ret = val;
+ if (size)
+ *size = oldlen;
+ return (0);
+}
+
+static int
+der_get_length(const unsigned char *p, size_t len,
+ size_t *val, size_t *size)
+{
+ size_t v;
+
+ if (len <= 0)
+ return (ASN1_OVERRUN);
+ --len;
+ v = *p++;
+ if (v < 128) {
+ *val = v;
+ if (size)
+ *size = 1;
+ } else {
+ int e;
+ size_t l;
+ unsigned tmp;
+
+ if (v == 0x80) {
+ *val = ASN1_INDEFINITE;
+ if (size)
+ *size = 1;
+ return (0);
+ }
+ v &= 0x7F;
+ if (len < v)
+ return (ASN1_OVERRUN);
+ e = der_get_unsigned(p, v, &tmp, &l);
+ if (e)
+ return (e);
+ *val = tmp;
+ if (size)
+ *size = l + 1;
+ }
+ return (0);
+}
+
+static int
+der_get_octet_string(const unsigned char *p, size_t len,
+ octet_string *data, size_t *size)
+{
+ data->length = len;
+ data->data = malloc(len);
+ if (data->data == NULL && data->length != 0)
+ return (ENOMEM);
+ memcpy(data->data, p, len);
+ if (size)
+ *size = len;
+ return (0);
+}
+
+static int
+der_get_oid(const unsigned char *p, size_t len,
+ oid *data, size_t *size)
+{
+ int n;
+ size_t oldlen = len;
+
+ if (len < 1)
+ return (ASN1_OVERRUN);
+
+ data->components = malloc(len * sizeof(*data->components));
+ if (data->components == NULL && len != 0)
+ return (ENOMEM);
+ data->components[0] = (*p) / 40;
+ data->components[1] = (*p) % 40;
+ --len;
+ ++p;
+ for (n = 2; len > 0; ++n) {
+ unsigned u = 0;
+
+ do {
+ --len;
+ u = u * 128 + (*p++ % 128);
+ } while (len > 0 && p[-1] & 0x80);
+ data->components[n] = u;
+ }
+ if (p[-1] & 0x80) {
+ free_oid(data);
+ return (ASN1_OVERRUN);
+ }
+ data->length = n;
+ if (size)
+ *size = oldlen;
+ return (0);
+}
+
+static int
+der_get_tag(const unsigned char *p, size_t len,
+ Der_class *class, Der_type *type,
+ int *tag, size_t *size)
+{
+ if (len < 1)
+ return (ASN1_OVERRUN);
+ *class = (Der_class) (((*p) >> 6) & 0x03);
+ *type = (Der_type) (((*p) >> 5) & 0x01);
+ *tag = (*p) & 0x1F;
+ if (size)
+ *size = 1;
+ return (0);
+}
+
+static int
+der_match_tag(const unsigned char *p, size_t len,
+ Der_class class, Der_type type,
+ int tag, size_t *size)
+{
+ size_t l;
+ Der_class thisclass;
+ Der_type thistype;
+ int thistag;
+ int e;
+
+ e = der_get_tag(p, len, &thisclass, &thistype, &thistag, &l);
+ if (e)
+ return (e);
+ if (class != thisclass || type != thistype)
+ return (ASN1_BAD_ID);
+ if (tag > thistag)
+ return (ASN1_MISPLACED_FIELD);
+ if (tag < thistag)
+ return (ASN1_MISSING_FIELD);
+ if (size)
+ *size = l;
+ return (0);
+}
+
+static int
+der_match_tag_and_length(const unsigned char *p, size_t len,
+ Der_class class, Der_type type, int tag,
+ size_t *length_ret, size_t *size)
+{
+ size_t l, ret = 0;
+ int e;
+
+ e = der_match_tag(p, len, class, type, tag, &l);
+ if (e)
+ return (e);
+ p += l;
+ len -= l;
+ ret += l;
+ e = der_get_length(p, len, length_ret, &l);
+ if (e)
+ return (e);
+ p += l;
+ len -= l;
+ ret += l;
+ if (size)
+ *size = ret;
+ return (0);
+}
+
+static int
+decode_enumerated(const unsigned char *p, size_t len,
+ unsigned *num, size_t *size)
+{
+ size_t ret = 0;
+ size_t l, reallen;
+ int e;
+
+ e = der_match_tag(p, len, ASN1_C_UNIV, PRIM, UT_Enumerated, &l);
+ if (e)
+ return (e);
+ p += l;
+ len -= l;
+ ret += l;
+ e = der_get_length(p, len, &reallen, &l);
+ if (e)
+ return (e);
+ p += l;
+ len -= l;
+ ret += l;
+ e = der_get_int(p, reallen, num, &l);
+ if (e)
+ return (e);
+ p += l;
+ len -= l;
+ ret += l;
+ if (size)
+ *size = ret;
+ return (0);
+}
+
+static int
+decode_octet_string(const unsigned char *p, size_t len,
+ octet_string *k, size_t *size)
+{
+ size_t ret = 0;
+ size_t l;
+ int e;
+ size_t slen;
+
+ e = der_match_tag(p, len, ASN1_C_UNIV, PRIM, UT_OctetString, &l);
+ if (e)
+ return (e);
+ p += l;
+ len -= l;
+ ret += l;
+
+ e = der_get_length(p, len, &slen, &l);
+ if (e)
+ return (e);
+ p += l;
+ len -= l;
+ ret += l;
+ if (len < slen)
+ return (ASN1_OVERRUN);
+
+ e = der_get_octet_string(p, slen, k, &l);
+ if (e)
+ return (e);
+ p += l;
+ len -= l;
+ ret += l;
+ if (size)
+ *size = ret;
+ return (0);
+}
+
+static int
+decode_oid(const unsigned char *p, size_t len,
+ oid *k, size_t *size)
+{
+ size_t ret = 0;
+ size_t l;
+ int e;
+ size_t slen;
+
+ e = der_match_tag(p, len, ASN1_C_UNIV, PRIM, UT_OID, &l);
+ if (e)
+ return (e);
+ p += l;
+ len -= l;
+ ret += l;
+
+ e = der_get_length(p, len, &slen, &l);
+ if (e)
+ return (e);
+ p += l;
+ len -= l;
+ ret += l;
+ if (len < slen)
+ return (ASN1_OVERRUN);
+
+ e = der_get_oid(p, slen, k, &l);
+ if (e)
+ return (e);
+ p += l;
+ len -= l;
+ ret += l;
+ if (size)
+ *size = ret;
+ return (0);
+}
+
+static int
+fix_dce(size_t reallen, size_t *len)
+{
+ if (reallen == ASN1_INDEFINITE)
+ return (1);
+ if (*len < reallen)
+ return (-1);
+ *len = reallen;
+ return (0);
+}
+
+/* der_length.c */
+
+static size_t
+len_unsigned(unsigned val)
+{
+ size_t ret = 0;
+
+ do {
+ ++ret;
+ val /= 256;
+ } while (val);
+ return (ret);
+}
+
+static size_t
+length_len(size_t len)
+{
+ if (len < 128)
+ return (1);
+ else
+ return (len_unsigned(len) + 1);
+}
+
+
+/* der_put.c */
+
+/*
+ * All encoding functions take a pointer `p' to first position in which to
+ * write, from the right, `len' which means the maximum number of characters
+ * we are able to write. The function returns the number of characters
+ * written in `size' (if non-NULL). The return value is 0 or an error.
+ */
+
+static int
+der_put_unsigned(unsigned char *p, size_t len, unsigned val, size_t *size)
+{
+ unsigned char *base = p;
+
+ if (val) {
+ while (len > 0 && val) {
+ *p-- = val % 256;
+ val /= 256;
+ --len;
+ }
+ if (val != 0)
+ return (ASN1_OVERFLOW);
+ else {
+ *size = base - p;
+ return (0);
+ }
+ } else if (len < 1)
+ return (ASN1_OVERFLOW);
+ else {
+ *p = 0;
+ *size = 1;
+ return (0);
+ }
+}
+
+static int
+der_put_int(unsigned char *p, size_t len, int val, size_t *size)
+{
+ unsigned char *base = p;
+
+ if (val >= 0) {
+ do {
+ if (len < 1)
+ return (ASN1_OVERFLOW);
+ *p-- = val % 256;
+ len--;
+ val /= 256;
+ } while (val);
+ if (p[1] >= 128) {
+ if (len < 1)
+ return (ASN1_OVERFLOW);
+ *p-- = 0;
+ len--;
+ }
+ } else {
+ val = ~val;
+ do {
+ if (len < 1)
+ return (ASN1_OVERFLOW);
+ *p-- = ~(val % 256);
+ len--;
+ val /= 256;
+ } while (val);
+ if (p[1] < 128) {
+ if (len < 1)
+ return (ASN1_OVERFLOW);
+ *p-- = 0xff;
+ len--;
+ }
+ }
+ *size = base - p;
+ return (0);
+}
+
+static int
+der_put_length(unsigned char *p, size_t len, size_t val, size_t *size)
+{
+ if (len < 1)
+ return (ASN1_OVERFLOW);
+ if (val < 128) {
+ *p = val;
+ *size = 1;
+ return (0);
+ } else {
+ size_t l;
+ int e;
+
+ e = der_put_unsigned(p, len - 1, val, &l);
+ if (e)
+ return (e);
+ p -= l;
+ *p = 0x80 | l;
+ *size = l + 1;
+ return (0);
+ }
+}
+
+static int
+der_put_octet_string(unsigned char *p, size_t len,
+ const octet_string *data, size_t *size)
+{
+ if (len < data->length)
+ return (ASN1_OVERFLOW);
+ p -= data->length;
+ len -= data->length;
+ memcpy(p + 1, data->data, data->length);
+ *size = data->length;
+ return (0);
+}
+
+static int
+der_put_oid(unsigned char *p, size_t len,
+ const oid *data, size_t *size)
+{
+ unsigned char *base = p;
+ int n;
+
+ for (n = data->length - 1; n >= 2; --n) {
+ unsigned u = data->components[n];
+
+ if (len < 1)
+ return (ASN1_OVERFLOW);
+ *p-- = u % 128;
+ u /= 128;
+ --len;
+ while (u > 0) {
+ if (len < 1)
+ return (ASN1_OVERFLOW);
+ *p-- = 128 + u % 128;
+ u /= 128;
+ --len;
+ }
+ }
+ if (len < 1)
+ return (ASN1_OVERFLOW);
+ *p-- = 40 * data->components[0] + data->components[1];
+ *size = base - p;
+ return (0);
+}
+
+static int
+der_put_tag(unsigned char *p, size_t len, Der_class class, Der_type type,
+ int tag, size_t *size)
+{
+ if (len < 1)
+ return (ASN1_OVERFLOW);
+ *p = (class << 6) | (type << 5) | tag; /* XXX */
+ *size = 1;
+ return (0);
+}
+
+static int
+der_put_length_and_tag(unsigned char *p, size_t len, size_t len_val,
+ Der_class class, Der_type type, int tag, size_t *size)
+{
+ size_t ret = 0;
+ size_t l;
+ int e;
+
+ e = der_put_length(p, len, len_val, &l);
+ if (e)
+ return (e);
+ p -= l;
+ len -= l;
+ ret += l;
+ e = der_put_tag(p, len, class, type, tag, &l);
+ if (e)
+ return (e);
+ p -= l;
+ len -= l;
+ ret += l;
+ *size = ret;
+ return (0);
+}
+
+static int
+encode_enumerated(unsigned char *p, size_t len, const unsigned *data,
+ size_t *size)
+{
+ unsigned num = *data;
+ size_t ret = 0;
+ size_t l;
+ int e;
+
+ e = der_put_int(p, len, num, &l);
+ if (e)
+ return (e);
+ p -= l;
+ len -= l;
+ ret += l;
+ e = der_put_length_and_tag(p, len, l, ASN1_C_UNIV, PRIM, UT_Enumerated, &l);
+ if (e)
+ return (e);
+ p -= l;
+ len -= l;
+ ret += l;
+ *size = ret;
+ return (0);
+}
+
+static int
+encode_octet_string(unsigned char *p, size_t len,
+ const octet_string *k, size_t *size)
+{
+ size_t ret = 0;
+ size_t l;
+ int e;
+
+ e = der_put_octet_string(p, len, k, &l);
+ if (e)
+ return (e);
+ p -= l;
+ len -= l;
+ ret += l;
+ e = der_put_length_and_tag(p, len, l, ASN1_C_UNIV, PRIM, UT_OctetString, &l);
+ if (e)
+ return (e);
+ p -= l;
+ len -= l;
+ ret += l;
+ *size = ret;
+ return (0);
+}
+
+static int
+encode_oid(unsigned char *p, size_t len,
+ const oid *k, size_t *size)
+{
+ size_t ret = 0;
+ size_t l;
+ int e;
+
+ e = der_put_oid(p, len, k, &l);
+ if (e)
+ return (e);
+ p -= l;
+ len -= l;
+ ret += l;
+ e = der_put_length_and_tag(p, len, l, ASN1_C_UNIV, PRIM, UT_OID, &l);
+ if (e)
+ return (e);
+ p -= l;
+ len -= l;
+ ret += l;
+ *size = ret;
+ return (0);
+}
+
+
+/* encapsulate.c */
+
+static void
+gssapi_encap_length(size_t data_len,
+ size_t *len,
+ size_t *total_len,
+ const gss_OID mech)
+{
+ size_t len_len;
+
+ *len = 1 + 1 + mech->length + data_len;
+
+ len_len = length_len(*len);
+
+ *total_len = 1 + len_len + *len;
+}
+
+static u_char *
+gssapi_mech_make_header(u_char *p,
+ size_t len,
+ const gss_OID mech)
+{
+ int e;
+ size_t len_len, foo;
+
+ *p++ = 0x60;
+ len_len = length_len(len);
+ e = der_put_length(p + len_len - 1, len_len, len, &foo);
+ if (e || foo != len_len)
+ return (NULL);
+ p += len_len;
+ *p++ = 0x06;
+ *p++ = mech->length;
+ memcpy(p, mech->elements, mech->length);
+ p += mech->length;
+ return (p);
+}
+
+/*
+ * Give it a krb5_data and it will encapsulate with extra GSS-API wrappings.
+ */
+
+static OM_uint32
+gssapi_spnego_encapsulate(OM_uint32 * minor_status,
+ unsigned char *buf,
+ size_t buf_size,
+ gss_buffer_t output_token,
+ const gss_OID mech)
+{
+ size_t len, outer_len;
+ u_char *p;
+
+ gssapi_encap_length(buf_size, &len, &outer_len, mech);
+
+ output_token->length = outer_len;
+ output_token->value = malloc(outer_len);
+ if (output_token->value == NULL) {
+ *minor_status = ENOMEM;
+ return (GSS_S_FAILURE);
+ }
+ p = gssapi_mech_make_header(output_token->value, len, mech);
+ if (p == NULL) {
+ if (output_token->length != 0)
+ gss_release_buffer(minor_status, output_token);
+ return (GSS_S_FAILURE);
+ }
+ memcpy(p, buf, buf_size);
+ return (GSS_S_COMPLETE);
+}
+
+/* init_sec_context.c */
+/*
+ * SPNEGO wrapper for Kerberos5 GSS-API kouril@ics.muni.cz, 2003 (mostly
+ * based on Heimdal code)
+ */
+
+static int
+add_mech(MechTypeList * mech_list, gss_OID mech)
+{
+ MechType *tmp;
+ int ret;
+
+ tmp = realloc(mech_list->val, (mech_list->len + 1) * sizeof(*tmp));
+ if (tmp == NULL)
+ return (ENOMEM);
+ mech_list->val = tmp;
+
+ ret = der_get_oid(mech->elements, mech->length,
+ &mech_list->val[mech_list->len], NULL);
+ if (ret)
+ return (ret);
+
+ mech_list->len++;
+ return (0);
+}
+
+/*
+ * return the length of the mechanism in token or -1
+ * (which implies that the token was bad - GSS_S_DEFECTIVE_TOKEN
+ */
+
+static ssize_t
+gssapi_krb5_get_mech(const u_char *ptr,
+ size_t total_len,
+ const u_char **mech_ret)
+{
+ size_t len, len_len, mech_len, foo;
+ const u_char *p = ptr;
+ int e;
+
+ if (total_len < 1)
+ return (-1);
+ if (*p++ != 0x60)
+ return (-1);
+ e = der_get_length (p, total_len - 1, &len, &len_len);
+ if (e || 1 + len_len + len != total_len)
+ return (-1);
+ p += len_len;
+ if (*p++ != 0x06)
+ return (-1);
+ e = der_get_length (p, total_len - 1 - len_len - 1,
+ &mech_len, &foo);
+ if (e)
+ return (-1);
+ p += foo;
+ *mech_ret = p;
+ return (mech_len);
+}
+
+static OM_uint32
+spnego_initial(OM_uint32 *minor_status,
+ const gss_cred_id_t initiator_cred_handle,
+ gss_ctx_id_t *context_handle,
+ const gss_name_t target_name,
+ const gss_OID mech_type,
+ OM_uint32 req_flags,
+ OM_uint32 time_req,
+ const gss_channel_bindings_t input_chan_bindings,
+ const gss_buffer_t input_token,
+ gss_OID *actual_mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 *ret_flags,
+ OM_uint32 *time_rec)
+{
+ NegTokenInit token_init;
+ OM_uint32 major_status, minor_status2;
+ gss_buffer_desc krb5_output_token = GSS_C_EMPTY_BUFFER;
+ unsigned char *buf = NULL;
+ size_t buf_size;
+ size_t len;
+ int ret;
+
+ (void)mech_type;
+
+ memset(&token_init, 0, sizeof(token_init));
+
+ ret = add_mech(&token_init.mechTypes, GSS_KRB5_MECH);
+ if (ret) {
+ *minor_status = ret;
+ ret = GSS_S_FAILURE;
+ goto end;
+ }
+
+ major_status = gss_init_sec_context(minor_status,
+ initiator_cred_handle,
+ context_handle,
+ target_name,
+ GSS_KRB5_MECH,
+ req_flags,
+ time_req,
+ input_chan_bindings,
+ input_token,
+ actual_mech_type,
+ &krb5_output_token,
+ ret_flags,
+ time_rec);
+ if (GSS_ERROR(major_status)) {
+ ret = major_status;
+ goto end;
+ }
+ if (krb5_output_token.length > 0) {
+ token_init.mechToken = malloc(sizeof(*token_init.mechToken));
+ if (token_init.mechToken == NULL) {
+ *minor_status = ENOMEM;
+ ret = GSS_S_FAILURE;
+ goto end;
+ }
+ token_init.mechToken->data = krb5_output_token.value;
+ token_init.mechToken->length = krb5_output_token.length;
+ }
+ /*
+ * The MS implementation of SPNEGO seems to not like the mechListMIC
+ * field, so we omit it (it's optional anyway)
+ */
+
+ buf_size = 1024;
+ buf = malloc(buf_size);
+
+ do {
+ ret = encode_NegTokenInit(buf + buf_size - 1,
+ buf_size,
+ &token_init, &len);
+ if (ret == 0) {
+ size_t tmp;
+
+ ret = der_put_length_and_tag(buf + buf_size - len - 1,
+ buf_size - len,
+ len,
+ ASN1_C_CONTEXT,
+ CONS,
+ 0,
+ &tmp);
+ if (ret == 0)
+ len += tmp;
+ }
+ if (ret) {
+ if (ret == ASN1_OVERFLOW) {
+ u_char *tmp;
+
+ buf_size *= 2;
+ tmp = realloc(buf, buf_size);
+ if (tmp == NULL) {
+ *minor_status = ENOMEM;
+ ret = GSS_S_FAILURE;
+ goto end;
+ }
+ buf = tmp;
+ } else {
+ *minor_status = ret;
+ ret = GSS_S_FAILURE;
+ goto end;
+ }
+ }
+ } while (ret == ASN1_OVERFLOW);
+
+ ret = gssapi_spnego_encapsulate(minor_status,
+ buf + buf_size - len, len,
+ output_token, GSS_SPNEGO_MECH);
+ if (ret == GSS_S_COMPLETE)
+ ret = major_status;
+
+end:
+ if (token_init.mechToken != NULL) {
+ free(token_init.mechToken);
+ token_init.mechToken = NULL;
+ }
+ free_NegTokenInit(&token_init);
+ if (krb5_output_token.length != 0)
+ gss_release_buffer(&minor_status2, &krb5_output_token);
+ if (buf)
+ free(buf);
+
+ return (ret);
+}
+
+static OM_uint32
+spnego_reply(OM_uint32 *minor_status,
+ const gss_cred_id_t initiator_cred_handle,
+ gss_ctx_id_t *context_handle,
+ const gss_name_t target_name,
+ const gss_OID mech_type,
+ OM_uint32 req_flags,
+ OM_uint32 time_req,
+ const gss_channel_bindings_t input_chan_bindings,
+ const gss_buffer_t input_token,
+ gss_OID *actual_mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 *ret_flags,
+ OM_uint32 *time_rec)
+{
+ OM_uint32 ret;
+ NegTokenResp resp;
+ unsigned char *buf;
+ size_t buf_size;
+ u_char oidbuf[17];
+ size_t oidlen;
+ gss_buffer_desc sub_token;
+ ssize_t mech_len;
+ const u_char *p;
+ size_t len, taglen;
+
+ (void)mech_type;
+
+ output_token->length = 0;
+ output_token->value = NULL;
+
+ /*
+ * SPNEGO doesn't include gss wrapping on SubsequentContextToken
+ * like the Kerberos 5 mech does. But lets check for it anyway.
+ */
+
+ mech_len = gssapi_krb5_get_mech(input_token->value,
+ input_token->length,
+ &p);
+
+ if (mech_len < 0) {
+ buf = input_token->value;
+ buf_size = input_token->length;
+ } else if ((size_t)mech_len == GSS_KRB5_MECH->length &&
+ memcmp(GSS_KRB5_MECH->elements, p, mech_len) == 0)
+ return (gss_init_sec_context(minor_status,
+ initiator_cred_handle,
+ context_handle,
+ target_name,
+ GSS_KRB5_MECH,
+ req_flags,
+ time_req,
+ input_chan_bindings,
+ input_token,
+ actual_mech_type,
+ output_token,
+ ret_flags,
+ time_rec));
+ else if ((size_t)mech_len == GSS_SPNEGO_MECH->length &&
+ memcmp(GSS_SPNEGO_MECH->elements, p, mech_len) == 0) {
+ ret = gssapi_spnego_decapsulate(minor_status,
+ input_token,
+ &buf,
+ &buf_size,
+ GSS_SPNEGO_MECH);
+ if (ret)
+ return (ret);
+ } else
+ return (GSS_S_BAD_MECH);
+
+ ret = der_match_tag_and_length(buf, buf_size,
+ ASN1_C_CONTEXT, CONS, 1, &len, &taglen);
+ if (ret)
+ return (ret);
+
+ if(len > buf_size - taglen)
+ return (ASN1_OVERRUN);
+
+ ret = decode_NegTokenResp(buf + taglen, len, &resp, NULL);
+ if (ret) {
+ *minor_status = ENOMEM;
+ return (GSS_S_FAILURE);
+ }
+
+ if (resp.negState == NULL ||
+ *(resp.negState) == reject ||
+ resp.supportedMech == NULL) {
+ free_NegTokenResp(&resp);
+ return (GSS_S_BAD_MECH);
+ }
+
+ ret = der_put_oid(oidbuf + sizeof(oidbuf) - 1,
+ sizeof(oidbuf),
+ resp.supportedMech,
+ &oidlen);
+ if (ret || oidlen != GSS_KRB5_MECH->length ||
+ memcmp(oidbuf + sizeof(oidbuf) - oidlen,
+ GSS_KRB5_MECH->elements,
+ oidlen) != 0) {
+ free_NegTokenResp(&resp);
+ return GSS_S_BAD_MECH;
+ }
+
+ if (resp.responseToken != NULL) {
+ sub_token.length = resp.responseToken->length;
+ sub_token.value = resp.responseToken->data;
+ } else {
+ sub_token.length = 0;
+ sub_token.value = NULL;
+ }
+
+ ret = gss_init_sec_context(minor_status,
+ initiator_cred_handle,
+ context_handle,
+ target_name,
+ GSS_KRB5_MECH,
+ req_flags,
+ time_req,
+ input_chan_bindings,
+ &sub_token,
+ actual_mech_type,
+ output_token,
+ ret_flags,
+ time_rec);
+ if (ret) {
+ free_NegTokenResp(&resp);
+ return (ret);
+ }
+
+ /*
+ * XXXSRA I don't think this limited implementation ever needs
+ * to check the MIC -- our preferred mechanism (Kerberos)
+ * authenticates its own messages and is the only mechanism
+ * we'll accept, so if the mechanism negotiation completes
+ * sucessfully, we don't need the MIC. See RFC 4178.
+ */
+
+ free_NegTokenResp(&resp);
+ return (ret);
+}
+
+
+
+OM_uint32
+gss_init_sec_context_spnego(OM_uint32 *minor_status,
+ const gss_cred_id_t initiator_cred_handle,
+ gss_ctx_id_t *context_handle,
+ const gss_name_t target_name,
+ const gss_OID mech_type,
+ OM_uint32 req_flags,
+ OM_uint32 time_req,
+ const gss_channel_bindings_t input_chan_bindings,
+ const gss_buffer_t input_token,
+ gss_OID *actual_mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 *ret_flags,
+ OM_uint32 *time_rec)
+{
+ /* Dirty trick to suppress compiler warnings */
+
+ /* Figure out whether we're starting over or processing a reply */
+
+ if (input_token == GSS_C_NO_BUFFER || input_token->length == 0)
+ return (spnego_initial(minor_status,
+ initiator_cred_handle,
+ context_handle,
+ target_name,
+ mech_type,
+ req_flags,
+ time_req,
+ input_chan_bindings,
+ input_token,
+ actual_mech_type,
+ output_token,
+ ret_flags,
+ time_rec));
+ else
+ return (spnego_reply(minor_status,
+ initiator_cred_handle,
+ context_handle,
+ target_name,
+ mech_type,
+ req_flags,
+ time_req,
+ input_chan_bindings,
+ input_token,
+ actual_mech_type,
+ output_token,
+ ret_flags,
+ time_rec));
+}
+
+#endif /* GSSAPI */
diff --git a/lib/dns/spnego.h b/lib/dns/spnego.h
new file mode 100644
index 0000000..c44614b
--- /dev/null
+++ b/lib/dns/spnego.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2006, 2007 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 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.
+ */
+
+/* $Id: spnego.h,v 1.4 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file
+ * \brief
+ * Entry points into portable SPNEGO implementation.
+ * See spnego.c for information on the SPNEGO implementation itself.
+ */
+
+#ifndef _SPNEGO_H_
+#define _SPNEGO_H_
+
+/*%
+ * Wrapper for GSSAPI gss_init_sec_context(), using portable SPNEGO
+ * implementation instead of the one that's part of the GSSAPI
+ * library. Takes arguments identical to the standard GSSAPI
+ * function, uses standard gss_init_sec_context() to handle
+ * everything inside the SPNEGO wrapper.
+ */
+OM_uint32
+gss_init_sec_context_spnego(OM_uint32 *,
+ const gss_cred_id_t,
+ gss_ctx_id_t *,
+ const gss_name_t,
+ const gss_OID,
+ OM_uint32,
+ OM_uint32,
+ const gss_channel_bindings_t,
+ const gss_buffer_t,
+ gss_OID *,
+ gss_buffer_t,
+ OM_uint32 *,
+ OM_uint32 *);
+
+/*%
+ * Wrapper for GSSAPI gss_accept_sec_context(), using portable SPNEGO
+ * implementation instead of the one that's part of the GSSAPI
+ * library. Takes arguments identical to the standard GSSAPI
+ * function. Checks the OID of the input token to see if it's SPNEGO;
+ * if so, processes it, otherwise hands the call off to the standard
+ * gss_accept_sec_context() function.
+ */
+OM_uint32 gss_accept_sec_context_spnego(OM_uint32 *,
+ gss_ctx_id_t *,
+ const gss_cred_id_t,
+ const gss_buffer_t,
+ const gss_channel_bindings_t,
+ gss_name_t *,
+ gss_OID *,
+ gss_buffer_t,
+ OM_uint32 *,
+ OM_uint32 *,
+ gss_cred_id_t *);
+
+
+#endif
diff --git a/lib/dns/spnego_asn1.c b/lib/dns/spnego_asn1.c
new file mode 100644
index 0000000..75c2304
--- /dev/null
+++ b/lib/dns/spnego_asn1.c
@@ -0,0 +1,885 @@
+/*
+ * Copyright (C) 2006, 2007 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 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.
+ */
+
+/* $Id: spnego_asn1.c,v 1.4 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file
+ * \brief Method routines generated from SPNEGO ASN.1 module.
+ * See spnego_asn1.pl for details. Do not edit.
+ */
+
+/* Generated from spnego.asn1 */
+/* Do not edit */
+
+#ifndef __asn1_h__
+#define __asn1_h__
+
+
+#ifndef __asn1_common_definitions__
+#define __asn1_common_definitions__
+
+typedef struct octet_string {
+ size_t length;
+ void *data;
+} octet_string;
+
+typedef char *general_string;
+
+typedef char *utf8_string;
+
+typedef struct oid {
+ size_t length;
+ unsigned *components;
+} oid;
+
+#define ASN1_MALLOC_ENCODE(T, B, BL, S, L, R) \
+ do { \
+ (BL) = length_##T((S)); \
+ (B) = malloc((BL)); \
+ if((B) == NULL) { \
+ (R) = ENOMEM; \
+ } else { \
+ (R) = encode_##T(((unsigned char*)(B)) + (BL) - 1, (BL), \
+ (S), (L)); \
+ if((R) != 0) { \
+ free((B)); \
+ (B) = NULL; \
+ } \
+ } \
+ } while (0)
+
+#endif
+
+/*
+ * MechType ::= OBJECT IDENTIFIER
+ */
+
+typedef oid MechType;
+
+static int encode_MechType(unsigned char *, size_t, const MechType *, size_t *);
+static int decode_MechType(const unsigned char *, size_t, MechType *, size_t *);
+static void free_MechType(MechType *);
+/* unused declaration: length_MechType */
+/* unused declaration: copy_MechType */
+
+
+/*
+ * MechTypeList ::= SEQUENCE OF MechType
+ */
+
+typedef struct MechTypeList {
+ unsigned int len;
+ MechType *val;
+} MechTypeList;
+
+static int encode_MechTypeList(unsigned char *, size_t, const MechTypeList *, size_t *);
+static int decode_MechTypeList(const unsigned char *, size_t, MechTypeList *, size_t *);
+static void free_MechTypeList(MechTypeList *);
+/* unused declaration: length_MechTypeList */
+/* unused declaration: copy_MechTypeList */
+
+
+/*
+ * ContextFlags ::= BIT STRING { delegFlag(0), mutualFlag(1), replayFlag(2),
+ * sequenceFlag(3), anonFlag(4), confFlag(5), integFlag(6) }
+ */
+
+typedef struct ContextFlags {
+ unsigned int delegFlag:1;
+ unsigned int mutualFlag:1;
+ unsigned int replayFlag:1;
+ unsigned int sequenceFlag:1;
+ unsigned int anonFlag:1;
+ unsigned int confFlag:1;
+ unsigned int integFlag:1;
+} ContextFlags;
+
+
+static int encode_ContextFlags(unsigned char *, size_t, const ContextFlags *, size_t *);
+static int decode_ContextFlags(const unsigned char *, size_t, ContextFlags *, size_t *);
+static void free_ContextFlags(ContextFlags *);
+/* unused declaration: length_ContextFlags */
+/* unused declaration: copy_ContextFlags */
+/* unused declaration: ContextFlags2int */
+/* unused declaration: int2ContextFlags */
+/* unused declaration: asn1_ContextFlags_units */
+
+/*
+ * NegTokenInit ::= SEQUENCE { mechTypes[0] MechTypeList, reqFlags[1]
+ * ContextFlags OPTIONAL, mechToken[2] OCTET STRING OPTIONAL,
+ * mechListMIC[3] OCTET STRING OPTIONAL }
+ */
+
+typedef struct NegTokenInit {
+ MechTypeList mechTypes;
+ ContextFlags *reqFlags;
+ octet_string *mechToken;
+ octet_string *mechListMIC;
+} NegTokenInit;
+
+static int encode_NegTokenInit(unsigned char *, size_t, const NegTokenInit *, size_t *);
+static int decode_NegTokenInit(const unsigned char *, size_t, NegTokenInit *, size_t *);
+static void free_NegTokenInit(NegTokenInit *);
+/* unused declaration: length_NegTokenInit */
+/* unused declaration: copy_NegTokenInit */
+
+
+/*
+ * NegTokenResp ::= SEQUENCE { negState[0] ENUMERATED {
+ * accept-completed(0), accept-incomplete(1), reject(2), request-mic(3) }
+ * OPTIONAL, supportedMech[1] MechType OPTIONAL, responseToken[2] OCTET
+ * STRING OPTIONAL, mechListMIC[3] OCTET STRING OPTIONAL }
+ */
+
+typedef struct NegTokenResp {
+ enum {
+ accept_completed = 0,
+ accept_incomplete = 1,
+ reject = 2,
+ request_mic = 3
+ } *negState;
+
+ MechType *supportedMech;
+ octet_string *responseToken;
+ octet_string *mechListMIC;
+} NegTokenResp;
+
+static int encode_NegTokenResp(unsigned char *, size_t, const NegTokenResp *, size_t *);
+static int decode_NegTokenResp(const unsigned char *, size_t, NegTokenResp *, size_t *);
+static void free_NegTokenResp(NegTokenResp *);
+/* unused declaration: length_NegTokenResp */
+/* unused declaration: copy_NegTokenResp */
+
+
+
+
+#endif /* __asn1_h__ */
+/* Generated from spnego.asn1 */
+/* Do not edit */
+
+
+#define BACK if (e) return e; p -= l; len -= l; ret += l
+
+static int
+encode_MechType(unsigned char *p, size_t len, const MechType * data, size_t * size)
+{
+ size_t ret = 0;
+ size_t l;
+ int i, e;
+
+ i = 0;
+ e = encode_oid(p, len, data, &l);
+ BACK;
+ *size = ret;
+ return 0;
+}
+
+#define FORW if(e) goto fail; p += l; len -= l; ret += l
+
+static int
+decode_MechType(const unsigned char *p, size_t len, MechType * data, size_t * size)
+{
+ size_t ret = 0, reallen;
+ size_t l;
+ int e;
+
+ memset(data, 0, sizeof(*data));
+ reallen = 0;
+ e = decode_oid(p, len, data, &l);
+ FORW;
+ if (size)
+ *size = ret;
+ return 0;
+fail:
+ free_MechType(data);
+ return e;
+}
+
+static void
+free_MechType(MechType * data)
+{
+ free_oid(data);
+}
+
+/* unused function: length_MechType */
+
+
+/* unused function: copy_MechType */
+
+/* Generated from spnego.asn1 */
+/* Do not edit */
+
+
+#define BACK if (e) return e; p -= l; len -= l; ret += l
+
+static int
+encode_MechTypeList(unsigned char *p, size_t len, const MechTypeList * data, size_t * size)
+{
+ size_t ret = 0;
+ size_t l;
+ int i, e;
+
+ i = 0;
+ for (i = (data)->len - 1; i >= 0; --i) {
+ int oldret = ret;
+ ret = 0;
+ e = encode_MechType(p, len, &(data)->val[i], &l);
+ BACK;
+ ret += oldret;
+ }
+ e = der_put_length_and_tag(p, len, ret, ASN1_C_UNIV, CONS, UT_Sequence, &l);
+ BACK;
+ *size = ret;
+ return 0;
+}
+
+#define FORW if(e) goto fail; p += l; len -= l; ret += l
+
+static int
+decode_MechTypeList(const unsigned char *p, size_t len, MechTypeList * data, size_t * size)
+{
+ size_t ret = 0, reallen;
+ size_t l;
+ int e;
+
+ memset(data, 0, sizeof(*data));
+ reallen = 0;
+ e = der_match_tag_and_length(p, len, ASN1_C_UNIV, CONS, UT_Sequence, &reallen, &l);
+ FORW;
+ if (len < reallen)
+ return ASN1_OVERRUN;
+ len = reallen;
+ {
+ size_t origlen = len;
+ int oldret = ret;
+ ret = 0;
+ (data)->len = 0;
+ (data)->val = NULL;
+ while (ret < origlen) {
+ (data)->len++;
+ (data)->val = realloc((data)->val, sizeof(*((data)->val)) * (data)->len);
+ e = decode_MechType(p, len, &(data)->val[(data)->len - 1], &l);
+ FORW;
+ len = origlen - ret;
+ }
+ ret += oldret;
+ }
+ if (size)
+ *size = ret;
+ return 0;
+fail:
+ free_MechTypeList(data);
+ return e;
+}
+
+static void
+free_MechTypeList(MechTypeList * data)
+{
+ while ((data)->len) {
+ free_MechType(&(data)->val[(data)->len - 1]);
+ (data)->len--;
+ }
+ free((data)->val);
+ (data)->val = NULL;
+}
+
+/* unused function: length_MechTypeList */
+
+
+/* unused function: copy_MechTypeList */
+
+/* Generated from spnego.asn1 */
+/* Do not edit */
+
+
+#define BACK if (e) return e; p -= l; len -= l; ret += l
+
+static int
+encode_ContextFlags(unsigned char *p, size_t len, const ContextFlags * data, size_t * size)
+{
+ size_t ret = 0;
+ size_t l;
+ int i, e;
+
+ i = 0;
+ {
+ unsigned char c = 0;
+ *p-- = c;
+ len--;
+ ret++;
+ c = 0;
+ *p-- = c;
+ len--;
+ ret++;
+ c = 0;
+ *p-- = c;
+ len--;
+ ret++;
+ c = 0;
+ if (data->integFlag)
+ c |= 1 << 1;
+ if (data->confFlag)
+ c |= 1 << 2;
+ if (data->anonFlag)
+ c |= 1 << 3;
+ if (data->sequenceFlag)
+ c |= 1 << 4;
+ if (data->replayFlag)
+ c |= 1 << 5;
+ if (data->mutualFlag)
+ c |= 1 << 6;
+ if (data->delegFlag)
+ c |= 1 << 7;
+ *p-- = c;
+ *p-- = 0;
+ len -= 2;
+ ret += 2;
+ }
+
+ e = der_put_length_and_tag(p, len, ret, ASN1_C_UNIV, PRIM, UT_BitString, &l);
+ BACK;
+ *size = ret;
+ return 0;
+}
+
+#define FORW if(e) goto fail; p += l; len -= l; ret += l
+
+static int
+decode_ContextFlags(const unsigned char *p, size_t len, ContextFlags * data, size_t * size)
+{
+ size_t ret = 0, reallen;
+ size_t l;
+ int e;
+
+ memset(data, 0, sizeof(*data));
+ reallen = 0;
+ e = der_match_tag_and_length(p, len, ASN1_C_UNIV, PRIM, UT_BitString, &reallen, &l);
+ FORW;
+ if (len < reallen)
+ return ASN1_OVERRUN;
+ p++;
+ len--;
+ reallen--;
+ ret++;
+ data->delegFlag = (*p >> 7) & 1;
+ data->mutualFlag = (*p >> 6) & 1;
+ data->replayFlag = (*p >> 5) & 1;
+ data->sequenceFlag = (*p >> 4) & 1;
+ data->anonFlag = (*p >> 3) & 1;
+ data->confFlag = (*p >> 2) & 1;
+ data->integFlag = (*p >> 1) & 1;
+ p += reallen;
+ len -= reallen;
+ ret += reallen;
+ if (size)
+ *size = ret;
+ return 0;
+fail:
+ free_ContextFlags(data);
+ return e;
+}
+
+static void
+free_ContextFlags(ContextFlags * data)
+{
+ (void)data;
+}
+
+/* unused function: length_ContextFlags */
+
+
+/* unused function: copy_ContextFlags */
+
+
+/* unused function: ContextFlags2int */
+
+
+/* unused function: int2ContextFlags */
+
+
+/* unused variable: ContextFlags_units */
+
+/* unused function: asn1_ContextFlags_units */
+
+/* Generated from spnego.asn1 */
+/* Do not edit */
+
+
+#define BACK if (e) return e; p -= l; len -= l; ret += l
+
+static int
+encode_NegTokenInit(unsigned char *p, size_t len, const NegTokenInit * data, size_t * size)
+{
+ size_t ret = 0;
+ size_t l;
+ int i, e;
+
+ i = 0;
+ if ((data)->mechListMIC) {
+ int oldret = ret;
+ ret = 0;
+ e = encode_octet_string(p, len, (data)->mechListMIC, &l);
+ BACK;
+ e = der_put_length_and_tag(p, len, ret, ASN1_C_CONTEXT, CONS, 3, &l);
+ BACK;
+ ret += oldret;
+ }
+ if ((data)->mechToken) {
+ int oldret = ret;
+ ret = 0;
+ e = encode_octet_string(p, len, (data)->mechToken, &l);
+ BACK;
+ e = der_put_length_and_tag(p, len, ret, ASN1_C_CONTEXT, CONS, 2, &l);
+ BACK;
+ ret += oldret;
+ }
+ if ((data)->reqFlags) {
+ int oldret = ret;
+ ret = 0;
+ e = encode_ContextFlags(p, len, (data)->reqFlags, &l);
+ BACK;
+ e = der_put_length_and_tag(p, len, ret, ASN1_C_CONTEXT, CONS, 1, &l);
+ BACK;
+ ret += oldret;
+ } {
+ int oldret = ret;
+ ret = 0;
+ e = encode_MechTypeList(p, len, &(data)->mechTypes, &l);
+ BACK;
+ e = der_put_length_and_tag(p, len, ret, ASN1_C_CONTEXT, CONS, 0, &l);
+ BACK;
+ ret += oldret;
+ }
+ e = der_put_length_and_tag(p, len, ret, ASN1_C_UNIV, CONS, UT_Sequence, &l);
+ BACK;
+ *size = ret;
+ return 0;
+}
+
+#define FORW if(e) goto fail; p += l; len -= l; ret += l
+
+static int
+decode_NegTokenInit(const unsigned char *p, size_t len, NegTokenInit * data, size_t * size)
+{
+ size_t ret = 0, reallen;
+ size_t l;
+ int e;
+
+ memset(data, 0, sizeof(*data));
+ reallen = 0;
+ e = der_match_tag_and_length(p, len, ASN1_C_UNIV, CONS, UT_Sequence, &reallen, &l);
+ FORW;
+ {
+ int dce_fix;
+ if ((dce_fix = fix_dce(reallen, &len)) < 0)
+ return ASN1_BAD_FORMAT;
+ {
+ size_t newlen, oldlen;
+
+ e = der_match_tag(p, len, ASN1_C_CONTEXT, CONS, 0, &l);
+ if (e)
+ return e;
+ else {
+ p += l;
+ len -= l;
+ ret += l;
+ e = der_get_length(p, len, &newlen, &l);
+ FORW;
+ {
+ int dce_fix;
+ oldlen = len;
+ if ((dce_fix = fix_dce(newlen, &len)) < 0)
+ return ASN1_BAD_FORMAT;
+ e = decode_MechTypeList(p, len, &(data)->mechTypes, &l);
+ FORW;
+ if (dce_fix) {
+ e = der_match_tag_and_length(p, len, (Der_class) 0, (Der_type) 0, 0, &reallen, &l);
+ FORW;
+ } else
+ len = oldlen - newlen;
+ }
+ }
+ }
+ {
+ size_t newlen, oldlen;
+
+ e = der_match_tag(p, len, ASN1_C_CONTEXT, CONS, 1, &l);
+ if (e)
+ (data)->reqFlags = NULL;
+ else {
+ p += l;
+ len -= l;
+ ret += l;
+ e = der_get_length(p, len, &newlen, &l);
+ FORW;
+ {
+ int dce_fix;
+ oldlen = len;
+ if ((dce_fix = fix_dce(newlen, &len)) < 0)
+ return ASN1_BAD_FORMAT;
+ (data)->reqFlags = malloc(sizeof(*(data)->reqFlags));
+ if ((data)->reqFlags == NULL)
+ return ENOMEM;
+ e = decode_ContextFlags(p, len, (data)->reqFlags, &l);
+ FORW;
+ if (dce_fix) {
+ e = der_match_tag_and_length(p, len, (Der_class) 0, (Der_type) 0, 0, &reallen, &l);
+ FORW;
+ } else
+ len = oldlen - newlen;
+ }
+ }
+ }
+ {
+ size_t newlen, oldlen;
+
+ e = der_match_tag(p, len, ASN1_C_CONTEXT, CONS, 2, &l);
+ if (e)
+ (data)->mechToken = NULL;
+ else {
+ p += l;
+ len -= l;
+ ret += l;
+ e = der_get_length(p, len, &newlen, &l);
+ FORW;
+ {
+ int dce_fix;
+ oldlen = len;
+ if ((dce_fix = fix_dce(newlen, &len)) < 0)
+ return ASN1_BAD_FORMAT;
+ (data)->mechToken = malloc(sizeof(*(data)->mechToken));
+ if ((data)->mechToken == NULL)
+ return ENOMEM;
+ e = decode_octet_string(p, len, (data)->mechToken, &l);
+ FORW;
+ if (dce_fix) {
+ e = der_match_tag_and_length(p, len, (Der_class) 0, (Der_type) 0, 0, &reallen, &l);
+ FORW;
+ } else
+ len = oldlen - newlen;
+ }
+ }
+ }
+ {
+ size_t newlen, oldlen;
+
+ e = der_match_tag(p, len, ASN1_C_CONTEXT, CONS, 3, &l);
+ if (e)
+ (data)->mechListMIC = NULL;
+ else {
+ p += l;
+ len -= l;
+ ret += l;
+ e = der_get_length(p, len, &newlen, &l);
+ FORW;
+ {
+ int dce_fix;
+ oldlen = len;
+ if ((dce_fix = fix_dce(newlen, &len)) < 0)
+ return ASN1_BAD_FORMAT;
+ (data)->mechListMIC = malloc(sizeof(*(data)->mechListMIC));
+ if ((data)->mechListMIC == NULL)
+ return ENOMEM;
+ e = decode_octet_string(p, len, (data)->mechListMIC, &l);
+ FORW;
+ if (dce_fix) {
+ e = der_match_tag_and_length(p, len, (Der_class) 0, (Der_type) 0, 0, &reallen, &l);
+ FORW;
+ } else
+ len = oldlen - newlen;
+ }
+ }
+ }
+ if (dce_fix) {
+ e = der_match_tag_and_length(p, len, (Der_class) 0, (Der_type) 0, 0, &reallen, &l);
+ FORW;
+ }
+ }
+ if (size)
+ *size = ret;
+ return 0;
+fail:
+ free_NegTokenInit(data);
+ return e;
+}
+
+static void
+free_NegTokenInit(NegTokenInit * data)
+{
+ free_MechTypeList(&(data)->mechTypes);
+ if ((data)->reqFlags) {
+ free_ContextFlags((data)->reqFlags);
+ free((data)->reqFlags);
+ (data)->reqFlags = NULL;
+ }
+ if ((data)->mechToken) {
+ free_octet_string((data)->mechToken);
+ free((data)->mechToken);
+ (data)->mechToken = NULL;
+ }
+ if ((data)->mechListMIC) {
+ free_octet_string((data)->mechListMIC);
+ free((data)->mechListMIC);
+ (data)->mechListMIC = NULL;
+ }
+}
+
+/* unused function: length_NegTokenInit */
+
+
+/* unused function: copy_NegTokenInit */
+
+/* Generated from spnego.asn1 */
+/* Do not edit */
+
+
+#define BACK if (e) return e; p -= l; len -= l; ret += l
+
+static int
+encode_NegTokenResp(unsigned char *p, size_t len, const NegTokenResp * data, size_t * size)
+{
+ size_t ret = 0;
+ size_t l;
+ int i, e;
+
+ i = 0;
+ if ((data)->mechListMIC) {
+ int oldret = ret;
+ ret = 0;
+ e = encode_octet_string(p, len, (data)->mechListMIC, &l);
+ BACK;
+ e = der_put_length_and_tag(p, len, ret, ASN1_C_CONTEXT, CONS, 3, &l);
+ BACK;
+ ret += oldret;
+ }
+ if ((data)->responseToken) {
+ int oldret = ret;
+ ret = 0;
+ e = encode_octet_string(p, len, (data)->responseToken, &l);
+ BACK;
+ e = der_put_length_and_tag(p, len, ret, ASN1_C_CONTEXT, CONS, 2, &l);
+ BACK;
+ ret += oldret;
+ }
+ if ((data)->supportedMech) {
+ int oldret = ret;
+ ret = 0;
+ e = encode_MechType(p, len, (data)->supportedMech, &l);
+ BACK;
+ e = der_put_length_and_tag(p, len, ret, ASN1_C_CONTEXT, CONS, 1, &l);
+ BACK;
+ ret += oldret;
+ }
+ if ((data)->negState) {
+ int oldret = ret;
+ ret = 0;
+ e = encode_enumerated(p, len, (data)->negState, &l);
+ BACK;
+ e = der_put_length_and_tag(p, len, ret, ASN1_C_CONTEXT, CONS, 0, &l);
+ BACK;
+ ret += oldret;
+ }
+ e = der_put_length_and_tag(p, len, ret, ASN1_C_UNIV, CONS, UT_Sequence, &l);
+ BACK;
+ *size = ret;
+ return 0;
+}
+
+#define FORW if(e) goto fail; p += l; len -= l; ret += l
+
+static int
+decode_NegTokenResp(const unsigned char *p, size_t len, NegTokenResp * data, size_t * size)
+{
+ size_t ret = 0, reallen;
+ size_t l;
+ int e;
+
+ memset(data, 0, sizeof(*data));
+ reallen = 0;
+ e = der_match_tag_and_length(p, len, ASN1_C_UNIV, CONS, UT_Sequence, &reallen, &l);
+ FORW;
+ {
+ int dce_fix;
+ if ((dce_fix = fix_dce(reallen, &len)) < 0)
+ return ASN1_BAD_FORMAT;
+ {
+ size_t newlen, oldlen;
+
+ e = der_match_tag(p, len, ASN1_C_CONTEXT, CONS, 0, &l);
+ if (e)
+ (data)->negState = NULL;
+ else {
+ p += l;
+ len -= l;
+ ret += l;
+ e = der_get_length(p, len, &newlen, &l);
+ FORW;
+ {
+ int dce_fix;
+ oldlen = len;
+ if ((dce_fix = fix_dce(newlen, &len)) < 0)
+ return ASN1_BAD_FORMAT;
+ (data)->negState = malloc(sizeof(*(data)->negState));
+ if ((data)->negState == NULL)
+ return ENOMEM;
+ e = decode_enumerated(p, len, (data)->negState, &l);
+ FORW;
+ if (dce_fix) {
+ e = der_match_tag_and_length(p, len, (Der_class) 0, (Der_type) 0, 0, &reallen, &l);
+ FORW;
+ } else
+ len = oldlen - newlen;
+ }
+ }
+ }
+ {
+ size_t newlen, oldlen;
+
+ e = der_match_tag(p, len, ASN1_C_CONTEXT, CONS, 1, &l);
+ if (e)
+ (data)->supportedMech = NULL;
+ else {
+ p += l;
+ len -= l;
+ ret += l;
+ e = der_get_length(p, len, &newlen, &l);
+ FORW;
+ {
+ int dce_fix;
+ oldlen = len;
+ if ((dce_fix = fix_dce(newlen, &len)) < 0)
+ return ASN1_BAD_FORMAT;
+ (data)->supportedMech = malloc(sizeof(*(data)->supportedMech));
+ if ((data)->supportedMech == NULL)
+ return ENOMEM;
+ e = decode_MechType(p, len, (data)->supportedMech, &l);
+ FORW;
+ if (dce_fix) {
+ e = der_match_tag_and_length(p, len, (Der_class) 0, (Der_type) 0, 0, &reallen, &l);
+ FORW;
+ } else
+ len = oldlen - newlen;
+ }
+ }
+ }
+ {
+ size_t newlen, oldlen;
+
+ e = der_match_tag(p, len, ASN1_C_CONTEXT, CONS, 2, &l);
+ if (e)
+ (data)->responseToken = NULL;
+ else {
+ p += l;
+ len -= l;
+ ret += l;
+ e = der_get_length(p, len, &newlen, &l);
+ FORW;
+ {
+ int dce_fix;
+ oldlen = len;
+ if ((dce_fix = fix_dce(newlen, &len)) < 0)
+ return ASN1_BAD_FORMAT;
+ (data)->responseToken = malloc(sizeof(*(data)->responseToken));
+ if ((data)->responseToken == NULL)
+ return ENOMEM;
+ e = decode_octet_string(p, len, (data)->responseToken, &l);
+ FORW;
+ if (dce_fix) {
+ e = der_match_tag_and_length(p, len, (Der_class) 0, (Der_type) 0, 0, &reallen, &l);
+ FORW;
+ } else
+ len = oldlen - newlen;
+ }
+ }
+ }
+ {
+ size_t newlen, oldlen;
+
+ e = der_match_tag(p, len, ASN1_C_CONTEXT, CONS, 3, &l);
+ if (e)
+ (data)->mechListMIC = NULL;
+ else {
+ p += l;
+ len -= l;
+ ret += l;
+ e = der_get_length(p, len, &newlen, &l);
+ FORW;
+ {
+ int dce_fix;
+ oldlen = len;
+ if ((dce_fix = fix_dce(newlen, &len)) < 0)
+ return ASN1_BAD_FORMAT;
+ (data)->mechListMIC = malloc(sizeof(*(data)->mechListMIC));
+ if ((data)->mechListMIC == NULL)
+ return ENOMEM;
+ e = decode_octet_string(p, len, (data)->mechListMIC, &l);
+ FORW;
+ if (dce_fix) {
+ e = der_match_tag_and_length(p, len, (Der_class) 0, (Der_type) 0, 0, &reallen, &l);
+ FORW;
+ } else
+ len = oldlen - newlen;
+ }
+ }
+ }
+ if (dce_fix) {
+ e = der_match_tag_and_length(p, len, (Der_class) 0, (Der_type) 0, 0, &reallen, &l);
+ FORW;
+ }
+ }
+ if (size)
+ *size = ret;
+ return 0;
+fail:
+ free_NegTokenResp(data);
+ return e;
+}
+
+static void
+free_NegTokenResp(NegTokenResp * data)
+{
+ if ((data)->negState) {
+ free((data)->negState);
+ (data)->negState = NULL;
+ }
+ if ((data)->supportedMech) {
+ free_MechType((data)->supportedMech);
+ free((data)->supportedMech);
+ (data)->supportedMech = NULL;
+ }
+ if ((data)->responseToken) {
+ free_octet_string((data)->responseToken);
+ free((data)->responseToken);
+ (data)->responseToken = NULL;
+ }
+ if ((data)->mechListMIC) {
+ free_octet_string((data)->mechListMIC);
+ free((data)->mechListMIC);
+ (data)->mechListMIC = NULL;
+ }
+}
+
+/* unused function: length_NegTokenResp */
+
+
+/* unused function: copy_NegTokenResp */
+
+/* Generated from spnego.asn1 */
+/* Do not edit */
+
+
+/* CHOICE */
+/* unused variable: asn1_NegotiationToken_dummy_holder */
diff --git a/lib/dns/spnego_asn1.pl b/lib/dns/spnego_asn1.pl
new file mode 100644
index 0000000..93dd676
--- /dev/null
+++ b/lib/dns/spnego_asn1.pl
@@ -0,0 +1,200 @@
+#!/bin/bin/perl -w
+#
+# Copyright (C) 2006, 2007 Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 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.
+
+# $Id: spnego_asn1.pl,v 1.4 2007/06/19 23:47:16 tbox Exp $
+
+# Our SPNEGO implementation uses some functions generated by the
+# Heimdal ASN.1 compiler, which this script then whacks a bit to make
+# them work properly in this stripped down implementation. We don't
+# want to require our users to have a copy of the compiler, so we ship
+# the output of this script, but we need to keep the script around in
+# any case to cope with future changes to the SPNEGO ASN.1 code, so we
+# might as well supply the script for users who want it.
+
+# Overall plan: run the ASN.1 compiler, run each of its output files
+# through indent, fix up symbols and whack everything to be static.
+# We use indent for two reasons: (1) to whack the Heimdal compiler's
+# output into something closer to ISC's coding standard, and (2) to
+# make it easier for this script to parse the result.
+
+# Output from this script is C code which we expect to be #included
+# into another C file, which is why everything generated by this
+# script is marked "static". The intent is to minimize the number of
+# extern symbols exported by the SPNEGO implementation, to avoid
+# potential conflicts with the GSSAPI libraries.
+
+###
+
+# Filename of the ASN.1 specification. Hardcoded for the moment
+# since this script is intended for compiling exactly one module.
+
+my $asn1_source = $ENV{ASN1_SOURCE} || "spnego.asn1";
+
+# Heimdal ASN.1 compiler. This script was written using the version
+# from Heimdal 0.7.1. To build this, download a copy of
+# heimdal-0.7.1.tar.gz, configure and build with the default options,
+# then look for the compiler in heimdal-0.7.1/lib/asn1/asn1_compile.
+
+my $asn1_compile = $ENV{ASN1_COMPILE} || "asn1_compile";
+
+# BSD indent program. This script was written using the version of
+# indent that comes with FreeBSD 4.11-STABLE. The GNU project, as
+# usual, couldn't resist the temptation to monkey with indent's
+# command line syntax, so this probably won't work with GNU indent.
+
+my $indent = $ENV{INDENT} || "indent";
+
+###
+
+# Step 1: run the compiler. Input is the ASN.1 file. Outputs are a
+# header file (name specified on command line without the .h suffix),
+# a file called "asn1_files" listing the names of the other output
+# files, and a set of files containing C code generated by the
+# compiler for each data type that the compiler found.
+
+if (! -r $asn1_source || system($asn1_compile, $asn1_source, "asn1")) {
+ die("Couldn't compile ASN.1 source file $asn1_source\n");
+}
+
+my @files = ("asn1.h");
+
+open(F, "asn1_files")
+ or die("Couldn't open asn1_files: $!\n");
+push(@files, split)
+ while (<F>);
+close(F);
+
+unlink("asn1_files");
+
+###
+
+# Step 2: generate header block.
+
+print(q~/*
+ * Copyright (C) 2006 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 ISC 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.
+ */
+
+/* $Id: spnego_asn1.pl,v 1.4 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file
+ * \brief Method routines generated from SPNEGO ASN.1 module.
+ * See spnego_asn1.pl for details. Do not edit.
+ */
+
+~);
+
+###
+
+# Step 3: read and process each generated file, then delete it.
+
+my $output;
+
+for my $file (@files) {
+
+ my $is_static = 0;
+
+ system($indent, "-di1", "-ldi1", $file) == 0
+ or die("Couldn't indent $file");
+
+ unlink("$file.BAK");
+
+ open(F, $file)
+ or die("Couldn't open $file: $!");
+
+ while (<F>) {
+
+ # Symbol name fixups
+
+ s/heim_general_string/general_string/g;
+ s/heim_octet_string/octet_string/g;
+ s/heim_oid/oid/g;
+ s/heim_utf8_string/utf8_string/g;
+
+ # Convert all externs to statics
+
+ if (/^static/) {
+ $is_static = 1;
+ }
+
+ if (!/^typedef/ &&
+ !$is_static &&
+ /^[A-Za-z_][0-9A-Za-z_]*[ \t]*($|[^:0-9A-Za-z_])/) {
+ $_ = "static " . $_;
+ $is_static = 1;
+ }
+
+ if (/[{};]/) {
+ $is_static = 0;
+ }
+
+ # Suppress file inclusion, pass anything else through
+
+ if (!/#include/) {
+ $output .= $_;
+ }
+ }
+
+ close(F);
+ unlink($file);
+}
+
+# Step 4: Delete unused stuff to avoid code bloat and compiler warnings.
+
+my @unused_functions = qw(ContextFlags2int
+ int2ContextFlags
+ asn1_ContextFlags_units
+ length_NegTokenInit
+ copy_NegTokenInit
+ length_NegTokenResp
+ copy_NegTokenResp
+ length_MechTypeList
+ length_MechType
+ copy_MechTypeList
+ length_ContextFlags
+ copy_ContextFlags
+ copy_MechType);
+
+$output =~ s<^static [^\n]+\n$_\(.+?^}></* unused function: $_ */\n>ms
+ foreach (@unused_functions);
+
+$output =~ s<^static .+$_\(.*\);$></* unused declaration: $_ */>m
+ foreach (@unused_functions);
+
+$output =~ s<^static struct units ContextFlags_units\[\].+?^};>
+ </* unused variable: ContextFlags_units */>ms;
+
+$output =~ s<^static int asn1_NegotiationToken_dummy_holder = 1;>
+ </* unused variable: asn1_NegotiationToken_dummy_holder */>ms;
+
+$output =~ s<^static void\nfree_ContextFlags\(ContextFlags \* data\)\n{\n>
+ <$&\t(void)data;\n>ms;
+
+# Step 5: Write the result.
+
+print($output);
+
diff --git a/lib/dns/ssu.c b/lib/dns/ssu.c
new file mode 100644
index 0000000..ab69242
--- /dev/null
+++ b/lib/dns/ssu.c
@@ -0,0 +1,552 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001, 2003 Internet Software Consortium.
+ *
+ * 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 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 */
+/*
+ * $Id: ssu.c,v 1.34 2008/01/18 23:46:58 tbox Exp $
+ * Principal Author: Brian Wellington
+ */
+
+#include <config.h>
+
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/netaddr.h>
+#include <isc/result.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/ssu.h>
+
+#include <dst/gssapi.h>
+
+#define SSUTABLEMAGIC ISC_MAGIC('S', 'S', 'U', 'T')
+#define VALID_SSUTABLE(table) ISC_MAGIC_VALID(table, SSUTABLEMAGIC)
+
+#define SSURULEMAGIC ISC_MAGIC('S', 'S', 'U', 'R')
+#define VALID_SSURULE(table) ISC_MAGIC_VALID(table, SSURULEMAGIC)
+
+struct dns_ssurule {
+ unsigned int magic;
+ isc_boolean_t grant; /*%< is this a grant or a deny? */
+ unsigned int matchtype; /*%< which type of pattern match? */
+ dns_name_t *identity; /*%< the identity to match */
+ dns_name_t *name; /*%< the name being updated */
+ unsigned int ntypes; /*%< number of data types covered */
+ dns_rdatatype_t *types; /*%< the data types. Can include ANY, */
+ /*%< defaults to all but SIG,SOA,NS if NULL */
+ ISC_LINK(dns_ssurule_t) link;
+};
+
+struct dns_ssutable {
+ unsigned int magic;
+ isc_mem_t *mctx;
+ unsigned int references;
+ isc_mutex_t lock;
+ ISC_LIST(dns_ssurule_t) rules;
+};
+
+isc_result_t
+dns_ssutable_create(isc_mem_t *mctx, dns_ssutable_t **tablep) {
+ isc_result_t result;
+ dns_ssutable_t *table;
+
+ REQUIRE(tablep != NULL && *tablep == NULL);
+ REQUIRE(mctx != NULL);
+
+ table = isc_mem_get(mctx, sizeof(dns_ssutable_t));
+ if (table == NULL)
+ return (ISC_R_NOMEMORY);
+ result = isc_mutex_init(&table->lock);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, table, sizeof(dns_ssutable_t));
+ return (result);
+ }
+ table->references = 1;
+ table->mctx = mctx;
+ ISC_LIST_INIT(table->rules);
+ table->magic = SSUTABLEMAGIC;
+ *tablep = table;
+ return (ISC_R_SUCCESS);
+}
+
+static inline void
+destroy(dns_ssutable_t *table) {
+ isc_mem_t *mctx;
+
+ REQUIRE(VALID_SSUTABLE(table));
+
+ mctx = table->mctx;
+ while (!ISC_LIST_EMPTY(table->rules)) {
+ dns_ssurule_t *rule = ISC_LIST_HEAD(table->rules);
+ if (rule->identity != NULL) {
+ dns_name_free(rule->identity, mctx);
+ isc_mem_put(mctx, rule->identity, sizeof(dns_name_t));
+ }
+ if (rule->name != NULL) {
+ dns_name_free(rule->name, mctx);
+ isc_mem_put(mctx, rule->name, sizeof(dns_name_t));
+ }
+ if (rule->types != NULL)
+ isc_mem_put(mctx, rule->types,
+ rule->ntypes * sizeof(dns_rdatatype_t));
+ ISC_LIST_UNLINK(table->rules, rule, link);
+ rule->magic = 0;
+ isc_mem_put(mctx, rule, sizeof(dns_ssurule_t));
+ }
+ DESTROYLOCK(&table->lock);
+ table->magic = 0;
+ isc_mem_put(mctx, table, sizeof(dns_ssutable_t));
+}
+
+void
+dns_ssutable_attach(dns_ssutable_t *source, dns_ssutable_t **targetp) {
+ REQUIRE(VALID_SSUTABLE(source));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ LOCK(&source->lock);
+
+ INSIST(source->references > 0);
+ source->references++;
+ INSIST(source->references != 0);
+
+ UNLOCK(&source->lock);
+
+ *targetp = source;
+}
+
+void
+dns_ssutable_detach(dns_ssutable_t **tablep) {
+ dns_ssutable_t *table;
+ isc_boolean_t done = ISC_FALSE;
+
+ REQUIRE(tablep != NULL);
+ table = *tablep;
+ REQUIRE(VALID_SSUTABLE(table));
+
+ LOCK(&table->lock);
+
+ INSIST(table->references > 0);
+ if (--table->references == 0)
+ done = ISC_TRUE;
+ UNLOCK(&table->lock);
+
+ *tablep = NULL;
+
+ if (done)
+ destroy(table);
+}
+
+isc_result_t
+dns_ssutable_addrule(dns_ssutable_t *table, isc_boolean_t grant,
+ dns_name_t *identity, unsigned int matchtype,
+ dns_name_t *name, unsigned int ntypes,
+ dns_rdatatype_t *types)
+{
+ dns_ssurule_t *rule;
+ isc_mem_t *mctx;
+ isc_result_t result;
+
+ REQUIRE(VALID_SSUTABLE(table));
+ REQUIRE(dns_name_isabsolute(identity));
+ REQUIRE(dns_name_isabsolute(name));
+ REQUIRE(matchtype <= DNS_SSUMATCHTYPE_MAX);
+ if (matchtype == DNS_SSUMATCHTYPE_WILDCARD)
+ REQUIRE(dns_name_iswildcard(name));
+ if (ntypes > 0)
+ REQUIRE(types != NULL);
+
+ mctx = table->mctx;
+ rule = isc_mem_get(mctx, sizeof(dns_ssurule_t));
+ if (rule == NULL)
+ return (ISC_R_NOMEMORY);
+
+ rule->identity = NULL;
+ rule->name = NULL;
+ rule->types = NULL;
+
+ rule->grant = grant;
+
+ rule->identity = isc_mem_get(mctx, sizeof(dns_name_t));
+ if (rule->identity == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto failure;
+ }
+ dns_name_init(rule->identity, NULL);
+ result = dns_name_dup(identity, mctx, rule->identity);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ rule->name = isc_mem_get(mctx, sizeof(dns_name_t));
+ if (rule->name == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto failure;
+ }
+ dns_name_init(rule->name, NULL);
+ result = dns_name_dup(name, mctx, rule->name);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ rule->matchtype = matchtype;
+
+ rule->ntypes = ntypes;
+ if (ntypes > 0) {
+ rule->types = isc_mem_get(mctx,
+ ntypes * sizeof(dns_rdatatype_t));
+ if (rule->types == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto failure;
+ }
+ memcpy(rule->types, types, ntypes * sizeof(dns_rdatatype_t));
+ } else
+ rule->types = NULL;
+
+ rule->magic = SSURULEMAGIC;
+ ISC_LIST_INITANDAPPEND(table->rules, rule, link);
+
+ return (ISC_R_SUCCESS);
+
+ failure:
+ if (rule->identity != NULL) {
+ if (dns_name_dynamic(rule->identity))
+ dns_name_free(rule->identity, mctx);
+ isc_mem_put(mctx, rule->identity, sizeof(dns_name_t));
+ }
+ if (rule->name != NULL) {
+ if (dns_name_dynamic(rule->name))
+ dns_name_free(rule->name, mctx);
+ isc_mem_put(mctx, rule->name, sizeof(dns_name_t));
+ }
+ if (rule->types != NULL)
+ isc_mem_put(mctx, rule->types,
+ ntypes * sizeof(dns_rdatatype_t));
+ isc_mem_put(mctx, rule, sizeof(dns_ssurule_t));
+
+ return (result);
+}
+
+static inline isc_boolean_t
+isusertype(dns_rdatatype_t type) {
+ return (ISC_TF(type != dns_rdatatype_ns &&
+ type != dns_rdatatype_soa &&
+ type != dns_rdatatype_rrsig));
+}
+
+static void
+reverse_from_address(dns_name_t *tcpself, isc_netaddr_t *tcpaddr) {
+ char buf[16 * 4 + sizeof("IP6.ARPA.")];
+ isc_result_t result;
+ unsigned char *ap;
+ isc_buffer_t b;
+ unsigned long l;
+
+ switch (tcpaddr->family) {
+ case AF_INET:
+ l = ntohl(tcpaddr->type.in.s_addr);
+ result = isc_string_printf(buf, sizeof(buf),
+ "%lu.%lu.%lu.%lu.IN-ADDR.ARPA.",
+ (l >> 0) & 0xff, (l >> 8) & 0xff,
+ (l >> 16) & 0xff, (l >> 24) & 0xff);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ break;
+ case AF_INET6:
+ ap = tcpaddr->type.in6.s6_addr;
+ result = isc_string_printf(buf, sizeof(buf),
+ "%x.%x.%x.%x.%x.%x.%x.%x."
+ "%x.%x.%x.%x.%x.%x.%x.%x."
+ "%x.%x.%x.%x.%x.%x.%x.%x."
+ "%x.%x.%x.%x.%x.%x.%x.%x."
+ "IP6.ARPA.",
+ ap[15] & 0x0f, (ap[15] >> 4) & 0x0f,
+ ap[14] & 0x0f, (ap[14] >> 4) & 0x0f,
+ ap[13] & 0x0f, (ap[13] >> 4) & 0x0f,
+ ap[12] & 0x0f, (ap[12] >> 4) & 0x0f,
+ ap[11] & 0x0f, (ap[11] >> 4) & 0x0f,
+ ap[10] & 0x0f, (ap[10] >> 4) & 0x0f,
+ ap[9] & 0x0f, (ap[9] >> 4) & 0x0f,
+ ap[8] & 0x0f, (ap[8] >> 4) & 0x0f,
+ ap[7] & 0x0f, (ap[7] >> 4) & 0x0f,
+ ap[6] & 0x0f, (ap[6] >> 4) & 0x0f,
+ ap[5] & 0x0f, (ap[5] >> 4) & 0x0f,
+ ap[4] & 0x0f, (ap[4] >> 4) & 0x0f,
+ ap[3] & 0x0f, (ap[3] >> 4) & 0x0f,
+ ap[2] & 0x0f, (ap[2] >> 4) & 0x0f,
+ ap[1] & 0x0f, (ap[1] >> 4) & 0x0f,
+ ap[0] & 0x0f, (ap[0] >> 4) & 0x0f);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ break;
+ default:
+ INSIST(0);
+ }
+ isc_buffer_init(&b, buf, strlen(buf));
+ isc_buffer_add(&b, strlen(buf));
+ result = dns_name_fromtext(tcpself, &b, dns_rootname, 0, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+}
+
+static void
+stf_from_address(dns_name_t *stfself, isc_netaddr_t *tcpaddr) {
+ char buf[sizeof("X.X.X.X.Y.Y.Y.Y.2.0.0.2.IP6.ARPA.")];
+ isc_result_t result;
+ unsigned char *ap;
+ isc_buffer_t b;
+ unsigned long l;
+
+ switch(tcpaddr->family) {
+ case AF_INET:
+ l = ntohl(tcpaddr->type.in.s_addr);
+ result = isc_string_printf(buf, sizeof(buf),
+ "%lx.%lx.%lx.%lx.%lx.%lx.%lx.%lx"
+ "2.0.0.2.IP6.ARPA.",
+ l & 0xf, (l >> 4) & 0xf,
+ (l >> 8) & 0xf, (l >> 12) & 0xf,
+ (l >> 16) & 0xf, (l >> 20) & 0xf,
+ (l >> 24) & 0xf, (l >> 28) & 0xf);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ break;
+ case AF_INET6:
+ ap = tcpaddr->type.in6.s6_addr;
+ result = isc_string_printf(buf, sizeof(buf),
+ "%x.%x.%x.%x.%x.%x.%x.%x."
+ "%x.%x.%x.%x.IP6.ARPA.",
+ ap[5] & 0x0f, (ap[5] >> 4) & 0x0f,
+ ap[4] & 0x0f, (ap[4] >> 4) & 0x0f,
+ ap[3] & 0x0f, (ap[3] >> 4) & 0x0f,
+ ap[2] & 0x0f, (ap[2] >> 4) & 0x0f,
+ ap[1] & 0x0f, (ap[1] >> 4) & 0x0f,
+ ap[0] & 0x0f, (ap[0] >> 4) & 0x0f);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ break;
+ default:
+ INSIST(0);
+ }
+ isc_buffer_init(&b, buf, strlen(buf));
+ isc_buffer_add(&b, strlen(buf));
+ result = dns_name_fromtext(stfself, &b, dns_rootname, 0, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+}
+
+isc_boolean_t
+dns_ssutable_checkrules(dns_ssutable_t *table, dns_name_t *signer,
+ dns_name_t *name, isc_netaddr_t *tcpaddr,
+ dns_rdatatype_t type)
+{
+ dns_ssurule_t *rule;
+ unsigned int i;
+ dns_fixedname_t fixed;
+ dns_name_t *wildcard;
+ dns_name_t *tcpself;
+ dns_name_t *stfself;
+ isc_result_t result;
+
+ REQUIRE(VALID_SSUTABLE(table));
+ REQUIRE(signer == NULL || dns_name_isabsolute(signer));
+ REQUIRE(dns_name_isabsolute(name));
+
+ if (signer == NULL && tcpaddr == NULL)
+ return (ISC_FALSE);
+
+ for (rule = ISC_LIST_HEAD(table->rules);
+ rule != NULL;
+ rule = ISC_LIST_NEXT(rule, link))
+ {
+ switch (rule->matchtype) {
+ case DNS_SSUMATCHTYPE_NAME:
+ case DNS_SSUMATCHTYPE_SUBDOMAIN:
+ case DNS_SSUMATCHTYPE_WILDCARD:
+ case DNS_SSUMATCHTYPE_SELF:
+ case DNS_SSUMATCHTYPE_SELFSUB:
+ case DNS_SSUMATCHTYPE_SELFWILD:
+ if (signer == NULL)
+ continue;
+ if (dns_name_iswildcard(rule->identity)) {
+ if (!dns_name_matcheswildcard(signer,
+ rule->identity))
+ continue;
+ } else {
+ if (!dns_name_equal(signer, rule->identity))
+ continue;
+ }
+ break;
+ case DNS_SSUMATCHTYPE_SELFKRB5:
+ case DNS_SSUMATCHTYPE_SELFMS:
+ case DNS_SSUMATCHTYPE_SUBDOMAINKRB5:
+ case DNS_SSUMATCHTYPE_SUBDOMAINMS:
+ if (signer == NULL)
+ continue;
+ break;
+ case DNS_SSUMATCHTYPE_TCPSELF:
+ case DNS_SSUMATCHTYPE_6TO4SELF:
+ if (tcpaddr == NULL)
+ continue;
+ break;
+ }
+
+ switch (rule->matchtype) {
+ case DNS_SSUMATCHTYPE_NAME:
+ if (!dns_name_equal(name, rule->name))
+ continue;
+ break;
+ case DNS_SSUMATCHTYPE_SUBDOMAIN:
+ if (!dns_name_issubdomain(name, rule->name))
+ continue;
+ break;
+ case DNS_SSUMATCHTYPE_WILDCARD:
+ if (!dns_name_matcheswildcard(name, rule->name))
+ continue;
+ break;
+ case DNS_SSUMATCHTYPE_SELF:
+ if (!dns_name_equal(signer, name))
+ continue;
+ break;
+ case DNS_SSUMATCHTYPE_SELFSUB:
+ if (!dns_name_issubdomain(name, signer))
+ continue;
+ break;
+ case DNS_SSUMATCHTYPE_SELFWILD:
+ dns_fixedname_init(&fixed);
+ wildcard = dns_fixedname_name(&fixed);
+ result = dns_name_concatenate(dns_wildcardname, signer,
+ wildcard, NULL);
+ if (result != ISC_R_SUCCESS)
+ continue;
+ if (!dns_name_matcheswildcard(name, wildcard))
+ continue;
+ break;
+ case DNS_SSUMATCHTYPE_SELFKRB5:
+ if (!dst_gssapi_identitymatchesrealmkrb5(signer, name,
+ rule->identity))
+ continue;
+ break;
+ case DNS_SSUMATCHTYPE_SELFMS:
+ if (!dst_gssapi_identitymatchesrealmms(signer, name,
+ rule->identity))
+ continue;
+ break;
+ case DNS_SSUMATCHTYPE_SUBDOMAINKRB5:
+ if (!dns_name_issubdomain(name, rule->name))
+ continue;
+ if (!dst_gssapi_identitymatchesrealmkrb5(signer, NULL,
+ rule->identity))
+ continue;
+ break;
+ case DNS_SSUMATCHTYPE_SUBDOMAINMS:
+ if (!dns_name_issubdomain(name, rule->name))
+ continue;
+ if (!dst_gssapi_identitymatchesrealmms(signer, NULL,
+ rule->identity))
+ continue;
+ break;
+ case DNS_SSUMATCHTYPE_TCPSELF:
+ dns_fixedname_init(&fixed);
+ tcpself = dns_fixedname_name(&fixed);
+ reverse_from_address(tcpself, tcpaddr);
+ if (dns_name_iswildcard(rule->identity)) {
+ if (!dns_name_matcheswildcard(tcpself,
+ rule->identity))
+ continue;
+ } else {
+ if (!dns_name_equal(tcpself, rule->identity))
+ continue;
+ }
+ if (!dns_name_equal(tcpself, name))
+ continue;
+ break;
+ case DNS_SSUMATCHTYPE_6TO4SELF:
+ dns_fixedname_init(&fixed);
+ stfself = dns_fixedname_name(&fixed);
+ stf_from_address(stfself, tcpaddr);
+ if (dns_name_iswildcard(rule->identity)) {
+ if (!dns_name_matcheswildcard(stfself,
+ rule->identity))
+ continue;
+ } else {
+ if (!dns_name_equal(stfself, rule->identity))
+ continue;
+ }
+ if (!dns_name_equal(stfself, name))
+ continue;
+ break;
+ }
+
+ if (rule->ntypes == 0) {
+ if (!isusertype(type))
+ continue;
+ } else {
+ for (i = 0; i < rule->ntypes; i++) {
+ if (rule->types[i] == dns_rdatatype_any ||
+ rule->types[i] == type)
+ break;
+ }
+ if (i == rule->ntypes)
+ continue;
+ }
+ return (rule->grant);
+ }
+
+ return (ISC_FALSE);
+}
+
+isc_boolean_t
+dns_ssurule_isgrant(const dns_ssurule_t *rule) {
+ REQUIRE(VALID_SSURULE(rule));
+ return (rule->grant);
+}
+
+dns_name_t *
+dns_ssurule_identity(const dns_ssurule_t *rule) {
+ REQUIRE(VALID_SSURULE(rule));
+ return (rule->identity);
+}
+
+unsigned int
+dns_ssurule_matchtype(const dns_ssurule_t *rule) {
+ REQUIRE(VALID_SSURULE(rule));
+ return (rule->matchtype);
+}
+
+dns_name_t *
+dns_ssurule_name(const dns_ssurule_t *rule) {
+ REQUIRE(VALID_SSURULE(rule));
+ return (rule->name);
+}
+
+unsigned int
+dns_ssurule_types(const dns_ssurule_t *rule, dns_rdatatype_t **types) {
+ REQUIRE(VALID_SSURULE(rule));
+ REQUIRE(types != NULL && *types != NULL);
+ *types = rule->types;
+ return (rule->ntypes);
+}
+
+isc_result_t
+dns_ssutable_firstrule(const dns_ssutable_t *table, dns_ssurule_t **rule) {
+ REQUIRE(VALID_SSUTABLE(table));
+ REQUIRE(rule != NULL && *rule == NULL);
+ *rule = ISC_LIST_HEAD(table->rules);
+ return (*rule != NULL ? ISC_R_SUCCESS : ISC_R_NOMORE);
+}
+
+isc_result_t
+dns_ssutable_nextrule(dns_ssurule_t *rule, dns_ssurule_t **nextrule) {
+ REQUIRE(VALID_SSURULE(rule));
+ REQUIRE(nextrule != NULL && *nextrule == NULL);
+ *nextrule = ISC_LIST_NEXT(rule, link);
+ return (*nextrule != NULL ? ISC_R_SUCCESS : ISC_R_NOMORE);
+}
diff --git a/lib/dns/stats.c b/lib/dns/stats.c
new file mode 100644
index 0000000..7110883
--- /dev/null
+++ b/lib/dns/stats.c
@@ -0,0 +1,564 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: stats.c,v 1.16 2008/04/03 06:09:04 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <isc/atomic.h>
+#include <isc/buffer.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/platform.h>
+#include <isc/print.h>
+#include <isc/rwlock.h>
+#include <isc/util.h>
+
+#include <dns/opcode.h>
+#include <dns/rdatatype.h>
+#include <dns/stats.h>
+
+#define DNS_STATS_MAGIC ISC_MAGIC('D', 's', 't', 't')
+#define DNS_STATS_VALID(x) ISC_MAGIC_VALID(x, DNS_STATS_MAGIC)
+
+/*%
+ * Statistics types.
+ */
+typedef enum {
+ dns_statstype_general = 0,
+ dns_statstype_rdtype = 1,
+ dns_statstype_rdataset = 2,
+ dns_statstype_opcode = 3
+} dns_statstype_t;
+
+/*%
+ * It doesn't make sense to have 2^16 counters for all possible types since
+ * most of them won't be used. We have counters for the first 256 types and
+ * those explicitly supported in the rdata implementation.
+ * XXXJT: this introduces tight coupling with the rdata implementation.
+ * Ideally, we should have rdata handle this type of details.
+ */
+enum {
+ /* For 0-255, we use the rdtype value as counter indices */
+ rdtypecounter_dlv = 256, /* for dns_rdatatype_dlv */
+ rdtypecounter_others = 257, /* anything else */
+ rdtypecounter_max = 258,
+ /* The following are used for rdataset */
+ rdtypenxcounter_max = rdtypecounter_max * 2,
+ rdtypecounter_nxdomain = rdtypenxcounter_max,
+ rdatasettypecounter_max = rdtypecounter_nxdomain + 1
+};
+
+#ifndef DNS_STATS_USEMULTIFIELDS
+#if defined(ISC_RWLOCK_USEATOMIC) && defined(ISC_PLATFORM_HAVEXADD) && !defined(ISC_PLATFORM_HAVEXADDQ)
+#define DNS_STATS_USEMULTIFIELDS 1
+#else
+#define DNS_STATS_USEMULTIFIELDS 0
+#endif
+#endif /* DNS_STATS_USEMULTIFIELDS */
+
+#if DNS_STATS_USEMULTIFIELDS
+typedef struct {
+ isc_uint32_t hi;
+ isc_uint32_t lo;
+} dns_stat_t;
+#else
+typedef isc_uint64_t dns_stat_t;
+#endif
+
+struct dns_stats {
+ /*% Unlocked */
+ unsigned int magic;
+ dns_statstype_t type;
+ isc_mem_t *mctx;
+ int ncounters;
+
+ isc_mutex_t lock;
+ unsigned int references; /* locked by lock */
+
+ /*%
+ * Locked by counterlock or unlocked if efficient rwlock is not
+ * available.
+ */
+#ifdef ISC_RWLOCK_USEATOMIC
+ isc_rwlock_t counterlock;
+#endif
+ dns_stat_t *counters;
+
+ /*%
+ * We don't want to lock the counters while we are dumping, so we first
+ * copy the current counter values into a local array. This buffer
+ * will be used as the copy destination. It's allocated on creation
+ * of the stats structure so that the dump operation won't fail due
+ * to memory allocation failure.
+ * XXX: this approach is weird for non-threaded build because the
+ * additional memory and the copy overhead could be avoided. We prefer
+ * simplicity here, however, under the assumption that this function
+ * should be only rarely called.
+ */
+ isc_uint64_t *copiedcounters;
+};
+
+static isc_result_t
+create_stats(isc_mem_t *mctx, dns_statstype_t type, int ncounters,
+ dns_stats_t **statsp)
+{
+ dns_stats_t *stats;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ REQUIRE(statsp != NULL && *statsp == NULL);
+
+ stats = isc_mem_get(mctx, sizeof(*stats));
+ if (stats == NULL)
+ return (ISC_R_NOMEMORY);
+
+ result = isc_mutex_init(&stats->lock);
+ if (result != ISC_R_SUCCESS)
+ goto clean_stats;
+
+ stats->counters = isc_mem_get(mctx, sizeof(dns_stat_t) * ncounters);
+ if (stats->counters == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto clean_mutex;
+ }
+ stats->copiedcounters = isc_mem_get(mctx,
+ sizeof(isc_uint64_t) * ncounters);
+ if (stats->copiedcounters == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto clean_counters;
+ }
+
+#ifdef ISC_RWLOCK_USEATOMIC
+ result = isc_rwlock_init(&stats->counterlock, 0, 0);
+ if (result != ISC_R_SUCCESS)
+ goto clean_copiedcounters;
+#endif
+
+ stats->type = type;
+ stats->references = 1;
+ memset(stats->counters, 0, sizeof(dns_stat_t) * ncounters);
+ stats->mctx = NULL;
+ isc_mem_attach(mctx, &stats->mctx);
+ stats->ncounters = ncounters;
+ stats->magic = DNS_STATS_MAGIC;
+
+ *statsp = stats;
+
+ return (result);
+
+clean_counters:
+ isc_mem_put(mctx, stats->counters, sizeof(dns_stat_t) * ncounters);
+
+#ifdef ISC_RWLOCK_USEATOMIC
+clean_copiedcounters:
+ isc_mem_put(mctx, stats->copiedcounters,
+ sizeof(dns_stat_t) * ncounters);
+#endif
+
+clean_mutex:
+ DESTROYLOCK(&stats->lock);
+
+clean_stats:
+ isc_mem_put(mctx, stats, sizeof(*stats));
+
+ return (result);
+}
+
+void
+dns_stats_attach(dns_stats_t *stats, dns_stats_t **statsp) {
+ REQUIRE(DNS_STATS_VALID(stats));
+ REQUIRE(statsp != NULL && *statsp == NULL);
+
+ LOCK(&stats->lock);
+ stats->references++;
+ UNLOCK(&stats->lock);
+
+ *statsp = stats;
+}
+
+void
+dns_stats_detach(dns_stats_t **statsp) {
+ dns_stats_t *stats;
+
+ REQUIRE(statsp != NULL && DNS_STATS_VALID(*statsp));
+
+ stats = *statsp;
+ *statsp = NULL;
+
+ LOCK(&stats->lock);
+ stats->references--;
+ UNLOCK(&stats->lock);
+
+ if (stats->references == 0) {
+ isc_mem_put(stats->mctx, stats->copiedcounters,
+ sizeof(dns_stat_t) * stats->ncounters);
+ isc_mem_put(stats->mctx, stats->counters,
+ sizeof(dns_stat_t) * stats->ncounters);
+ DESTROYLOCK(&stats->lock);
+#ifdef ISC_RWLOCK_USEATOMIC
+ isc_rwlock_destroy(&stats->counterlock);
+#endif
+ isc_mem_putanddetach(&stats->mctx, stats, sizeof(*stats));
+ }
+}
+
+static inline void
+incrementcounter(dns_stats_t *stats, int counter) {
+ isc_int32_t prev;
+
+#ifdef ISC_RWLOCK_USEATOMIC
+ /*
+ * We use a "read" lock to prevent other threads from reading the
+ * counter while we "writing" a counter field. The write access itself
+ * is protected by the atomic operation.
+ */
+ isc_rwlock_lock(&stats->counterlock, isc_rwlocktype_read);
+#endif
+
+#if DNS_STATS_USEMULTIFIELDS
+ prev = isc_atomic_xadd((isc_int32_t *)&stats->counters[counter].lo, 1);
+ /*
+ * If the lower 32-bit field overflows, increment the higher field.
+ * Note that it's *theoretically* possible that the lower field
+ * overlaps again before the higher field is incremented. It doesn't
+ * matter, however, because we don't read the value until
+ * dns_stats_copy() is called where the whole process is protected
+ * by the write (exclusive) lock.
+ */
+ if (prev == (isc_int32_t)0xffffffff)
+ isc_atomic_xadd((isc_int32_t *)&stats->counters[counter].hi, 1);
+#elif defined(ISC_PLATFORM_HAVEXADDQ)
+ UNUSED(prev);
+ isc_atomic_xaddq((isc_int64_t *)&stats->counters[counter], 1);
+#else
+ UNUSED(prev);
+ stats->counters[counter]++;
+#endif
+
+#ifdef ISC_RWLOCK_USEATOMIC
+ isc_rwlock_unlock(&stats->counterlock, isc_rwlocktype_read);
+#endif
+}
+
+static inline void
+decrementcounter(dns_stats_t *stats, int counter) {
+ isc_int32_t prev;
+
+#ifdef ISC_RWLOCK_USEATOMIC
+ isc_rwlock_lock(&stats->counterlock, isc_rwlocktype_read);
+#endif
+
+#if DNS_STATS_USEMULTIFIELDS
+ prev = isc_atomic_xadd((isc_int32_t *)&stats->counters[counter].lo, -1);
+ if (prev == 0)
+ isc_atomic_xadd((isc_int32_t *)&stats->counters[counter].hi,
+ -1);
+#elif defined(ISC_PLATFORM_HAVEXADDQ)
+ UNUSED(prev);
+ isc_atomic_xaddq((isc_int64_t *)&stats->counters[counter], -1);
+#else
+ UNUSED(prev);
+ stats->counters[counter]--;
+#endif
+
+#ifdef ISC_RWLOCK_USEATOMIC
+ isc_rwlock_unlock(&stats->counterlock, isc_rwlocktype_read);
+#endif
+}
+
+static void
+copy_counters(dns_stats_t *stats) {
+ int i;
+
+#ifdef ISC_RWLOCK_USEATOMIC
+ /*
+ * We use a "write" lock before "reading" the statistics counters as
+ * an exclusive lock.
+ */
+ isc_rwlock_lock(&stats->counterlock, isc_rwlocktype_write);
+#endif
+
+#if DNS_STATS_USEMULTIFIELDS
+ for (i = 0; i < stats->ncounters; i++) {
+ stats->copiedcounters[i] =
+ (isc_uint64_t)(stats->counters[i].hi) << 32 |
+ stats->counters[i].lo;
+ }
+#else
+ UNUSED(i);
+ memcpy(stats->copiedcounters, stats->counters,
+ stats->ncounters * sizeof(dns_stat_t));
+#endif
+
+#ifdef ISC_RWLOCK_USEATOMIC
+ isc_rwlock_unlock(&stats->counterlock, isc_rwlocktype_write);
+#endif
+}
+
+/*%
+ * Create methods
+ */
+isc_result_t
+dns_generalstats_create(isc_mem_t *mctx, dns_stats_t **statsp, int ncounters) {
+ REQUIRE(statsp != NULL && *statsp == NULL);
+
+ return (create_stats(mctx, dns_statstype_general, ncounters, statsp));
+}
+
+isc_result_t
+dns_rdatatypestats_create(isc_mem_t *mctx, dns_stats_t **statsp) {
+ REQUIRE(statsp != NULL && *statsp == NULL);
+
+ return (create_stats(mctx, dns_statstype_rdtype, rdtypecounter_max,
+ statsp));
+}
+
+isc_result_t
+dns_rdatasetstats_create(isc_mem_t *mctx, dns_stats_t **statsp) {
+ REQUIRE(statsp != NULL && *statsp == NULL);
+
+ return (create_stats(mctx, dns_statstype_rdataset,
+ (rdtypecounter_max * 2) + 1, statsp));
+}
+
+isc_result_t
+dns_opcodestats_create(isc_mem_t *mctx, dns_stats_t **statsp) {
+ REQUIRE(statsp != NULL && *statsp == NULL);
+
+ return (create_stats(mctx, dns_statstype_opcode, 16, statsp));
+}
+
+/*%
+ * Increment/Decrement methods
+ */
+void
+dns_generalstats_increment(dns_stats_t *stats, dns_statscounter_t counter) {
+ REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_general);
+ REQUIRE(counter < stats->ncounters);
+
+ incrementcounter(stats, (int)counter);
+}
+
+void
+dns_rdatatypestats_increment(dns_stats_t *stats, dns_rdatatype_t type) {
+ int counter;
+
+ REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_rdtype);
+
+ if (type == dns_rdatatype_dlv)
+ counter = rdtypecounter_dlv;
+ else if (type > dns_rdatatype_any)
+ counter = rdtypecounter_others;
+ else
+ counter = (int)type;
+
+ incrementcounter(stats, counter);
+}
+
+static inline void
+update_rdatasetstats(dns_stats_t *stats, dns_rdatastatstype_t rrsettype,
+ isc_boolean_t increment)
+{
+ int counter;
+ dns_rdatatype_t rdtype;
+
+ if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) &
+ DNS_RDATASTATSTYPE_ATTR_NXDOMAIN) != 0) {
+ counter = rdtypecounter_nxdomain;
+ } else {
+ rdtype = DNS_RDATASTATSTYPE_BASE(rrsettype);
+ if (rdtype == dns_rdatatype_dlv)
+ counter = (int)rdtypecounter_dlv;
+ else if (rdtype > dns_rdatatype_any)
+ counter = (int)rdtypecounter_others;
+ else
+ counter = (int)rdtype;
+
+ if ((DNS_RDATASTATSTYPE_ATTR(rrsettype) &
+ DNS_RDATASTATSTYPE_ATTR_NXRRSET) != 0)
+ counter += rdtypecounter_max;
+ }
+
+ if (increment)
+ incrementcounter(stats, counter);
+ else
+ decrementcounter(stats, counter);
+}
+
+void
+dns_rdatasetstats_increment(dns_stats_t *stats, dns_rdatastatstype_t rrsettype)
+{
+ REQUIRE(DNS_STATS_VALID(stats) &&
+ stats->type == dns_statstype_rdataset);
+
+ update_rdatasetstats(stats, rrsettype, ISC_TRUE);
+}
+
+void
+dns_rdatasetstats_decrement(dns_stats_t *stats, dns_rdatastatstype_t rrsettype)
+{
+ REQUIRE(DNS_STATS_VALID(stats) &&
+ stats->type == dns_statstype_rdataset);
+
+ update_rdatasetstats(stats, rrsettype, ISC_FALSE);
+}
+void
+dns_opcodestats_increment(dns_stats_t *stats, dns_opcode_t code) {
+ REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_opcode);
+
+ incrementcounter(stats, (int)code);
+}
+
+/*%
+ * Dump methods
+ */
+void
+dns_generalstats_dump(dns_stats_t *stats, dns_generalstats_dumper_t dump_fn,
+ void *arg, unsigned int options)
+{
+ int i;
+
+ REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_general);
+
+ copy_counters(stats);
+
+ for (i = 0; i < stats->ncounters; i++) {
+ if ((options & DNS_STATSDUMP_VERBOSE) == 0 &&
+ stats->copiedcounters[i] == 0)
+ continue;
+ dump_fn(i, stats->copiedcounters[i], arg);
+ }
+}
+
+static void
+dump_rdentry(dns_stats_t *stats, int counter, int rdcounter,
+ dns_rdatastatstype_t attributes,
+ dns_rdatatypestats_dumper_t dump_fn, void * arg,
+ unsigned int options)
+{
+ dns_rdatatype_t rdtype = dns_rdatatype_none; /* sentinel */
+ dns_rdatastatstype_t type;
+
+ if ((options & DNS_STATSDUMP_VERBOSE) == 0 &&
+ stats->copiedcounters[counter] == 0)
+ return;
+ if (rdcounter == rdtypecounter_others)
+ attributes |= DNS_RDATASTATSTYPE_ATTR_OTHERTYPE;
+ else {
+ if (rdcounter == rdtypecounter_dlv)
+ rdtype = dns_rdatatype_dlv;
+ else
+ rdtype = (dns_rdatatype_t)rdcounter;
+ }
+ type = DNS_RDATASTATSTYPE_VALUE((dns_rdatastatstype_t)rdtype,
+ attributes);
+ dump_fn(type, stats->copiedcounters[counter], arg);
+}
+
+void
+dns_rdatatypestats_dump(dns_stats_t *stats, dns_rdatatypestats_dumper_t dump_fn,
+ void *arg, unsigned int options)
+{
+ int i;
+
+ REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_rdtype);
+
+ copy_counters(stats);
+
+ for (i = 0; i < stats->ncounters; i++)
+ dump_rdentry(stats, i, i, 0, dump_fn, arg, options);
+}
+
+void
+dns_rdatasetstats_dump(dns_stats_t *stats, dns_rdatatypestats_dumper_t dump_fn,
+ void *arg, unsigned int options)
+{
+ int i;
+
+ REQUIRE(DNS_STATS_VALID(stats) &&
+ stats->type == dns_statstype_rdataset);
+
+ copy_counters(stats);
+
+ for (i = 0; i < rdtypecounter_max; i++)
+ dump_rdentry(stats, i, i, 0, dump_fn, arg, options);
+ for (i = rdtypecounter_max; i < rdtypenxcounter_max; i++) {
+ dump_rdentry(stats, i, i - rdtypecounter_max,
+ DNS_RDATASTATSTYPE_ATTR_NXRRSET,
+ dump_fn, arg, options);
+ }
+ dump_rdentry(stats, rdtypecounter_nxdomain, 0,
+ DNS_RDATASTATSTYPE_ATTR_NXDOMAIN, dump_fn, arg, options);
+
+ INSIST(i < stats->ncounters);
+}
+
+void
+dns_opcodestats_dump(dns_stats_t *stats, dns_opcodestats_dumper_t dump_fn,
+ void *arg, unsigned int options)
+{
+ int i;
+
+ REQUIRE(DNS_STATS_VALID(stats) && stats->type == dns_statstype_opcode);
+
+ copy_counters(stats);
+
+ for (i = 0; i < stats->ncounters; i++) {
+ if ((options & DNS_STATSDUMP_VERBOSE) == 0 &&
+ stats->copiedcounters[i] == 0)
+ continue;
+ dump_fn((dns_opcode_t)i, stats->copiedcounters[i], arg);
+ }
+}
+
+/***
+ *** Obsolete variables and functions follow:
+ ***/
+LIBDNS_EXTERNAL_DATA const char *dns_statscounter_names[DNS_STATS_NCOUNTERS] =
+ {
+ "success",
+ "referral",
+ "nxrrset",
+ "nxdomain",
+ "recursion",
+ "failure",
+ "duplicate",
+ "dropped"
+ };
+
+isc_result_t
+dns_stats_alloccounters(isc_mem_t *mctx, isc_uint64_t **ctrp) {
+ int i;
+ isc_uint64_t *p =
+ isc_mem_get(mctx, DNS_STATS_NCOUNTERS * sizeof(isc_uint64_t));
+ if (p == NULL)
+ return (ISC_R_NOMEMORY);
+ for (i = 0; i < DNS_STATS_NCOUNTERS; i++)
+ p[i] = 0;
+ *ctrp = p;
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_stats_freecounters(isc_mem_t *mctx, isc_uint64_t **ctrp) {
+ isc_mem_put(mctx, *ctrp, DNS_STATS_NCOUNTERS * sizeof(isc_uint64_t));
+ *ctrp = NULL;
+}
diff --git a/lib/dns/tcpmsg.c b/lib/dns/tcpmsg.c
new file mode 100644
index 0000000..49add56
--- /dev/null
+++ b/lib/dns/tcpmsg.c
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: tcpmsg.c,v 1.31 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/mem.h>
+#include <isc/task.h>
+#include <isc/util.h>
+
+#include <dns/events.h>
+#include <dns/result.h>
+#include <dns/tcpmsg.h>
+
+#ifdef TCPMSG_DEBUG
+#include <stdio.h> /* Required for printf. */
+#define XDEBUG(x) printf x
+#else
+#define XDEBUG(x)
+#endif
+
+#define TCPMSG_MAGIC ISC_MAGIC('T', 'C', 'P', 'm')
+#define VALID_TCPMSG(foo) ISC_MAGIC_VALID(foo, TCPMSG_MAGIC)
+
+static void recv_length(isc_task_t *, isc_event_t *);
+static void recv_message(isc_task_t *, isc_event_t *);
+
+
+static void
+recv_length(isc_task_t *task, isc_event_t *ev_in) {
+ isc_socketevent_t *ev = (isc_socketevent_t *)ev_in;
+ isc_event_t *dev;
+ dns_tcpmsg_t *tcpmsg = ev_in->ev_arg;
+ isc_region_t region;
+ isc_result_t result;
+
+ INSIST(VALID_TCPMSG(tcpmsg));
+
+ dev = &tcpmsg->event;
+ tcpmsg->address = ev->address;
+
+ if (ev->result != ISC_R_SUCCESS) {
+ tcpmsg->result = ev->result;
+ goto send_and_free;
+ }
+
+ /*
+ * Success.
+ */
+ tcpmsg->size = ntohs(tcpmsg->size);
+ if (tcpmsg->size == 0) {
+ tcpmsg->result = ISC_R_UNEXPECTEDEND;
+ goto send_and_free;
+ }
+ if (tcpmsg->size > tcpmsg->maxsize) {
+ tcpmsg->result = ISC_R_RANGE;
+ goto send_and_free;
+ }
+
+ region.base = isc_mem_get(tcpmsg->mctx, tcpmsg->size);
+ region.length = tcpmsg->size;
+ if (region.base == NULL) {
+ tcpmsg->result = ISC_R_NOMEMORY;
+ goto send_and_free;
+ }
+ XDEBUG(("Allocated %d bytes\n", tcpmsg->size));
+
+ isc_buffer_init(&tcpmsg->buffer, region.base, region.length);
+ result = isc_socket_recv(tcpmsg->sock, &region, 0,
+ task, recv_message, tcpmsg);
+ if (result != ISC_R_SUCCESS) {
+ tcpmsg->result = result;
+ goto send_and_free;
+ }
+
+ isc_event_free(&ev_in);
+ return;
+
+ send_and_free:
+ isc_task_send(tcpmsg->task, &dev);
+ tcpmsg->task = NULL;
+ isc_event_free(&ev_in);
+ return;
+}
+
+static void
+recv_message(isc_task_t *task, isc_event_t *ev_in) {
+ isc_socketevent_t *ev = (isc_socketevent_t *)ev_in;
+ isc_event_t *dev;
+ dns_tcpmsg_t *tcpmsg = ev_in->ev_arg;
+
+ (void)task;
+
+ INSIST(VALID_TCPMSG(tcpmsg));
+
+ dev = &tcpmsg->event;
+ tcpmsg->address = ev->address;
+
+ if (ev->result != ISC_R_SUCCESS) {
+ tcpmsg->result = ev->result;
+ goto send_and_free;
+ }
+
+ tcpmsg->result = ISC_R_SUCCESS;
+ isc_buffer_add(&tcpmsg->buffer, ev->n);
+
+ XDEBUG(("Received %d bytes (of %d)\n", ev->n, tcpmsg->size));
+
+ send_and_free:
+ isc_task_send(tcpmsg->task, &dev);
+ tcpmsg->task = NULL;
+ isc_event_free(&ev_in);
+}
+
+void
+dns_tcpmsg_init(isc_mem_t *mctx, isc_socket_t *sock, dns_tcpmsg_t *tcpmsg) {
+ REQUIRE(mctx != NULL);
+ REQUIRE(sock != NULL);
+ REQUIRE(tcpmsg != NULL);
+
+ tcpmsg->magic = TCPMSG_MAGIC;
+ tcpmsg->size = 0;
+ tcpmsg->buffer.base = NULL;
+ tcpmsg->buffer.length = 0;
+ tcpmsg->maxsize = 65535; /* Largest message possible. */
+ tcpmsg->mctx = mctx;
+ tcpmsg->sock = sock;
+ tcpmsg->task = NULL; /* None yet. */
+ tcpmsg->result = ISC_R_UNEXPECTED; /* None yet. */
+ /*
+ * Should probably initialize the event here, but it can wait.
+ */
+}
+
+
+void
+dns_tcpmsg_setmaxsize(dns_tcpmsg_t *tcpmsg, unsigned int maxsize) {
+ REQUIRE(VALID_TCPMSG(tcpmsg));
+ REQUIRE(maxsize < 65536);
+
+ tcpmsg->maxsize = maxsize;
+}
+
+
+isc_result_t
+dns_tcpmsg_readmessage(dns_tcpmsg_t *tcpmsg,
+ isc_task_t *task, isc_taskaction_t action, void *arg)
+{
+ isc_result_t result;
+ isc_region_t region;
+
+ REQUIRE(VALID_TCPMSG(tcpmsg));
+ REQUIRE(task != NULL);
+ REQUIRE(tcpmsg->task == NULL); /* not currently in use */
+
+ if (tcpmsg->buffer.base != NULL) {
+ isc_mem_put(tcpmsg->mctx, tcpmsg->buffer.base,
+ tcpmsg->buffer.length);
+ tcpmsg->buffer.base = NULL;
+ tcpmsg->buffer.length = 0;
+ }
+
+ tcpmsg->task = task;
+ tcpmsg->action = action;
+ tcpmsg->arg = arg;
+ tcpmsg->result = ISC_R_UNEXPECTED; /* unknown right now */
+
+ ISC_EVENT_INIT(&tcpmsg->event, sizeof(isc_event_t), 0, 0,
+ DNS_EVENT_TCPMSG, action, arg, tcpmsg,
+ NULL, NULL);
+
+ region.base = (unsigned char *)&tcpmsg->size;
+ region.length = 2; /* isc_uint16_t */
+ result = isc_socket_recv(tcpmsg->sock, &region, 0,
+ tcpmsg->task, recv_length, tcpmsg);
+
+ if (result != ISC_R_SUCCESS)
+ tcpmsg->task = NULL;
+
+ return (result);
+}
+
+void
+dns_tcpmsg_cancelread(dns_tcpmsg_t *tcpmsg) {
+ REQUIRE(VALID_TCPMSG(tcpmsg));
+
+ isc_socket_cancel(tcpmsg->sock, NULL, ISC_SOCKCANCEL_RECV);
+}
+
+void
+dns_tcpmsg_keepbuffer(dns_tcpmsg_t *tcpmsg, isc_buffer_t *buffer) {
+ REQUIRE(VALID_TCPMSG(tcpmsg));
+ REQUIRE(buffer != NULL);
+
+ *buffer = tcpmsg->buffer;
+ tcpmsg->buffer.base = NULL;
+ tcpmsg->buffer.length = 0;
+}
+
+#if 0
+void
+dns_tcpmsg_freebuffer(dns_tcpmsg_t *tcpmsg) {
+ REQUIRE(VALID_TCPMSG(tcpmsg));
+
+ if (tcpmsg->buffer.base == NULL)
+ return;
+
+ isc_mem_put(tcpmsg->mctx, tcpmsg->buffer.base, tcpmsg->buffer.length);
+ tcpmsg->buffer.base = NULL;
+ tcpmsg->buffer.length = 0;
+}
+#endif
+
+void
+dns_tcpmsg_invalidate(dns_tcpmsg_t *tcpmsg) {
+ REQUIRE(VALID_TCPMSG(tcpmsg));
+
+ tcpmsg->magic = 0;
+
+ if (tcpmsg->buffer.base != NULL) {
+ isc_mem_put(tcpmsg->mctx, tcpmsg->buffer.base,
+ tcpmsg->buffer.length);
+ tcpmsg->buffer.base = NULL;
+ tcpmsg->buffer.length = 0;
+ }
+}
diff --git a/lib/dns/time.c b/lib/dns/time.c
new file mode 100644
index 0000000..69b3d2d
--- /dev/null
+++ b/lib/dns/time.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: time.c,v 1.31 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <isc/string.h> /* Required for HP/UX (and others?) */
+#include <time.h>
+
+#include <isc/print.h>
+#include <isc/region.h>
+#include <isc/stdtime.h>
+#include <isc/util.h>
+
+#include <dns/result.h>
+#include <dns/time.h>
+
+static int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+isc_result_t
+dns_time64_totext(isc_int64_t t, isc_buffer_t *target) {
+ struct tm tm;
+ char buf[sizeof("YYYYMMDDHHMMSS")];
+ int secs;
+ unsigned int l;
+ isc_region_t region;
+
+ REQUIRE(t >= 0);
+
+#define is_leap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
+#define year_secs(y) ((is_leap(y) ? 366 : 365 ) * 86400)
+#define month_secs(m,y) ((days[m] + ((m == 1 && is_leap(y)) ? 1 : 0 )) * 86400)
+
+ tm.tm_year = 70;
+ while ((secs = year_secs(tm.tm_year + 1900)) <= t) {
+ t -= secs;
+ tm.tm_year++;
+ if (tm.tm_year + 1900 > 9999)
+ return (ISC_R_RANGE);
+ }
+ tm.tm_mon = 0;
+ while ((secs = month_secs(tm.tm_mon, tm.tm_year + 1900)) <= t) {
+ t -= secs;
+ tm.tm_mon++;
+ }
+ tm.tm_mday = 1;
+ while (86400 <= t) {
+ t -= 86400;
+ tm.tm_mday++;
+ }
+ tm.tm_hour = 0;
+ while (3600 <= t) {
+ t -= 3600;
+ tm.tm_hour++;
+ }
+ tm.tm_min = 0;
+ while (60 <= t) {
+ t -= 60;
+ tm.tm_min++;
+ }
+ tm.tm_sec = (int)t;
+ /* yyyy mm dd HH MM SS */
+ snprintf(buf, sizeof(buf), "%04d%02d%02d%02d%02d%02d",
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+
+ isc_buffer_availableregion(target, &region);
+ l = strlen(buf);
+
+ if (l > region.length)
+ return (ISC_R_NOSPACE);
+
+ memcpy(region.base, buf, l);
+ isc_buffer_add(target, l);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_time32_totext(isc_uint32_t value, isc_buffer_t *target) {
+ isc_stdtime_t now;
+ isc_int64_t start;
+ isc_int64_t base;
+ isc_int64_t t;
+
+ /*
+ * Adjust the time to the closest epoch. This should be changed
+ * to use a 64-bit counterpart to isc_stdtime_get() if one ever
+ * is defined, but even the current code is good until the year
+ * 2106.
+ */
+ isc_stdtime_get(&now);
+ start = (isc_int64_t) now;
+ start -= 0x7fffffff;
+ base = 0;
+ while ((t = (base + value)) < start) {
+ base += 0x80000000;
+ base += 0x80000000;
+ }
+ return (dns_time64_totext(t, target));
+}
+
+isc_result_t
+dns_time64_fromtext(const char *source, isc_int64_t *target) {
+ int year, month, day, hour, minute, second;
+ isc_int64_t value;
+ int secs;
+ int i;
+
+#define RANGE(min, max, value) \
+ do { \
+ if (value < (min) || value > (max)) \
+ return (ISC_R_RANGE); \
+ } while (0)
+
+ if (strlen(source) != 14U)
+ return (DNS_R_SYNTAX);
+ if (sscanf(source, "%4d%2d%2d%2d%2d%2d",
+ &year, &month, &day, &hour, &minute, &second) != 6)
+ return (DNS_R_SYNTAX);
+
+ RANGE(1970, 9999, year);
+ RANGE(1, 12, month);
+ RANGE(1, days[month - 1] +
+ ((month == 2 && is_leap(year)) ? 1 : 0), day);
+ RANGE(0, 23, hour);
+ RANGE(0, 59, minute);
+ RANGE(0, 60, second); /* 60 == leap second. */
+
+ /*
+ * Calulate seconds since epoch.
+ */
+ value = second + (60 * minute) + (3600 * hour) + ((day - 1) * 86400);
+ for (i = 0; i < (month - 1); i++)
+ value += days[i] * 86400;
+ if (is_leap(year) && month > 2)
+ value += 86400;
+ for (i = 1970; i < year; i++) {
+ secs = (is_leap(i) ? 366 : 365) * 86400;
+ value += secs;
+ }
+
+ *target = value;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_time32_fromtext(const char *source, isc_uint32_t *target) {
+ isc_int64_t value64;
+ isc_result_t result;
+ result = dns_time64_fromtext(source, &value64);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ *target = (isc_uint32_t)value64;
+
+ return (ISC_R_SUCCESS);
+}
diff --git a/lib/dns/timer.c b/lib/dns/timer.c
new file mode 100644
index 0000000..39e4551
--- /dev/null
+++ b/lib/dns/timer.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: timer.c,v 1.7 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/result.h>
+#include <isc/time.h>
+#include <isc/timer.h>
+
+#include <dns/types.h>
+#include <dns/timer.h>
+
+#define CHECK(op) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+
+isc_result_t
+dns_timer_setidle(isc_timer_t *timer, unsigned int maxtime,
+ unsigned int idletime, isc_boolean_t purge)
+{
+ isc_result_t result;
+ isc_interval_t maxinterval, idleinterval;
+ isc_time_t expires;
+
+ /* Compute the time of expiry. */
+ isc_interval_set(&maxinterval, maxtime, 0);
+ CHECK(isc_time_nowplusinterval(&expires, &maxinterval));
+
+ /*
+ * Compute the idle interval, and add a spare nanosecond to
+ * work around the silly limitation of the ISC timer interface
+ * that you cannot specify an idle interval of zero.
+ */
+ isc_interval_set(&idleinterval, idletime, 1);
+
+ CHECK(isc_timer_reset(timer, isc_timertype_once,
+ &expires, &idleinterval,
+ purge));
+ failure:
+ return (result);
+}
diff --git a/lib/dns/tkey.c b/lib/dns/tkey.c
new file mode 100644
index 0000000..9e59dfa
--- /dev/null
+++ b/lib/dns/tkey.c
@@ -0,0 +1,1419 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001, 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/*
+ * $Id: tkey.c,v 1.90 2008/04/03 00:45:23 marka Exp $
+ */
+/*! \file */
+#include <config.h>
+
+#include <isc/buffer.h>
+#include <isc/entropy.h>
+#include <isc/md5.h>
+#include <isc/mem.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/dnssec.h>
+#include <dns/fixedname.h>
+#include <dns/keyvalues.h>
+#include <dns/log.h>
+#include <dns/message.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatastruct.h>
+#include <dns/result.h>
+#include <dns/tkey.h>
+#include <dns/tsig.h>
+
+#include <dst/dst.h>
+#include <dst/gssapi.h>
+
+#define TKEY_RANDOM_AMOUNT 16
+
+#define RETERR(x) do { \
+ result = (x); \
+ if (result != ISC_R_SUCCESS) \
+ goto failure; \
+ } while (0)
+
+static void
+tkey_log(const char *fmt, ...) ISC_FORMAT_PRINTF(1, 2);
+
+static void
+tkey_log(const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_REQUEST, ISC_LOG_DEBUG(4), fmt, ap);
+ va_end(ap);
+}
+
+static void
+_dns_tkey_dumpmessage(dns_message_t *msg) {
+ isc_buffer_t outbuf;
+ unsigned char output[4096];
+ isc_result_t result;
+
+ isc_buffer_init(&outbuf, output, sizeof(output));
+ result = dns_message_totext(msg, &dns_master_style_debug, 0,
+ &outbuf);
+ /* XXXMLG ignore result */
+ fprintf(stderr, "%.*s\n", (int)isc_buffer_usedlength(&outbuf),
+ (char *)isc_buffer_base(&outbuf));
+}
+
+isc_result_t
+dns_tkeyctx_create(isc_mem_t *mctx, isc_entropy_t *ectx, dns_tkeyctx_t **tctxp)
+{
+ dns_tkeyctx_t *tctx;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(ectx != NULL);
+ REQUIRE(tctxp != NULL && *tctxp == NULL);
+
+ tctx = isc_mem_get(mctx, sizeof(dns_tkeyctx_t));
+ if (tctx == NULL)
+ return (ISC_R_NOMEMORY);
+ tctx->mctx = NULL;
+ isc_mem_attach(mctx, &tctx->mctx);
+ tctx->ectx = NULL;
+ isc_entropy_attach(ectx, &tctx->ectx);
+ tctx->dhkey = NULL;
+ tctx->domain = NULL;
+ tctx->gsscred = NULL;
+
+ *tctxp = tctx;
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_tkeyctx_destroy(dns_tkeyctx_t **tctxp) {
+ isc_mem_t *mctx;
+ dns_tkeyctx_t *tctx;
+
+ REQUIRE(tctxp != NULL && *tctxp != NULL);
+
+ tctx = *tctxp;
+ mctx = tctx->mctx;
+
+ if (tctx->dhkey != NULL)
+ dst_key_free(&tctx->dhkey);
+ if (tctx->domain != NULL) {
+ if (dns_name_dynamic(tctx->domain))
+ dns_name_free(tctx->domain, mctx);
+ isc_mem_put(mctx, tctx->domain, sizeof(dns_name_t));
+ }
+ if (tctx->gsscred != NULL)
+ dst_gssapi_releasecred(&tctx->gsscred);
+ isc_entropy_detach(&tctx->ectx);
+ isc_mem_put(mctx, tctx, sizeof(dns_tkeyctx_t));
+ isc_mem_detach(&mctx);
+ *tctxp = NULL;
+}
+
+static isc_result_t
+add_rdata_to_list(dns_message_t *msg, dns_name_t *name, dns_rdata_t *rdata,
+ isc_uint32_t ttl, dns_namelist_t *namelist)
+{
+ isc_result_t result;
+ isc_region_t r, newr;
+ dns_rdata_t *newrdata = NULL;
+ dns_name_t *newname = NULL;
+ dns_rdatalist_t *newlist = NULL;
+ dns_rdataset_t *newset = NULL;
+ isc_buffer_t *tmprdatabuf = NULL;
+
+ RETERR(dns_message_gettemprdata(msg, &newrdata));
+
+ dns_rdata_toregion(rdata, &r);
+ RETERR(isc_buffer_allocate(msg->mctx, &tmprdatabuf, r.length));
+ isc_buffer_availableregion(tmprdatabuf, &newr);
+ memcpy(newr.base, r.base, r.length);
+ dns_rdata_fromregion(newrdata, rdata->rdclass, rdata->type, &newr);
+ dns_message_takebuffer(msg, &tmprdatabuf);
+
+ RETERR(dns_message_gettempname(msg, &newname));
+ dns_name_init(newname, NULL);
+ RETERR(dns_name_dup(name, msg->mctx, newname));
+
+ RETERR(dns_message_gettemprdatalist(msg, &newlist));
+ newlist->rdclass = newrdata->rdclass;
+ newlist->type = newrdata->type;
+ newlist->covers = 0;
+ newlist->ttl = ttl;
+ ISC_LIST_INIT(newlist->rdata);
+ ISC_LIST_APPEND(newlist->rdata, newrdata, link);
+
+ RETERR(dns_message_gettemprdataset(msg, &newset));
+ dns_rdataset_init(newset);
+ RETERR(dns_rdatalist_tordataset(newlist, newset));
+
+ ISC_LIST_INIT(newname->list);
+ ISC_LIST_APPEND(newname->list, newset, link);
+
+ ISC_LIST_APPEND(*namelist, newname, link);
+
+ return (ISC_R_SUCCESS);
+
+ failure:
+ if (newrdata != NULL) {
+ if (ISC_LINK_LINKED(newrdata, link))
+ ISC_LIST_UNLINK(newlist->rdata, newrdata, link);
+ dns_message_puttemprdata(msg, &newrdata);
+ }
+ if (newname != NULL)
+ dns_message_puttempname(msg, &newname);
+ if (newset != NULL) {
+ dns_rdataset_disassociate(newset);
+ dns_message_puttemprdataset(msg, &newset);
+ }
+ if (newlist != NULL)
+ dns_message_puttemprdatalist(msg, &newlist);
+ return (result);
+}
+
+static void
+free_namelist(dns_message_t *msg, dns_namelist_t *namelist) {
+ dns_name_t *name;
+ dns_rdataset_t *set;
+
+ while (!ISC_LIST_EMPTY(*namelist)) {
+ name = ISC_LIST_HEAD(*namelist);
+ ISC_LIST_UNLINK(*namelist, name, link);
+ while (!ISC_LIST_EMPTY(name->list)) {
+ set = ISC_LIST_HEAD(name->list);
+ ISC_LIST_UNLINK(name->list, set, link);
+ dns_message_puttemprdataset(msg, &set);
+ }
+ dns_message_puttempname(msg, &name);
+ }
+}
+
+static isc_result_t
+compute_secret(isc_buffer_t *shared, isc_region_t *queryrandomness,
+ isc_region_t *serverrandomness, isc_buffer_t *secret)
+{
+ isc_md5_t md5ctx;
+ isc_region_t r, r2;
+ unsigned char digests[32];
+ unsigned int i;
+
+ isc_buffer_usedregion(shared, &r);
+
+ /*
+ * MD5 ( query data | DH value ).
+ */
+ isc_md5_init(&md5ctx);
+ isc_md5_update(&md5ctx, queryrandomness->base,
+ queryrandomness->length);
+ isc_md5_update(&md5ctx, r.base, r.length);
+ isc_md5_final(&md5ctx, digests);
+
+ /*
+ * MD5 ( server data | DH value ).
+ */
+ isc_md5_init(&md5ctx);
+ isc_md5_update(&md5ctx, serverrandomness->base,
+ serverrandomness->length);
+ isc_md5_update(&md5ctx, r.base, r.length);
+ isc_md5_final(&md5ctx, &digests[ISC_MD5_DIGESTLENGTH]);
+
+ /*
+ * XOR ( DH value, MD5-1 | MD5-2).
+ */
+ isc_buffer_availableregion(secret, &r);
+ isc_buffer_usedregion(shared, &r2);
+ if (r.length < sizeof(digests) || r.length < r2.length)
+ return (ISC_R_NOSPACE);
+ if (r2.length > sizeof(digests)) {
+ memcpy(r.base, r2.base, r2.length);
+ for (i = 0; i < sizeof(digests); i++)
+ r.base[i] ^= digests[i];
+ isc_buffer_add(secret, r2.length);
+ } else {
+ memcpy(r.base, digests, sizeof(digests));
+ for (i = 0; i < r2.length; i++)
+ r.base[i] ^= r2.base[i];
+ isc_buffer_add(secret, sizeof(digests));
+ }
+ return (ISC_R_SUCCESS);
+
+}
+
+static isc_result_t
+process_dhtkey(dns_message_t *msg, dns_name_t *signer, dns_name_t *name,
+ dns_rdata_tkey_t *tkeyin, dns_tkeyctx_t *tctx,
+ dns_rdata_tkey_t *tkeyout,
+ dns_tsig_keyring_t *ring, dns_namelist_t *namelist)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_name_t *keyname, ourname;
+ dns_rdataset_t *keyset = NULL;
+ dns_rdata_t keyrdata = DNS_RDATA_INIT, ourkeyrdata = DNS_RDATA_INIT;
+ isc_boolean_t found_key = ISC_FALSE, found_incompatible = ISC_FALSE;
+ dst_key_t *pubkey = NULL;
+ isc_buffer_t ourkeybuf, *shared = NULL;
+ isc_region_t r, r2, ourkeyr;
+ unsigned char keydata[DST_KEY_MAXSIZE];
+ unsigned int sharedsize;
+ isc_buffer_t secret;
+ unsigned char *randomdata = NULL, secretdata[256];
+ dns_ttl_t ttl = 0;
+
+ if (tctx->dhkey == NULL) {
+ tkey_log("process_dhtkey: tkey-dhkey not defined");
+ tkeyout->error = dns_tsigerror_badalg;
+ return (DNS_R_REFUSED);
+ }
+
+ if (!dns_name_equal(&tkeyin->algorithm, DNS_TSIG_HMACMD5_NAME)) {
+ tkey_log("process_dhtkey: algorithms other than "
+ "hmac-md5 are not supported");
+ tkeyout->error = dns_tsigerror_badalg;
+ return (ISC_R_SUCCESS);
+ }
+
+ /*
+ * Look for a DH KEY record that will work with ours.
+ */
+ for (result = dns_message_firstname(msg, DNS_SECTION_ADDITIONAL);
+ result == ISC_R_SUCCESS && !found_key;
+ result = dns_message_nextname(msg, DNS_SECTION_ADDITIONAL)) {
+ keyname = NULL;
+ dns_message_currentname(msg, DNS_SECTION_ADDITIONAL, &keyname);
+ keyset = NULL;
+ result = dns_message_findtype(keyname, dns_rdatatype_key, 0,
+ &keyset);
+ if (result != ISC_R_SUCCESS)
+ continue;
+
+ for (result = dns_rdataset_first(keyset);
+ result == ISC_R_SUCCESS && !found_key;
+ result = dns_rdataset_next(keyset)) {
+ dns_rdataset_current(keyset, &keyrdata);
+ pubkey = NULL;
+ result = dns_dnssec_keyfromrdata(keyname, &keyrdata,
+ msg->mctx, &pubkey);
+ if (result != ISC_R_SUCCESS) {
+ dns_rdata_reset(&keyrdata);
+ continue;
+ }
+ if (dst_key_alg(pubkey) == DNS_KEYALG_DH) {
+ if (dst_key_paramcompare(pubkey, tctx->dhkey))
+ {
+ found_key = ISC_TRUE;
+ ttl = keyset->ttl;
+ break;
+ } else
+ found_incompatible = ISC_TRUE;
+ }
+ dst_key_free(&pubkey);
+ dns_rdata_reset(&keyrdata);
+ }
+ }
+
+ if (!found_key) {
+ if (found_incompatible) {
+ tkey_log("process_dhtkey: found an incompatible key");
+ tkeyout->error = dns_tsigerror_badkey;
+ return (ISC_R_SUCCESS);
+ } else {
+ tkey_log("process_dhtkey: failed to find a key");
+ return (DNS_R_FORMERR);
+ }
+ }
+
+ RETERR(add_rdata_to_list(msg, keyname, &keyrdata, ttl, namelist));
+
+ isc_buffer_init(&ourkeybuf, keydata, sizeof(keydata));
+ RETERR(dst_key_todns(tctx->dhkey, &ourkeybuf));
+ isc_buffer_usedregion(&ourkeybuf, &ourkeyr);
+ dns_rdata_fromregion(&ourkeyrdata, dns_rdataclass_any,
+ dns_rdatatype_key, &ourkeyr);
+
+ dns_name_init(&ourname, NULL);
+ dns_name_clone(dst_key_name(tctx->dhkey), &ourname);
+
+ /*
+ * XXXBEW The TTL should be obtained from the database, if it exists.
+ */
+ RETERR(add_rdata_to_list(msg, &ourname, &ourkeyrdata, 0, namelist));
+
+ RETERR(dst_key_secretsize(tctx->dhkey, &sharedsize));
+ RETERR(isc_buffer_allocate(msg->mctx, &shared, sharedsize));
+
+ result = dst_key_computesecret(pubkey, tctx->dhkey, shared);
+ if (result != ISC_R_SUCCESS) {
+ tkey_log("process_dhtkey: failed to compute shared secret: %s",
+ isc_result_totext(result));
+ goto failure;
+ }
+ dst_key_free(&pubkey);
+
+ isc_buffer_init(&secret, secretdata, sizeof(secretdata));
+
+ randomdata = isc_mem_get(tkeyout->mctx, TKEY_RANDOM_AMOUNT);
+ if (randomdata == NULL)
+ goto failure;
+
+ result = isc_entropy_getdata(tctx->ectx, randomdata,
+ TKEY_RANDOM_AMOUNT, NULL, 0);
+ if (result != ISC_R_SUCCESS) {
+ tkey_log("process_dhtkey: failed to obtain entropy: %s",
+ isc_result_totext(result));
+ goto failure;
+ }
+
+ r.base = randomdata;
+ r.length = TKEY_RANDOM_AMOUNT;
+ r2.base = tkeyin->key;
+ r2.length = tkeyin->keylen;
+ RETERR(compute_secret(shared, &r2, &r, &secret));
+ isc_buffer_free(&shared);
+
+ RETERR(dns_tsigkey_create(name, &tkeyin->algorithm,
+ isc_buffer_base(&secret),
+ isc_buffer_usedlength(&secret),
+ ISC_TRUE, signer, tkeyin->inception,
+ tkeyin->expire, ring->mctx, ring, NULL));
+
+ /* This key is good for a long time */
+ tkeyout->inception = tkeyin->inception;
+ tkeyout->expire = tkeyin->expire;
+
+ tkeyout->key = randomdata;
+ tkeyout->keylen = TKEY_RANDOM_AMOUNT;
+
+ return (ISC_R_SUCCESS);
+
+ failure:
+ if (!ISC_LIST_EMPTY(*namelist))
+ free_namelist(msg, namelist);
+ if (shared != NULL)
+ isc_buffer_free(&shared);
+ if (pubkey != NULL)
+ dst_key_free(&pubkey);
+ if (randomdata != NULL)
+ isc_mem_put(tkeyout->mctx, randomdata, TKEY_RANDOM_AMOUNT);
+ return (result);
+}
+
+static isc_result_t
+process_gsstkey(dns_message_t *msg, dns_name_t *signer, dns_name_t *name,
+ dns_rdata_tkey_t *tkeyin, dns_tkeyctx_t *tctx,
+ dns_rdata_tkey_t *tkeyout,
+ dns_tsig_keyring_t *ring, dns_namelist_t *namelist)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ dst_key_t *dstkey = NULL;
+ dns_tsigkey_t *tsigkey = NULL;
+ dns_fixedname_t principal;
+ isc_stdtime_t now;
+ isc_region_t intoken;
+ isc_buffer_t *outtoken = NULL;
+ gss_ctx_id_t gss_ctx = NULL;
+
+ UNUSED(namelist);
+ UNUSED(signer);
+
+ if (tctx->gsscred == NULL)
+ return (ISC_R_NOPERM);
+
+ if (!dns_name_equal(&tkeyin->algorithm, DNS_TSIG_GSSAPI_NAME) &&
+ !dns_name_equal(&tkeyin->algorithm, DNS_TSIG_GSSAPIMS_NAME)) {
+ tkeyout->error = dns_tsigerror_badalg;
+ tkey_log("process_gsstkey(): dns_tsigerror_badalg"); /* XXXSRA */
+ return (ISC_R_SUCCESS);
+ }
+
+ /*
+ * XXXDCL need to check for key expiry per 4.1.1
+ * XXXDCL need a way to check fully established, perhaps w/key_flags
+ */
+
+ intoken.base = tkeyin->key;
+ intoken.length = tkeyin->keylen;
+
+ result = dns_tsigkey_find(&tsigkey, name, &tkeyin->algorithm, ring);
+ if (result == ISC_R_SUCCESS)
+ gss_ctx = dst_key_getgssctx(tsigkey->key);
+
+
+ dns_fixedname_init(&principal);
+
+ result = dst_gssapi_acceptctx(tctx->gsscred, &intoken,
+ &outtoken, &gss_ctx,
+ dns_fixedname_name(&principal),
+ tctx->mctx);
+
+ if (tsigkey != NULL)
+ dns_tsigkey_detach(&tsigkey);
+
+ if (result == DNS_R_INVALIDTKEY) {
+ tkeyout->error = dns_tsigerror_badkey;
+ tkey_log("process_gsstkey(): dns_tsigerror_badkey"); /* XXXSRA */
+ return (ISC_R_SUCCESS);
+ } else if (result == ISC_R_FAILURE)
+ goto failure;
+ ENSURE(result == DNS_R_CONTINUE || result == ISC_R_SUCCESS);
+ /*
+ * XXXDCL Section 4.1.3: Limit GSS_S_CONTINUE_NEEDED to 10 times.
+ */
+
+ if (tsigkey == NULL) {
+ RETERR(dst_key_fromgssapi(name, gss_ctx, msg->mctx, &dstkey));
+ RETERR(dns_tsigkey_createfromkey(name, &tkeyin->algorithm,
+ dstkey, ISC_TRUE,
+ dns_fixedname_name(&principal),
+ tkeyin->inception,
+ tkeyin->expire,
+ ring->mctx, ring, NULL));
+ }
+
+ isc_stdtime_get(&now);
+ tkeyout->inception = tkeyin->inception;
+ tkeyout->expire = tkeyin->expire;
+
+ if (outtoken) {
+ tkeyout->key = isc_mem_get(tkeyout->mctx,
+ isc_buffer_usedlength(outtoken));
+ if (tkeyout->key == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto failure;
+ }
+ tkeyout->keylen = isc_buffer_usedlength(outtoken);
+ memcpy(tkeyout->key, isc_buffer_base(outtoken),
+ isc_buffer_usedlength(outtoken));
+ isc_buffer_free(&outtoken);
+ } else {
+ tkeyout->key = isc_mem_get(tkeyout->mctx, tkeyin->keylen);
+ if (tkeyout->key == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto failure;
+ }
+ tkeyout->keylen = tkeyin->keylen;
+ memcpy(tkeyout->key, tkeyin->key, tkeyin->keylen);
+ }
+
+ tkeyout->error = dns_rcode_noerror;
+
+ tkey_log("process_gsstkey(): dns_tsigerror_noerror"); /* XXXSRA */
+
+ return (ISC_R_SUCCESS);
+
+failure:
+ if (dstkey != NULL)
+ dst_key_free(&dstkey);
+
+ if (outtoken != NULL)
+ isc_buffer_free(&outtoken);
+
+ tkey_log("process_gsstkey(): %s",
+ isc_result_totext(result)); /* XXXSRA */
+
+ return (result);
+}
+
+static isc_result_t
+process_deletetkey(dns_message_t *msg, dns_name_t *signer, dns_name_t *name,
+ dns_rdata_tkey_t *tkeyin,
+ dns_rdata_tkey_t *tkeyout,
+ dns_tsig_keyring_t *ring,
+ dns_namelist_t *namelist)
+{
+ isc_result_t result;
+ dns_tsigkey_t *tsigkey = NULL;
+ dns_name_t *identity;
+
+ UNUSED(msg);
+ UNUSED(namelist);
+
+ result = dns_tsigkey_find(&tsigkey, name, &tkeyin->algorithm, ring);
+ if (result != ISC_R_SUCCESS) {
+ tkeyout->error = dns_tsigerror_badname;
+ return (ISC_R_SUCCESS);
+ }
+
+ /*
+ * Only allow a delete if the identity that created the key is the
+ * same as the identity that signed the message.
+ */
+ identity = dns_tsigkey_identity(tsigkey);
+ if (identity == NULL || !dns_name_equal(identity, signer)) {
+ dns_tsigkey_detach(&tsigkey);
+ return (DNS_R_REFUSED);
+ }
+
+ /*
+ * Set the key to be deleted when no references are left. If the key
+ * was not generated with TKEY and is in the config file, it may be
+ * reloaded later.
+ */
+ dns_tsigkey_setdeleted(tsigkey);
+
+ /* Release the reference */
+ dns_tsigkey_detach(&tsigkey);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_tkey_processquery(dns_message_t *msg, dns_tkeyctx_t *tctx,
+ dns_tsig_keyring_t *ring)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_rdata_tkey_t tkeyin, tkeyout;
+ isc_boolean_t freetkeyin = ISC_FALSE;
+ dns_name_t *qname, *name, *keyname, *signer, tsigner;
+ dns_fixedname_t fkeyname;
+ dns_rdataset_t *tkeyset;
+ dns_rdata_t rdata;
+ dns_namelist_t namelist;
+ char tkeyoutdata[512];
+ isc_buffer_t tkeyoutbuf;
+
+ REQUIRE(msg != NULL);
+ REQUIRE(tctx != NULL);
+ REQUIRE(ring != NULL);
+
+ ISC_LIST_INIT(namelist);
+
+ /*
+ * Interpret the question section.
+ */
+ result = dns_message_firstname(msg, DNS_SECTION_QUESTION);
+ if (result != ISC_R_SUCCESS)
+ return (DNS_R_FORMERR);
+
+ qname = NULL;
+ dns_message_currentname(msg, DNS_SECTION_QUESTION, &qname);
+
+ /*
+ * Look for a TKEY record that matches the question.
+ */
+ tkeyset = NULL;
+ name = NULL;
+ result = dns_message_findname(msg, DNS_SECTION_ADDITIONAL, qname,
+ dns_rdatatype_tkey, 0, &name, &tkeyset);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * Try the answer section, since that's where Win2000
+ * puts it.
+ */
+ if (dns_message_findname(msg, DNS_SECTION_ANSWER, qname,
+ dns_rdatatype_tkey, 0, &name,
+ &tkeyset) != ISC_R_SUCCESS) {
+ result = DNS_R_FORMERR;
+ tkey_log("dns_tkey_processquery: couldn't find a TKEY "
+ "matching the question");
+ goto failure;
+ }
+ }
+ result = dns_rdataset_first(tkeyset);
+ if (result != ISC_R_SUCCESS) {
+ result = DNS_R_FORMERR;
+ goto failure;
+ }
+ dns_rdata_init(&rdata);
+ dns_rdataset_current(tkeyset, &rdata);
+
+ RETERR(dns_rdata_tostruct(&rdata, &tkeyin, NULL));
+ freetkeyin = ISC_TRUE;
+
+ if (tkeyin.error != dns_rcode_noerror) {
+ result = DNS_R_FORMERR;
+ goto failure;
+ }
+
+ /*
+ * Before we go any farther, verify that the message was signed.
+ * GSSAPI TKEY doesn't require a signature, the rest do.
+ */
+ dns_name_init(&tsigner, NULL);
+ result = dns_message_signer(msg, &tsigner);
+ if (result != ISC_R_SUCCESS) {
+ if (tkeyin.mode == DNS_TKEYMODE_GSSAPI &&
+ result == ISC_R_NOTFOUND)
+ signer = NULL;
+ else {
+ tkey_log("dns_tkey_processquery: query was not "
+ "properly signed - rejecting");
+ result = DNS_R_FORMERR;
+ goto failure;
+ }
+ } else
+ signer = &tsigner;
+
+ tkeyout.common.rdclass = tkeyin.common.rdclass;
+ tkeyout.common.rdtype = tkeyin.common.rdtype;
+ ISC_LINK_INIT(&tkeyout.common, link);
+ tkeyout.mctx = msg->mctx;
+
+ dns_name_init(&tkeyout.algorithm, NULL);
+ dns_name_clone(&tkeyin.algorithm, &tkeyout.algorithm);
+
+ tkeyout.inception = tkeyout.expire = 0;
+ tkeyout.mode = tkeyin.mode;
+ tkeyout.error = 0;
+ tkeyout.keylen = tkeyout.otherlen = 0;
+ tkeyout.key = tkeyout.other = NULL;
+
+ /*
+ * A delete operation must have a fully specified key name. If this
+ * is not a delete, we do the following:
+ * if (qname != ".")
+ * keyname = qname + defaultdomain
+ * else
+ * keyname = <random hex> + defaultdomain
+ */
+ if (tkeyin.mode != DNS_TKEYMODE_DELETE) {
+ dns_tsigkey_t *tsigkey = NULL;
+
+ if (tctx->domain == NULL && tkeyin.mode != DNS_TKEYMODE_GSSAPI) {
+ tkey_log("dns_tkey_processquery: tkey-domain not set");
+ result = DNS_R_REFUSED;
+ goto failure;
+ }
+
+ dns_fixedname_init(&fkeyname);
+ keyname = dns_fixedname_name(&fkeyname);
+
+ if (!dns_name_equal(qname, dns_rootname)) {
+ unsigned int n = dns_name_countlabels(qname);
+ RUNTIME_CHECK(dns_name_copy(qname, keyname, NULL)
+ == ISC_R_SUCCESS);
+ dns_name_getlabelsequence(keyname, 0, n - 1, keyname);
+ } else {
+ static char hexdigits[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+ unsigned char randomdata[16];
+ char randomtext[32];
+ isc_buffer_t b;
+ unsigned int i, j;
+
+ result = isc_entropy_getdata(tctx->ectx,
+ randomdata,
+ sizeof(randomdata),
+ NULL, 0);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ for (i = 0, j = 0; i < sizeof(randomdata); i++) {
+ unsigned char val = randomdata[i];
+ randomtext[j++] = hexdigits[val >> 4];
+ randomtext[j++] = hexdigits[val & 0xF];
+ }
+ isc_buffer_init(&b, randomtext, sizeof(randomtext));
+ isc_buffer_add(&b, sizeof(randomtext));
+ result = dns_name_fromtext(keyname, &b, NULL,
+ ISC_FALSE, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ }
+
+ if (tkeyin.mode == DNS_TKEYMODE_GSSAPI) {
+ /* Yup. This is a hack */
+ result = dns_name_concatenate(keyname, dns_rootname,
+ keyname, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ } else {
+ result = dns_name_concatenate(keyname, tctx->domain,
+ keyname, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ }
+
+ result = dns_tsigkey_find(&tsigkey, keyname, NULL, ring);
+
+ if (result == ISC_R_SUCCESS) {
+ tkeyout.error = dns_tsigerror_badname;
+ dns_tsigkey_detach(&tsigkey);
+ goto failure_with_tkey;
+ } else if (result != ISC_R_NOTFOUND)
+ goto failure;
+ } else
+ keyname = qname;
+
+ switch (tkeyin.mode) {
+ case DNS_TKEYMODE_DIFFIEHELLMAN:
+ tkeyout.error = dns_rcode_noerror;
+ RETERR(process_dhtkey(msg, signer, keyname, &tkeyin,
+ tctx, &tkeyout, ring,
+ &namelist));
+ break;
+ case DNS_TKEYMODE_GSSAPI:
+ tkeyout.error = dns_rcode_noerror;
+ RETERR(process_gsstkey(msg, signer, keyname, &tkeyin,
+ tctx, &tkeyout, ring,
+ &namelist));
+
+ break;
+ case DNS_TKEYMODE_DELETE:
+ tkeyout.error = dns_rcode_noerror;
+ RETERR(process_deletetkey(msg, signer, keyname,
+ &tkeyin, &tkeyout,
+ ring, &namelist));
+ break;
+ case DNS_TKEYMODE_SERVERASSIGNED:
+ case DNS_TKEYMODE_RESOLVERASSIGNED:
+ result = DNS_R_NOTIMP;
+ goto failure;
+ default:
+ tkeyout.error = dns_tsigerror_badmode;
+ }
+
+ failure_with_tkey:
+ dns_rdata_init(&rdata);
+ isc_buffer_init(&tkeyoutbuf, tkeyoutdata, sizeof(tkeyoutdata));
+ result = dns_rdata_fromstruct(&rdata, tkeyout.common.rdclass,
+ tkeyout.common.rdtype, &tkeyout,
+ &tkeyoutbuf);
+
+ if (freetkeyin) {
+ dns_rdata_freestruct(&tkeyin);
+ freetkeyin = ISC_FALSE;
+ }
+
+ if (tkeyout.key != NULL)
+ isc_mem_put(tkeyout.mctx, tkeyout.key, tkeyout.keylen);
+ if (tkeyout.other != NULL)
+ isc_mem_put(tkeyout.mctx, tkeyout.other, tkeyout.otherlen);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ RETERR(add_rdata_to_list(msg, keyname, &rdata, 0, &namelist));
+
+ RETERR(dns_message_reply(msg, ISC_TRUE));
+
+ name = ISC_LIST_HEAD(namelist);
+ while (name != NULL) {
+ dns_name_t *next = ISC_LIST_NEXT(name, link);
+ ISC_LIST_UNLINK(namelist, name, link);
+ dns_message_addname(msg, name, DNS_SECTION_ANSWER);
+ name = next;
+ }
+
+ return (ISC_R_SUCCESS);
+
+ failure:
+ if (freetkeyin)
+ dns_rdata_freestruct(&tkeyin);
+ if (!ISC_LIST_EMPTY(namelist))
+ free_namelist(msg, &namelist);
+ return (result);
+}
+
+static isc_result_t
+buildquery(dns_message_t *msg, dns_name_t *name,
+ dns_rdata_tkey_t *tkey, isc_boolean_t win2k)
+{
+ dns_name_t *qname = NULL, *aname = NULL;
+ dns_rdataset_t *question = NULL, *tkeyset = NULL;
+ dns_rdatalist_t *tkeylist = NULL;
+ dns_rdata_t *rdata = NULL;
+ isc_buffer_t *dynbuf = NULL;
+ isc_result_t result;
+
+ REQUIRE(msg != NULL);
+ REQUIRE(name != NULL);
+ REQUIRE(tkey != NULL);
+
+ RETERR(dns_message_gettempname(msg, &qname));
+ RETERR(dns_message_gettempname(msg, &aname));
+
+ RETERR(dns_message_gettemprdataset(msg, &question));
+ dns_rdataset_init(question);
+ dns_rdataset_makequestion(question, dns_rdataclass_any,
+ dns_rdatatype_tkey);
+
+ RETERR(isc_buffer_allocate(msg->mctx, &dynbuf, 4096));
+ RETERR(dns_message_gettemprdata(msg, &rdata));
+
+ RETERR(dns_rdata_fromstruct(rdata, dns_rdataclass_any,
+ dns_rdatatype_tkey, tkey, dynbuf));
+ dns_message_takebuffer(msg, &dynbuf);
+
+ RETERR(dns_message_gettemprdatalist(msg, &tkeylist));
+ tkeylist->rdclass = dns_rdataclass_any;
+ tkeylist->type = dns_rdatatype_tkey;
+ tkeylist->covers = 0;
+ tkeylist->ttl = 0;
+ ISC_LIST_INIT(tkeylist->rdata);
+ ISC_LIST_APPEND(tkeylist->rdata, rdata, link);
+
+ RETERR(dns_message_gettemprdataset(msg, &tkeyset));
+ dns_rdataset_init(tkeyset);
+ RETERR(dns_rdatalist_tordataset(tkeylist, tkeyset));
+
+ dns_name_init(qname, NULL);
+ dns_name_clone(name, qname);
+
+ dns_name_init(aname, NULL);
+ dns_name_clone(name, aname);
+
+ ISC_LIST_APPEND(qname->list, question, link);
+ ISC_LIST_APPEND(aname->list, tkeyset, link);
+
+ dns_message_addname(msg, qname, DNS_SECTION_QUESTION);
+
+ /*
+ * Windows 2000 needs this in the answer section, not the additional
+ * section where the RFC specifies.
+ */
+ if (win2k)
+ dns_message_addname(msg, aname, DNS_SECTION_ANSWER);
+ else
+ dns_message_addname(msg, aname, DNS_SECTION_ADDITIONAL);
+
+ return (ISC_R_SUCCESS);
+
+ failure:
+ if (qname != NULL)
+ dns_message_puttempname(msg, &qname);
+ if (aname != NULL)
+ dns_message_puttempname(msg, &aname);
+ if (question != NULL) {
+ dns_rdataset_disassociate(question);
+ dns_message_puttemprdataset(msg, &question);
+ }
+ if (dynbuf != NULL)
+ isc_buffer_free(&dynbuf);
+ printf("buildquery error\n");
+ return (result);
+}
+
+isc_result_t
+dns_tkey_builddhquery(dns_message_t *msg, dst_key_t *key, dns_name_t *name,
+ dns_name_t *algorithm, isc_buffer_t *nonce,
+ isc_uint32_t lifetime)
+{
+ dns_rdata_tkey_t tkey;
+ dns_rdata_t *rdata = NULL;
+ isc_buffer_t *dynbuf = NULL;
+ isc_region_t r;
+ dns_name_t keyname;
+ dns_namelist_t namelist;
+ isc_result_t result;
+ isc_stdtime_t now;
+
+ REQUIRE(msg != NULL);
+ REQUIRE(key != NULL);
+ REQUIRE(dst_key_alg(key) == DNS_KEYALG_DH);
+ REQUIRE(dst_key_isprivate(key));
+ REQUIRE(name != NULL);
+ REQUIRE(algorithm != NULL);
+
+ tkey.common.rdclass = dns_rdataclass_any;
+ tkey.common.rdtype = dns_rdatatype_tkey;
+ ISC_LINK_INIT(&tkey.common, link);
+ tkey.mctx = msg->mctx;
+ dns_name_init(&tkey.algorithm, NULL);
+ dns_name_clone(algorithm, &tkey.algorithm);
+ isc_stdtime_get(&now);
+ tkey.inception = now;
+ tkey.expire = now + lifetime;
+ tkey.mode = DNS_TKEYMODE_DIFFIEHELLMAN;
+ if (nonce != NULL)
+ isc_buffer_usedregion(nonce, &r);
+ else {
+ r.base = isc_mem_get(msg->mctx, 0);
+ r.length = 0;
+ }
+ tkey.error = 0;
+ tkey.key = r.base;
+ tkey.keylen = r.length;
+ tkey.other = NULL;
+ tkey.otherlen = 0;
+
+ RETERR(buildquery(msg, name, &tkey, ISC_FALSE));
+
+ if (nonce == NULL)
+ isc_mem_put(msg->mctx, r.base, 0);
+
+ RETERR(dns_message_gettemprdata(msg, &rdata));
+ RETERR(isc_buffer_allocate(msg->mctx, &dynbuf, 1024));
+ RETERR(dst_key_todns(key, dynbuf));
+ isc_buffer_usedregion(dynbuf, &r);
+ dns_rdata_fromregion(rdata, dns_rdataclass_any,
+ dns_rdatatype_key, &r);
+ dns_message_takebuffer(msg, &dynbuf);
+
+ dns_name_init(&keyname, NULL);
+ dns_name_clone(dst_key_name(key), &keyname);
+
+ ISC_LIST_INIT(namelist);
+ RETERR(add_rdata_to_list(msg, &keyname, rdata, 0, &namelist));
+ dns_message_addname(msg, ISC_LIST_HEAD(namelist),
+ DNS_SECTION_ADDITIONAL);
+
+ return (ISC_R_SUCCESS);
+
+ failure:
+
+ if (dynbuf != NULL)
+ isc_buffer_free(&dynbuf);
+ return (result);
+}
+
+isc_result_t
+dns_tkey_buildgssquery(dns_message_t *msg, dns_name_t *name, dns_name_t *gname,
+ isc_buffer_t *intoken, isc_uint32_t lifetime,
+ gss_ctx_id_t *context, isc_boolean_t win2k)
+{
+ dns_rdata_tkey_t tkey;
+ isc_result_t result;
+ isc_stdtime_t now;
+ isc_buffer_t token;
+ unsigned char array[4096];
+
+ UNUSED(intoken);
+
+ REQUIRE(msg != NULL);
+ REQUIRE(name != NULL);
+ REQUIRE(gname != NULL);
+ REQUIRE(context != NULL);
+
+ isc_buffer_init(&token, array, sizeof(array));
+ result = dst_gssapi_initctx(gname, NULL, &token, context);
+ if (result != DNS_R_CONTINUE && result != ISC_R_SUCCESS)
+ return (result);
+
+ tkey.common.rdclass = dns_rdataclass_any;
+ tkey.common.rdtype = dns_rdatatype_tkey;
+ ISC_LINK_INIT(&tkey.common, link);
+ tkey.mctx = NULL;
+ dns_name_init(&tkey.algorithm, NULL);
+
+ if (win2k)
+ dns_name_clone(DNS_TSIG_GSSAPIMS_NAME, &tkey.algorithm);
+ else
+ dns_name_clone(DNS_TSIG_GSSAPI_NAME, &tkey.algorithm);
+
+ isc_stdtime_get(&now);
+ tkey.inception = now;
+ tkey.expire = now + lifetime;
+ tkey.mode = DNS_TKEYMODE_GSSAPI;
+ tkey.error = 0;
+ tkey.key = isc_buffer_base(&token);
+ tkey.keylen = isc_buffer_usedlength(&token);
+ tkey.other = NULL;
+ tkey.otherlen = 0;
+
+ RETERR(buildquery(msg, name, &tkey, win2k));
+
+ return (ISC_R_SUCCESS);
+
+ failure:
+ return (result);
+}
+
+isc_result_t
+dns_tkey_builddeletequery(dns_message_t *msg, dns_tsigkey_t *key) {
+ dns_rdata_tkey_t tkey;
+
+ REQUIRE(msg != NULL);
+ REQUIRE(key != NULL);
+
+ tkey.common.rdclass = dns_rdataclass_any;
+ tkey.common.rdtype = dns_rdatatype_tkey;
+ ISC_LINK_INIT(&tkey.common, link);
+ tkey.mctx = msg->mctx;
+ dns_name_init(&tkey.algorithm, NULL);
+ dns_name_clone(key->algorithm, &tkey.algorithm);
+ tkey.inception = tkey.expire = 0;
+ tkey.mode = DNS_TKEYMODE_DELETE;
+ tkey.error = 0;
+ tkey.keylen = tkey.otherlen = 0;
+ tkey.key = tkey.other = NULL;
+
+ return (buildquery(msg, &key->name, &tkey, ISC_FALSE));
+}
+
+static isc_result_t
+find_tkey(dns_message_t *msg, dns_name_t **name, dns_rdata_t *rdata,
+ int section)
+{
+ dns_rdataset_t *tkeyset;
+ isc_result_t result;
+
+ result = dns_message_firstname(msg, section);
+ while (result == ISC_R_SUCCESS) {
+ *name = NULL;
+ dns_message_currentname(msg, section, name);
+ tkeyset = NULL;
+ result = dns_message_findtype(*name, dns_rdatatype_tkey, 0,
+ &tkeyset);
+ if (result == ISC_R_SUCCESS) {
+ result = dns_rdataset_first(tkeyset);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ dns_rdataset_current(tkeyset, rdata);
+ return (ISC_R_SUCCESS);
+ }
+ result = dns_message_nextname(msg, section);
+ }
+ if (result == ISC_R_NOMORE)
+ return (ISC_R_NOTFOUND);
+ return (result);
+}
+
+isc_result_t
+dns_tkey_processdhresponse(dns_message_t *qmsg, dns_message_t *rmsg,
+ dst_key_t *key, isc_buffer_t *nonce,
+ dns_tsigkey_t **outkey, dns_tsig_keyring_t *ring)
+{
+ dns_rdata_t qtkeyrdata = DNS_RDATA_INIT, rtkeyrdata = DNS_RDATA_INIT;
+ dns_name_t keyname, *tkeyname, *theirkeyname, *ourkeyname, *tempname;
+ dns_rdataset_t *theirkeyset = NULL, *ourkeyset = NULL;
+ dns_rdata_t theirkeyrdata = DNS_RDATA_INIT;
+ dst_key_t *theirkey = NULL;
+ dns_rdata_tkey_t qtkey, rtkey;
+ unsigned char secretdata[256];
+ unsigned int sharedsize;
+ isc_buffer_t *shared = NULL, secret;
+ isc_region_t r, r2;
+ isc_result_t result;
+ isc_boolean_t freertkey = ISC_FALSE;
+
+ REQUIRE(qmsg != NULL);
+ REQUIRE(rmsg != NULL);
+ REQUIRE(key != NULL);
+ REQUIRE(dst_key_alg(key) == DNS_KEYALG_DH);
+ REQUIRE(dst_key_isprivate(key));
+ if (outkey != NULL)
+ REQUIRE(*outkey == NULL);
+
+ if (rmsg->rcode != dns_rcode_noerror)
+ return (ISC_RESULTCLASS_DNSRCODE + rmsg->rcode);
+ RETERR(find_tkey(rmsg, &tkeyname, &rtkeyrdata, DNS_SECTION_ANSWER));
+ RETERR(dns_rdata_tostruct(&rtkeyrdata, &rtkey, NULL));
+ freertkey = ISC_TRUE;
+
+ RETERR(find_tkey(qmsg, &tempname, &qtkeyrdata,
+ DNS_SECTION_ADDITIONAL));
+ RETERR(dns_rdata_tostruct(&qtkeyrdata, &qtkey, NULL));
+
+ if (rtkey.error != dns_rcode_noerror ||
+ rtkey.mode != DNS_TKEYMODE_DIFFIEHELLMAN ||
+ rtkey.mode != qtkey.mode ||
+ !dns_name_equal(&rtkey.algorithm, &qtkey.algorithm) ||
+ rmsg->rcode != dns_rcode_noerror) {
+ tkey_log("dns_tkey_processdhresponse: tkey mode invalid "
+ "or error set(1)");
+ result = DNS_R_INVALIDTKEY;
+ dns_rdata_freestruct(&qtkey);
+ goto failure;
+ }
+
+ dns_rdata_freestruct(&qtkey);
+
+ dns_name_init(&keyname, NULL);
+ dns_name_clone(dst_key_name(key), &keyname);
+
+ ourkeyname = NULL;
+ ourkeyset = NULL;
+ RETERR(dns_message_findname(rmsg, DNS_SECTION_ANSWER, &keyname,
+ dns_rdatatype_key, 0, &ourkeyname,
+ &ourkeyset));
+
+ result = dns_message_firstname(rmsg, DNS_SECTION_ANSWER);
+ while (result == ISC_R_SUCCESS) {
+ theirkeyname = NULL;
+ dns_message_currentname(rmsg, DNS_SECTION_ANSWER,
+ &theirkeyname);
+ if (dns_name_equal(theirkeyname, ourkeyname))
+ goto next;
+ theirkeyset = NULL;
+ result = dns_message_findtype(theirkeyname, dns_rdatatype_key,
+ 0, &theirkeyset);
+ if (result == ISC_R_SUCCESS) {
+ RETERR(dns_rdataset_first(theirkeyset));
+ break;
+ }
+ next:
+ result = dns_message_nextname(rmsg, DNS_SECTION_ANSWER);
+ }
+
+ if (theirkeyset == NULL) {
+ tkey_log("dns_tkey_processdhresponse: failed to find server "
+ "key");
+ result = ISC_R_NOTFOUND;
+ goto failure;
+ }
+
+ dns_rdataset_current(theirkeyset, &theirkeyrdata);
+ RETERR(dns_dnssec_keyfromrdata(theirkeyname, &theirkeyrdata,
+ rmsg->mctx, &theirkey));
+
+ RETERR(dst_key_secretsize(key, &sharedsize));
+ RETERR(isc_buffer_allocate(rmsg->mctx, &shared, sharedsize));
+
+ RETERR(dst_key_computesecret(theirkey, key, shared));
+
+ isc_buffer_init(&secret, secretdata, sizeof(secretdata));
+
+ r.base = rtkey.key;
+ r.length = rtkey.keylen;
+ if (nonce != NULL)
+ isc_buffer_usedregion(nonce, &r2);
+ else {
+ r2.base = isc_mem_get(rmsg->mctx, 0);
+ r2.length = 0;
+ }
+ RETERR(compute_secret(shared, &r2, &r, &secret));
+ if (nonce == NULL)
+ isc_mem_put(rmsg->mctx, r2.base, 0);
+
+ isc_buffer_usedregion(&secret, &r);
+ result = dns_tsigkey_create(tkeyname, &rtkey.algorithm,
+ r.base, r.length, ISC_TRUE,
+ NULL, rtkey.inception, rtkey.expire,
+ rmsg->mctx, ring, outkey);
+ isc_buffer_free(&shared);
+ dns_rdata_freestruct(&rtkey);
+ dst_key_free(&theirkey);
+ return (result);
+
+ failure:
+ if (shared != NULL)
+ isc_buffer_free(&shared);
+
+ if (theirkey != NULL)
+ dst_key_free(&theirkey);
+
+ if (freertkey)
+ dns_rdata_freestruct(&rtkey);
+
+ return (result);
+}
+
+isc_result_t
+dns_tkey_processgssresponse(dns_message_t *qmsg, dns_message_t *rmsg,
+ dns_name_t *gname, gss_ctx_id_t *context,
+ isc_buffer_t *outtoken, dns_tsigkey_t **outkey,
+ dns_tsig_keyring_t *ring)
+{
+ dns_rdata_t rtkeyrdata = DNS_RDATA_INIT, qtkeyrdata = DNS_RDATA_INIT;
+ dns_name_t *tkeyname;
+ dns_rdata_tkey_t rtkey, qtkey;
+ dst_key_t *dstkey = NULL;
+ isc_buffer_t intoken;
+ isc_result_t result;
+ unsigned char array[1024];
+
+ REQUIRE(outtoken != NULL);
+ REQUIRE(qmsg != NULL);
+ REQUIRE(rmsg != NULL);
+ REQUIRE(gname != NULL);
+ if (outkey != NULL)
+ REQUIRE(*outkey == NULL);
+
+ if (rmsg->rcode != dns_rcode_noerror)
+ return (ISC_RESULTCLASS_DNSRCODE + rmsg->rcode);
+ RETERR(find_tkey(rmsg, &tkeyname, &rtkeyrdata, DNS_SECTION_ANSWER));
+ RETERR(dns_rdata_tostruct(&rtkeyrdata, &rtkey, NULL));
+
+ /*
+ * Win2k puts the item in the ANSWER section, while the RFC
+ * specifies it should be in the ADDITIONAL section. Check first
+ * where it should be, and then where it may be.
+ */
+ result = find_tkey(qmsg, &tkeyname, &qtkeyrdata,
+ DNS_SECTION_ADDITIONAL);
+ if (result == ISC_R_NOTFOUND)
+ result = find_tkey(qmsg, &tkeyname, &qtkeyrdata,
+ DNS_SECTION_ANSWER);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ RETERR(dns_rdata_tostruct(&qtkeyrdata, &qtkey, NULL));
+
+ if (rtkey.error != dns_rcode_noerror ||
+ rtkey.mode != DNS_TKEYMODE_GSSAPI ||
+ !dns_name_equal(&rtkey.algorithm, &qtkey.algorithm)) {
+ tkey_log("dns_tkey_processgssresponse: tkey mode invalid "
+ "or error set(2) %d", rtkey.error);
+ _dns_tkey_dumpmessage(qmsg);
+ _dns_tkey_dumpmessage(rmsg);
+ result = DNS_R_INVALIDTKEY;
+ goto failure;
+ }
+
+ isc_buffer_init(outtoken, array, sizeof(array));
+ isc_buffer_init(&intoken, rtkey.key, rtkey.keylen);
+ RETERR(dst_gssapi_initctx(gname, &intoken, outtoken, context));
+
+ dstkey = NULL;
+ RETERR(dst_key_fromgssapi(dns_rootname, *context, rmsg->mctx,
+ &dstkey));
+
+ RETERR(dns_tsigkey_createfromkey(tkeyname, DNS_TSIG_GSSAPI_NAME,
+ dstkey, ISC_FALSE, NULL,
+ rtkey.inception, rtkey.expire,
+ ring->mctx, ring, outkey));
+
+ dns_rdata_freestruct(&rtkey);
+ return (result);
+
+ failure:
+ /*
+ * XXXSRA This probably leaks memory from rtkey and qtkey.
+ */
+ return (result);
+}
+
+isc_result_t
+dns_tkey_processdeleteresponse(dns_message_t *qmsg, dns_message_t *rmsg,
+ dns_tsig_keyring_t *ring)
+{
+ dns_rdata_t qtkeyrdata = DNS_RDATA_INIT, rtkeyrdata = DNS_RDATA_INIT;
+ dns_name_t *tkeyname, *tempname;
+ dns_rdata_tkey_t qtkey, rtkey;
+ dns_tsigkey_t *tsigkey = NULL;
+ isc_result_t result;
+
+ REQUIRE(qmsg != NULL);
+ REQUIRE(rmsg != NULL);
+
+ if (rmsg->rcode != dns_rcode_noerror)
+ return(ISC_RESULTCLASS_DNSRCODE + rmsg->rcode);
+
+ RETERR(find_tkey(rmsg, &tkeyname, &rtkeyrdata, DNS_SECTION_ANSWER));
+ RETERR(dns_rdata_tostruct(&rtkeyrdata, &rtkey, NULL));
+
+ RETERR(find_tkey(qmsg, &tempname, &qtkeyrdata,
+ DNS_SECTION_ADDITIONAL));
+ RETERR(dns_rdata_tostruct(&qtkeyrdata, &qtkey, NULL));
+
+ if (rtkey.error != dns_rcode_noerror ||
+ rtkey.mode != DNS_TKEYMODE_DELETE ||
+ rtkey.mode != qtkey.mode ||
+ !dns_name_equal(&rtkey.algorithm, &qtkey.algorithm) ||
+ rmsg->rcode != dns_rcode_noerror) {
+ tkey_log("dns_tkey_processdeleteresponse: tkey mode invalid "
+ "or error set(3)");
+ result = DNS_R_INVALIDTKEY;
+ dns_rdata_freestruct(&qtkey);
+ dns_rdata_freestruct(&rtkey);
+ goto failure;
+ }
+
+ dns_rdata_freestruct(&qtkey);
+
+ RETERR(dns_tsigkey_find(&tsigkey, tkeyname, &rtkey.algorithm, ring));
+
+ dns_rdata_freestruct(&rtkey);
+
+ /*
+ * Mark the key as deleted.
+ */
+ dns_tsigkey_setdeleted(tsigkey);
+ /*
+ * Release the reference.
+ */
+ dns_tsigkey_detach(&tsigkey);
+
+ failure:
+ return (result);
+}
+
+isc_result_t
+dns_tkey_gssnegotiate(dns_message_t *qmsg, dns_message_t *rmsg,
+ dns_name_t *server, gss_ctx_id_t *context,
+ dns_tsigkey_t **outkey, dns_tsig_keyring_t *ring,
+ isc_boolean_t win2k)
+{
+ dns_rdata_t rtkeyrdata = DNS_RDATA_INIT, qtkeyrdata = DNS_RDATA_INIT;
+ dns_name_t *tkeyname;
+ dns_rdata_tkey_t rtkey, qtkey;
+ isc_buffer_t intoken, outtoken;
+ dst_key_t *dstkey = NULL;
+ isc_result_t result;
+ unsigned char array[1024];
+
+ REQUIRE(qmsg != NULL);
+ REQUIRE(rmsg != NULL);
+ REQUIRE(server != NULL);
+ if (outkey != NULL)
+ REQUIRE(*outkey == NULL);
+
+ if (rmsg->rcode != dns_rcode_noerror)
+ return (ISC_RESULTCLASS_DNSRCODE + rmsg->rcode);
+
+ RETERR(find_tkey(rmsg, &tkeyname, &rtkeyrdata, DNS_SECTION_ANSWER));
+ RETERR(dns_rdata_tostruct(&rtkeyrdata, &rtkey, NULL));
+
+ if (win2k == ISC_TRUE)
+ RETERR(find_tkey(qmsg, &tkeyname, &qtkeyrdata,
+ DNS_SECTION_ANSWER));
+ else
+ RETERR(find_tkey(qmsg, &tkeyname, &qtkeyrdata,
+ DNS_SECTION_ADDITIONAL));
+
+ RETERR(dns_rdata_tostruct(&qtkeyrdata, &qtkey, NULL));
+
+ if (rtkey.error != dns_rcode_noerror ||
+ rtkey.mode != DNS_TKEYMODE_GSSAPI ||
+ !dns_name_equal(&rtkey.algorithm, &qtkey.algorithm))
+ {
+ tkey_log("dns_tkey_processdhresponse: tkey mode invalid "
+ "or error set(4)");
+ result = DNS_R_INVALIDTKEY;
+ goto failure;
+ }
+
+ isc_buffer_init(&intoken, rtkey.key, rtkey.keylen);
+ isc_buffer_init(&outtoken, array, sizeof(array));
+
+ result = dst_gssapi_initctx(server, &intoken, &outtoken, context);
+ if (result != DNS_R_CONTINUE && result != ISC_R_SUCCESS)
+ return (result);
+
+ dstkey = NULL;
+ RETERR(dst_key_fromgssapi(dns_rootname, *context, rmsg->mctx,
+ &dstkey));
+
+ /*
+ * XXXSRA This seems confused. If we got CONTINUE from initctx,
+ * the GSS negotiation hasn't completed yet, so we can't sign
+ * anything yet.
+ */
+
+ RETERR(dns_tsigkey_createfromkey(tkeyname,
+ (win2k
+ ? DNS_TSIG_GSSAPIMS_NAME
+ : DNS_TSIG_GSSAPI_NAME),
+ dstkey, ISC_TRUE, NULL,
+ rtkey.inception, rtkey.expire,
+ ring->mctx, ring, outkey));
+
+ dns_rdata_freestruct(&rtkey);
+ return (result);
+
+ failure:
+ /*
+ * XXXSRA This probably leaks memory from qtkey.
+ */
+ dns_rdata_freestruct(&rtkey);
+ return (result);
+}
diff --git a/lib/dns/tsig.c b/lib/dns/tsig.c
new file mode 100644
index 0000000..74a7af3
--- /dev/null
+++ b/lib/dns/tsig.c
@@ -0,0 +1,1549 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/*
+ * $Id: tsig.c,v 1.136 2008/11/04 21:23:14 marka Exp $
+ */
+/*! \file */
+#include <config.h>
+#include <stdlib.h>
+
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/refcount.h>
+#include <isc/string.h> /* Required for HP/UX (and others?) */
+#include <isc/util.h>
+#include <isc/time.h>
+
+#include <dns/keyvalues.h>
+#include <dns/log.h>
+#include <dns/message.h>
+#include <dns/fixedname.h>
+#include <dns/rbt.h>
+#include <dns/rdata.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatastruct.h>
+#include <dns/result.h>
+#include <dns/tsig.h>
+
+#include <dst/result.h>
+
+#define TSIG_MAGIC ISC_MAGIC('T', 'S', 'I', 'G')
+#define VALID_TSIG_KEY(x) ISC_MAGIC_VALID(x, TSIG_MAGIC)
+
+#define is_response(msg) (msg->flags & DNS_MESSAGEFLAG_QR)
+#define algname_is_allocated(algname) \
+ ((algname) != dns_tsig_hmacmd5_name && \
+ (algname) != dns_tsig_hmacsha1_name && \
+ (algname) != dns_tsig_hmacsha224_name && \
+ (algname) != dns_tsig_hmacsha256_name && \
+ (algname) != dns_tsig_hmacsha384_name && \
+ (algname) != dns_tsig_hmacsha512_name && \
+ (algname) != dns_tsig_gssapi_name && \
+ (algname) != dns_tsig_gssapims_name)
+
+#define BADTIMELEN 6
+
+static unsigned char hmacmd5_ndata[] = "\010hmac-md5\007sig-alg\003reg\003int";
+static unsigned char hmacmd5_offsets[] = { 0, 9, 17, 21, 25 };
+
+static dns_name_t hmacmd5 = {
+ DNS_NAME_MAGIC,
+ hmacmd5_ndata, 26, 5,
+ DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
+ hmacmd5_offsets, NULL,
+ {(void *)-1, (void *)-1},
+ {NULL, NULL}
+};
+
+dns_name_t *dns_tsig_hmacmd5_name = &hmacmd5;
+
+static unsigned char gsstsig_ndata[] = "\010gss-tsig";
+static unsigned char gsstsig_offsets[] = { 0, 9 };
+static dns_name_t gsstsig = {
+ DNS_NAME_MAGIC,
+ gsstsig_ndata, 10, 2,
+ DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
+ gsstsig_offsets, NULL,
+ {(void *)-1, (void *)-1},
+ {NULL, NULL}
+};
+LIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_gssapi_name = &gsstsig;
+
+/*
+ * Since Microsoft doesn't follow its own standard, we will use this
+ * alternate name as a second guess.
+ */
+static unsigned char gsstsigms_ndata[] = "\003gss\011microsoft\003com";
+static unsigned char gsstsigms_offsets[] = { 0, 4, 14, 18 };
+static dns_name_t gsstsigms = {
+ DNS_NAME_MAGIC,
+ gsstsigms_ndata, 19, 4,
+ DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
+ gsstsigms_offsets, NULL,
+ {(void *)-1, (void *)-1},
+ {NULL, NULL}
+};
+LIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_gssapims_name = &gsstsigms;
+
+static unsigned char hmacsha1_ndata[] = "\011hmac-sha1";
+static unsigned char hmacsha1_offsets[] = { 0, 10 };
+
+static dns_name_t hmacsha1 = {
+ DNS_NAME_MAGIC,
+ hmacsha1_ndata, 11, 2,
+ DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
+ hmacsha1_offsets, NULL,
+ {(void *)-1, (void *)-1},
+ {NULL, NULL}
+};
+
+LIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_hmacsha1_name = &hmacsha1;
+
+static unsigned char hmacsha224_ndata[] = "\013hmac-sha224";
+static unsigned char hmacsha224_offsets[] = { 0, 12 };
+
+static dns_name_t hmacsha224 = {
+ DNS_NAME_MAGIC,
+ hmacsha224_ndata, 13, 2,
+ DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
+ hmacsha224_offsets, NULL,
+ {(void *)-1, (void *)-1},
+ {NULL, NULL}
+};
+
+LIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_hmacsha224_name = &hmacsha224;
+
+static unsigned char hmacsha256_ndata[] = "\013hmac-sha256";
+static unsigned char hmacsha256_offsets[] = { 0, 12 };
+
+static dns_name_t hmacsha256 = {
+ DNS_NAME_MAGIC,
+ hmacsha256_ndata, 13, 2,
+ DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
+ hmacsha256_offsets, NULL,
+ {(void *)-1, (void *)-1},
+ {NULL, NULL}
+};
+
+LIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_hmacsha256_name = &hmacsha256;
+
+static unsigned char hmacsha384_ndata[] = "\013hmac-sha384";
+static unsigned char hmacsha384_offsets[] = { 0, 12 };
+
+static dns_name_t hmacsha384 = {
+ DNS_NAME_MAGIC,
+ hmacsha384_ndata, 13, 2,
+ DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
+ hmacsha384_offsets, NULL,
+ {(void *)-1, (void *)-1},
+ {NULL, NULL}
+};
+
+LIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_hmacsha384_name = &hmacsha384;
+
+static unsigned char hmacsha512_ndata[] = "\013hmac-sha512";
+static unsigned char hmacsha512_offsets[] = { 0, 12 };
+
+static dns_name_t hmacsha512 = {
+ DNS_NAME_MAGIC,
+ hmacsha512_ndata, 13, 2,
+ DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
+ hmacsha512_offsets, NULL,
+ {(void *)-1, (void *)-1},
+ {NULL, NULL}
+};
+
+LIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_hmacsha512_name = &hmacsha512;
+
+static isc_result_t
+tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg);
+
+static void
+tsig_log(dns_tsigkey_t *key, int level, const char *fmt, ...)
+ ISC_FORMAT_PRINTF(3, 4);
+
+static void
+cleanup_ring(dns_tsig_keyring_t *ring);
+static void
+tsigkey_free(dns_tsigkey_t *key);
+
+static void
+tsig_log(dns_tsigkey_t *key, int level, const char *fmt, ...) {
+ va_list ap;
+ char message[4096];
+ char namestr[DNS_NAME_FORMATSIZE];
+ char creatorstr[DNS_NAME_FORMATSIZE];
+
+ if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE)
+ return;
+ if (key != NULL)
+ dns_name_format(&key->name, namestr, sizeof(namestr));
+ else
+ strcpy(namestr, "<null>");
+
+ if (key != NULL && key->generated)
+ dns_name_format(key->creator, creatorstr, sizeof(creatorstr));
+
+ va_start(ap, fmt);
+ vsnprintf(message, sizeof(message), fmt, ap);
+ va_end(ap);
+ if (key != NULL && key->generated)
+ isc_log_write(dns_lctx,
+ DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_TSIG,
+ level, "tsig key '%s' (%s): %s",
+ namestr, creatorstr, message);
+ else
+ isc_log_write(dns_lctx,
+ DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_TSIG,
+ level, "tsig key '%s': %s", namestr, message);
+}
+
+isc_result_t
+dns_tsigkey_createfromkey(dns_name_t *name, dns_name_t *algorithm,
+ dst_key_t *dstkey, isc_boolean_t generated,
+ dns_name_t *creator, isc_stdtime_t inception,
+ isc_stdtime_t expire, isc_mem_t *mctx,
+ dns_tsig_keyring_t *ring, dns_tsigkey_t **key)
+{
+ dns_tsigkey_t *tkey;
+ isc_result_t ret;
+ unsigned int refs = 0;
+
+ REQUIRE(key == NULL || *key == NULL);
+ REQUIRE(name != NULL);
+ REQUIRE(algorithm != NULL);
+ REQUIRE(mctx != NULL);
+ REQUIRE(key != NULL || ring != NULL);
+
+ tkey = (dns_tsigkey_t *) isc_mem_get(mctx, sizeof(dns_tsigkey_t));
+ if (tkey == NULL)
+ return (ISC_R_NOMEMORY);
+
+ dns_name_init(&tkey->name, NULL);
+ ret = dns_name_dup(name, mctx, &tkey->name);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_key;
+ (void)dns_name_downcase(&tkey->name, &tkey->name, NULL);
+
+ if (dns_name_equal(algorithm, DNS_TSIG_HMACMD5_NAME)) {
+ tkey->algorithm = DNS_TSIG_HMACMD5_NAME;
+ if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_HMACMD5) {
+ ret = DNS_R_BADALG;
+ goto cleanup_name;
+ }
+ } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA1_NAME)) {
+ tkey->algorithm = DNS_TSIG_HMACSHA1_NAME;
+ if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_HMACSHA1) {
+ ret = DNS_R_BADALG;
+ goto cleanup_name;
+ }
+ } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA224_NAME)) {
+ tkey->algorithm = DNS_TSIG_HMACSHA224_NAME;
+ if (dstkey != NULL &&
+ dst_key_alg(dstkey) != DST_ALG_HMACSHA224) {
+ ret = DNS_R_BADALG;
+ goto cleanup_name;
+ }
+ } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA256_NAME)) {
+ tkey->algorithm = DNS_TSIG_HMACSHA256_NAME;
+ if (dstkey != NULL &&
+ dst_key_alg(dstkey) != DST_ALG_HMACSHA256) {
+ ret = DNS_R_BADALG;
+ goto cleanup_name;
+ }
+ } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA384_NAME)) {
+ tkey->algorithm = DNS_TSIG_HMACSHA384_NAME;
+ if (dstkey != NULL &&
+ dst_key_alg(dstkey) != DST_ALG_HMACSHA384) {
+ ret = DNS_R_BADALG;
+ goto cleanup_name;
+ }
+ } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA512_NAME)) {
+ tkey->algorithm = DNS_TSIG_HMACSHA512_NAME;
+ if (dstkey != NULL &&
+ dst_key_alg(dstkey) != DST_ALG_HMACSHA512) {
+ ret = DNS_R_BADALG;
+ goto cleanup_name;
+ }
+ } else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPI_NAME)) {
+ tkey->algorithm = DNS_TSIG_GSSAPI_NAME;
+ if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_GSSAPI) {
+ ret = DNS_R_BADALG;
+ goto cleanup_name;
+ }
+ } else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPIMS_NAME)) {
+ tkey->algorithm = DNS_TSIG_GSSAPIMS_NAME;
+ if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_GSSAPI) {
+ ret = DNS_R_BADALG;
+ goto cleanup_name;
+ }
+ } else {
+ if (dstkey != NULL) {
+ ret = DNS_R_BADALG;
+ goto cleanup_name;
+ }
+ tkey->algorithm = isc_mem_get(mctx, sizeof(dns_name_t));
+ if (tkey->algorithm == NULL) {
+ ret = ISC_R_NOMEMORY;
+ goto cleanup_name;
+ }
+ dns_name_init(tkey->algorithm, NULL);
+ ret = dns_name_dup(algorithm, mctx, tkey->algorithm);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_algorithm;
+ (void)dns_name_downcase(tkey->algorithm, tkey->algorithm,
+ NULL);
+ }
+
+ if (creator != NULL) {
+ tkey->creator = isc_mem_get(mctx, sizeof(dns_name_t));
+ if (tkey->creator == NULL) {
+ ret = ISC_R_NOMEMORY;
+ goto cleanup_algorithm;
+ }
+ dns_name_init(tkey->creator, NULL);
+ ret = dns_name_dup(creator, mctx, tkey->creator);
+ if (ret != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, tkey->creator, sizeof(dns_name_t));
+ goto cleanup_algorithm;
+ }
+ } else
+ tkey->creator = NULL;
+
+ tkey->key = dstkey;
+ tkey->ring = ring;
+
+ if (key != NULL)
+ refs++;
+ if (ring != NULL)
+ refs++;
+ ret = isc_refcount_init(&tkey->refs, refs);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_creator;
+
+ tkey->generated = generated;
+ tkey->inception = inception;
+ tkey->expire = expire;
+ tkey->mctx = NULL;
+ isc_mem_attach(mctx, &tkey->mctx);
+
+ tkey->magic = TSIG_MAGIC;
+
+ if (ring != NULL) {
+ RWLOCK(&ring->lock, isc_rwlocktype_write);
+ ring->writecount++;
+
+ /*
+ * Do on the fly cleaning. Find some nodes we might not
+ * want around any more.
+ */
+ if (ring->writecount > 10) {
+ cleanup_ring(ring);
+ ring->writecount = 0;
+ }
+ ret = dns_rbt_addname(ring->keys, name, tkey);
+ if (ret != ISC_R_SUCCESS) {
+ RWUNLOCK(&ring->lock, isc_rwlocktype_write);
+ goto cleanup_refs;
+ }
+ RWUNLOCK(&ring->lock, isc_rwlocktype_write);
+ }
+
+ /*
+ * Ignore this if it's a GSS key, since the key size is meaningless.
+ */
+ if (dstkey != NULL && dst_key_size(dstkey) < 64 &&
+ !dns_name_equal(algorithm, DNS_TSIG_GSSAPI_NAME) &&
+ !dns_name_equal(algorithm, DNS_TSIG_GSSAPIMS_NAME)) {
+ char namestr[DNS_NAME_FORMATSIZE];
+ dns_name_format(name, namestr, sizeof(namestr));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
+ DNS_LOGMODULE_TSIG, ISC_LOG_INFO,
+ "the key '%s' is too short to be secure",
+ namestr);
+ }
+ if (key != NULL)
+ *key = tkey;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup_refs:
+ tkey->magic = 0;
+ while (refs-- > 0)
+ isc_refcount_decrement(&tkey->refs, NULL);
+ isc_refcount_destroy(&tkey->refs);
+ cleanup_creator:
+ if (tkey->creator != NULL) {
+ dns_name_free(tkey->creator, mctx);
+ isc_mem_put(mctx, tkey->creator, sizeof(dns_name_t));
+ }
+ cleanup_algorithm:
+ if (algname_is_allocated(tkey->algorithm)) {
+ if (dns_name_dynamic(tkey->algorithm))
+ dns_name_free(tkey->algorithm, mctx);
+ isc_mem_put(mctx, tkey->algorithm, sizeof(dns_name_t));
+ }
+ cleanup_name:
+ dns_name_free(&tkey->name, mctx);
+ cleanup_key:
+ isc_mem_put(mctx, tkey, sizeof(dns_tsigkey_t));
+
+ return (ret);
+}
+
+/*
+ * Find a few nodes to destroy if possible.
+ */
+static void
+cleanup_ring(dns_tsig_keyring_t *ring)
+{
+ isc_result_t result;
+ dns_rbtnodechain_t chain;
+ dns_name_t foundname;
+ dns_fixedname_t fixedorigin;
+ dns_name_t *origin;
+ isc_stdtime_t now;
+ dns_rbtnode_t *node;
+ dns_tsigkey_t *tkey;
+
+ /*
+ * Start up a new iterator each time.
+ */
+ isc_stdtime_get(&now);
+ dns_name_init(&foundname, NULL);
+ dns_fixedname_init(&fixedorigin);
+ origin = dns_fixedname_name(&fixedorigin);
+
+ again:
+ dns_rbtnodechain_init(&chain, ring->mctx);
+ result = dns_rbtnodechain_first(&chain, ring->keys, &foundname,
+ origin);
+ if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
+ dns_rbtnodechain_invalidate(&chain);
+ return;
+ }
+
+ for (;;) {
+ node = NULL;
+ dns_rbtnodechain_current(&chain, &foundname, origin, &node);
+ tkey = node->data;
+ if (tkey != NULL) {
+ if (tkey->generated
+ && isc_refcount_current(&tkey->refs) == 1
+ && tkey->inception != tkey->expire
+ && tkey->expire < now) {
+ tsig_log(tkey, 2, "tsig expire: deleting");
+ /* delete the key */
+ dns_rbtnodechain_invalidate(&chain);
+ (void)dns_rbt_deletename(ring->keys,
+ &tkey->name,
+ ISC_FALSE);
+ goto again;
+ }
+ }
+ result = dns_rbtnodechain_next(&chain, &foundname,
+ origin);
+ if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
+ dns_rbtnodechain_invalidate(&chain);
+ return;
+ }
+
+ }
+}
+
+isc_result_t
+dns_tsigkey_create(dns_name_t *name, dns_name_t *algorithm,
+ unsigned char *secret, int length, isc_boolean_t generated,
+ dns_name_t *creator, isc_stdtime_t inception,
+ isc_stdtime_t expire, isc_mem_t *mctx,
+ dns_tsig_keyring_t *ring, dns_tsigkey_t **key)
+{
+ dst_key_t *dstkey = NULL;
+ isc_result_t result;
+
+ REQUIRE(length >= 0);
+ if (length > 0)
+ REQUIRE(secret != NULL);
+
+ if (dns_name_equal(algorithm, DNS_TSIG_HMACMD5_NAME)) {
+ if (secret != NULL) {
+ isc_buffer_t b;
+
+ isc_buffer_init(&b, secret, length);
+ isc_buffer_add(&b, length);
+ result = dst_key_frombuffer(name, DST_ALG_HMACMD5,
+ DNS_KEYOWNER_ENTITY,
+ DNS_KEYPROTO_DNSSEC,
+ dns_rdataclass_in,
+ &b, mctx, &dstkey);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA1_NAME)) {
+ if (secret != NULL) {
+ isc_buffer_t b;
+
+ isc_buffer_init(&b, secret, length);
+ isc_buffer_add(&b, length);
+ result = dst_key_frombuffer(name, DST_ALG_HMACSHA1,
+ DNS_KEYOWNER_ENTITY,
+ DNS_KEYPROTO_DNSSEC,
+ dns_rdataclass_in,
+ &b, mctx, &dstkey);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA224_NAME)) {
+ if (secret != NULL) {
+ isc_buffer_t b;
+
+ isc_buffer_init(&b, secret, length);
+ isc_buffer_add(&b, length);
+ result = dst_key_frombuffer(name, DST_ALG_HMACSHA224,
+ DNS_KEYOWNER_ENTITY,
+ DNS_KEYPROTO_DNSSEC,
+ dns_rdataclass_in,
+ &b, mctx, &dstkey);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA256_NAME)) {
+ if (secret != NULL) {
+ isc_buffer_t b;
+
+ isc_buffer_init(&b, secret, length);
+ isc_buffer_add(&b, length);
+ result = dst_key_frombuffer(name, DST_ALG_HMACSHA256,
+ DNS_KEYOWNER_ENTITY,
+ DNS_KEYPROTO_DNSSEC,
+ dns_rdataclass_in,
+ &b, mctx, &dstkey);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA384_NAME)) {
+ if (secret != NULL) {
+ isc_buffer_t b;
+
+ isc_buffer_init(&b, secret, length);
+ isc_buffer_add(&b, length);
+ result = dst_key_frombuffer(name, DST_ALG_HMACSHA384,
+ DNS_KEYOWNER_ENTITY,
+ DNS_KEYPROTO_DNSSEC,
+ dns_rdataclass_in,
+ &b, mctx, &dstkey);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA512_NAME)) {
+ if (secret != NULL) {
+ isc_buffer_t b;
+
+ isc_buffer_init(&b, secret, length);
+ isc_buffer_add(&b, length);
+ result = dst_key_frombuffer(name, DST_ALG_HMACSHA512,
+ DNS_KEYOWNER_ENTITY,
+ DNS_KEYPROTO_DNSSEC,
+ dns_rdataclass_in,
+ &b, mctx, &dstkey);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ } else if (length > 0)
+ return (DNS_R_BADALG);
+
+ result = dns_tsigkey_createfromkey(name, algorithm, dstkey,
+ generated, creator,
+ inception, expire, mctx, ring, key);
+ if (result != ISC_R_SUCCESS && dstkey != NULL)
+ dst_key_free(&dstkey);
+ return (result);
+}
+
+void
+dns_tsigkey_attach(dns_tsigkey_t *source, dns_tsigkey_t **targetp) {
+ REQUIRE(VALID_TSIG_KEY(source));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ isc_refcount_increment(&source->refs, NULL);
+ *targetp = source;
+}
+
+static void
+tsigkey_free(dns_tsigkey_t *key) {
+ REQUIRE(VALID_TSIG_KEY(key));
+
+ key->magic = 0;
+ dns_name_free(&key->name, key->mctx);
+ if (algname_is_allocated(key->algorithm)) {
+ dns_name_free(key->algorithm, key->mctx);
+ isc_mem_put(key->mctx, key->algorithm, sizeof(dns_name_t));
+ }
+ if (key->key != NULL)
+ dst_key_free(&key->key);
+ if (key->creator != NULL) {
+ dns_name_free(key->creator, key->mctx);
+ isc_mem_put(key->mctx, key->creator, sizeof(dns_name_t));
+ }
+ isc_refcount_destroy(&key->refs);
+ isc_mem_putanddetach(&key->mctx, key, sizeof(dns_tsigkey_t));
+}
+
+void
+dns_tsigkey_detach(dns_tsigkey_t **keyp) {
+ dns_tsigkey_t *key;
+ unsigned int refs;
+
+ REQUIRE(keyp != NULL);
+ REQUIRE(VALID_TSIG_KEY(*keyp));
+
+ key = *keyp;
+ isc_refcount_decrement(&key->refs, &refs);
+
+ if (refs == 0)
+ tsigkey_free(key);
+
+ *keyp = NULL;
+}
+
+void
+dns_tsigkey_setdeleted(dns_tsigkey_t *key) {
+ REQUIRE(VALID_TSIG_KEY(key));
+ REQUIRE(key->ring != NULL);
+
+ RWLOCK(&key->ring->lock, isc_rwlocktype_write);
+ (void)dns_rbt_deletename(key->ring->keys, &key->name, ISC_FALSE);
+ RWUNLOCK(&key->ring->lock, isc_rwlocktype_write);
+}
+
+isc_result_t
+dns_tsig_sign(dns_message_t *msg) {
+ dns_tsigkey_t *key;
+ dns_rdata_any_tsig_t tsig, querytsig;
+ unsigned char data[128];
+ isc_buffer_t databuf, sigbuf;
+ isc_buffer_t *dynbuf;
+ dns_name_t *owner;
+ dns_rdata_t *rdata = NULL;
+ dns_rdatalist_t *datalist;
+ dns_rdataset_t *dataset;
+ isc_region_t r;
+ isc_stdtime_t now;
+ isc_mem_t *mctx;
+ dst_context_t *ctx = NULL;
+ isc_result_t ret;
+ unsigned char badtimedata[BADTIMELEN];
+ unsigned int sigsize = 0;
+
+ REQUIRE(msg != NULL);
+ REQUIRE(VALID_TSIG_KEY(dns_message_gettsigkey(msg)));
+
+ /*
+ * If this is a response, there should be a query tsig.
+ */
+ if (is_response(msg) && msg->querytsig == NULL)
+ return (DNS_R_EXPECTEDTSIG);
+
+ dynbuf = NULL;
+
+ mctx = msg->mctx;
+ key = dns_message_gettsigkey(msg);
+
+ tsig.mctx = mctx;
+ tsig.common.rdclass = dns_rdataclass_any;
+ tsig.common.rdtype = dns_rdatatype_tsig;
+ ISC_LINK_INIT(&tsig.common, link);
+ dns_name_init(&tsig.algorithm, NULL);
+ dns_name_clone(key->algorithm, &tsig.algorithm);
+
+ isc_stdtime_get(&now);
+ tsig.timesigned = now + msg->timeadjust;
+ tsig.fudge = DNS_TSIG_FUDGE;
+
+ tsig.originalid = msg->id;
+
+ isc_buffer_init(&databuf, data, sizeof(data));
+
+ if (is_response(msg))
+ tsig.error = msg->querytsigstatus;
+ else
+ tsig.error = dns_rcode_noerror;
+
+ if (tsig.error != dns_tsigerror_badtime) {
+ tsig.otherlen = 0;
+ tsig.other = NULL;
+ } else {
+ isc_buffer_t otherbuf;
+
+ tsig.otherlen = BADTIMELEN;
+ tsig.other = badtimedata;
+ isc_buffer_init(&otherbuf, tsig.other, tsig.otherlen);
+ isc_buffer_putuint48(&otherbuf, tsig.timesigned);
+ }
+
+ if (key->key != NULL && tsig.error != dns_tsigerror_badsig) {
+ unsigned char header[DNS_MESSAGE_HEADERLEN];
+ isc_buffer_t headerbuf;
+ isc_uint16_t digestbits;
+
+ ret = dst_context_create(key->key, mctx, &ctx);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ /*
+ * If this is a response, digest the query signature.
+ */
+ if (is_response(msg)) {
+ dns_rdata_t querytsigrdata = DNS_RDATA_INIT;
+
+ ret = dns_rdataset_first(msg->querytsig);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+ dns_rdataset_current(msg->querytsig, &querytsigrdata);
+ ret = dns_rdata_tostruct(&querytsigrdata, &querytsig,
+ NULL);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+ isc_buffer_putuint16(&databuf, querytsig.siglen);
+ if (isc_buffer_availablelength(&databuf) <
+ querytsig.siglen) {
+ ret = ISC_R_NOSPACE;
+ goto cleanup_context;
+ }
+ isc_buffer_putmem(&databuf, querytsig.signature,
+ querytsig.siglen);
+ isc_buffer_usedregion(&databuf, &r);
+ ret = dst_context_adddata(ctx, &r);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+ }
+
+ /*
+ * Digest the header.
+ */
+ isc_buffer_init(&headerbuf, header, sizeof(header));
+ dns_message_renderheader(msg, &headerbuf);
+ isc_buffer_usedregion(&headerbuf, &r);
+ ret = dst_context_adddata(ctx, &r);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+
+ /*
+ * Digest the remainder of the message.
+ */
+ isc_buffer_usedregion(msg->buffer, &r);
+ isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
+ ret = dst_context_adddata(ctx, &r);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+
+ if (msg->tcp_continuation == 0) {
+ /*
+ * Digest the name, class, ttl, alg.
+ */
+ dns_name_toregion(&key->name, &r);
+ ret = dst_context_adddata(ctx, &r);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+
+ isc_buffer_clear(&databuf);
+ isc_buffer_putuint16(&databuf, dns_rdataclass_any);
+ isc_buffer_putuint32(&databuf, 0); /* ttl */
+ isc_buffer_usedregion(&databuf, &r);
+ ret = dst_context_adddata(ctx, &r);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+
+ dns_name_toregion(&tsig.algorithm, &r);
+ ret = dst_context_adddata(ctx, &r);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+
+ }
+ /* Digest the timesigned and fudge */
+ isc_buffer_clear(&databuf);
+ if (tsig.error == dns_tsigerror_badtime)
+ tsig.timesigned = querytsig.timesigned;
+ isc_buffer_putuint48(&databuf, tsig.timesigned);
+ isc_buffer_putuint16(&databuf, tsig.fudge);
+ isc_buffer_usedregion(&databuf, &r);
+ ret = dst_context_adddata(ctx, &r);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+
+ if (msg->tcp_continuation == 0) {
+ /*
+ * Digest the error and other data length.
+ */
+ isc_buffer_clear(&databuf);
+ isc_buffer_putuint16(&databuf, tsig.error);
+ isc_buffer_putuint16(&databuf, tsig.otherlen);
+
+ isc_buffer_usedregion(&databuf, &r);
+ ret = dst_context_adddata(ctx, &r);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+
+ /*
+ * Digest the error and other data.
+ */
+ if (tsig.otherlen > 0) {
+ r.length = tsig.otherlen;
+ r.base = tsig.other;
+ ret = dst_context_adddata(ctx, &r);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+ }
+ }
+
+ ret = dst_key_sigsize(key->key, &sigsize);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+ tsig.signature = (unsigned char *) isc_mem_get(mctx, sigsize);
+ if (tsig.signature == NULL) {
+ ret = ISC_R_NOMEMORY;
+ goto cleanup_context;
+ }
+
+ isc_buffer_init(&sigbuf, tsig.signature, sigsize);
+ ret = dst_context_sign(ctx, &sigbuf);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_signature;
+ dst_context_destroy(&ctx);
+ digestbits = dst_key_getbits(key->key);
+ if (digestbits != 0) {
+ unsigned int bytes = (digestbits + 1) / 8;
+ if (is_response(msg) && bytes < querytsig.siglen)
+ bytes = querytsig.siglen;
+ if (bytes > isc_buffer_usedlength(&sigbuf))
+ bytes = isc_buffer_usedlength(&sigbuf);
+ tsig.siglen = bytes;
+ } else
+ tsig.siglen = isc_buffer_usedlength(&sigbuf);
+ } else {
+ tsig.siglen = 0;
+ tsig.signature = NULL;
+ }
+
+ ret = dns_message_gettemprdata(msg, &rdata);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_signature;
+ ret = isc_buffer_allocate(msg->mctx, &dynbuf, 512);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_rdata;
+ ret = dns_rdata_fromstruct(rdata, dns_rdataclass_any,
+ dns_rdatatype_tsig, &tsig, dynbuf);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_dynbuf;
+
+ dns_message_takebuffer(msg, &dynbuf);
+
+ if (tsig.signature != NULL) {
+ isc_mem_put(mctx, tsig.signature, sigsize);
+ tsig.signature = NULL;
+ }
+
+ owner = NULL;
+ ret = dns_message_gettempname(msg, &owner);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_rdata;
+ dns_name_init(owner, NULL);
+ ret = dns_name_dup(&key->name, msg->mctx, owner);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_owner;
+
+ datalist = NULL;
+ ret = dns_message_gettemprdatalist(msg, &datalist);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_owner;
+ dataset = NULL;
+ ret = dns_message_gettemprdataset(msg, &dataset);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_rdatalist;
+ datalist->rdclass = dns_rdataclass_any;
+ datalist->type = dns_rdatatype_tsig;
+ datalist->covers = 0;
+ datalist->ttl = 0;
+ ISC_LIST_INIT(datalist->rdata);
+ ISC_LIST_APPEND(datalist->rdata, rdata, link);
+ dns_rdataset_init(dataset);
+ RUNTIME_CHECK(dns_rdatalist_tordataset(datalist, dataset)
+ == ISC_R_SUCCESS);
+ msg->tsig = dataset;
+ msg->tsigname = owner;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup_rdatalist:
+ dns_message_puttemprdatalist(msg, &datalist);
+ cleanup_owner:
+ dns_message_puttempname(msg, &owner);
+ goto cleanup_rdata;
+ cleanup_dynbuf:
+ isc_buffer_free(&dynbuf);
+ cleanup_rdata:
+ dns_message_puttemprdata(msg, &rdata);
+ cleanup_signature:
+ if (tsig.signature != NULL)
+ isc_mem_put(mctx, tsig.signature, sigsize);
+ cleanup_context:
+ if (ctx != NULL)
+ dst_context_destroy(&ctx);
+ return (ret);
+}
+
+isc_result_t
+dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg,
+ dns_tsig_keyring_t *ring1, dns_tsig_keyring_t *ring2)
+{
+ dns_rdata_any_tsig_t tsig, querytsig;
+ isc_region_t r, source_r, header_r, sig_r;
+ isc_buffer_t databuf;
+ unsigned char data[32];
+ dns_name_t *keyname;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_stdtime_t now;
+ isc_result_t ret;
+ dns_tsigkey_t *tsigkey;
+ dst_key_t *key = NULL;
+ unsigned char header[DNS_MESSAGE_HEADERLEN];
+ dst_context_t *ctx = NULL;
+ isc_mem_t *mctx;
+ isc_uint16_t addcount, id;
+ unsigned int siglen;
+ unsigned int alg;
+
+ REQUIRE(source != NULL);
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ tsigkey = dns_message_gettsigkey(msg);
+
+ REQUIRE(tsigkey == NULL || VALID_TSIG_KEY(tsigkey));
+
+ msg->verify_attempted = 1;
+
+ if (msg->tcp_continuation) {
+ if (tsigkey == NULL || msg->querytsig == NULL)
+ return (DNS_R_UNEXPECTEDTSIG);
+ return (tsig_verify_tcp(source, msg));
+ }
+
+ /*
+ * There should be a TSIG record...
+ */
+ if (msg->tsig == NULL)
+ return (DNS_R_EXPECTEDTSIG);
+
+ /*
+ * If this is a response and there's no key or query TSIG, there
+ * shouldn't be one on the response.
+ */
+ if (is_response(msg) &&
+ (tsigkey == NULL || msg->querytsig == NULL))
+ return (DNS_R_UNEXPECTEDTSIG);
+
+ mctx = msg->mctx;
+
+ /*
+ * If we're here, we know the message is well formed and contains a
+ * TSIG record.
+ */
+
+ keyname = msg->tsigname;
+ ret = dns_rdataset_first(msg->tsig);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+ dns_rdataset_current(msg->tsig, &rdata);
+ ret = dns_rdata_tostruct(&rdata, &tsig, NULL);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+ dns_rdata_reset(&rdata);
+ if (is_response(msg)) {
+ ret = dns_rdataset_first(msg->querytsig);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+ dns_rdataset_current(msg->querytsig, &rdata);
+ ret = dns_rdata_tostruct(&rdata, &querytsig, NULL);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+ }
+
+ /*
+ * Do the key name and algorithm match that of the query?
+ */
+ if (is_response(msg) &&
+ (!dns_name_equal(keyname, &tsigkey->name) ||
+ !dns_name_equal(&tsig.algorithm, &querytsig.algorithm))) {
+ msg->tsigstatus = dns_tsigerror_badkey;
+ tsig_log(msg->tsigkey, 2,
+ "key name and algorithm do not match");
+ return (DNS_R_TSIGVERIFYFAILURE);
+ }
+
+ /*
+ * Get the current time.
+ */
+ isc_stdtime_get(&now);
+
+ /*
+ * Find dns_tsigkey_t based on keyname.
+ */
+ if (tsigkey == NULL) {
+ ret = ISC_R_NOTFOUND;
+ if (ring1 != NULL)
+ ret = dns_tsigkey_find(&tsigkey, keyname,
+ &tsig.algorithm, ring1);
+ if (ret == ISC_R_NOTFOUND && ring2 != NULL)
+ ret = dns_tsigkey_find(&tsigkey, keyname,
+ &tsig.algorithm, ring2);
+ if (ret != ISC_R_SUCCESS) {
+ msg->tsigstatus = dns_tsigerror_badkey;
+ ret = dns_tsigkey_create(keyname, &tsig.algorithm,
+ NULL, 0, ISC_FALSE, NULL,
+ now, now,
+ mctx, NULL, &msg->tsigkey);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+ tsig_log(msg->tsigkey, 2, "unknown key");
+ return (DNS_R_TSIGVERIFYFAILURE);
+ }
+ msg->tsigkey = tsigkey;
+ }
+
+ key = tsigkey->key;
+
+ /*
+ * Is the time ok?
+ */
+ if (now + msg->timeadjust > tsig.timesigned + tsig.fudge) {
+ msg->tsigstatus = dns_tsigerror_badtime;
+ tsig_log(msg->tsigkey, 2, "signature has expired");
+ return (DNS_R_CLOCKSKEW);
+ } else if (now + msg->timeadjust < tsig.timesigned - tsig.fudge) {
+ msg->tsigstatus = dns_tsigerror_badtime;
+ tsig_log(msg->tsigkey, 2, "signature is in the future");
+ return (DNS_R_CLOCKSKEW);
+ }
+
+ /*
+ * Check digest length.
+ */
+ alg = dst_key_alg(key);
+ ret = dst_key_sigsize(key, &siglen);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+ if (alg == DST_ALG_HMACMD5 || alg == DST_ALG_HMACSHA1 ||
+ alg == DST_ALG_HMACSHA224 || alg == DST_ALG_HMACSHA256 ||
+ alg == DST_ALG_HMACSHA384 || alg == DST_ALG_HMACSHA512) {
+ isc_uint16_t digestbits = dst_key_getbits(key);
+ if (tsig.siglen > siglen) {
+ tsig_log(msg->tsigkey, 2, "signature length to big");
+ return (DNS_R_FORMERR);
+ }
+ if (tsig.siglen > 0 &&
+ (tsig.siglen < 10 || tsig.siglen < ((siglen + 1) / 2))) {
+ tsig_log(msg->tsigkey, 2,
+ "signature length below minimum");
+ return (DNS_R_FORMERR);
+ }
+ if (tsig.siglen > 0 && digestbits != 0 &&
+ tsig.siglen < ((digestbits + 1) / 8)) {
+ msg->tsigstatus = dns_tsigerror_badtrunc;
+ tsig_log(msg->tsigkey, 2,
+ "truncated signature length too small");
+ return (DNS_R_TSIGVERIFYFAILURE);
+ }
+ if (tsig.siglen > 0 && digestbits == 0 &&
+ tsig.siglen < siglen) {
+ msg->tsigstatus = dns_tsigerror_badtrunc;
+ tsig_log(msg->tsigkey, 2, "signature length too small");
+ return (DNS_R_TSIGVERIFYFAILURE);
+ }
+ }
+
+ if (tsig.siglen > 0) {
+ sig_r.base = tsig.signature;
+ sig_r.length = tsig.siglen;
+
+ ret = dst_context_create(key, mctx, &ctx);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+
+ if (is_response(msg)) {
+ isc_buffer_init(&databuf, data, sizeof(data));
+ isc_buffer_putuint16(&databuf, querytsig.siglen);
+ isc_buffer_usedregion(&databuf, &r);
+ ret = dst_context_adddata(ctx, &r);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+ if (querytsig.siglen > 0) {
+ r.length = querytsig.siglen;
+ r.base = querytsig.signature;
+ ret = dst_context_adddata(ctx, &r);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+ }
+ }
+
+ /*
+ * Extract the header.
+ */
+ isc_buffer_usedregion(source, &r);
+ memcpy(header, r.base, DNS_MESSAGE_HEADERLEN);
+ isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
+
+ /*
+ * Decrement the additional field counter.
+ */
+ memcpy(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2);
+ addcount = htons((isc_uint16_t)(ntohs(addcount) - 1));
+ memcpy(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2);
+
+ /*
+ * Put in the original id.
+ */
+ id = htons(tsig.originalid);
+ memcpy(&header[0], &id, 2);
+
+ /*
+ * Digest the modified header.
+ */
+ header_r.base = (unsigned char *) header;
+ header_r.length = DNS_MESSAGE_HEADERLEN;
+ ret = dst_context_adddata(ctx, &header_r);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+
+ /*
+ * Digest all non-TSIG records.
+ */
+ isc_buffer_usedregion(source, &source_r);
+ r.base = source_r.base + DNS_MESSAGE_HEADERLEN;
+ r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN;
+ ret = dst_context_adddata(ctx, &r);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+
+ /*
+ * Digest the key name.
+ */
+ dns_name_toregion(&tsigkey->name, &r);
+ ret = dst_context_adddata(ctx, &r);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+
+ isc_buffer_init(&databuf, data, sizeof(data));
+ isc_buffer_putuint16(&databuf, tsig.common.rdclass);
+ isc_buffer_putuint32(&databuf, msg->tsig->ttl);
+ isc_buffer_usedregion(&databuf, &r);
+ ret = dst_context_adddata(ctx, &r);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+
+ /*
+ * Digest the key algorithm.
+ */
+ dns_name_toregion(tsigkey->algorithm, &r);
+ ret = dst_context_adddata(ctx, &r);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+
+ isc_buffer_clear(&databuf);
+ isc_buffer_putuint48(&databuf, tsig.timesigned);
+ isc_buffer_putuint16(&databuf, tsig.fudge);
+ isc_buffer_putuint16(&databuf, tsig.error);
+ isc_buffer_putuint16(&databuf, tsig.otherlen);
+ isc_buffer_usedregion(&databuf, &r);
+ ret = dst_context_adddata(ctx, &r);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+
+ if (tsig.otherlen > 0) {
+ r.base = tsig.other;
+ r.length = tsig.otherlen;
+ ret = dst_context_adddata(ctx, &r);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+ }
+
+ ret = dst_context_verify(ctx, &sig_r);
+ if (ret == DST_R_VERIFYFAILURE) {
+ msg->tsigstatus = dns_tsigerror_badsig;
+ ret = DNS_R_TSIGVERIFYFAILURE;
+ tsig_log(msg->tsigkey, 2,
+ "signature failed to verify(1)");
+ goto cleanup_context;
+ } else if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+
+ dst_context_destroy(&ctx);
+ } else if (tsig.error != dns_tsigerror_badsig &&
+ tsig.error != dns_tsigerror_badkey) {
+ msg->tsigstatus = dns_tsigerror_badsig;
+ tsig_log(msg->tsigkey, 2, "signature was empty");
+ return (DNS_R_TSIGVERIFYFAILURE);
+ }
+
+ msg->tsigstatus = dns_rcode_noerror;
+
+ if (tsig.error != dns_rcode_noerror) {
+ if (tsig.error == dns_tsigerror_badtime)
+ return (DNS_R_CLOCKSKEW);
+ else
+ return (DNS_R_TSIGERRORSET);
+ }
+
+ msg->verified_sig = 1;
+
+ return (ISC_R_SUCCESS);
+
+cleanup_context:
+ if (ctx != NULL)
+ dst_context_destroy(&ctx);
+
+ return (ret);
+}
+
+static isc_result_t
+tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg) {
+ dns_rdata_any_tsig_t tsig, querytsig;
+ isc_region_t r, source_r, header_r, sig_r;
+ isc_buffer_t databuf;
+ unsigned char data[32];
+ dns_name_t *keyname;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_stdtime_t now;
+ isc_result_t ret;
+ dns_tsigkey_t *tsigkey;
+ dst_key_t *key = NULL;
+ unsigned char header[DNS_MESSAGE_HEADERLEN];
+ isc_uint16_t addcount, id;
+ isc_boolean_t has_tsig = ISC_FALSE;
+ isc_mem_t *mctx;
+
+ REQUIRE(source != NULL);
+ REQUIRE(msg != NULL);
+ REQUIRE(dns_message_gettsigkey(msg) != NULL);
+ REQUIRE(msg->tcp_continuation == 1);
+ REQUIRE(msg->querytsig != NULL);
+
+ if (!is_response(msg))
+ return (DNS_R_EXPECTEDRESPONSE);
+
+ mctx = msg->mctx;
+
+ tsigkey = dns_message_gettsigkey(msg);
+
+ /*
+ * Extract and parse the previous TSIG
+ */
+ ret = dns_rdataset_first(msg->querytsig);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+ dns_rdataset_current(msg->querytsig, &rdata);
+ ret = dns_rdata_tostruct(&rdata, &querytsig, NULL);
+ if (ret != ISC_R_SUCCESS)
+ return (ret);
+ dns_rdata_reset(&rdata);
+
+ /*
+ * If there is a TSIG in this message, do some checks.
+ */
+ if (msg->tsig != NULL) {
+ has_tsig = ISC_TRUE;
+
+ keyname = msg->tsigname;
+ ret = dns_rdataset_first(msg->tsig);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_querystruct;
+ dns_rdataset_current(msg->tsig, &rdata);
+ ret = dns_rdata_tostruct(&rdata, &tsig, NULL);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_querystruct;
+
+ /*
+ * Do the key name and algorithm match that of the query?
+ */
+ if (!dns_name_equal(keyname, &tsigkey->name) ||
+ !dns_name_equal(&tsig.algorithm, &querytsig.algorithm)) {
+ msg->tsigstatus = dns_tsigerror_badkey;
+ ret = DNS_R_TSIGVERIFYFAILURE;
+ tsig_log(msg->tsigkey, 2,
+ "key name and algorithm do not match");
+ goto cleanup_querystruct;
+ }
+
+ /*
+ * Is the time ok?
+ */
+ isc_stdtime_get(&now);
+
+ if (now + msg->timeadjust > tsig.timesigned + tsig.fudge) {
+ msg->tsigstatus = dns_tsigerror_badtime;
+ tsig_log(msg->tsigkey, 2, "signature has expired");
+ ret = DNS_R_CLOCKSKEW;
+ goto cleanup_querystruct;
+ } else if (now + msg->timeadjust <
+ tsig.timesigned - tsig.fudge) {
+ msg->tsigstatus = dns_tsigerror_badtime;
+ tsig_log(msg->tsigkey, 2,
+ "signature is in the future");
+ ret = DNS_R_CLOCKSKEW;
+ goto cleanup_querystruct;
+ }
+ }
+
+ key = tsigkey->key;
+
+ if (msg->tsigctx == NULL) {
+ ret = dst_context_create(key, mctx, &msg->tsigctx);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_querystruct;
+
+ /*
+ * Digest the length of the query signature
+ */
+ isc_buffer_init(&databuf, data, sizeof(data));
+ isc_buffer_putuint16(&databuf, querytsig.siglen);
+ isc_buffer_usedregion(&databuf, &r);
+ ret = dst_context_adddata(msg->tsigctx, &r);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+
+ /*
+ * Digest the data of the query signature
+ */
+ if (querytsig.siglen > 0) {
+ r.length = querytsig.siglen;
+ r.base = querytsig.signature;
+ ret = dst_context_adddata(msg->tsigctx, &r);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+ }
+ }
+
+ /*
+ * Extract the header.
+ */
+ isc_buffer_usedregion(source, &r);
+ memcpy(header, r.base, DNS_MESSAGE_HEADERLEN);
+ isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
+
+ /*
+ * Decrement the additional field counter if necessary.
+ */
+ if (has_tsig) {
+ memcpy(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2);
+ addcount = htons((isc_uint16_t)(ntohs(addcount) - 1));
+ memcpy(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2);
+ }
+
+ /*
+ * Put in the original id.
+ */
+ /* XXX Can TCP transfers be forwarded? How would that work? */
+ if (has_tsig) {
+ id = htons(tsig.originalid);
+ memcpy(&header[0], &id, 2);
+ }
+
+ /*
+ * Digest the modified header.
+ */
+ header_r.base = (unsigned char *) header;
+ header_r.length = DNS_MESSAGE_HEADERLEN;
+ ret = dst_context_adddata(msg->tsigctx, &header_r);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+
+ /*
+ * Digest all non-TSIG records.
+ */
+ isc_buffer_usedregion(source, &source_r);
+ r.base = source_r.base + DNS_MESSAGE_HEADERLEN;
+ if (has_tsig)
+ r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN;
+ else
+ r.length = source_r.length - DNS_MESSAGE_HEADERLEN;
+ ret = dst_context_adddata(msg->tsigctx, &r);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+
+ /*
+ * Digest the time signed and fudge.
+ */
+ if (has_tsig) {
+ isc_buffer_init(&databuf, data, sizeof(data));
+ isc_buffer_putuint48(&databuf, tsig.timesigned);
+ isc_buffer_putuint16(&databuf, tsig.fudge);
+ isc_buffer_usedregion(&databuf, &r);
+ ret = dst_context_adddata(msg->tsigctx, &r);
+ if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+
+ sig_r.base = tsig.signature;
+ sig_r.length = tsig.siglen;
+ if (tsig.siglen == 0) {
+ if (tsig.error != dns_rcode_noerror) {
+ if (tsig.error == dns_tsigerror_badtime)
+ ret = DNS_R_CLOCKSKEW;
+ else
+ ret = DNS_R_TSIGERRORSET;
+ } else {
+ tsig_log(msg->tsigkey, 2,
+ "signature is empty");
+ ret = DNS_R_TSIGVERIFYFAILURE;
+ }
+ goto cleanup_context;
+ }
+
+ ret = dst_context_verify(msg->tsigctx, &sig_r);
+ if (ret == DST_R_VERIFYFAILURE) {
+ msg->tsigstatus = dns_tsigerror_badsig;
+ tsig_log(msg->tsigkey, 2,
+ "signature failed to verify(2)");
+ ret = DNS_R_TSIGVERIFYFAILURE;
+ goto cleanup_context;
+ }
+ else if (ret != ISC_R_SUCCESS)
+ goto cleanup_context;
+
+ dst_context_destroy(&msg->tsigctx);
+ }
+
+ msg->tsigstatus = dns_rcode_noerror;
+ return (ISC_R_SUCCESS);
+
+ cleanup_context:
+ dst_context_destroy(&msg->tsigctx);
+
+ cleanup_querystruct:
+ dns_rdata_freestruct(&querytsig);
+
+ return (ret);
+
+}
+
+isc_result_t
+dns_tsigkey_find(dns_tsigkey_t **tsigkey, dns_name_t *name,
+ dns_name_t *algorithm, dns_tsig_keyring_t *ring)
+{
+ dns_tsigkey_t *key;
+ isc_stdtime_t now;
+ isc_result_t result;
+
+ REQUIRE(tsigkey != NULL);
+ REQUIRE(*tsigkey == NULL);
+ REQUIRE(name != NULL);
+ REQUIRE(ring != NULL);
+
+ RWLOCK(&ring->lock, isc_rwlocktype_write);
+ cleanup_ring(ring);
+ RWUNLOCK(&ring->lock, isc_rwlocktype_write);
+
+ isc_stdtime_get(&now);
+ RWLOCK(&ring->lock, isc_rwlocktype_read);
+ key = NULL;
+ result = dns_rbt_findname(ring->keys, name, 0, NULL, (void *)&key);
+ if (result == DNS_R_PARTIALMATCH || result == ISC_R_NOTFOUND) {
+ RWUNLOCK(&ring->lock, isc_rwlocktype_read);
+ return (ISC_R_NOTFOUND);
+ }
+ if (algorithm != NULL && !dns_name_equal(key->algorithm, algorithm)) {
+ RWUNLOCK(&ring->lock, isc_rwlocktype_read);
+ return (ISC_R_NOTFOUND);
+ }
+ if (key->inception != key->expire && key->expire < now) {
+ /*
+ * The key has expired.
+ */
+ RWUNLOCK(&ring->lock, isc_rwlocktype_read);
+ RWLOCK(&ring->lock, isc_rwlocktype_write);
+ (void)dns_rbt_deletename(ring->keys, name, ISC_FALSE);
+ RWUNLOCK(&ring->lock, isc_rwlocktype_write);
+ return (ISC_R_NOTFOUND);
+ }
+
+ isc_refcount_increment(&key->refs, NULL);
+ RWUNLOCK(&ring->lock, isc_rwlocktype_read);
+ *tsigkey = key;
+ return (ISC_R_SUCCESS);
+}
+
+static void
+free_tsignode(void *node, void *_unused) {
+ dns_tsigkey_t *key;
+
+ UNUSED(_unused);
+
+ REQUIRE(node != NULL);
+
+ key = node;
+ dns_tsigkey_detach(&key);
+}
+
+isc_result_t
+dns_tsigkeyring_create(isc_mem_t *mctx, dns_tsig_keyring_t **ringp) {
+ isc_result_t result;
+ dns_tsig_keyring_t *ring;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(ringp != NULL);
+ REQUIRE(*ringp == NULL);
+
+ ring = isc_mem_get(mctx, sizeof(dns_tsig_keyring_t));
+ if (ring == NULL)
+ return (ISC_R_NOMEMORY);
+
+ result = isc_rwlock_init(&ring->lock, 0, 0);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, ring, sizeof(dns_tsig_keyring_t));
+ return (result);
+ }
+
+ ring->keys = NULL;
+ result = dns_rbt_create(mctx, free_tsignode, NULL, &ring->keys);
+ if (result != ISC_R_SUCCESS) {
+ isc_rwlock_destroy(&ring->lock);
+ isc_mem_put(mctx, ring, sizeof(dns_tsig_keyring_t));
+ return (result);
+ }
+
+ ring->writecount = 0;
+ ring->mctx = NULL;
+ isc_mem_attach(mctx, &ring->mctx);
+
+ *ringp = ring;
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_tsigkeyring_destroy(dns_tsig_keyring_t **ringp) {
+ dns_tsig_keyring_t *ring;
+
+ REQUIRE(ringp != NULL);
+ REQUIRE(*ringp != NULL);
+
+ ring = *ringp;
+ *ringp = NULL;
+
+ dns_rbt_destroy(&ring->keys);
+ isc_rwlock_destroy(&ring->lock);
+ isc_mem_putanddetach(&ring->mctx, ring, sizeof(dns_tsig_keyring_t));
+}
diff --git a/lib/dns/ttl.c b/lib/dns/ttl.c
new file mode 100644
index 0000000..9d0dec5
--- /dev/null
+++ b/lib/dns/ttl.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: ttl.c,v 1.29 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <ctype.h>
+#include <errno.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/string.h>
+#include <isc/util.h>
+
+#include <dns/result.h>
+#include <dns/ttl.h>
+
+#define RETERR(x) do { \
+ isc_result_t _r = (x); \
+ if (_r != ISC_R_SUCCESS) \
+ return (_r); \
+ } while (0)
+
+
+static isc_result_t bind_ttl(isc_textregion_t *source, isc_uint32_t *ttl);
+
+/*
+ * Helper for dns_ttl_totext().
+ */
+static isc_result_t
+ttlfmt(unsigned int t, const char *s, isc_boolean_t verbose,
+ isc_boolean_t space, isc_buffer_t *target)
+{
+ char tmp[60];
+ size_t len;
+ isc_region_t region;
+
+ if (verbose)
+ len = snprintf(tmp, sizeof(tmp), "%s%u %s%s",
+ space ? " " : "",
+ t, s,
+ t == 1 ? "" : "s");
+ else
+ len = snprintf(tmp, sizeof(tmp), "%u%c", t, s[0]);
+
+ INSIST(len + 1 <= sizeof(tmp));
+ isc_buffer_availableregion(target, &region);
+ if (len > region.length)
+ return (ISC_R_NOSPACE);
+ memcpy(region.base, tmp, len);
+ isc_buffer_add(target, len);
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Derived from bind8 ns_format_ttl().
+ */
+isc_result_t
+dns_ttl_totext(isc_uint32_t src, isc_boolean_t verbose, isc_buffer_t *target) {
+ unsigned secs, mins, hours, days, weeks, x;
+
+ secs = src % 60; src /= 60;
+ mins = src % 60; src /= 60;
+ hours = src % 24; src /= 24;
+ days = src % 7; src /= 7;
+ weeks = src; src = 0;
+
+ x = 0;
+ if (weeks != 0) {
+ RETERR(ttlfmt(weeks, "week", verbose, ISC_TF(x > 0), target));
+ x++;
+ }
+ if (days != 0) {
+ RETERR(ttlfmt(days, "day", verbose, ISC_TF(x > 0), target));
+ x++;
+ }
+ if (hours != 0) {
+ RETERR(ttlfmt(hours, "hour", verbose, ISC_TF(x > 0), target));
+ x++;
+ }
+ if (mins != 0) {
+ RETERR(ttlfmt(mins, "minute", verbose, ISC_TF(x > 0), target));
+ x++;
+ }
+ if (secs != 0 ||
+ (weeks == 0 && days == 0 && hours == 0 && mins == 0)) {
+ RETERR(ttlfmt(secs, "second", verbose, ISC_TF(x > 0), target));
+ x++;
+ }
+ INSIST (x > 0);
+ /*
+ * If only a single unit letter is printed, print it
+ * in upper case. (Why? Because BIND 8 does that.
+ * Presumably it has a reason.)
+ */
+ if (x == 1 && !verbose) {
+ isc_region_t region;
+ /*
+ * The unit letter is the last character in the
+ * used region of the buffer.
+ *
+ * toupper() does not need its argument to be masked of cast
+ * here because region.base is type unsigned char *.
+ */
+ isc_buffer_usedregion(target, &region);
+ region.base[region.length - 1] =
+ toupper(region.base[region.length - 1]);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_counter_fromtext(isc_textregion_t *source, isc_uint32_t *ttl) {
+ return (bind_ttl(source, ttl));
+}
+
+isc_result_t
+dns_ttl_fromtext(isc_textregion_t *source, isc_uint32_t *ttl) {
+ isc_result_t result;
+
+ result = bind_ttl(source, ttl);
+ if (result != ISC_R_SUCCESS)
+ result = DNS_R_BADTTL;
+ return (result);
+}
+
+static isc_result_t
+bind_ttl(isc_textregion_t *source, isc_uint32_t *ttl) {
+ isc_uint32_t tmp = 0;
+ isc_uint32_t n;
+ char *s;
+ char buf[64];
+ char nbuf[64]; /* Number buffer */
+
+ /*
+ * Copy the buffer as it may not be NULL terminated.
+ * No legal counter / ttl is longer that 63 characters.
+ */
+ if (source->length > sizeof(buf) - 1)
+ return (DNS_R_SYNTAX);
+ strncpy(buf, source->base, source->length);
+ buf[source->length] = '\0';
+ s = buf;
+
+ do {
+ isc_result_t result;
+
+ char *np = nbuf;
+ while (*s != '\0' && isdigit((unsigned char)*s))
+ *np++ = *s++;
+ *np++ = '\0';
+ INSIST(np - nbuf <= (int)sizeof(nbuf));
+ result = isc_parse_uint32(&n, nbuf, 10);
+ if (result != ISC_R_SUCCESS)
+ return (DNS_R_SYNTAX);
+ switch (*s) {
+ case 'w':
+ case 'W':
+ tmp += n * 7 * 24 * 3600;
+ s++;
+ break;
+ case 'd':
+ case 'D':
+ tmp += n * 24 * 3600;
+ s++;
+ break;
+ case 'h':
+ case 'H':
+ tmp += n * 3600;
+ s++;
+ break;
+ case 'm':
+ case 'M':
+ tmp += n * 60;
+ s++;
+ break;
+ case 's':
+ case 'S':
+ tmp += n;
+ s++;
+ break;
+ case '\0':
+ /* Plain number? */
+ if (tmp != 0)
+ return (DNS_R_SYNTAX);
+ tmp = n;
+ break;
+ default:
+ return (DNS_R_SYNTAX);
+ }
+ } while (*s != '\0');
+ *ttl = tmp;
+ return (ISC_R_SUCCESS);
+}
diff --git a/lib/dns/validator.c b/lib/dns/validator.c
new file mode 100644
index 0000000..39c17d5
--- /dev/null
+++ b/lib/dns/validator.c
@@ -0,0 +1,3776 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: validator.c,v 1.164 2008/11/14 23:47:33 tbox Exp $ */
+
+#include <config.h>
+
+#include <isc/base32.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/sha2.h>
+#include <isc/string.h>
+#include <isc/task.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/ds.h>
+#include <dns/dnssec.h>
+#include <dns/events.h>
+#include <dns/keytable.h>
+#include <dns/log.h>
+#include <dns/message.h>
+#include <dns/ncache.h>
+#include <dns/nsec.h>
+#include <dns/nsec3.h>
+#include <dns/rdata.h>
+#include <dns/rdatastruct.h>
+#include <dns/rdataset.h>
+#include <dns/rdatatype.h>
+#include <dns/resolver.h>
+#include <dns/result.h>
+#include <dns/validator.h>
+#include <dns/view.h>
+
+/*! \file
+ * \brief
+ * Basic processing sequences.
+ *
+ * \li When called with rdataset and sigrdataset:
+ * validator_start -> validate -> proveunsecure -> startfinddlvsep ->
+ * dlv_validator_start -> validator_start -> validate -> proveunsecure
+ *
+ * validator_start -> validate -> nsecvalidate (secure wildcard answer)
+ *
+ * \li When called with rdataset, sigrdataset and with DNS_VALIDATOR_DLV:
+ * validator_start -> startfinddlvsep -> dlv_validator_start ->
+ * validator_start -> validate -> proveunsecure
+ *
+ * \li When called with rdataset:
+ * validator_start -> proveunsecure -> startfinddlvsep ->
+ * dlv_validator_start -> validator_start -> proveunsecure
+ *
+ * \li When called with rdataset and with DNS_VALIDATOR_DLV:
+ * validator_start -> startfinddlvsep -> dlv_validator_start ->
+ * validator_start -> proveunsecure
+ *
+ * \li When called without a rdataset:
+ * validator_start -> nsecvalidate -> proveunsecure -> startfinddlvsep ->
+ * dlv_validator_start -> validator_start -> nsecvalidate -> proveunsecure
+ *
+ * Note: there isn't a case for DNS_VALIDATOR_DLV here as we want nsecvalidate()
+ * to always validate the authority section even when it does not contain
+ * signatures.
+ *
+ * validator_start: determines what type of validation to do.
+ * validate: attempts to perform a positive validation.
+ * proveunsecure: attempts to prove the answer comes from a unsecure zone.
+ * nsecvalidate: attempts to prove a negative response.
+ * startfinddlvsep: starts the DLV record lookup.
+ * dlv_validator_start: resets state and restarts the lookup using the
+ * DLV RRset found by startfinddlvsep.
+ */
+
+#define VALIDATOR_MAGIC ISC_MAGIC('V', 'a', 'l', '?')
+#define VALID_VALIDATOR(v) ISC_MAGIC_VALID(v, VALIDATOR_MAGIC)
+
+#define VALATTR_SHUTDOWN 0x0001 /*%< Shutting down. */
+#define VALATTR_CANCELED 0x0002 /*%< Cancelled. */
+#define VALATTR_TRIEDVERIFY 0x0004 /*%< We have found a key and
+ * have attempted a verify. */
+#define VALATTR_INSECURITY 0x0010 /*%< Attempting proveunsecure. */
+#define VALATTR_DLVTRIED 0x0020 /*%< Looked for a DLV record. */
+
+/*!
+ * NSEC proofs to be looked for.
+ */
+#define VALATTR_NEEDNOQNAME 0x00000100
+#define VALATTR_NEEDNOWILDCARD 0x00000200
+#define VALATTR_NEEDNODATA 0x00000400
+
+/*!
+ * NSEC proofs that have been found.
+ */
+#define VALATTR_FOUNDNOQNAME 0x00001000
+#define VALATTR_FOUNDNOWILDCARD 0x00002000
+#define VALATTR_FOUNDNODATA 0x00004000
+#define VALATTR_FOUNDCLOSEST 0x00008000
+
+/*
+ *
+ */
+#define VALATTR_FOUNDOPTOUT 0x00010000
+#define VALATTR_FOUNDUNKNOWN 0x00020000
+
+#define NEEDNODATA(val) ((val->attributes & VALATTR_NEEDNODATA) != 0)
+#define NEEDNOQNAME(val) ((val->attributes & VALATTR_NEEDNOQNAME) != 0)
+#define NEEDNOWILDCARD(val) ((val->attributes & VALATTR_NEEDNOWILDCARD) != 0)
+#define DLVTRIED(val) ((val->attributes & VALATTR_DLVTRIED) != 0)
+
+#define SHUTDOWN(v) (((v)->attributes & VALATTR_SHUTDOWN) != 0)
+#define CANCELED(v) (((v)->attributes & VALATTR_CANCELED) != 0)
+
+static void
+destroy(dns_validator_t *val);
+
+static isc_result_t
+get_dst_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo,
+ dns_rdataset_t *rdataset);
+
+static isc_result_t
+validate(dns_validator_t *val, isc_boolean_t resume);
+
+static isc_result_t
+validatezonekey(dns_validator_t *val);
+
+static isc_result_t
+nsecvalidate(dns_validator_t *val, isc_boolean_t resume);
+
+static isc_result_t
+proveunsecure(dns_validator_t *val, isc_boolean_t have_ds,
+ isc_boolean_t resume);
+
+static void
+validator_logv(dns_validator_t *val, isc_logcategory_t *category,
+ isc_logmodule_t *module, int level, const char *fmt, va_list ap)
+ ISC_FORMAT_PRINTF(5, 0);
+
+static void
+validator_log(dns_validator_t *val, int level, const char *fmt, ...)
+ ISC_FORMAT_PRINTF(3, 4);
+
+static void
+validator_logcreate(dns_validator_t *val,
+ dns_name_t *name, dns_rdatatype_t type,
+ const char *caller, const char *operation);
+
+static isc_result_t
+dlv_validatezonekey(dns_validator_t *val);
+
+static void
+dlv_validator_start(dns_validator_t *val);
+
+static isc_result_t
+finddlvsep(dns_validator_t *val, isc_boolean_t resume);
+
+static isc_result_t
+startfinddlvsep(dns_validator_t *val, dns_name_t *unsecure);
+
+/*%
+ * Mark the RRsets as a answer.
+ */
+static inline void
+markanswer(dns_validator_t *val) {
+ validator_log(val, ISC_LOG_DEBUG(3), "marking as answer");
+ if (val->event->rdataset != NULL)
+ val->event->rdataset->trust = dns_trust_answer;
+ if (val->event->sigrdataset != NULL)
+ val->event->sigrdataset->trust = dns_trust_answer;
+}
+
+static void
+validator_done(dns_validator_t *val, isc_result_t result) {
+ isc_task_t *task;
+
+ if (val->event == NULL)
+ return;
+
+ /*
+ * Caller must be holding the lock.
+ */
+
+ val->event->result = result;
+ task = val->event->ev_sender;
+ val->event->ev_sender = val;
+ val->event->ev_type = DNS_EVENT_VALIDATORDONE;
+ val->event->ev_action = val->action;
+ val->event->ev_arg = val->arg;
+ isc_task_sendanddetach(&task, (isc_event_t **)&val->event);
+}
+
+static inline isc_boolean_t
+exit_check(dns_validator_t *val) {
+ /*
+ * Caller must be holding the lock.
+ */
+ if (!SHUTDOWN(val))
+ return (ISC_FALSE);
+
+ INSIST(val->event == NULL);
+
+ if (val->fetch != NULL || val->subvalidator != NULL)
+ return (ISC_FALSE);
+
+ return (ISC_TRUE);
+}
+
+/*%
+ * Look in the NSEC record returned from a DS query to see if there is
+ * a NS RRset at this name. If it is found we are at a delegation point.
+ */
+static isc_boolean_t
+isdelegation(dns_name_t *name, dns_rdataset_t *rdataset,
+ isc_result_t dbresult)
+{
+ dns_label_t hashlabel;
+ dns_name_t nsec3name;
+ dns_rdata_nsec3_t nsec3;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_t set;
+ int order;
+ int scope;
+ isc_boolean_t found;
+ isc_buffer_t buffer;
+ isc_result_t result;
+ unsigned char hash[NSEC3_MAX_HASH_LENGTH];
+ unsigned char owner[NSEC3_MAX_HASH_LENGTH];
+ unsigned int length;
+
+ REQUIRE(dbresult == DNS_R_NXRRSET || dbresult == DNS_R_NCACHENXRRSET);
+
+ dns_rdataset_init(&set);
+ if (dbresult == DNS_R_NXRRSET)
+ dns_rdataset_clone(rdataset, &set);
+ else {
+ result = dns_ncache_getrdataset(rdataset, name,
+ dns_rdatatype_nsec, &set);
+ if (result == ISC_R_NOTFOUND)
+ goto trynsec3;
+ if (result != ISC_R_SUCCESS)
+ return (ISC_FALSE);
+ }
+
+ INSIST(set.type == dns_rdatatype_nsec);
+
+ found = ISC_FALSE;
+ result = dns_rdataset_first(&set);
+ if (result == ISC_R_SUCCESS) {
+ dns_rdataset_current(&set, &rdata);
+ found = dns_nsec_typepresent(&rdata, dns_rdatatype_ns);
+ dns_rdata_reset(&rdata);
+ }
+ dns_rdataset_disassociate(&set);
+ return (found);
+
+ trynsec3:
+ /*
+ * Iterate over the ncache entry.
+ */
+ found = ISC_FALSE;
+ dns_name_init(&nsec3name, NULL);
+ result = dns_rdataset_first(rdataset);
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset))
+ {
+ dns_ncache_current(rdataset, &nsec3name, &set);
+ if (set.type != dns_rdatatype_nsec3) {
+ dns_rdataset_disassociate(&set);
+ continue;
+ }
+ dns_name_getlabel(&nsec3name, 0, &hashlabel);
+ isc_region_consume(&hashlabel, 1);
+ isc_buffer_init(&buffer, owner, sizeof(owner));
+ result = isc_base32hex_decoderegion(&hashlabel, &buffer);
+ if (result != ISC_R_SUCCESS) {
+ dns_rdataset_disassociate(&set);
+ continue;
+ }
+ for (result = dns_rdataset_first(&set);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&set))
+ {
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(&set, &rdata);
+ (void)dns_rdata_tostruct(&rdata, &nsec3, NULL);
+ if (nsec3.hash != 1)
+ continue;
+ length = isc_iterated_hash(hash, nsec3.hash,
+ nsec3.iterations, nsec3.salt,
+ nsec3.salt_length,
+ name->ndata, name->length);
+ if (length != isc_buffer_usedlength(&buffer))
+ continue;
+ order = memcmp(hash, owner, length);
+ if (order == 0) {
+ found = dns_nsec3_typepresent(&rdata,
+ dns_rdatatype_ns);
+ dns_rdataset_disassociate(&set);
+ return (found);
+ }
+ if ((nsec3.flags & DNS_NSEC3FLAG_OPTOUT) == 0)
+ continue;
+ /*
+ * Does this optout span cover the name?
+ */
+ scope = memcmp(owner, nsec3.next, nsec3.next_length);
+ if ((scope < 0 && order > 0 &&
+ memcmp(hash, nsec3.next, length) < 0) ||
+ (scope >= 0 && (order > 0 ||
+ memcmp(hash, nsec3.next, length) < 0)))
+ {
+ dns_rdataset_disassociate(&set);
+ return (ISC_TRUE);
+ }
+ }
+ dns_rdataset_disassociate(&set);
+ }
+ return (found);
+}
+
+/*%
+ * We have been asked to to look for a key.
+ * If found resume the validation process.
+ * If not found fail the validation process.
+ */
+static void
+fetch_callback_validator(isc_task_t *task, isc_event_t *event) {
+ dns_fetchevent_t *devent;
+ dns_validator_t *val;
+ dns_rdataset_t *rdataset;
+ isc_boolean_t want_destroy;
+ isc_result_t result;
+ isc_result_t eresult;
+
+ UNUSED(task);
+ INSIST(event->ev_type == DNS_EVENT_FETCHDONE);
+ devent = (dns_fetchevent_t *)event;
+ val = devent->ev_arg;
+ rdataset = &val->frdataset;
+ eresult = devent->result;
+
+ /* Free resources which are not of interest. */
+ if (devent->node != NULL)
+ dns_db_detachnode(devent->db, &devent->node);
+ if (devent->db != NULL)
+ dns_db_detach(&devent->db);
+ if (dns_rdataset_isassociated(&val->fsigrdataset))
+ dns_rdataset_disassociate(&val->fsigrdataset);
+ isc_event_free(&event);
+ dns_resolver_destroyfetch(&val->fetch);
+
+ INSIST(val->event != NULL);
+
+ validator_log(val, ISC_LOG_DEBUG(3), "in fetch_callback_validator");
+ LOCK(&val->lock);
+ if (CANCELED(val)) {
+ validator_done(val, ISC_R_CANCELED);
+ } else if (eresult == ISC_R_SUCCESS) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "keyset with trust %d", rdataset->trust);
+ /*
+ * Only extract the dst key if the keyset is secure.
+ */
+ if (rdataset->trust >= dns_trust_secure) {
+ result = get_dst_key(val, val->siginfo, rdataset);
+ if (result == ISC_R_SUCCESS)
+ val->keyset = &val->frdataset;
+ }
+ result = validate(val, ISC_TRUE);
+ if (result != DNS_R_WAIT)
+ validator_done(val, result);
+ } else {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "fetch_callback_validator: got %s",
+ isc_result_totext(eresult));
+ if (eresult == ISC_R_CANCELED)
+ validator_done(val, eresult);
+ else
+ validator_done(val, DNS_R_NOVALIDKEY);
+ }
+ want_destroy = exit_check(val);
+ UNLOCK(&val->lock);
+ if (want_destroy)
+ destroy(val);
+}
+
+/*%
+ * We were asked to look for a DS record as part of following a key chain
+ * upwards. If found resume the validation process. If not found fail the
+ * validation process.
+ */
+static void
+dsfetched(isc_task_t *task, isc_event_t *event) {
+ dns_fetchevent_t *devent;
+ dns_validator_t *val;
+ dns_rdataset_t *rdataset;
+ isc_boolean_t want_destroy;
+ isc_result_t result;
+ isc_result_t eresult;
+
+ UNUSED(task);
+ INSIST(event->ev_type == DNS_EVENT_FETCHDONE);
+ devent = (dns_fetchevent_t *)event;
+ val = devent->ev_arg;
+ rdataset = &val->frdataset;
+ eresult = devent->result;
+
+ /* Free resources which are not of interest. */
+ if (devent->node != NULL)
+ dns_db_detachnode(devent->db, &devent->node);
+ if (devent->db != NULL)
+ dns_db_detach(&devent->db);
+ if (dns_rdataset_isassociated(&val->fsigrdataset))
+ dns_rdataset_disassociate(&val->fsigrdataset);
+ isc_event_free(&event);
+ dns_resolver_destroyfetch(&val->fetch);
+
+ INSIST(val->event != NULL);
+
+ validator_log(val, ISC_LOG_DEBUG(3), "in dsfetched");
+ LOCK(&val->lock);
+ if (CANCELED(val)) {
+ validator_done(val, ISC_R_CANCELED);
+ } else if (eresult == ISC_R_SUCCESS) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "dsset with trust %d", rdataset->trust);
+ val->dsset = &val->frdataset;
+ result = validatezonekey(val);
+ if (result != DNS_R_WAIT)
+ validator_done(val, result);
+ } else if (eresult == DNS_R_NXRRSET ||
+ eresult == DNS_R_NCACHENXRRSET ||
+ eresult == DNS_R_SERVFAIL) /* RFC 1034 parent? */
+ {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "falling back to insecurity proof (%s)",
+ dns_result_totext(eresult));
+ val->attributes |= VALATTR_INSECURITY;
+ result = proveunsecure(val, ISC_FALSE, ISC_FALSE);
+ if (result != DNS_R_WAIT)
+ validator_done(val, result);
+ } else {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "dsfetched: got %s",
+ isc_result_totext(eresult));
+ if (eresult == ISC_R_CANCELED)
+ validator_done(val, eresult);
+ else
+ validator_done(val, DNS_R_NOVALIDDS);
+ }
+ want_destroy = exit_check(val);
+ UNLOCK(&val->lock);
+ if (want_destroy)
+ destroy(val);
+}
+
+/*%
+ * We were asked to look for the DS record as part of proving that a
+ * name is unsecure.
+ *
+ * If the DS record doesn't exist and the query name corresponds to
+ * a delegation point we are transitioning from a secure zone to a
+ * unsecure zone.
+ *
+ * If the DS record exists it will be secure. We can continue looking
+ * for the break point in the chain of trust.
+ */
+static void
+dsfetched2(isc_task_t *task, isc_event_t *event) {
+ dns_fetchevent_t *devent;
+ dns_validator_t *val;
+ dns_name_t *tname;
+ isc_boolean_t want_destroy;
+ isc_result_t result;
+ isc_result_t eresult;
+
+ UNUSED(task);
+ INSIST(event->ev_type == DNS_EVENT_FETCHDONE);
+ devent = (dns_fetchevent_t *)event;
+ val = devent->ev_arg;
+ eresult = devent->result;
+
+ /* Free resources which are not of interest. */
+ if (devent->node != NULL)
+ dns_db_detachnode(devent->db, &devent->node);
+ if (devent->db != NULL)
+ dns_db_detach(&devent->db);
+ if (dns_rdataset_isassociated(&val->fsigrdataset))
+ dns_rdataset_disassociate(&val->fsigrdataset);
+ dns_resolver_destroyfetch(&val->fetch);
+
+ INSIST(val->event != NULL);
+
+ validator_log(val, ISC_LOG_DEBUG(3), "in dsfetched2: %s",
+ dns_result_totext(eresult));
+ LOCK(&val->lock);
+ if (CANCELED(val)) {
+ validator_done(val, ISC_R_CANCELED);
+ } else if (eresult == DNS_R_NXRRSET || eresult == DNS_R_NCACHENXRRSET) {
+ /*
+ * There is no DS. If this is a delegation, we're done.
+ */
+ tname = dns_fixedname_name(&devent->foundname);
+ if (isdelegation(tname, &val->frdataset, eresult)) {
+ if (val->mustbesecure) {
+ validator_log(val, ISC_LOG_WARNING,
+ "must be secure failure");
+ validator_done(val, DNS_R_MUSTBESECURE);
+ } else if (val->view->dlv == NULL || DLVTRIED(val)) {
+ markanswer(val);
+ validator_done(val, ISC_R_SUCCESS);
+ } else {
+ result = startfinddlvsep(val, tname);
+ if (result != DNS_R_WAIT)
+ validator_done(val, result);
+ }
+ } else {
+ result = proveunsecure(val, ISC_FALSE, ISC_TRUE);
+ if (result != DNS_R_WAIT)
+ validator_done(val, result);
+ }
+ } else if (eresult == ISC_R_SUCCESS ||
+ eresult == DNS_R_NXDOMAIN ||
+ eresult == DNS_R_NCACHENXDOMAIN)
+ {
+ /*
+ * There is a DS which may or may not be a zone cut.
+ * In either case we are still in a secure zone resume
+ * validation.
+ */
+ result = proveunsecure(val, ISC_TF(eresult == ISC_R_SUCCESS),
+ ISC_TRUE);
+ if (result != DNS_R_WAIT)
+ validator_done(val, result);
+ } else {
+ if (eresult == ISC_R_CANCELED)
+ validator_done(val, eresult);
+ else
+ validator_done(val, DNS_R_NOVALIDDS);
+ }
+ isc_event_free(&event);
+ want_destroy = exit_check(val);
+ UNLOCK(&val->lock);
+ if (want_destroy)
+ destroy(val);
+}
+
+/*%
+ * Callback from when a DNSKEY RRset has been validated.
+ *
+ * Resumes the stalled validation process.
+ */
+static void
+keyvalidated(isc_task_t *task, isc_event_t *event) {
+ dns_validatorevent_t *devent;
+ dns_validator_t *val;
+ isc_boolean_t want_destroy;
+ isc_result_t result;
+ isc_result_t eresult;
+
+ UNUSED(task);
+ INSIST(event->ev_type == DNS_EVENT_VALIDATORDONE);
+
+ devent = (dns_validatorevent_t *)event;
+ val = devent->ev_arg;
+ eresult = devent->result;
+
+ isc_event_free(&event);
+ dns_validator_destroy(&val->subvalidator);
+
+ INSIST(val->event != NULL);
+
+ validator_log(val, ISC_LOG_DEBUG(3), "in keyvalidated");
+ LOCK(&val->lock);
+ if (CANCELED(val)) {
+ validator_done(val, ISC_R_CANCELED);
+ } else if (eresult == ISC_R_SUCCESS) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "keyset with trust %d", val->frdataset.trust);
+ /*
+ * Only extract the dst key if the keyset is secure.
+ */
+ if (val->frdataset.trust >= dns_trust_secure)
+ (void) get_dst_key(val, val->siginfo, &val->frdataset);
+ result = validate(val, ISC_TRUE);
+ if (result != DNS_R_WAIT)
+ validator_done(val, result);
+ } else {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "keyvalidated: got %s",
+ isc_result_totext(eresult));
+ validator_done(val, eresult);
+ }
+ want_destroy = exit_check(val);
+ UNLOCK(&val->lock);
+ if (want_destroy)
+ destroy(val);
+}
+
+/*%
+ * Callback when the DS record has been validated.
+ *
+ * Resumes validation of the zone key or the unsecure zone proof.
+ */
+static void
+dsvalidated(isc_task_t *task, isc_event_t *event) {
+ dns_validatorevent_t *devent;
+ dns_validator_t *val;
+ isc_boolean_t want_destroy;
+ isc_result_t result;
+ isc_result_t eresult;
+
+ UNUSED(task);
+ INSIST(event->ev_type == DNS_EVENT_VALIDATORDONE);
+
+ devent = (dns_validatorevent_t *)event;
+ val = devent->ev_arg;
+ eresult = devent->result;
+
+ isc_event_free(&event);
+ dns_validator_destroy(&val->subvalidator);
+
+ INSIST(val->event != NULL);
+
+ validator_log(val, ISC_LOG_DEBUG(3), "in dsvalidated");
+ LOCK(&val->lock);
+ if (CANCELED(val)) {
+ validator_done(val, ISC_R_CANCELED);
+ } else if (eresult == ISC_R_SUCCESS) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "dsset with trust %d", val->frdataset.trust);
+ if ((val->attributes & VALATTR_INSECURITY) != 0)
+ result = proveunsecure(val, ISC_TRUE, ISC_TRUE);
+ else
+ result = validatezonekey(val);
+ if (result != DNS_R_WAIT)
+ validator_done(val, result);
+ } else {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "dsvalidated: got %s",
+ isc_result_totext(eresult));
+ validator_done(val, eresult);
+ }
+ want_destroy = exit_check(val);
+ UNLOCK(&val->lock);
+ if (want_destroy)
+ destroy(val);
+}
+
+/*%
+ * Return ISC_R_SUCCESS if we can determine that the name doesn't exist
+ * or we can determine whether there is data or not at the name.
+ * If the name does not exist return the wildcard name.
+ *
+ * Return ISC_R_IGNORE when the NSEC is not the appropriate one.
+ */
+static isc_result_t
+nsecnoexistnodata(dns_validator_t *val, dns_name_t* name, dns_name_t *nsecname,
+ dns_rdataset_t *nsecset, isc_boolean_t *exists,
+ isc_boolean_t *data, dns_name_t *wild)
+{
+ int order;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_result_t result;
+ dns_namereln_t relation;
+ unsigned int olabels, nlabels, labels;
+ dns_rdata_nsec_t nsec;
+ isc_boolean_t atparent;
+ isc_boolean_t ns;
+ isc_boolean_t soa;
+
+ REQUIRE(exists != NULL);
+ REQUIRE(data != NULL);
+ REQUIRE(nsecset != NULL &&
+ nsecset->type == dns_rdatatype_nsec);
+
+ result = dns_rdataset_first(nsecset);
+ if (result != ISC_R_SUCCESS) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "failure processing NSEC set");
+ return (result);
+ }
+ dns_rdataset_current(nsecset, &rdata);
+
+ validator_log(val, ISC_LOG_DEBUG(3), "looking for relevant nsec");
+ relation = dns_name_fullcompare(name, nsecname, &order, &olabels);
+
+ if (order < 0) {
+ /*
+ * The name is not within the NSEC range.
+ */
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "NSEC does not cover name, before NSEC");
+ return (ISC_R_IGNORE);
+ }
+
+ if (order == 0) {
+ /*
+ * The names are the same.
+ */
+ atparent = dns_rdatatype_atparent(val->event->type);
+ ns = dns_nsec_typepresent(&rdata, dns_rdatatype_ns);
+ soa = dns_nsec_typepresent(&rdata, dns_rdatatype_soa);
+ if (ns && !soa) {
+ if (!atparent) {
+ /*
+ * This NSEC record is from somewhere higher in
+ * the DNS, and at the parent of a delegation.
+ * It can not be legitimately used here.
+ */
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "ignoring parent nsec");
+ return (ISC_R_IGNORE);
+ }
+ } else if (atparent && ns && soa) {
+ /*
+ * This NSEC record is from the child.
+ * It can not be legitimately used here.
+ */
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "ignoring child nsec");
+ return (ISC_R_IGNORE);
+ }
+ if (val->event->type == dns_rdatatype_cname ||
+ val->event->type == dns_rdatatype_nxt ||
+ val->event->type == dns_rdatatype_nsec ||
+ val->event->type == dns_rdatatype_key ||
+ !dns_nsec_typepresent(&rdata, dns_rdatatype_cname)) {
+ *exists = ISC_TRUE;
+ *data = dns_nsec_typepresent(&rdata, val->event->type);
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "nsec proves name exists (owner) data=%d",
+ *data);
+ return (ISC_R_SUCCESS);
+ }
+ validator_log(val, ISC_LOG_DEBUG(3), "NSEC proves CNAME exists");
+ return (ISC_R_IGNORE);
+ }
+
+ if (relation == dns_namereln_subdomain &&
+ dns_nsec_typepresent(&rdata, dns_rdatatype_ns) &&
+ !dns_nsec_typepresent(&rdata, dns_rdatatype_soa))
+ {
+ /*
+ * This NSEC record is from somewhere higher in
+ * the DNS, and at the parent of a delegation.
+ * It can not be legitimately used here.
+ */
+ validator_log(val, ISC_LOG_DEBUG(3), "ignoring parent nsec");
+ return (ISC_R_IGNORE);
+ }
+
+ result = dns_rdata_tostruct(&rdata, &nsec, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ relation = dns_name_fullcompare(&nsec.next, name, &order, &nlabels);
+ if (order == 0) {
+ dns_rdata_freestruct(&nsec);
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "ignoring nsec matches next name");
+ return (ISC_R_IGNORE);
+ }
+
+ if (order < 0 && !dns_name_issubdomain(nsecname, &nsec.next)) {
+ /*
+ * The name is not within the NSEC range.
+ */
+ dns_rdata_freestruct(&nsec);
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "ignoring nsec because name is past end of range");
+ return (ISC_R_IGNORE);
+ }
+
+ if (order > 0 && relation == dns_namereln_subdomain) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "nsec proves name exist (empty)");
+ dns_rdata_freestruct(&nsec);
+ *exists = ISC_TRUE;
+ *data = ISC_FALSE;
+ return (ISC_R_SUCCESS);
+ }
+ if (wild != NULL) {
+ dns_name_t common;
+ dns_name_init(&common, NULL);
+ if (olabels > nlabels) {
+ labels = dns_name_countlabels(nsecname);
+ dns_name_getlabelsequence(nsecname, labels - olabels,
+ olabels, &common);
+ } else {
+ labels = dns_name_countlabels(&nsec.next);
+ dns_name_getlabelsequence(&nsec.next, labels - nlabels,
+ nlabels, &common);
+ }
+ result = dns_name_concatenate(dns_wildcardname, &common,
+ wild, NULL);
+ if (result != ISC_R_SUCCESS) {
+ dns_rdata_freestruct(&nsec);
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "failure generating wildcard name");
+ return (result);
+ }
+ }
+ dns_rdata_freestruct(&nsec);
+ validator_log(val, ISC_LOG_DEBUG(3), "nsec range ok");
+ *exists = ISC_FALSE;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+nsec3noexistnodata(dns_validator_t *val, dns_name_t* name,
+ dns_name_t *nsec3name, dns_rdataset_t *nsec3set,
+ dns_name_t *zonename, isc_boolean_t *exists,
+ isc_boolean_t *data, isc_boolean_t *optout,
+ isc_boolean_t *unknown, isc_boolean_t *setclosest,
+ isc_boolean_t *setnearest, dns_name_t *closest,
+ dns_name_t *nearest)
+{
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_fixedname_t fzone;
+ dns_fixedname_t qfixed;
+ dns_label_t hashlabel;
+ dns_name_t *qname;
+ dns_name_t *zone;
+ dns_rdata_nsec3_t nsec3;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ int order;
+ int scope;
+ isc_boolean_t atparent;
+ isc_boolean_t first;
+ isc_boolean_t ns;
+ isc_boolean_t soa;
+ isc_buffer_t buffer;
+ isc_result_t answer = ISC_R_IGNORE;
+ isc_result_t result;
+ unsigned char hash[NSEC3_MAX_HASH_LENGTH];
+ unsigned char owner[NSEC3_MAX_HASH_LENGTH];
+ unsigned int length;
+ unsigned int qlabels;
+ unsigned int zlabels;
+
+ REQUIRE((exists == NULL && data == NULL) ||
+ (exists != NULL && data != NULL));
+ REQUIRE(nsec3set != NULL && nsec3set->type == dns_rdatatype_nsec3);
+ REQUIRE((setclosest == NULL && closest == NULL) ||
+ (setclosest != NULL && closest != NULL));
+ REQUIRE((setnearest == NULL && nearest == NULL) ||
+ (setnearest != NULL && nearest != NULL));
+
+ result = dns_rdataset_first(nsec3set);
+ if (result != ISC_R_SUCCESS) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "failure processing NSEC3 set");
+ return (result);
+ }
+
+ dns_rdataset_current(nsec3set, &rdata);
+
+ result = dns_rdata_tostruct(&rdata, &nsec3, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ validator_log(val, ISC_LOG_DEBUG(3), "looking for relevant NSEC3");
+
+ dns_fixedname_init(&fzone);
+ zone = dns_fixedname_name(&fzone);
+ zlabels = dns_name_countlabels(nsec3name);
+
+ /*
+ * NSEC3 records must have two or more labels to be valid.
+ */
+ if (zlabels < 2)
+ return (ISC_R_IGNORE);
+
+ /*
+ * Strip off the NSEC3 hash to get the zone.
+ */
+ zlabels--;
+ dns_name_split(nsec3name, zlabels, NULL, zone);
+
+ /*
+ * If not below the zone name we can ignore this record.
+ */
+ if (!dns_name_issubdomain(name, zone))
+ return (ISC_R_IGNORE);
+
+ /*
+ * Is this zone the same or deeper than the current zone?
+ */
+ if (dns_name_countlabels(zonename) == 0 ||
+ dns_name_issubdomain(zone, zonename))
+ dns_name_copy(zone, zonename, NULL);
+
+ if (!dns_name_equal(zone, zonename))
+ return (ISC_R_IGNORE);
+
+ /*
+ * Are we only looking for the most enclosing zone?
+ */
+ if (exists == NULL || data == NULL)
+ return (ISC_R_SUCCESS);
+
+ /*
+ * Only set unknown once we are sure that this NSEC3 is from
+ * the deepest covering zone.
+ */
+ if (!dns_nsec3_supportedhash(nsec3.hash)) {
+ if (unknown != NULL)
+ *unknown = ISC_TRUE;
+ return (ISC_R_IGNORE);
+ }
+
+ /*
+ * Recover the hash from the first label.
+ */
+ dns_name_getlabel(nsec3name, 0, &hashlabel);
+ isc_region_consume(&hashlabel, 1);
+ isc_buffer_init(&buffer, owner, sizeof(owner));
+ result = isc_base32hex_decoderegion(&hashlabel, &buffer);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * The hash lengths should match. If not ignore the record.
+ */
+ if (isc_buffer_usedlength(&buffer) != nsec3.next_length)
+ return (ISC_R_IGNORE);
+
+ /*
+ * Work out what this NSEC3 covers.
+ * Inside (<0) or outside (>=0).
+ */
+ scope = memcmp(owner, nsec3.next, nsec3.next_length);
+
+ /*
+ * Prepare to compute all the hashes.
+ */
+ dns_fixedname_init(&qfixed);
+ qname = dns_fixedname_name(&qfixed);
+ dns_name_downcase(name, qname, NULL);
+ qlabels = dns_name_countlabels(qname);
+ first = ISC_TRUE;
+
+ while (qlabels >= zlabels) {
+ length = isc_iterated_hash(hash, nsec3.hash, nsec3.iterations,
+ nsec3.salt, nsec3.salt_length,
+ qname->ndata, qname->length);
+ /*
+ * The computed hash length should match.
+ */
+ if (length != nsec3.next_length) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "ignoring NSEC bad length %u vs %u",
+ length, nsec3.next_length);
+ return (ISC_R_IGNORE);
+ }
+
+ order = memcmp(hash, owner, length);
+ if (first && order == 0) {
+ /*
+ * The hashes are the same.
+ */
+ atparent = dns_rdatatype_atparent(val->event->type);
+ ns = dns_nsec3_typepresent(&rdata, dns_rdatatype_ns);
+ soa = dns_nsec3_typepresent(&rdata, dns_rdatatype_soa);
+ if (ns && !soa) {
+ if (!atparent) {
+ /*
+ * This NSEC record is from somewhere
+ * higher in the DNS, and at the
+ * parent of a delegation. It can not
+ * be legitimately used here.
+ */
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "ignoring parent NSEC3");
+ return (ISC_R_IGNORE);
+ }
+ } else if (atparent && ns && soa) {
+ /*
+ * This NSEC record is from the child.
+ * It can not be legitimately used here.
+ */
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "ignoring child NSEC3");
+ return (ISC_R_IGNORE);
+ }
+ if (val->event->type == dns_rdatatype_cname ||
+ val->event->type == dns_rdatatype_nxt ||
+ val->event->type == dns_rdatatype_nsec ||
+ val->event->type == dns_rdatatype_key ||
+ !dns_nsec3_typepresent(&rdata, dns_rdatatype_cname)) {
+ *exists = ISC_TRUE;
+ *data = dns_nsec3_typepresent(&rdata,
+ val->event->type);
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "NSEC3 proves name exists (owner) "
+ "data=%d", *data);
+ return (ISC_R_SUCCESS);
+ }
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "NSEC3 proves CNAME exists");
+ return (ISC_R_IGNORE);
+ }
+
+ if (order == 0 &&
+ dns_nsec3_typepresent(&rdata, dns_rdatatype_ns) &&
+ !dns_nsec3_typepresent(&rdata, dns_rdatatype_soa))
+ {
+ /*
+ * This NSEC3 record is from somewhere higher in
+ * the DNS, and at the parent of a delegation.
+ * It can not be legitimately used here.
+ */
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "ignoring parent NSEC3");
+ return (ISC_R_IGNORE);
+ }
+
+ /*
+ * Potential closest encloser.
+ */
+ if (order == 0) {
+ if (closest != NULL &&
+ (dns_name_countlabels(closest) == 0 ||
+ dns_name_issubdomain(qname, closest)) &&
+ !dns_nsec3_typepresent(&rdata, dns_rdatatype_ds) &&
+ !dns_nsec3_typepresent(&rdata, dns_rdatatype_dname) &&
+ (dns_nsec3_typepresent(&rdata, dns_rdatatype_soa) ||
+ !dns_nsec3_typepresent(&rdata, dns_rdatatype_ns)))
+ {
+
+ dns_name_format(qname, namebuf,
+ sizeof(namebuf));
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "NSEC3 indicates potential "
+ "closest encloser: '%s'",
+ namebuf);
+ dns_name_copy(qname, closest, NULL);
+ *setclosest = ISC_TRUE;
+ }
+ dns_name_format(qname, namebuf, sizeof(namebuf));
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "NSEC3 at super-domain %s", namebuf);
+ return (answer);
+ }
+
+ /*
+ * Find if the name does not exist.
+ *
+ * We continue as we need to find the name closest to the
+ * closest encloser that doesn't exist.
+ *
+ * We also need to continue to ensure that we are not
+ * proving the non-existance of a record in a sub-zone.
+ * If that would be the case we will return ISC_R_IGNORE
+ * above.
+ */
+ if ((scope < 0 && order > 0 &&
+ memcmp(hash, nsec3.next, length) < 0) ||
+ (scope >= 0 && (order > 0 ||
+ memcmp(hash, nsec3.next, length) < 0)))
+ {
+ char namebuf[DNS_NAME_FORMATSIZE];
+
+ dns_name_format(qname, namebuf, sizeof(namebuf));
+ validator_log(val, ISC_LOG_DEBUG(3), "NSEC3 proves "
+ "name does not exist: '%s'", namebuf);
+ if (nearest != NULL &&
+ (dns_name_countlabels(nearest) == 0 ||
+ dns_name_issubdomain(nearest, qname))) {
+ dns_name_copy(qname, nearest, NULL);
+ *setnearest = ISC_TRUE;
+ }
+#if 0
+ /*
+ * The closest encloser may be the zone name.
+ */
+ if (closest != NULL &&
+ dns_name_countlabels(closest) == 0 &&
+ !dns_nsec3_typepresent(&rdata, dns_rdatatype_ds) &&
+ !dns_nsec3_typepresent(&rdata, dns_rdatatype_dname) &&
+ (dns_nsec3_typepresent(&rdata, dns_rdatatype_soa) ||
+ !dns_nsec3_typepresent(&rdata, dns_rdatatype_ns)))
+ {
+ char namebuf[DNS_NAME_FORMATSIZE];
+
+ dns_name_format(zone, namebuf,
+ sizeof(namebuf));
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "NSEC3 potential closest "
+ "encloser from zone name: '%s'",
+ namebuf);
+ dns_name_copy(zone, closest, NULL);
+ *setclosest = ISC_TRUE;
+ }
+#endif
+ *exists = ISC_FALSE;
+ *data = ISC_FALSE;
+ if (optout != NULL) {
+ if ((nsec3.flags & DNS_NSEC3FLAG_OPTOUT) != 0)
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "NSEC3 indicates optout");
+ *optout =
+ ISC_TF(nsec3.flags & DNS_NSEC3FLAG_OPTOUT);
+ }
+ answer = ISC_R_SUCCESS;
+ }
+
+ qlabels--;
+ if (qlabels > 0)
+ dns_name_split(qname, qlabels, NULL, qname);
+ first = ISC_FALSE;
+ }
+ return (answer);
+}
+
+/*%
+ * Callback for when NSEC records have been validated.
+ *
+ * Looks for NOQNAME, NODATA and OPTOUT proofs.
+ *
+ * Resumes nsecvalidate.
+ */
+static void
+authvalidated(isc_task_t *task, isc_event_t *event) {
+ dns_validatorevent_t *devent;
+ dns_validator_t *val;
+ dns_rdataset_t *rdataset;
+ dns_rdataset_t *sigrdataset;
+ isc_boolean_t want_destroy;
+ isc_result_t result;
+ isc_boolean_t exists, data;
+
+ UNUSED(task);
+ INSIST(event->ev_type == DNS_EVENT_VALIDATORDONE);
+
+ devent = (dns_validatorevent_t *)event;
+ rdataset = devent->rdataset;
+ sigrdataset = devent->sigrdataset;
+ val = devent->ev_arg;
+ result = devent->result;
+ dns_validator_destroy(&val->subvalidator);
+
+ INSIST(val->event != NULL);
+
+ validator_log(val, ISC_LOG_DEBUG(3), "in authvalidated");
+ LOCK(&val->lock);
+ if (CANCELED(val)) {
+ validator_done(val, ISC_R_CANCELED);
+ } else if (result != ISC_R_SUCCESS) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "authvalidated: got %s",
+ isc_result_totext(result));
+ if (result == ISC_R_CANCELED)
+ validator_done(val, result);
+ else {
+ result = nsecvalidate(val, ISC_TRUE);
+ if (result != DNS_R_WAIT)
+ validator_done(val, result);
+ }
+ } else {
+ dns_name_t **proofs = val->event->proofs;
+ dns_name_t *wild = dns_fixedname_name(&val->wild);
+
+ if (rdataset->trust == dns_trust_secure)
+ val->seensig = ISC_TRUE;
+
+ if (rdataset->type == dns_rdatatype_nsec &&
+ rdataset->trust == dns_trust_secure &&
+ ((val->attributes & VALATTR_NEEDNODATA) != 0 ||
+ (val->attributes & VALATTR_NEEDNOQNAME) != 0) &&
+ (val->attributes & VALATTR_FOUNDNODATA) == 0 &&
+ (val->attributes & VALATTR_FOUNDNOQNAME) == 0 &&
+ nsecnoexistnodata(val, val->event->name, devent->name,
+ rdataset, &exists, &data, wild)
+ == ISC_R_SUCCESS)
+ {
+ if (exists && !data) {
+ val->attributes |= VALATTR_FOUNDNODATA;
+ if (NEEDNODATA(val))
+ proofs[DNS_VALIDATOR_NODATAPROOF] =
+ devent->name;
+ }
+ if (!exists) {
+ val->attributes |= VALATTR_FOUNDNOQNAME;
+ val->attributes |= VALATTR_FOUNDCLOSEST;
+ /*
+ * The NSEC noqname proof also contains
+ * the closest encloser.
+
+ */
+ if (NEEDNOQNAME(val))
+ proofs[DNS_VALIDATOR_NOQNAMEPROOF] =
+ devent->name;
+ }
+ }
+
+ result = nsecvalidate(val, ISC_TRUE);
+ if (result != DNS_R_WAIT)
+ validator_done(val, result);
+ }
+ want_destroy = exit_check(val);
+ UNLOCK(&val->lock);
+ if (want_destroy)
+ destroy(val);
+
+ /*
+ * Free stuff from the event.
+ */
+ isc_event_free(&event);
+}
+
+/*%
+ * Looks for the requested name and type in the view (zones and cache).
+ *
+ * When looking for a DLV record also checks to make sure the NSEC record
+ * returns covers the query name as part of aggressive negative caching.
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li ISC_R_NOTFOUND
+ * \li DNS_R_NCACHENXDOMAIN
+ * \li DNS_R_NCACHENXRRSET
+ * \li DNS_R_NXRRSET
+ * \li DNS_R_NXDOMAIN
+ */
+static inline isc_result_t
+view_find(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type) {
+ dns_fixedname_t fixedname;
+ dns_name_t *foundname;
+ dns_rdata_nsec_t nsec;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_result_t result;
+ unsigned int options;
+ char buf1[DNS_NAME_FORMATSIZE];
+ char buf2[DNS_NAME_FORMATSIZE];
+ char buf3[DNS_NAME_FORMATSIZE];
+
+ if (dns_rdataset_isassociated(&val->frdataset))
+ dns_rdataset_disassociate(&val->frdataset);
+ if (dns_rdataset_isassociated(&val->fsigrdataset))
+ dns_rdataset_disassociate(&val->fsigrdataset);
+
+ if (val->view->zonetable == NULL)
+ return (ISC_R_CANCELED);
+
+ options = DNS_DBFIND_PENDINGOK;
+ if (type == dns_rdatatype_dlv)
+ options |= DNS_DBFIND_COVERINGNSEC;
+ dns_fixedname_init(&fixedname);
+ foundname = dns_fixedname_name(&fixedname);
+ result = dns_view_find(val->view, name, type, 0, options,
+ ISC_FALSE, NULL, NULL, foundname,
+ &val->frdataset, &val->fsigrdataset);
+ if (result == DNS_R_NXDOMAIN) {
+ if (dns_rdataset_isassociated(&val->frdataset))
+ dns_rdataset_disassociate(&val->frdataset);
+ if (dns_rdataset_isassociated(&val->fsigrdataset))
+ dns_rdataset_disassociate(&val->fsigrdataset);
+ } else if (result == DNS_R_COVERINGNSEC) {
+ validator_log(val, ISC_LOG_DEBUG(3), "DNS_R_COVERINGNSEC");
+ /*
+ * Check if the returned NSEC covers the name.
+ */
+ INSIST(type == dns_rdatatype_dlv);
+ if (val->frdataset.trust != dns_trust_secure) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "covering nsec: trust %u",
+ val->frdataset.trust);
+ goto notfound;
+ }
+ result = dns_rdataset_first(&val->frdataset);
+ if (result != ISC_R_SUCCESS)
+ goto notfound;
+ dns_rdataset_current(&val->frdataset, &rdata);
+ if (dns_nsec_typepresent(&rdata, dns_rdatatype_ns) &&
+ !dns_nsec_typepresent(&rdata, dns_rdatatype_soa)) {
+ /* Parent NSEC record. */
+ if (dns_name_issubdomain(name, foundname)) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "covering nsec: for parent");
+ goto notfound;
+ }
+ }
+ result = dns_rdata_tostruct(&rdata, &nsec, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto notfound;
+ if (dns_name_compare(foundname, &nsec.next) >= 0) {
+ /* End of zone chain. */
+ if (!dns_name_issubdomain(name, &nsec.next)) {
+ /*
+ * XXXMPA We could look for a parent NSEC
+ * at nsec.next and if found retest with
+ * this NSEC.
+ */
+ dns_rdata_freestruct(&nsec);
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "covering nsec: not in zone");
+ goto notfound;
+ }
+ } else if (dns_name_compare(name, &nsec.next) >= 0) {
+ /*
+ * XXXMPA We could check if this NSEC is at a zone
+ * apex and if the qname is not below it and look for
+ * a parent NSEC with the same name. This requires
+ * that we can cache both NSEC records which we
+ * currently don't support.
+ */
+ dns_rdata_freestruct(&nsec);
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "covering nsec: not in range");
+ goto notfound;
+ }
+ if (isc_log_wouldlog(dns_lctx,ISC_LOG_DEBUG(3))) {
+ dns_name_format(name, buf1, sizeof buf1);
+ dns_name_format(foundname, buf2, sizeof buf2);
+ dns_name_format(&nsec.next, buf3, sizeof buf3);
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "covering nsec found: '%s' '%s' '%s'",
+ buf1, buf2, buf3);
+ }
+ if (dns_rdataset_isassociated(&val->frdataset))
+ dns_rdataset_disassociate(&val->frdataset);
+ if (dns_rdataset_isassociated(&val->fsigrdataset))
+ dns_rdataset_disassociate(&val->fsigrdataset);
+ dns_rdata_freestruct(&nsec);
+ result = DNS_R_NCACHENXDOMAIN;
+ } else if (result != ISC_R_SUCCESS &&
+ result != DNS_R_NCACHENXDOMAIN &&
+ result != DNS_R_NCACHENXRRSET &&
+ result != DNS_R_EMPTYNAME &&
+ result != DNS_R_NXRRSET &&
+ result != ISC_R_NOTFOUND) {
+ goto notfound;
+ }
+ return (result);
+
+ notfound:
+ if (dns_rdataset_isassociated(&val->frdataset))
+ dns_rdataset_disassociate(&val->frdataset);
+ if (dns_rdataset_isassociated(&val->fsigrdataset))
+ dns_rdataset_disassociate(&val->fsigrdataset);
+ return (ISC_R_NOTFOUND);
+}
+
+/*%
+ * Checks to make sure we are not going to loop. As we use a SHARED fetch
+ * the validation process will stall if looping was to occur.
+ */
+static inline isc_boolean_t
+check_deadlock(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+ dns_validator_t *parent;
+
+ for (parent = val; parent != NULL; parent = parent->parent) {
+ if (parent->event != NULL &&
+ parent->event->type == type &&
+ dns_name_equal(parent->event->name, name) &&
+ /*
+ * As NSEC3 records are meta data you sometimes
+ * need to prove a NSEC3 record which says that
+ * itself doesn't exist.
+ */
+ (parent->event->type != dns_rdatatype_nsec3 ||
+ rdataset == NULL || sigrdataset == NULL ||
+ parent->event->message == NULL ||
+ parent->event->rdataset != NULL ||
+ parent->event->sigrdataset != NULL))
+ {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "continuing validation would lead to "
+ "deadlock: aborting validation");
+ return (ISC_TRUE);
+ }
+ }
+ return (ISC_FALSE);
+}
+
+/*%
+ * Start a fetch for the requested name and type.
+ */
+static inline isc_result_t
+create_fetch(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
+ isc_taskaction_t callback, const char *caller)
+{
+ if (dns_rdataset_isassociated(&val->frdataset))
+ dns_rdataset_disassociate(&val->frdataset);
+ if (dns_rdataset_isassociated(&val->fsigrdataset))
+ dns_rdataset_disassociate(&val->fsigrdataset);
+
+ if (check_deadlock(val, name, type, NULL, NULL))
+ return (DNS_R_NOVALIDSIG);
+
+ validator_logcreate(val, name, type, caller, "fetch");
+ return (dns_resolver_createfetch(val->view->resolver, name, type,
+ NULL, NULL, NULL, 0,
+ val->event->ev_sender,
+ callback, val,
+ &val->frdataset,
+ &val->fsigrdataset,
+ &val->fetch));
+}
+
+/*%
+ * Start a subvalidation process.
+ */
+static inline isc_result_t
+create_validator(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
+ isc_taskaction_t action, const char *caller)
+{
+ isc_result_t result;
+
+ if (check_deadlock(val, name, type, rdataset, sigrdataset))
+ return (DNS_R_NOVALIDSIG);
+
+ validator_logcreate(val, name, type, caller, "validator");
+ result = dns_validator_create(val->view, name, type,
+ rdataset, sigrdataset, NULL, 0,
+ val->task, action, val,
+ &val->subvalidator);
+ if (result == ISC_R_SUCCESS) {
+ val->subvalidator->parent = val;
+ val->subvalidator->depth = val->depth + 1;
+ }
+ return (result);
+}
+
+/*%
+ * Try to find a key that could have signed 'siginfo' among those
+ * in 'rdataset'. If found, build a dst_key_t for it and point
+ * val->key at it.
+ *
+ * If val->key is non-NULL, this returns the next matching key.
+ */
+static isc_result_t
+get_dst_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo,
+ dns_rdataset_t *rdataset)
+{
+ isc_result_t result;
+ isc_buffer_t b;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dst_key_t *oldkey = val->key;
+ isc_boolean_t foundold;
+
+ if (oldkey == NULL)
+ foundold = ISC_TRUE;
+ else {
+ foundold = ISC_FALSE;
+ val->key = NULL;
+ }
+
+ result = dns_rdataset_first(rdataset);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ do {
+ dns_rdataset_current(rdataset, &rdata);
+
+ isc_buffer_init(&b, rdata.data, rdata.length);
+ isc_buffer_add(&b, rdata.length);
+ INSIST(val->key == NULL);
+ result = dst_key_fromdns(&siginfo->signer, rdata.rdclass, &b,
+ val->view->mctx, &val->key);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ if (siginfo->algorithm ==
+ (dns_secalg_t)dst_key_alg(val->key) &&
+ siginfo->keyid ==
+ (dns_keytag_t)dst_key_id(val->key) &&
+ dst_key_iszonekey(val->key))
+ {
+ if (foundold)
+ /*
+ * This is the key we're looking for.
+ */
+ return (ISC_R_SUCCESS);
+ else if (dst_key_compare(oldkey, val->key) == ISC_TRUE)
+ {
+ foundold = ISC_TRUE;
+ dst_key_free(&oldkey);
+ }
+ }
+ dst_key_free(&val->key);
+ dns_rdata_reset(&rdata);
+ result = dns_rdataset_next(rdataset);
+ } while (result == ISC_R_SUCCESS);
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_NOTFOUND;
+
+ failure:
+ if (oldkey != NULL)
+ dst_key_free(&oldkey);
+
+ return (result);
+}
+
+/*%
+ * Get the key that genertated this signature.
+ */
+static isc_result_t
+get_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo) {
+ isc_result_t result;
+ unsigned int nlabels;
+ int order;
+ dns_namereln_t namereln;
+
+ /*
+ * Is the signer name appropriate for this signature?
+ *
+ * The signer name must be at the same level as the owner name
+ * or closer to the the DNS root.
+ */
+ namereln = dns_name_fullcompare(val->event->name, &siginfo->signer,
+ &order, &nlabels);
+ if (namereln != dns_namereln_subdomain &&
+ namereln != dns_namereln_equal)
+ return (DNS_R_CONTINUE);
+
+ if (namereln == dns_namereln_equal) {
+ /*
+ * If this is a self-signed keyset, it must not be a zone key
+ * (since get_key is not called from validatezonekey).
+ */
+ if (val->event->rdataset->type == dns_rdatatype_dnskey)
+ return (DNS_R_CONTINUE);
+
+ /*
+ * Records appearing in the parent zone at delegation
+ * points cannot be self-signed.
+ */
+ if (dns_rdatatype_atparent(val->event->rdataset->type))
+ return (DNS_R_CONTINUE);
+ } else {
+ /*
+ * SOA and NS RRsets can only be signed by a key with
+ * the same name.
+ */
+ if (val->event->rdataset->type == dns_rdatatype_soa ||
+ val->event->rdataset->type == dns_rdatatype_ns) {
+ const char *typename;
+
+ if (val->event->rdataset->type == dns_rdatatype_soa)
+ typename = "SOA";
+ else
+ typename = "NS";
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "%s signer mismatch", typename);
+ return (DNS_R_CONTINUE);
+ }
+ }
+
+ /*
+ * Do we know about this key?
+ */
+ result = view_find(val, &siginfo->signer, dns_rdatatype_dnskey);
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * We have an rrset for the given keyname.
+ */
+ val->keyset = &val->frdataset;
+ if (val->frdataset.trust == dns_trust_pending &&
+ dns_rdataset_isassociated(&val->fsigrdataset))
+ {
+ /*
+ * We know the key but haven't validated it yet.
+ */
+ result = create_validator(val, &siginfo->signer,
+ dns_rdatatype_dnskey,
+ &val->frdataset,
+ &val->fsigrdataset,
+ keyvalidated,
+ "get_key");
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ return (DNS_R_WAIT);
+ } else if (val->frdataset.trust == dns_trust_pending) {
+ /*
+ * Having a pending key with no signature means that
+ * something is broken.
+ */
+ result = DNS_R_CONTINUE;
+ } else if (val->frdataset.trust < dns_trust_secure) {
+ /*
+ * The key is legitimately insecure. There's no
+ * point in even attempting verification.
+ */
+ val->key = NULL;
+ result = ISC_R_SUCCESS;
+ } else {
+ /*
+ * See if we've got the key used in the signature.
+ */
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "keyset with trust %d",
+ val->frdataset.trust);
+ result = get_dst_key(val, siginfo, val->keyset);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * Either the key we're looking for is not
+ * in the rrset, or something bad happened.
+ * Give up.
+ */
+ result = DNS_R_CONTINUE;
+ }
+ }
+ } else if (result == ISC_R_NOTFOUND) {
+ /*
+ * We don't know anything about this key.
+ */
+ result = create_fetch(val, &siginfo->signer, dns_rdatatype_dnskey,
+ fetch_callback_validator, "get_key");
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ return (DNS_R_WAIT);
+ } else if (result == DNS_R_NCACHENXDOMAIN ||
+ result == DNS_R_NCACHENXRRSET ||
+ result == DNS_R_EMPTYNAME ||
+ result == DNS_R_NXDOMAIN ||
+ result == DNS_R_NXRRSET)
+ {
+ /*
+ * This key doesn't exist.
+ */
+ result = DNS_R_CONTINUE;
+ }
+
+ if (dns_rdataset_isassociated(&val->frdataset) &&
+ val->keyset != &val->frdataset)
+ dns_rdataset_disassociate(&val->frdataset);
+ if (dns_rdataset_isassociated(&val->fsigrdataset))
+ dns_rdataset_disassociate(&val->fsigrdataset);
+
+ return (result);
+}
+
+static dns_keytag_t
+compute_keytag(dns_rdata_t *rdata, dns_rdata_dnskey_t *key) {
+ isc_region_t r;
+
+ dns_rdata_toregion(rdata, &r);
+ return (dst_region_computeid(&r, key->algorithm));
+}
+
+/*%
+ * Is this keyset self-signed?
+ */
+static isc_boolean_t
+isselfsigned(dns_validator_t *val) {
+ dns_rdataset_t *rdataset, *sigrdataset;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_t sigrdata = DNS_RDATA_INIT;
+ dns_rdata_dnskey_t key;
+ dns_rdata_rrsig_t sig;
+ dns_keytag_t keytag;
+ isc_result_t result;
+
+ rdataset = val->event->rdataset;
+ sigrdataset = val->event->sigrdataset;
+
+ INSIST(rdataset->type == dns_rdatatype_dnskey);
+
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset))
+ {
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &key, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ keytag = compute_keytag(&rdata, &key);
+ for (result = dns_rdataset_first(sigrdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(sigrdataset))
+ {
+ dns_rdata_reset(&sigrdata);
+ dns_rdataset_current(sigrdataset, &sigrdata);
+ result = dns_rdata_tostruct(&sigrdata, &sig, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ if (sig.algorithm == key.algorithm &&
+ sig.keyid == keytag)
+ return (ISC_TRUE);
+ }
+ }
+ return (ISC_FALSE);
+}
+
+/*%
+ * Attempt to verify the rdataset using the given key and rdata (RRSIG).
+ * The signature was good and from a wildcard record and the QNAME does
+ * not match the wildcard we need to look for a NOQNAME proof.
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS if the verification succeeds.
+ * \li Others if the verification fails.
+ */
+static isc_result_t
+verify(dns_validator_t *val, dst_key_t *key, dns_rdata_t *rdata,
+ isc_uint16_t keyid)
+{
+ isc_result_t result;
+ dns_fixedname_t fixed;
+ isc_boolean_t ignore = ISC_FALSE;
+
+ val->attributes |= VALATTR_TRIEDVERIFY;
+ dns_fixedname_init(&fixed);
+ again:
+ result = dns_dnssec_verify2(val->event->name, val->event->rdataset,
+ key, ignore, val->view->mctx, rdata,
+ dns_fixedname_name(&fixed));
+ if (result == DNS_R_SIGEXPIRED && val->view->acceptexpired) {
+ ignore = ISC_TRUE;
+ goto again;
+ }
+ if (ignore && (result == ISC_R_SUCCESS || result == DNS_R_FROMWILDCARD))
+ validator_log(val, ISC_LOG_INFO,
+ "accepted expired %sRRSIG (keyid=%u)",
+ (result == DNS_R_FROMWILDCARD) ?
+ "wildcard " : "", keyid);
+ else
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "verify rdataset (keyid=%u): %s",
+ keyid, isc_result_totext(result));
+ if (result == DNS_R_FROMWILDCARD) {
+ if (!dns_name_equal(val->event->name,
+ dns_fixedname_name(&fixed)))
+ val->attributes |= VALATTR_NEEDNOQNAME;
+ result = ISC_R_SUCCESS;
+ }
+ return (result);
+}
+
+/*%
+ * Attempts positive response validation of a normal RRset.
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS Validation completed successfully
+ * \li DNS_R_WAIT Validation has started but is waiting
+ * for an event.
+ * \li Other return codes are possible and all indicate failure.
+ */
+static isc_result_t
+validate(dns_validator_t *val, isc_boolean_t resume) {
+ isc_result_t result;
+ dns_validatorevent_t *event;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ /*
+ * Caller must be holding the validator lock.
+ */
+
+ event = val->event;
+
+ if (resume) {
+ /*
+ * We already have a sigrdataset.
+ */
+ result = ISC_R_SUCCESS;
+ validator_log(val, ISC_LOG_DEBUG(3), "resuming validate");
+ } else {
+ result = dns_rdataset_first(event->sigrdataset);
+ }
+
+ for (;
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(event->sigrdataset))
+ {
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(event->sigrdataset, &rdata);
+ if (val->siginfo == NULL) {
+ val->siginfo = isc_mem_get(val->view->mctx,
+ sizeof(*val->siginfo));
+ if (val->siginfo == NULL)
+ return (ISC_R_NOMEMORY);
+ }
+ result = dns_rdata_tostruct(&rdata, val->siginfo, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * At this point we could check that the signature algorithm
+ * was known and "sufficiently good".
+ */
+ if (!dns_resolver_algorithm_supported(val->view->resolver,
+ event->name,
+ val->siginfo->algorithm))
+ continue;
+
+ if (!resume) {
+ result = get_key(val, val->siginfo);
+ if (result == DNS_R_CONTINUE)
+ continue; /* Try the next SIG RR. */
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ /*
+ * The key is insecure, so mark the data as insecure also.
+ */
+ if (val->key == NULL) {
+ if (val->mustbesecure) {
+ validator_log(val, ISC_LOG_WARNING,
+ "must be secure failure");
+ return (DNS_R_MUSTBESECURE);
+ }
+ markanswer(val);
+ return (ISC_R_SUCCESS);
+ }
+
+ do {
+ result = verify(val, val->key, &rdata,
+ val->siginfo->keyid);
+ if (result == ISC_R_SUCCESS)
+ break;
+ if (val->keynode != NULL) {
+ dns_keynode_t *nextnode = NULL;
+ result = dns_keytable_findnextkeynode(
+ val->keytable,
+ val->keynode,
+ &nextnode);
+ dns_keytable_detachkeynode(val->keytable,
+ &val->keynode);
+ val->keynode = nextnode;
+ if (result != ISC_R_SUCCESS) {
+ val->key = NULL;
+ break;
+ }
+ val->key = dns_keynode_key(val->keynode);
+ } else {
+ if (get_dst_key(val, val->siginfo, val->keyset)
+ != ISC_R_SUCCESS)
+ break;
+ }
+ } while (1);
+ if (result != ISC_R_SUCCESS)
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "failed to verify rdataset");
+ else {
+ isc_uint32_t ttl;
+ isc_stdtime_t now;
+
+ isc_stdtime_get(&now);
+ ttl = ISC_MIN(event->rdataset->ttl,
+ val->siginfo->timeexpire - now);
+ if (val->keyset != NULL)
+ ttl = ISC_MIN(ttl, val->keyset->ttl);
+ event->rdataset->ttl = ttl;
+ event->sigrdataset->ttl = ttl;
+ }
+
+ if (val->keynode != NULL)
+ dns_keytable_detachkeynode(val->keytable,
+ &val->keynode);
+ else {
+ if (val->key != NULL)
+ dst_key_free(&val->key);
+ if (val->keyset != NULL) {
+ dns_rdataset_disassociate(val->keyset);
+ val->keyset = NULL;
+ }
+ }
+ val->key = NULL;
+ if ((val->attributes & VALATTR_NEEDNOQNAME) != 0) {
+ if (val->event->message == NULL) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "no message available for noqname proof");
+ return (DNS_R_NOVALIDSIG);
+ }
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "looking for noqname proof");
+ return (nsecvalidate(val, ISC_FALSE));
+ } else if (result == ISC_R_SUCCESS) {
+ event->rdataset->trust = dns_trust_secure;
+ event->sigrdataset->trust = dns_trust_secure;
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "marking as secure");
+ return (result);
+ } else {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "verify failure: %s",
+ isc_result_totext(result));
+ resume = ISC_FALSE;
+ }
+ }
+ if (result != ISC_R_NOMORE) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "failed to iterate signatures: %s",
+ isc_result_totext(result));
+ return (result);
+ }
+
+ validator_log(val, ISC_LOG_INFO, "no valid signature found");
+ return (DNS_R_NOVALIDSIG);
+}
+
+/*%
+ * Validate the DNSKEY RRset by looking for a DNSKEY that matches a
+ * DLV record and that also verifies the DNSKEY RRset.
+ */
+static isc_result_t
+dlv_validatezonekey(dns_validator_t *val) {
+ dns_keytag_t keytag;
+ dns_rdata_dlv_t dlv;
+ dns_rdata_dnskey_t key;
+ dns_rdata_rrsig_t sig;
+ dns_rdata_t dlvrdata = DNS_RDATA_INIT;
+ dns_rdata_t keyrdata = DNS_RDATA_INIT;
+ dns_rdata_t newdsrdata = DNS_RDATA_INIT;
+ dns_rdata_t sigrdata = DNS_RDATA_INIT;
+ dns_rdataset_t trdataset;
+ dst_key_t *dstkey;
+ isc_boolean_t supported_algorithm;
+ isc_result_t result;
+ unsigned char dsbuf[DNS_DS_BUFFERSIZE];
+ isc_uint8_t digest_type;
+
+ validator_log(val, ISC_LOG_DEBUG(3), "dlv_validatezonekey");
+
+ /*
+ * Look through the DLV record and find the keys that can sign the
+ * key set and the matching signature. For each such key, attempt
+ * verification.
+ */
+ supported_algorithm = ISC_FALSE;
+
+ /*
+ * If DNS_DSDIGEST_SHA256 is present we are required to prefer
+ * it over DNS_DSDIGEST_SHA1. This in practice means that we
+ * need to ignore DNS_DSDIGEST_SHA1 if a DNS_DSDIGEST_SHA256
+ * is present.
+ */
+ digest_type = DNS_DSDIGEST_SHA1;
+ for (result = dns_rdataset_first(&val->dlv);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&val->dlv)) {
+ dns_rdata_reset(&dlvrdata);
+ dns_rdataset_current(&val->dlv, &dlvrdata);
+ result = dns_rdata_tostruct(&dlvrdata, &dlv, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ if (!dns_resolver_algorithm_supported(val->view->resolver,
+ val->event->name,
+ dlv.algorithm))
+ continue;
+
+ if (dlv.digest_type == DNS_DSDIGEST_SHA256 &&
+ dlv.length == ISC_SHA256_DIGESTLENGTH) {
+ digest_type = DNS_DSDIGEST_SHA256;
+ break;
+ }
+ }
+
+ for (result = dns_rdataset_first(&val->dlv);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&val->dlv))
+ {
+ dns_rdata_reset(&dlvrdata);
+ dns_rdataset_current(&val->dlv, &dlvrdata);
+ result = dns_rdata_tostruct(&dlvrdata, &dlv, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ if (!dns_resolver_digest_supported(val->view->resolver,
+ dlv.digest_type))
+ continue;
+
+ if (dlv.digest_type != digest_type)
+ continue;
+
+ if (!dns_resolver_algorithm_supported(val->view->resolver,
+ val->event->name,
+ dlv.algorithm))
+ continue;
+
+ supported_algorithm = ISC_TRUE;
+
+ dns_rdataset_init(&trdataset);
+ dns_rdataset_clone(val->event->rdataset, &trdataset);
+
+ for (result = dns_rdataset_first(&trdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&trdataset))
+ {
+ dns_rdata_reset(&keyrdata);
+ dns_rdataset_current(&trdataset, &keyrdata);
+ result = dns_rdata_tostruct(&keyrdata, &key, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ keytag = compute_keytag(&keyrdata, &key);
+ if (dlv.key_tag != keytag ||
+ dlv.algorithm != key.algorithm)
+ continue;
+ dns_rdata_reset(&newdsrdata);
+ result = dns_ds_buildrdata(val->event->name,
+ &keyrdata, dlv.digest_type,
+ dsbuf, &newdsrdata);
+ if (result != ISC_R_SUCCESS) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "dns_ds_buildrdata() -> %s",
+ dns_result_totext(result));
+ continue;
+ }
+ /* Covert to DLV */
+ newdsrdata.type = dns_rdatatype_dlv;
+ if (dns_rdata_compare(&dlvrdata, &newdsrdata) == 0)
+ break;
+ }
+ if (result != ISC_R_SUCCESS) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "no DNSKEY matching DLV");
+ continue;
+ }
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "Found matching DLV record: checking for signature");
+
+ for (result = dns_rdataset_first(val->event->sigrdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(val->event->sigrdataset))
+ {
+ dns_rdata_reset(&sigrdata);
+ dns_rdataset_current(val->event->sigrdataset,
+ &sigrdata);
+ result = dns_rdata_tostruct(&sigrdata, &sig, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ if (dlv.key_tag != sig.keyid &&
+ dlv.algorithm != sig.algorithm)
+ continue;
+ dstkey = NULL;
+ result = dns_dnssec_keyfromrdata(val->event->name,
+ &keyrdata,
+ val->view->mctx,
+ &dstkey);
+ if (result != ISC_R_SUCCESS)
+ /*
+ * This really shouldn't happen, but...
+ */
+ continue;
+
+ result = verify(val, dstkey, &sigrdata, sig.keyid);
+ dst_key_free(&dstkey);
+ if (result == ISC_R_SUCCESS)
+ break;
+ }
+ dns_rdataset_disassociate(&trdataset);
+ if (result == ISC_R_SUCCESS)
+ break;
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "no RRSIG matching DLV key");
+ }
+ if (result == ISC_R_SUCCESS) {
+ val->event->rdataset->trust = dns_trust_secure;
+ val->event->sigrdataset->trust = dns_trust_secure;
+ validator_log(val, ISC_LOG_DEBUG(3), "marking as secure");
+ return (result);
+ } else if (result == ISC_R_NOMORE && !supported_algorithm) {
+ if (val->mustbesecure) {
+ validator_log(val, ISC_LOG_WARNING,
+ "must be secure failure");
+ return (DNS_R_MUSTBESECURE);
+ }
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "no supported algorithm/digest (dlv)");
+ markanswer(val);
+ return (ISC_R_SUCCESS);
+ } else
+ return (DNS_R_NOVALIDSIG);
+}
+
+/*%
+ * Attempts positive response validation of an RRset containing zone keys.
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS Validation completed successfully
+ * \li DNS_R_WAIT Validation has started but is waiting
+ * for an event.
+ * \li Other return codes are possible and all indicate failure.
+ */
+static isc_result_t
+validatezonekey(dns_validator_t *val) {
+ isc_result_t result;
+ dns_validatorevent_t *event;
+ dns_rdataset_t trdataset;
+ dns_rdata_t dsrdata = DNS_RDATA_INIT;
+ dns_rdata_t newdsrdata = DNS_RDATA_INIT;
+ dns_rdata_t keyrdata = DNS_RDATA_INIT;
+ dns_rdata_t sigrdata = DNS_RDATA_INIT;
+ unsigned char dsbuf[DNS_DS_BUFFERSIZE];
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_keytag_t keytag;
+ dns_rdata_ds_t ds;
+ dns_rdata_dnskey_t key;
+ dns_rdata_rrsig_t sig;
+ dst_key_t *dstkey;
+ isc_boolean_t supported_algorithm;
+ isc_boolean_t atsep = ISC_FALSE;
+ isc_uint8_t digest_type;
+
+ /*
+ * Caller must be holding the validator lock.
+ */
+
+ event = val->event;
+
+ if (val->havedlvsep && val->dlv.trust >= dns_trust_secure &&
+ dns_name_equal(event->name, dns_fixedname_name(&val->dlvsep)))
+ return (dlv_validatezonekey(val));
+
+ if (val->dsset == NULL) {
+ /*
+ * First, see if this key was signed by a trusted key.
+ */
+ for (result = dns_rdataset_first(val->event->sigrdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(val->event->sigrdataset))
+ {
+ dns_keynode_t *keynode = NULL, *nextnode = NULL;
+
+ dns_rdata_reset(&sigrdata);
+ dns_rdataset_current(val->event->sigrdataset,
+ &sigrdata);
+ result = dns_rdata_tostruct(&sigrdata, &sig, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ if (!dns_name_equal(val->event->name, &sig.signer))
+ continue;
+
+ result = dns_keytable_findkeynode(val->keytable,
+ val->event->name,
+ sig.algorithm,
+ sig.keyid,
+ &keynode);
+ if (result == DNS_R_PARTIALMATCH ||
+ result == ISC_R_SUCCESS)
+ atsep = ISC_TRUE;
+ while (result == ISC_R_SUCCESS) {
+ dstkey = dns_keynode_key(keynode);
+ result = verify(val, dstkey, &sigrdata,
+ sig.keyid);
+ if (result == ISC_R_SUCCESS) {
+ dns_keytable_detachkeynode(val->keytable,
+ &keynode);
+ break;
+ }
+ result = dns_keytable_findnextkeynode(
+ val->keytable,
+ keynode,
+ &nextnode);
+ dns_keytable_detachkeynode(val->keytable,
+ &keynode);
+ keynode = nextnode;
+ }
+ if (result == ISC_R_SUCCESS) {
+ event->rdataset->trust = dns_trust_secure;
+ event->sigrdataset->trust = dns_trust_secure;
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "signed by trusted key; "
+ "marking as secure");
+ return (result);
+ }
+ }
+
+ /*
+ * If this is the root name and there was no trusted key,
+ * give up, since there's no DS at the root.
+ */
+ if (dns_name_equal(event->name, dns_rootname)) {
+ if ((val->attributes & VALATTR_TRIEDVERIFY) != 0)
+ return (DNS_R_NOVALIDSIG);
+ else
+ return (DNS_R_NOVALIDDS);
+ }
+
+ if (atsep) {
+ /*
+ * We have not found a key to verify this DNSKEY
+ * RRset. As this is a SEP we have to assume that
+ * the RRset is invalid.
+ */
+ dns_name_format(val->event->name, namebuf,
+ sizeof(namebuf));
+ validator_log(val, ISC_LOG_DEBUG(2),
+ "unable to find a DNSKEY which verifies "
+ "the DNSKEY RRset and also matches one "
+ "of specified trusted-keys for '%s'",
+ namebuf);
+ return (DNS_R_NOVALIDKEY);
+ }
+
+ /*
+ * Otherwise, try to find the DS record.
+ */
+ result = view_find(val, val->event->name, dns_rdatatype_ds);
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * We have DS records.
+ */
+ val->dsset = &val->frdataset;
+ if (val->frdataset.trust == dns_trust_pending &&
+ dns_rdataset_isassociated(&val->fsigrdataset))
+ {
+ result = create_validator(val,
+ val->event->name,
+ dns_rdatatype_ds,
+ &val->frdataset,
+ &val->fsigrdataset,
+ dsvalidated,
+ "validatezonekey");
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ return (DNS_R_WAIT);
+ } else if (val->frdataset.trust == dns_trust_pending) {
+ /*
+ * There should never be an unsigned DS.
+ */
+ dns_rdataset_disassociate(&val->frdataset);
+ validator_log(val, ISC_LOG_DEBUG(2),
+ "unsigned DS record");
+ return (DNS_R_NOVALIDSIG);
+ } else
+ result = ISC_R_SUCCESS;
+ } else if (result == ISC_R_NOTFOUND) {
+ /*
+ * We don't have the DS. Find it.
+ */
+ result = create_fetch(val, val->event->name,
+ dns_rdatatype_ds, dsfetched,
+ "validatezonekey");
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ return (DNS_R_WAIT);
+ } else if (result == DNS_R_NCACHENXDOMAIN ||
+ result == DNS_R_NCACHENXRRSET ||
+ result == DNS_R_EMPTYNAME ||
+ result == DNS_R_NXDOMAIN ||
+ result == DNS_R_NXRRSET)
+ {
+ /*
+ * The DS does not exist.
+ */
+ if (dns_rdataset_isassociated(&val->frdataset))
+ dns_rdataset_disassociate(&val->frdataset);
+ if (dns_rdataset_isassociated(&val->fsigrdataset))
+ dns_rdataset_disassociate(&val->fsigrdataset);
+ validator_log(val, ISC_LOG_DEBUG(2), "no DS record");
+ return (DNS_R_NOVALIDSIG);
+ }
+ }
+
+ /*
+ * We have a DS set.
+ */
+ INSIST(val->dsset != NULL);
+
+ if (val->dsset->trust < dns_trust_secure) {
+ if (val->mustbesecure) {
+ validator_log(val, ISC_LOG_WARNING,
+ "must be secure failure");
+ return (DNS_R_MUSTBESECURE);
+ }
+ markanswer(val);
+ return (ISC_R_SUCCESS);
+ }
+
+ /*
+ * Look through the DS record and find the keys that can sign the
+ * key set and the matching signature. For each such key, attempt
+ * verification.
+ */
+
+ supported_algorithm = ISC_FALSE;
+
+ /*
+ * If DNS_DSDIGEST_SHA256 is present we are required to prefer
+ * it over DNS_DSDIGEST_SHA1. This in practice means that we
+ * need to ignore DNS_DSDIGEST_SHA1 if a DNS_DSDIGEST_SHA256
+ * is present.
+ */
+ digest_type = DNS_DSDIGEST_SHA1;
+ for (result = dns_rdataset_first(val->dsset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(val->dsset)) {
+ dns_rdata_reset(&dsrdata);
+ dns_rdataset_current(val->dsset, &dsrdata);
+ result = dns_rdata_tostruct(&dsrdata, &ds, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ if (!dns_resolver_algorithm_supported(val->view->resolver,
+ val->event->name,
+ ds.algorithm))
+ continue;
+
+ if (ds.digest_type == DNS_DSDIGEST_SHA256 &&
+ ds.length == ISC_SHA256_DIGESTLENGTH) {
+ digest_type = DNS_DSDIGEST_SHA256;
+ break;
+ }
+ }
+
+ for (result = dns_rdataset_first(val->dsset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(val->dsset))
+ {
+ dns_rdata_reset(&dsrdata);
+ dns_rdataset_current(val->dsset, &dsrdata);
+ result = dns_rdata_tostruct(&dsrdata, &ds, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ if (!dns_resolver_digest_supported(val->view->resolver,
+ ds.digest_type))
+ continue;
+
+ if (ds.digest_type != digest_type)
+ continue;
+
+ if (!dns_resolver_algorithm_supported(val->view->resolver,
+ val->event->name,
+ ds.algorithm))
+ continue;
+
+ supported_algorithm = ISC_TRUE;
+
+ dns_rdataset_init(&trdataset);
+ dns_rdataset_clone(val->event->rdataset, &trdataset);
+
+ /*
+ * Look for the KEY that matches the DS record.
+ */
+ for (result = dns_rdataset_first(&trdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&trdataset))
+ {
+ dns_rdata_reset(&keyrdata);
+ dns_rdataset_current(&trdataset, &keyrdata);
+ result = dns_rdata_tostruct(&keyrdata, &key, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ keytag = compute_keytag(&keyrdata, &key);
+ if (ds.key_tag != keytag ||
+ ds.algorithm != key.algorithm)
+ continue;
+ dns_rdata_reset(&newdsrdata);
+ result = dns_ds_buildrdata(val->event->name,
+ &keyrdata, ds.digest_type,
+ dsbuf, &newdsrdata);
+ if (result != ISC_R_SUCCESS)
+ continue;
+ if (dns_rdata_compare(&dsrdata, &newdsrdata) == 0)
+ break;
+ }
+ if (result != ISC_R_SUCCESS) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "no DNSKEY matching DS");
+ continue;
+ }
+
+ for (result = dns_rdataset_first(val->event->sigrdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(val->event->sigrdataset))
+ {
+ dns_rdata_reset(&sigrdata);
+ dns_rdataset_current(val->event->sigrdataset,
+ &sigrdata);
+ result = dns_rdata_tostruct(&sigrdata, &sig, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ if (ds.key_tag != sig.keyid ||
+ ds.algorithm != sig.algorithm)
+ continue;
+ if (!dns_name_equal(val->event->name, &sig.signer)) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "DNSKEY signer mismatch");
+ continue;
+ }
+ dstkey = NULL;
+ result = dns_dnssec_keyfromrdata(val->event->name,
+ &keyrdata,
+ val->view->mctx,
+ &dstkey);
+ if (result != ISC_R_SUCCESS)
+ /*
+ * This really shouldn't happen, but...
+ */
+ continue;
+ result = verify(val, dstkey, &sigrdata, sig.keyid);
+ dst_key_free(&dstkey);
+ if (result == ISC_R_SUCCESS)
+ break;
+ }
+ dns_rdataset_disassociate(&trdataset);
+ if (result == ISC_R_SUCCESS)
+ break;
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "no RRSIG matching DS key");
+ }
+ if (result == ISC_R_SUCCESS) {
+ event->rdataset->trust = dns_trust_secure;
+ event->sigrdataset->trust = dns_trust_secure;
+ validator_log(val, ISC_LOG_DEBUG(3), "marking as secure");
+ return (result);
+ } else if (result == ISC_R_NOMORE && !supported_algorithm) {
+ if (val->mustbesecure) {
+ validator_log(val, ISC_LOG_WARNING,
+ "must be secure failure");
+ return (DNS_R_MUSTBESECURE);
+ }
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "no supported algorithm/digest (DS)");
+ markanswer(val);
+ return (ISC_R_SUCCESS);
+ } else
+ return (DNS_R_NOVALIDSIG);
+}
+
+/*%
+ * Starts a positive response validation.
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS Validation completed successfully
+ * \li DNS_R_WAIT Validation has started but is waiting
+ * for an event.
+ * \li Other return codes are possible and all indicate failure.
+ */
+static isc_result_t
+start_positive_validation(dns_validator_t *val) {
+ /*
+ * If this is not a key, go straight into validate().
+ */
+ if (val->event->type != dns_rdatatype_dnskey || !isselfsigned(val))
+ return (validate(val, ISC_FALSE));
+
+ return (validatezonekey(val));
+}
+
+/*%
+ * Look for NODATA at the wildcard and NOWILDCARD proofs in the
+ * previously validated NSEC records. As these proofs are mutually
+ * exclusive we stop when one is found.
+ *
+ * Returns
+ * \li ISC_R_SUCCESS
+ */
+static isc_result_t
+checkwildcard(dns_validator_t *val, dns_rdatatype_t type, dns_name_t *zonename)
+{
+ dns_name_t *name, *wild;
+ dns_message_t *message = val->event->message;
+ isc_result_t result;
+ isc_boolean_t exists, data;
+ char namebuf[DNS_NAME_FORMATSIZE];
+
+ wild = dns_fixedname_name(&val->wild);
+
+ if (dns_name_countlabels(wild) == 0) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "in checkwildcard: no wildcard to check");
+ return (ISC_R_SUCCESS);
+ }
+
+ dns_name_format(wild, namebuf, sizeof(namebuf));
+ validator_log(val, ISC_LOG_DEBUG(3), "in checkwildcard: %s", namebuf);
+
+ for (result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
+ result == ISC_R_SUCCESS;
+ result = dns_message_nextname(message, DNS_SECTION_AUTHORITY))
+ {
+ dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL;
+
+ name = NULL;
+ dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
+
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link))
+ {
+ if (rdataset->type != type)
+ continue;
+
+ for (sigrdataset = ISC_LIST_HEAD(name->list);
+ sigrdataset != NULL;
+ sigrdataset = ISC_LIST_NEXT(sigrdataset, link))
+ {
+ if (sigrdataset->type == dns_rdatatype_rrsig &&
+ sigrdataset->covers == rdataset->type)
+ break;
+ }
+ if (sigrdataset == NULL)
+ continue;
+
+ if (rdataset->trust != dns_trust_secure)
+ continue;
+
+ if (rdataset->type == dns_rdatatype_nsec &&
+ ((val->attributes & VALATTR_NEEDNODATA) != 0 ||
+ (val->attributes & VALATTR_NEEDNOWILDCARD) != 0) &&
+ (val->attributes & VALATTR_FOUNDNODATA) == 0 &&
+ (val->attributes & VALATTR_FOUNDNOWILDCARD) == 0 &&
+ nsecnoexistnodata(val, wild, name, rdataset,
+ &exists, &data, NULL)
+ == ISC_R_SUCCESS)
+ {
+ dns_name_t **proofs = val->event->proofs;
+ if (exists && !data)
+ val->attributes |= VALATTR_FOUNDNODATA;
+ if (exists && !data && NEEDNODATA(val))
+ proofs[DNS_VALIDATOR_NODATAPROOF] =
+ name;
+ if (!exists)
+ val->attributes |=
+ VALATTR_FOUNDNOWILDCARD;
+ if (!exists && NEEDNOQNAME(val))
+ proofs[DNS_VALIDATOR_NOWILDCARDPROOF] =
+ name;
+ return (ISC_R_SUCCESS);
+ }
+
+ if (rdataset->type == dns_rdatatype_nsec3 &&
+ ((val->attributes & VALATTR_NEEDNODATA) != 0 ||
+ (val->attributes & VALATTR_NEEDNOWILDCARD) != 0) &&
+ (val->attributes & VALATTR_FOUNDNODATA) == 0 &&
+ (val->attributes & VALATTR_FOUNDNOWILDCARD) == 0 &&
+ nsec3noexistnodata(val, wild, name, rdataset,
+ zonename, &exists, &data,
+ NULL, NULL, NULL, NULL, NULL,
+ NULL) == ISC_R_SUCCESS)
+ {
+ dns_name_t **proofs = val->event->proofs;
+ if (exists && !data)
+ val->attributes |= VALATTR_FOUNDNODATA;
+ if (exists && !data && NEEDNODATA(val))
+ proofs[DNS_VALIDATOR_NODATAPROOF] =
+ name;
+ if (!exists)
+ val->attributes |=
+ VALATTR_FOUNDNOWILDCARD;
+ if (!exists && NEEDNOQNAME(val))
+ proofs[DNS_VALIDATOR_NOWILDCARDPROOF] =
+ name;
+ return (ISC_R_SUCCESS);
+ }
+ }
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ return (result);
+}
+
+
+static isc_result_t
+findnsec3proofs(dns_validator_t *val) {
+ dns_name_t *name;
+ dns_message_t *message = val->event->message;
+ isc_result_t result;
+ isc_boolean_t exists, data, optout, unknown;
+ isc_boolean_t setclosest, setnearest;
+ dns_fixedname_t fclosest, fnearest, fzonename;
+ dns_name_t *closest, *nearest, *zonename;
+ dns_name_t **proofs = val->event->proofs;
+
+ dns_fixedname_init(&fclosest);
+ dns_fixedname_init(&fnearest);
+ dns_fixedname_init(&fzonename);
+ closest = dns_fixedname_name(&fclosest);
+ nearest = dns_fixedname_name(&fnearest);
+ zonename = dns_fixedname_name(&fzonename);
+
+ for (result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
+ result == ISC_R_SUCCESS;
+ result = dns_message_nextname(message, DNS_SECTION_AUTHORITY))
+ {
+ dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL;
+
+ name = NULL;
+ dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
+
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link))
+ {
+ if (rdataset->type != dns_rdatatype_nsec3)
+ continue;
+
+ for (sigrdataset = ISC_LIST_HEAD(name->list);
+ sigrdataset != NULL;
+ sigrdataset = ISC_LIST_NEXT(sigrdataset, link))
+ {
+ if (sigrdataset->type == dns_rdatatype_rrsig &&
+ sigrdataset->covers == dns_rdatatype_nsec3)
+ break;
+ }
+ if (sigrdataset == NULL)
+ continue;
+
+ if (rdataset->trust != dns_trust_secure)
+ continue;
+
+ result = nsec3noexistnodata(val, val->event->name,
+ name, rdataset,
+ zonename, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL);
+ if (result != ISC_R_IGNORE && result != ISC_R_SUCCESS)
+ return (result);
+ }
+ }
+ if (result != ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+
+ if (dns_name_countlabels(zonename) == 0)
+ return (ISC_R_SUCCESS);
+
+ for (result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
+ result == ISC_R_SUCCESS;
+ result = dns_message_nextname(message, DNS_SECTION_AUTHORITY))
+ {
+ dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL;
+
+ name = NULL;
+ dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
+
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link))
+ {
+ if (rdataset->type != dns_rdatatype_nsec3)
+ continue;
+
+ for (sigrdataset = ISC_LIST_HEAD(name->list);
+ sigrdataset != NULL;
+ sigrdataset = ISC_LIST_NEXT(sigrdataset, link))
+ {
+ if (sigrdataset->type == dns_rdatatype_rrsig &&
+ sigrdataset->covers == dns_rdatatype_nsec3)
+ break;
+ }
+ if (sigrdataset == NULL)
+ continue;
+
+ if (rdataset->trust != dns_trust_secure)
+ continue;
+
+ /*
+ * We process all NSEC3 records to find the closest
+ * encloser and nearest name to the closest encloser.
+ */
+ setclosest = setnearest = ISC_FALSE;
+ optout = ISC_FALSE;
+ unknown = ISC_FALSE;
+ result = nsec3noexistnodata(val, val->event->name,
+ name, rdataset,
+ zonename, &exists,
+ &data, &optout, &unknown,
+ &setclosest, &setnearest,
+ closest, nearest);
+ if (setclosest)
+ proofs[DNS_VALIDATOR_CLOSESTENCLOSER] = name;
+ if (unknown)
+ val->attributes |= VALATTR_FOUNDUNKNOWN;
+ if (result != ISC_R_SUCCESS)
+ continue;
+ if (exists && !data && NEEDNODATA(val)) {
+ val->attributes |= VALATTR_FOUNDNODATA;
+ proofs[DNS_VALIDATOR_NODATAPROOF] = name;
+ }
+ if (!exists && setnearest) {
+ val->attributes |= VALATTR_FOUNDNOQNAME;
+ proofs[DNS_VALIDATOR_NOQNAMEPROOF] = name;
+ if (optout)
+ val->attributes |= VALATTR_FOUNDOPTOUT;
+ }
+ }
+ }
+ if (result != ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+
+ /*
+ * To know we have a valid noqname and optout proofs we need to also
+ * have a valid closest encloser. Otherwise we could still be looking
+ * at proofs from the parent zone.
+ */
+ if (dns_name_countlabels(closest) > 0 &&
+ dns_name_countlabels(nearest) ==
+ dns_name_countlabels(closest) + 1 &&
+ dns_name_issubdomain(nearest, closest))
+ {
+ val->attributes |= VALATTR_FOUNDCLOSEST;
+ result = dns_name_concatenate(dns_wildcardname, closest,
+ dns_fixedname_name(&val->wild),
+ NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ } else {
+ val->attributes &= ~VALATTR_FOUNDNOQNAME;
+ val->attributes &= ~VALATTR_FOUNDOPTOUT;
+ proofs[DNS_VALIDATOR_NOQNAMEPROOF] = NULL;
+ }
+
+ /*
+ * Do we need to check for the wildcard?
+ */
+ if ((val->attributes & VALATTR_FOUNDNOQNAME) != 0 &&
+ (val->attributes & VALATTR_FOUNDCLOSEST) != 0 &&
+ (((val->attributes & VALATTR_NEEDNODATA) != 0 &&
+ (val->attributes & VALATTR_FOUNDNODATA) == 0) ||
+ (val->attributes & VALATTR_NEEDNOWILDCARD) != 0)) {
+ result = checkwildcard(val, dns_rdatatype_nsec3, zonename);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ return (result);
+}
+
+/*%
+ * Prove a negative answer is good or that there is a NOQNAME when the
+ * answer is from a wildcard.
+ *
+ * Loop through the authority section looking for NODATA, NOWILDCARD
+ * and NOQNAME proofs in the NSEC records by calling authvalidated().
+ *
+ * If the required proofs are found we are done.
+ *
+ * If the proofs are not found attempt to prove this is a unsecure
+ * response.
+ */
+static isc_result_t
+nsecvalidate(dns_validator_t *val, isc_boolean_t resume) {
+ dns_name_t *name;
+ dns_message_t *message = val->event->message;
+ isc_result_t result;
+
+ if (!resume)
+ result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
+ else {
+ result = ISC_R_SUCCESS;
+ validator_log(val, ISC_LOG_DEBUG(3), "resuming nsecvalidate");
+ }
+
+ for (;
+ result == ISC_R_SUCCESS;
+ result = dns_message_nextname(message, DNS_SECTION_AUTHORITY))
+ {
+ dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL;
+
+ name = NULL;
+ dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
+ if (resume) {
+ rdataset = ISC_LIST_NEXT(val->currentset, link);
+ val->currentset = NULL;
+ resume = ISC_FALSE;
+ } else
+ rdataset = ISC_LIST_HEAD(name->list);
+
+ for (;
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link))
+ {
+ if (rdataset->type == dns_rdatatype_rrsig)
+ continue;
+
+ for (sigrdataset = ISC_LIST_HEAD(name->list);
+ sigrdataset != NULL;
+ sigrdataset = ISC_LIST_NEXT(sigrdataset,
+ link))
+ {
+ if (sigrdataset->type == dns_rdatatype_rrsig &&
+ sigrdataset->covers == rdataset->type)
+ break;
+ }
+ /*
+ * If a signed zone is missing the zone key, bad
+ * things could happen. A query for data in the zone
+ * would lead to a query for the zone key, which
+ * would return a negative answer, which would contain
+ * an SOA and an NSEC signed by the missing key, which
+ * would trigger another query for the DNSKEY (since
+ * the first one is still in progress), and go into an
+ * infinite loop. Avoid that.
+ */
+ if (val->event->type == dns_rdatatype_dnskey &&
+ dns_name_equal(name, val->event->name))
+ {
+ dns_rdata_t nsec = DNS_RDATA_INIT;
+
+ if (rdataset->type != dns_rdatatype_nsec)
+ continue;
+
+ result = dns_rdataset_first(rdataset);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ dns_rdataset_current(rdataset, &nsec);
+ if (dns_nsec_typepresent(&nsec,
+ dns_rdatatype_soa))
+ continue;
+ }
+ val->currentset = rdataset;
+ result = create_validator(val, name, rdataset->type,
+ rdataset, sigrdataset,
+ authvalidated,
+ "nsecvalidate");
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ return (DNS_R_WAIT);
+
+ }
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * Do we only need to check for NOQNAME? To get here we must have
+ * had a secure wildcard answer.
+ */
+ if ((val->attributes & VALATTR_NEEDNODATA) == 0 &&
+ (val->attributes & VALATTR_NEEDNOWILDCARD) == 0 &&
+ (val->attributes & VALATTR_NEEDNOQNAME) != 0) {
+ if ((val->attributes & VALATTR_FOUNDNOQNAME) == 0)
+ findnsec3proofs(val);
+ if ((val->attributes & VALATTR_FOUNDNOQNAME) != 0 &&
+ (val->attributes & VALATTR_FOUNDCLOSEST) != 0) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "noqname proof found");
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "marking as secure");
+ val->event->rdataset->trust = dns_trust_secure;
+ val->event->sigrdataset->trust = dns_trust_secure;
+ return (ISC_R_SUCCESS);
+ } else if ((val->attributes & VALATTR_FOUNDOPTOUT) != 0 &&
+ dns_name_countlabels(dns_fixedname_name(&val->wild))
+ != 0) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "optout proof found");
+ val->event->optout = ISC_TRUE;
+ markanswer(val);
+ return (ISC_R_SUCCESS);
+ } else if ((val->attributes & VALATTR_FOUNDUNKNOWN) != 0) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "unknown NSEC3 hash algorithm found");
+ markanswer(val);
+ return (ISC_R_SUCCESS);
+ }
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "noqname proof not found");
+ return (DNS_R_NOVALIDNSEC);
+ }
+
+ if ((val->attributes & VALATTR_FOUNDNOQNAME) == 0 &&
+ (val->attributes & VALATTR_FOUNDNODATA) == 0)
+ findnsec3proofs(val);
+
+ /*
+ * Do we need to check for the wildcard?
+ */
+ if ((val->attributes & VALATTR_FOUNDNOQNAME) != 0 &&
+ (val->attributes & VALATTR_FOUNDCLOSEST) != 0 &&
+ (((val->attributes & VALATTR_NEEDNODATA) != 0 &&
+ (val->attributes & VALATTR_FOUNDNODATA) == 0) ||
+ (val->attributes & VALATTR_NEEDNOWILDCARD) != 0)) {
+ result = checkwildcard(val, dns_rdatatype_nsec, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ if (((val->attributes & VALATTR_NEEDNODATA) != 0 &&
+ ((val->attributes & VALATTR_FOUNDNODATA) != 0 ||
+ (val->attributes & VALATTR_FOUNDOPTOUT) != 0)) ||
+ ((val->attributes & VALATTR_NEEDNOQNAME) != 0 &&
+ (val->attributes & VALATTR_FOUNDNOQNAME) != 0 &&
+ (val->attributes & VALATTR_NEEDNOWILDCARD) != 0 &&
+ (val->attributes & VALATTR_FOUNDNOWILDCARD) != 0 &&
+ (val->attributes & VALATTR_FOUNDCLOSEST) != 0)) {
+ if ((val->attributes & VALATTR_FOUNDOPTOUT) != 0)
+ val->event->optout = ISC_TRUE;
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "nonexistence proof(s) found");
+ return (ISC_R_SUCCESS);
+ }
+ findnsec3proofs(val);
+
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "nonexistence proof(s) not found");
+ val->attributes |= VALATTR_INSECURITY;
+ return (proveunsecure(val, ISC_FALSE, ISC_FALSE));
+}
+
+static isc_boolean_t
+check_ds(dns_validator_t *val, dns_name_t *name, dns_rdataset_t *rdataset) {
+ dns_rdata_t dsrdata = DNS_RDATA_INIT;
+ dns_rdata_ds_t ds;
+ isc_result_t result;
+
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset)) {
+ dns_rdataset_current(rdataset, &dsrdata);
+ result = dns_rdata_tostruct(&dsrdata, &ds, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ if (dns_resolver_digest_supported(val->view->resolver,
+ ds.digest_type) &&
+ dns_resolver_algorithm_supported(val->view->resolver,
+ name, ds.algorithm)) {
+ dns_rdata_reset(&dsrdata);
+ return (ISC_TRUE);
+ }
+ dns_rdata_reset(&dsrdata);
+ }
+ return (ISC_FALSE);
+}
+
+/*%
+ * Callback from fetching a DLV record.
+ *
+ * Resumes the DLV lookup process.
+ */
+static void
+dlvfetched(isc_task_t *task, isc_event_t *event) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_fetchevent_t *devent;
+ dns_validator_t *val;
+ isc_boolean_t want_destroy;
+ isc_result_t eresult;
+ isc_result_t result;
+
+ UNUSED(task);
+ INSIST(event->ev_type == DNS_EVENT_FETCHDONE);
+ devent = (dns_fetchevent_t *)event;
+ val = devent->ev_arg;
+ eresult = devent->result;
+
+ /* Free resources which are not of interest. */
+ if (devent->node != NULL)
+ dns_db_detachnode(devent->db, &devent->node);
+ if (devent->db != NULL)
+ dns_db_detach(&devent->db);
+ if (dns_rdataset_isassociated(&val->fsigrdataset))
+ dns_rdataset_disassociate(&val->fsigrdataset);
+ isc_event_free(&event);
+ dns_resolver_destroyfetch(&val->fetch);
+
+ INSIST(val->event != NULL);
+ validator_log(val, ISC_LOG_DEBUG(3), "in dlvfetched: %s",
+ dns_result_totext(eresult));
+
+ LOCK(&val->lock);
+ if (eresult == ISC_R_SUCCESS) {
+ dns_name_format(dns_fixedname_name(&val->dlvsep), namebuf,
+ sizeof(namebuf));
+ dns_rdataset_clone(&val->frdataset, &val->dlv);
+ val->havedlvsep = ISC_TRUE;
+ validator_log(val, ISC_LOG_DEBUG(3), "DLV %s found", namebuf);
+ dlv_validator_start(val);
+ } else if (eresult == DNS_R_NXRRSET ||
+ eresult == DNS_R_NXDOMAIN ||
+ eresult == DNS_R_NCACHENXRRSET ||
+ eresult == DNS_R_NCACHENXDOMAIN) {
+ result = finddlvsep(val, ISC_TRUE);
+ if (result == ISC_R_SUCCESS) {
+ dns_name_format(dns_fixedname_name(&val->dlvsep),
+ namebuf, sizeof(namebuf));
+ validator_log(val, ISC_LOG_DEBUG(3), "DLV %s found",
+ namebuf);
+ dlv_validator_start(val);
+ } else if (result == ISC_R_NOTFOUND) {
+ validator_log(val, ISC_LOG_DEBUG(3), "DLV not found");
+ markanswer(val);
+ validator_done(val, ISC_R_SUCCESS);
+ } else {
+ validator_log(val, ISC_LOG_DEBUG(3), "DLV lookup: %s",
+ dns_result_totext(result));
+ if (result != DNS_R_WAIT)
+ validator_done(val, result);
+ }
+ } else {
+ validator_log(val, ISC_LOG_DEBUG(3), "DLV lookup: %s",
+ dns_result_totext(eresult));
+ validator_done(val, eresult);
+ }
+ want_destroy = exit_check(val);
+ UNLOCK(&val->lock);
+ if (want_destroy)
+ destroy(val);
+}
+
+/*%
+ * Start the DLV lookup proccess.
+ *
+ * Returns
+ * \li ISC_R_SUCCESS
+ * \li DNS_R_WAIT
+ * \li Others on validation failures.
+ */
+static isc_result_t
+startfinddlvsep(dns_validator_t *val, dns_name_t *unsecure) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ isc_result_t result;
+
+ INSIST(!DLVTRIED(val));
+
+ val->attributes |= VALATTR_DLVTRIED;
+
+ dns_name_format(unsecure, namebuf, sizeof(namebuf));
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "plain DNSSEC returns unsecure (%s): looking for DLV",
+ namebuf);
+
+ if (dns_name_issubdomain(val->event->name, val->view->dlv)) {
+ validator_log(val, ISC_LOG_WARNING, "must be secure failure");
+ return (DNS_R_MUSTBESECURE);
+ }
+
+ val->dlvlabels = dns_name_countlabels(unsecure) - 1;
+ result = finddlvsep(val, ISC_FALSE);
+ if (result == ISC_R_NOTFOUND) {
+ validator_log(val, ISC_LOG_DEBUG(3), "DLV not found");
+ markanswer(val);
+ return (ISC_R_SUCCESS);
+ }
+ if (result != ISC_R_SUCCESS) {
+ validator_log(val, ISC_LOG_DEBUG(3), "DLV lookup: %s",
+ dns_result_totext(result));
+ return (result);
+ }
+ dns_name_format(dns_fixedname_name(&val->dlvsep), namebuf,
+ sizeof(namebuf));
+ validator_log(val, ISC_LOG_DEBUG(3), "DLV %s found", namebuf);
+ dlv_validator_start(val);
+ return (DNS_R_WAIT);
+}
+
+/*%
+ * Continue the DLV lookup process.
+ *
+ * Returns
+ * \li ISC_R_SUCCESS
+ * \li ISC_R_NOTFOUND
+ * \li DNS_R_WAIT
+ * \li Others on validation failure.
+ */
+static isc_result_t
+finddlvsep(dns_validator_t *val, isc_boolean_t resume) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_fixedname_t dlvfixed;
+ dns_name_t *dlvname;
+ dns_name_t *dlvsep;
+ dns_name_t noroot;
+ isc_result_t result;
+ unsigned int labels;
+
+ INSIST(val->view->dlv != NULL);
+
+ if (!resume) {
+
+ if (dns_name_issubdomain(val->event->name, val->view->dlv)) {
+ validator_log(val, ISC_LOG_WARNING,
+ "must be secure failure");
+ return (DNS_R_MUSTBESECURE);
+ }
+
+ dns_fixedname_init(&val->dlvsep);
+ dlvsep = dns_fixedname_name(&val->dlvsep);
+ dns_name_copy(val->event->name, dlvsep, NULL);
+ /*
+ * If this is a response to a DS query, we need to look in
+ * the parent zone for the trust anchor.
+ */
+ if (val->event->type == dns_rdatatype_ds) {
+ labels = dns_name_countlabels(dlvsep);
+ if (labels == 0)
+ return (ISC_R_NOTFOUND);
+ dns_name_getlabelsequence(dlvsep, 1, labels - 1,
+ dlvsep);
+ }
+ } else {
+ dlvsep = dns_fixedname_name(&val->dlvsep);
+ labels = dns_name_countlabels(dlvsep);
+ dns_name_getlabelsequence(dlvsep, 1, labels - 1, dlvsep);
+ }
+ dns_name_init(&noroot, NULL);
+ dns_fixedname_init(&dlvfixed);
+ dlvname = dns_fixedname_name(&dlvfixed);
+ labels = dns_name_countlabels(dlvsep);
+ if (labels == 0)
+ return (ISC_R_NOTFOUND);
+ dns_name_getlabelsequence(dlvsep, 0, labels - 1, &noroot);
+ result = dns_name_concatenate(&noroot, val->view->dlv, dlvname, NULL);
+ while (result == ISC_R_NOSPACE) {
+ labels = dns_name_countlabels(dlvsep);
+ dns_name_getlabelsequence(dlvsep, 1, labels - 1, dlvsep);
+ dns_name_getlabelsequence(dlvsep, 0, labels - 2, &noroot);
+ result = dns_name_concatenate(&noroot, val->view->dlv,
+ dlvname, NULL);
+ }
+ if (result != ISC_R_SUCCESS) {
+ validator_log(val, ISC_LOG_DEBUG(2), "DLV concatenate failed");
+ return (DNS_R_NOVALIDSIG);
+ }
+
+ while (dns_name_countlabels(dlvname) >=
+ dns_name_countlabels(val->view->dlv) + val->dlvlabels) {
+ dns_name_format(dlvname, namebuf, sizeof(namebuf));
+ validator_log(val, ISC_LOG_DEBUG(3), "looking for DLV %s",
+ namebuf);
+ result = view_find(val, dlvname, dns_rdatatype_dlv);
+ if (result == ISC_R_SUCCESS) {
+ if (val->frdataset.trust < dns_trust_secure)
+ return (DNS_R_NOVALIDSIG);
+ val->havedlvsep = ISC_TRUE;
+ dns_rdataset_clone(&val->frdataset, &val->dlv);
+ return (ISC_R_SUCCESS);
+ }
+ if (result == ISC_R_NOTFOUND) {
+ result = create_fetch(val, dlvname, dns_rdatatype_dlv,
+ dlvfetched, "finddlvsep");
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ return (DNS_R_WAIT);
+ }
+ if (result != DNS_R_NXRRSET &&
+ result != DNS_R_NXDOMAIN &&
+ result != DNS_R_EMPTYNAME &&
+ result != DNS_R_NCACHENXRRSET &&
+ result != DNS_R_NCACHENXDOMAIN)
+ return (result);
+ /*
+ * Strip first labels from both dlvsep and dlvname.
+ */
+ labels = dns_name_countlabels(dlvsep);
+ if (labels == 0)
+ break;
+ dns_name_getlabelsequence(dlvsep, 1, labels - 1, dlvsep);
+ labels = dns_name_countlabels(dlvname);
+ dns_name_getlabelsequence(dlvname, 1, labels - 1, dlvname);
+ }
+ return (ISC_R_NOTFOUND);
+}
+
+/*%
+ * proveunsecure walks down from the SEP looking for a break in the
+ * chain of trust. That occurs when we can prove the DS record does
+ * not exist at a delegation point or the DS exists at a delegation
+ * but we don't support the algorithm/digest.
+ *
+ * If DLV is active and we look for a DLV record at or below the
+ * point we go insecure. If found we restart the validation process.
+ * If not found or DLV isn't active we mark the response as a answer.
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS val->event->name is in a unsecure zone
+ * \li DNS_R_WAIT validation is in progress.
+ * \li DNS_R_MUSTBESECURE val->event->name is supposed to be secure
+ * (policy) but we proved that it is unsecure.
+ * \li DNS_R_NOVALIDSIG
+ * \li DNS_R_NOVALIDNSEC
+ * \li DNS_R_NOTINSECURE
+ */
+static isc_result_t
+proveunsecure(dns_validator_t *val, isc_boolean_t have_ds, isc_boolean_t resume)
+{
+ isc_result_t result;
+ dns_fixedname_t fixedsecroot;
+ dns_name_t *secroot;
+ dns_name_t *tname;
+ char namebuf[DNS_NAME_FORMATSIZE];
+
+ dns_fixedname_init(&fixedsecroot);
+ secroot = dns_fixedname_name(&fixedsecroot);
+ if (val->havedlvsep)
+ dns_name_copy(dns_fixedname_name(&val->dlvsep), secroot, NULL);
+ else {
+ dns_name_copy(val->event->name, secroot, NULL);
+ /*
+ * If this is a response to a DS query, we need to look in
+ * the parent zone for the trust anchor.
+ */
+ if (val->event->type == dns_rdatatype_ds &&
+ dns_name_countlabels(secroot) > 1U)
+ dns_name_split(secroot, 1, NULL, secroot);
+ result = dns_keytable_finddeepestmatch(val->keytable,
+ secroot, secroot);
+
+ if (result == ISC_R_NOTFOUND) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "not beneath secure root");
+ if (val->mustbesecure) {
+ validator_log(val, ISC_LOG_WARNING,
+ "must be secure failure");
+ result = DNS_R_MUSTBESECURE;
+ goto out;
+ }
+ if (val->view->dlv == NULL || DLVTRIED(val)) {
+ markanswer(val);
+ return (ISC_R_SUCCESS);
+ }
+ return (startfinddlvsep(val, dns_rootname));
+ } else if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ if (!resume) {
+ /*
+ * We are looking for breaks below the SEP so add a label.
+ */
+ val->labels = dns_name_countlabels(secroot) + 1;
+ } else {
+ validator_log(val, ISC_LOG_DEBUG(3), "resuming proveunsecure");
+ /*
+ * If we have a DS rdataset and it is secure then check if
+ * the DS rdataset has a supported algorithm combination.
+ * If not this is a insecure delegation as far as this
+ * resolver is concerned. Fall back to DLV if available.
+ */
+ if (have_ds && val->frdataset.trust >= dns_trust_secure &&
+ !check_ds(val, dns_fixedname_name(&val->fname),
+ &val->frdataset)) {
+ dns_name_format(dns_fixedname_name(&val->fname),
+ namebuf, sizeof(namebuf));
+ if ((val->view->dlv == NULL || DLVTRIED(val)) &&
+ val->mustbesecure) {
+ validator_log(val, ISC_LOG_WARNING,
+ "must be secure failure at '%s'",
+ namebuf);
+ result = DNS_R_MUSTBESECURE;
+ goto out;
+ }
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "no supported algorithm/digest (%s/DS)",
+ namebuf);
+ if (val->view->dlv == NULL || DLVTRIED(val)) {
+ markanswer(val);
+ result = ISC_R_SUCCESS;
+ goto out;
+ }
+ result = startfinddlvsep(val,
+ dns_fixedname_name(&val->fname));
+ goto out;
+ }
+ val->labels++;
+ }
+
+ for (;
+ val->labels <= dns_name_countlabels(val->event->name);
+ val->labels++)
+ {
+
+ dns_fixedname_init(&val->fname);
+ tname = dns_fixedname_name(&val->fname);
+ if (val->labels == dns_name_countlabels(val->event->name))
+ dns_name_copy(val->event->name, tname, NULL);
+ else
+ dns_name_split(val->event->name, val->labels,
+ NULL, tname);
+
+ dns_name_format(tname, namebuf, sizeof(namebuf));
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "checking existence of DS at '%s'",
+ namebuf);
+
+ result = view_find(val, tname, dns_rdatatype_ds);
+
+ if (result == DNS_R_NXRRSET || result == DNS_R_NCACHENXRRSET) {
+ /*
+ * There is no DS. If this is a delegation,
+ * we maybe done.
+ */
+ if (val->frdataset.trust == dns_trust_pending) {
+ result = create_fetch(val, tname,
+ dns_rdatatype_ds,
+ dsfetched2,
+ "proveunsecure");
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ return (DNS_R_WAIT);
+ }
+ if (val->frdataset.trust < dns_trust_secure) {
+ /*
+ * This shouldn't happen, since the negative
+ * response should have been validated. Since
+ * there's no way of validating existing
+ * negative response blobs, give up.
+ */
+ result = DNS_R_NOVALIDSIG;
+ goto out;
+ }
+ if (isdelegation(tname, &val->frdataset, result)) {
+ if (val->mustbesecure) {
+ validator_log(val, ISC_LOG_WARNING,
+ "must be secure failure");
+ return (DNS_R_MUSTBESECURE);
+ }
+ if (val->view->dlv == NULL || DLVTRIED(val)) {
+ markanswer(val);
+ return (ISC_R_SUCCESS);
+ }
+ return (startfinddlvsep(val, tname));
+ }
+ continue;
+ } else if (result == ISC_R_SUCCESS) {
+ /*
+ * There is a DS here. Verify that it's secure and
+ * continue.
+ */
+ if (val->frdataset.trust >= dns_trust_secure) {
+ if (!check_ds(val, tname, &val->frdataset)) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "no supported algorithm/"
+ "digest (%s/DS)", namebuf);
+ if (val->mustbesecure) {
+ validator_log(val,
+ ISC_LOG_WARNING,
+ "must be secure failure");
+ result = DNS_R_MUSTBESECURE;
+ goto out;
+ }
+ if (val->view->dlv == NULL ||
+ DLVTRIED(val)) {
+ markanswer(val);
+ result = ISC_R_SUCCESS;
+ goto out;
+ }
+ result = startfinddlvsep(val, tname);
+ goto out;
+ }
+ continue;
+ }
+ else if (!dns_rdataset_isassociated(&val->fsigrdataset))
+ {
+ result = DNS_R_NOVALIDSIG;
+ goto out;
+ }
+ result = create_validator(val, tname, dns_rdatatype_ds,
+ &val->frdataset,
+ &val->fsigrdataset,
+ dsvalidated,
+ "proveunsecure");
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ return (DNS_R_WAIT);
+ } else if (result == DNS_R_NXDOMAIN ||
+ result == DNS_R_NCACHENXDOMAIN) {
+ /*
+ * This is not a zone cut. Assuming things are
+ * as expected, continue.
+ */
+ if (!dns_rdataset_isassociated(&val->frdataset)) {
+ /*
+ * There should be an NSEC here, since we
+ * are still in a secure zone.
+ */
+ result = DNS_R_NOVALIDNSEC;
+ goto out;
+ } else if (val->frdataset.trust < dns_trust_secure) {
+ /*
+ * This shouldn't happen, since the negative
+ * response should have been validated. Since
+ * there's no way of validating existing
+ * negative response blobs, give up.
+ */
+ result = DNS_R_NOVALIDSIG;
+ goto out;
+ }
+ continue;
+ } else if (result == ISC_R_NOTFOUND) {
+ /*
+ * We don't know anything about the DS. Find it.
+ */
+ result = create_fetch(val, tname, dns_rdatatype_ds,
+ dsfetched2, "proveunsecure");
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ return (DNS_R_WAIT);
+ }
+ }
+
+/*
+ if ((val->attributes & VALATTR_NEEDOPTOUT) == 0 &&
+ val->event->message != NULL) {
+ val->attributes |= VALATTR_NEEDOPTOUT;
+ return (nsecvalidate(val, ISC_FALSE));
+ }
+*/
+
+ validator_log(val, ISC_LOG_DEBUG(3), "insecurity proof failed");
+ return (DNS_R_NOTINSECURE); /* Couldn't complete insecurity proof */
+
+ out:
+ if (dns_rdataset_isassociated(&val->frdataset))
+ dns_rdataset_disassociate(&val->frdataset);
+ if (dns_rdataset_isassociated(&val->fsigrdataset))
+ dns_rdataset_disassociate(&val->fsigrdataset);
+ return (result);
+}
+
+/*%
+ * Reset state and revalidate the answer using DLV.
+ */
+static void
+dlv_validator_start(dns_validator_t *val) {
+ isc_event_t *event;
+
+ validator_log(val, ISC_LOG_DEBUG(3), "dlv_validator_start");
+
+ /*
+ * Reset state and try again.
+ */
+ val->attributes &= VALATTR_DLVTRIED;
+ val->options &= ~DNS_VALIDATOR_DLV;
+
+ event = (isc_event_t *)val->event;
+ isc_task_send(val->task, &event);
+}
+
+/*%
+ * Start the validation process.
+ *
+ * Attempt to valididate the answer based on the category it appears to
+ * fall in.
+ * \li 1. secure positive answer.
+ * \li 2. unsecure positive answer.
+ * \li 3. a negative answer (secure or unsecure).
+ *
+ * Note a answer that appears to be a secure positive answer may actually
+ * be a unsecure positive answer.
+ */
+static void
+validator_start(isc_task_t *task, isc_event_t *event) {
+ dns_validator_t *val;
+ dns_validatorevent_t *vevent;
+ isc_boolean_t want_destroy = ISC_FALSE;
+ isc_result_t result = ISC_R_FAILURE;
+
+ UNUSED(task);
+ REQUIRE(event->ev_type == DNS_EVENT_VALIDATORSTART);
+ vevent = (dns_validatorevent_t *)event;
+ val = vevent->validator;
+
+ /* If the validator has been cancelled, val->event == NULL */
+ if (val->event == NULL)
+ return;
+
+ if (DLVTRIED(val))
+ validator_log(val, ISC_LOG_DEBUG(3), "restarting using DLV");
+ else
+ validator_log(val, ISC_LOG_DEBUG(3), "starting");
+
+ LOCK(&val->lock);
+
+ if ((val->options & DNS_VALIDATOR_DLV) != 0 &&
+ val->event->rdataset != NULL) {
+ validator_log(val, ISC_LOG_DEBUG(3), "looking for DLV");
+ result = startfinddlvsep(val, dns_rootname);
+ } else if (val->event->rdataset != NULL &&
+ val->event->sigrdataset != NULL) {
+ isc_result_t saved_result;
+
+ /*
+ * This looks like a simple validation. We say "looks like"
+ * because it might end up requiring an insecurity proof.
+ */
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "attempting positive response validation");
+
+ INSIST(dns_rdataset_isassociated(val->event->rdataset));
+ INSIST(dns_rdataset_isassociated(val->event->sigrdataset));
+ result = start_positive_validation(val);
+ if (result == DNS_R_NOVALIDSIG &&
+ (val->attributes & VALATTR_TRIEDVERIFY) == 0)
+ {
+ saved_result = result;
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "falling back to insecurity proof");
+ val->attributes |= VALATTR_INSECURITY;
+ result = proveunsecure(val, ISC_FALSE, ISC_FALSE);
+ if (result == DNS_R_NOTINSECURE)
+ result = saved_result;
+ }
+ } else if (val->event->rdataset != NULL) {
+ /*
+ * This is either an unsecure subdomain or a response from
+ * a broken server.
+ */
+ INSIST(dns_rdataset_isassociated(val->event->rdataset));
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "attempting insecurity proof");
+
+ val->attributes |= VALATTR_INSECURITY;
+ result = proveunsecure(val, ISC_FALSE, ISC_FALSE);
+ } else if (val->event->rdataset == NULL &&
+ val->event->sigrdataset == NULL)
+ {
+ /*
+ * This is a nonexistence validation.
+ */
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "attempting negative response validation");
+
+ if (val->event->message->rcode == dns_rcode_nxdomain) {
+ val->attributes |= VALATTR_NEEDNOQNAME;
+ val->attributes |= VALATTR_NEEDNOWILDCARD;
+ } else
+ val->attributes |= VALATTR_NEEDNODATA;
+ result = nsecvalidate(val, ISC_FALSE);
+ } else {
+ /*
+ * This shouldn't happen.
+ */
+ INSIST(0);
+ }
+
+ if (result != DNS_R_WAIT) {
+ want_destroy = exit_check(val);
+ validator_done(val, result);
+ }
+
+ UNLOCK(&val->lock);
+ if (want_destroy)
+ destroy(val);
+}
+
+isc_result_t
+dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
+ dns_message_t *message, unsigned int options,
+ isc_task_t *task, isc_taskaction_t action, void *arg,
+ dns_validator_t **validatorp)
+{
+ isc_result_t result;
+ dns_validator_t *val;
+ isc_task_t *tclone;
+ dns_validatorevent_t *event;
+
+ REQUIRE(name != NULL);
+ REQUIRE(rdataset != NULL ||
+ (rdataset == NULL && sigrdataset == NULL && message != NULL));
+ REQUIRE(validatorp != NULL && *validatorp == NULL);
+
+ tclone = NULL;
+ result = ISC_R_FAILURE;
+
+ val = isc_mem_get(view->mctx, sizeof(*val));
+ if (val == NULL)
+ return (ISC_R_NOMEMORY);
+ val->view = NULL;
+ dns_view_weakattach(view, &val->view);
+ event = (dns_validatorevent_t *)
+ isc_event_allocate(view->mctx, task,
+ DNS_EVENT_VALIDATORSTART,
+ validator_start, NULL,
+ sizeof(dns_validatorevent_t));
+ if (event == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_val;
+ }
+ isc_task_attach(task, &tclone);
+ event->validator = val;
+ event->result = ISC_R_FAILURE;
+ event->name = name;
+ event->type = type;
+ event->rdataset = rdataset;
+ event->sigrdataset = sigrdataset;
+ event->message = message;
+ memset(event->proofs, 0, sizeof(event->proofs));
+ event->optout = ISC_FALSE;
+ result = isc_mutex_init(&val->lock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_event;
+ val->event = event;
+ val->options = options;
+ val->attributes = 0;
+ val->fetch = NULL;
+ val->subvalidator = NULL;
+ val->parent = NULL;
+ val->keytable = NULL;
+ dns_keytable_attach(val->view->secroots, &val->keytable);
+ val->keynode = NULL;
+ val->key = NULL;
+ val->siginfo = NULL;
+ val->task = task;
+ val->action = action;
+ val->arg = arg;
+ val->labels = 0;
+ val->currentset = NULL;
+ val->keyset = NULL;
+ val->dsset = NULL;
+ dns_rdataset_init(&val->dlv);
+ val->seensig = ISC_FALSE;
+ val->havedlvsep = ISC_FALSE;
+ val->depth = 0;
+ val->mustbesecure = dns_resolver_getmustbesecure(view->resolver, name);
+ dns_rdataset_init(&val->frdataset);
+ dns_rdataset_init(&val->fsigrdataset);
+ dns_fixedname_init(&val->wild);
+ dns_fixedname_init(&val->nearest);
+ dns_fixedname_init(&val->closest);
+ ISC_LINK_INIT(val, link);
+ val->magic = VALIDATOR_MAGIC;
+
+ if ((options & DNS_VALIDATOR_DEFER) == 0)
+ isc_task_send(task, ISC_EVENT_PTR(&event));
+
+ *validatorp = val;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup_event:
+ isc_task_detach(&tclone);
+ isc_event_free(ISC_EVENT_PTR(&event));
+
+ cleanup_val:
+ dns_view_weakdetach(&val->view);
+ isc_mem_put(view->mctx, val, sizeof(*val));
+
+ return (result);
+}
+
+void
+dns_validator_send(dns_validator_t *validator) {
+ isc_event_t *event;
+ REQUIRE(VALID_VALIDATOR(validator));
+
+ LOCK(&validator->lock);
+
+ INSIST((validator->options & DNS_VALIDATOR_DEFER) != 0);
+ event = (isc_event_t *)validator->event;
+ validator->options &= ~DNS_VALIDATOR_DEFER;
+ UNLOCK(&validator->lock);
+
+ isc_task_send(validator->task, ISC_EVENT_PTR(&event));
+}
+
+void
+dns_validator_cancel(dns_validator_t *validator) {
+ REQUIRE(VALID_VALIDATOR(validator));
+
+ LOCK(&validator->lock);
+
+ validator_log(validator, ISC_LOG_DEBUG(3), "dns_validator_cancel");
+
+ if (validator->event != NULL) {
+ if (validator->fetch != NULL)
+ dns_resolver_cancelfetch(validator->fetch);
+
+ if (validator->subvalidator != NULL)
+ dns_validator_cancel(validator->subvalidator);
+ if ((validator->options & DNS_VALIDATOR_DEFER) != 0) {
+ isc_task_t *task = validator->event->ev_sender;
+ validator->options &= ~DNS_VALIDATOR_DEFER;
+ isc_event_free((isc_event_t **)&validator->event);
+ isc_task_detach(&task);
+ }
+ validator->attributes |= VALATTR_CANCELED;
+ }
+ UNLOCK(&validator->lock);
+}
+
+static void
+destroy(dns_validator_t *val) {
+ isc_mem_t *mctx;
+
+ REQUIRE(SHUTDOWN(val));
+ REQUIRE(val->event == NULL);
+ REQUIRE(val->fetch == NULL);
+
+ if (val->keynode != NULL)
+ dns_keytable_detachkeynode(val->keytable, &val->keynode);
+ else if (val->key != NULL)
+ dst_key_free(&val->key);
+ if (val->keytable != NULL)
+ dns_keytable_detach(&val->keytable);
+ if (val->subvalidator != NULL)
+ dns_validator_destroy(&val->subvalidator);
+ if (val->havedlvsep)
+ dns_rdataset_disassociate(&val->dlv);
+ if (dns_rdataset_isassociated(&val->frdataset))
+ dns_rdataset_disassociate(&val->frdataset);
+ if (dns_rdataset_isassociated(&val->fsigrdataset))
+ dns_rdataset_disassociate(&val->fsigrdataset);
+ mctx = val->view->mctx;
+ if (val->siginfo != NULL)
+ isc_mem_put(mctx, val->siginfo, sizeof(*val->siginfo));
+ DESTROYLOCK(&val->lock);
+ dns_view_weakdetach(&val->view);
+ val->magic = 0;
+ isc_mem_put(mctx, val, sizeof(*val));
+}
+
+void
+dns_validator_destroy(dns_validator_t **validatorp) {
+ dns_validator_t *val;
+ isc_boolean_t want_destroy = ISC_FALSE;
+
+ REQUIRE(validatorp != NULL);
+ val = *validatorp;
+ REQUIRE(VALID_VALIDATOR(val));
+
+ LOCK(&val->lock);
+
+ val->attributes |= VALATTR_SHUTDOWN;
+ validator_log(val, ISC_LOG_DEBUG(3), "dns_validator_destroy");
+
+ want_destroy = exit_check(val);
+
+ UNLOCK(&val->lock);
+
+ if (want_destroy)
+ destroy(val);
+
+ *validatorp = NULL;
+}
+
+static void
+validator_logv(dns_validator_t *val, isc_logcategory_t *category,
+ isc_logmodule_t *module, int level, const char *fmt, va_list ap)
+{
+ char msgbuf[2048];
+ static const char spaces[] = " *";
+ int depth = val->depth * 2;
+
+ vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
+
+ if ((unsigned int) depth >= sizeof spaces)
+ depth = sizeof spaces - 1;
+
+ if (val->event != NULL && val->event->name != NULL) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
+
+ dns_name_format(val->event->name, namebuf, sizeof(namebuf));
+ dns_rdatatype_format(val->event->type, typebuf,
+ sizeof(typebuf));
+ isc_log_write(dns_lctx, category, module, level,
+ "%.*svalidating @%p: %s %s: %s", depth, spaces,
+ val, namebuf, typebuf, msgbuf);
+ } else {
+ isc_log_write(dns_lctx, category, module, level,
+ "%.*svalidator @%p: %s", depth, spaces,
+ val, msgbuf);
+ }
+}
+
+static void
+validator_log(dns_validator_t *val, int level, const char *fmt, ...) {
+ va_list ap;
+
+ if (! isc_log_wouldlog(dns_lctx, level))
+ return;
+
+ va_start(ap, fmt);
+
+ validator_logv(val, DNS_LOGCATEGORY_DNSSEC,
+ DNS_LOGMODULE_VALIDATOR, level, fmt, ap);
+ va_end(ap);
+}
+
+static void
+validator_logcreate(dns_validator_t *val,
+ dns_name_t *name, dns_rdatatype_t type,
+ const char *caller, const char *operation)
+{
+ char namestr[DNS_NAME_FORMATSIZE];
+ char typestr[DNS_RDATATYPE_FORMATSIZE];
+
+ dns_name_format(name, namestr, sizeof(namestr));
+ dns_rdatatype_format(type, typestr, sizeof(typestr));
+ validator_log(val, ISC_LOG_DEBUG(9), "%s: creating %s for %s %s",
+ caller, operation, namestr, typestr);
+}
diff --git a/lib/dns/version.c b/lib/dns/version.c
new file mode 100644
index 0000000..fbc8889
--- /dev/null
+++ b/lib/dns/version.c
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: version.c,v 1.15 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file */
+
+#include <dns/version.h>
+
+const char dns_version[] = VERSION;
+
+const unsigned int dns_libinterface = LIBINTERFACE;
+const unsigned int dns_librevision = LIBREVISION;
+const unsigned int dns_libage = LIBAGE;
diff --git a/lib/dns/view.c b/lib/dns/view.c
new file mode 100644
index 0000000..ba5a811
--- /dev/null
+++ b/lib/dns/view.c
@@ -0,0 +1,1467 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: view.c,v 1.150 2008/06/17 03:14:20 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/hash.h>
+#include <isc/string.h> /* Required for HP/UX (and others?) */
+#include <isc/task.h>
+#include <isc/util.h>
+
+#include <dns/acache.h>
+#include <dns/acl.h>
+#include <dns/adb.h>
+#include <dns/cache.h>
+#include <dns/db.h>
+#include <dns/dlz.h>
+#include <dns/events.h>
+#include <dns/forward.h>
+#include <dns/keytable.h>
+#include <dns/master.h>
+#include <dns/masterdump.h>
+#include <dns/order.h>
+#include <dns/peer.h>
+#include <dns/rdataset.h>
+#include <dns/request.h>
+#include <dns/resolver.h>
+#include <dns/result.h>
+#include <dns/stats.h>
+#include <dns/tsig.h>
+#include <dns/zone.h>
+#include <dns/zt.h>
+
+#define RESSHUTDOWN(v) (((v)->attributes & DNS_VIEWATTR_RESSHUTDOWN) != 0)
+#define ADBSHUTDOWN(v) (((v)->attributes & DNS_VIEWATTR_ADBSHUTDOWN) != 0)
+#define REQSHUTDOWN(v) (((v)->attributes & DNS_VIEWATTR_REQSHUTDOWN) != 0)
+
+#define DNS_VIEW_DELONLYHASH 111
+
+static void resolver_shutdown(isc_task_t *task, isc_event_t *event);
+static void adb_shutdown(isc_task_t *task, isc_event_t *event);
+static void req_shutdown(isc_task_t *task, isc_event_t *event);
+
+isc_result_t
+dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
+ const char *name, dns_view_t **viewp)
+{
+ dns_view_t *view;
+ isc_result_t result;
+
+ /*
+ * Create a view.
+ */
+
+ REQUIRE(name != NULL);
+ REQUIRE(viewp != NULL && *viewp == NULL);
+
+ view = isc_mem_get(mctx, sizeof(*view));
+ if (view == NULL)
+ return (ISC_R_NOMEMORY);
+ view->name = isc_mem_strdup(mctx, name);
+ if (view->name == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_view;
+ }
+ result = isc_mutex_init(&view->lock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_name;
+
+ view->zonetable = NULL;
+ result = dns_zt_create(mctx, rdclass, &view->zonetable);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "dns_zt_create() failed: %s",
+ isc_result_totext(result));
+ result = ISC_R_UNEXPECTED;
+ goto cleanup_mutex;
+ }
+ view->secroots = NULL;
+ result = dns_keytable_create(mctx, &view->secroots);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "dns_keytable_create() failed: %s",
+ isc_result_totext(result));
+ result = ISC_R_UNEXPECTED;
+ goto cleanup_zt;
+ }
+ view->trustedkeys = NULL;
+ result = dns_keytable_create(mctx, &view->trustedkeys);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "dns_keytable_create() failed: %s",
+ isc_result_totext(result));
+ result = ISC_R_UNEXPECTED;
+ goto cleanup_secroots;
+ }
+ view->fwdtable = NULL;
+ result = dns_fwdtable_create(mctx, &view->fwdtable);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "dns_fwdtable_create() failed: %s",
+ isc_result_totext(result));
+ result = ISC_R_UNEXPECTED;
+ goto cleanup_trustedkeys;
+ }
+
+ view->acache = NULL;
+ view->cache = NULL;
+ view->cachedb = NULL;
+ view->dlzdatabase = NULL;
+ view->hints = NULL;
+ view->resolver = NULL;
+ view->adb = NULL;
+ view->requestmgr = NULL;
+ view->mctx = mctx;
+ view->rdclass = rdclass;
+ view->frozen = ISC_FALSE;
+ view->task = NULL;
+ result = isc_refcount_init(&view->references, 1);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_fwdtable;
+ view->weakrefs = 0;
+ view->attributes = (DNS_VIEWATTR_RESSHUTDOWN|DNS_VIEWATTR_ADBSHUTDOWN|
+ DNS_VIEWATTR_REQSHUTDOWN);
+ view->statickeys = NULL;
+ view->dynamickeys = NULL;
+ view->matchclients = NULL;
+ view->matchdestinations = NULL;
+ view->matchrecursiveonly = ISC_FALSE;
+ result = dns_tsigkeyring_create(view->mctx, &view->dynamickeys);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_references;
+ view->peers = NULL;
+ view->order = NULL;
+ view->delonly = NULL;
+ view->rootdelonly = ISC_FALSE;
+ view->rootexclude = NULL;
+ view->resstats = NULL;
+ view->resquerystats = NULL;
+
+ /*
+ * Initialize configuration data with default values.
+ */
+ view->recursion = ISC_TRUE;
+ view->auth_nxdomain = ISC_FALSE; /* Was true in BIND 8 */
+ view->additionalfromcache = ISC_TRUE;
+ view->additionalfromauth = ISC_TRUE;
+ view->enablednssec = ISC_TRUE;
+ view->enablevalidation = ISC_TRUE;
+ view->acceptexpired = ISC_FALSE;
+ view->minimalresponses = ISC_FALSE;
+ view->transfer_format = dns_one_answer;
+ view->queryacl = NULL;
+ view->queryonacl = NULL;
+ view->recursionacl = NULL;
+ view->recursiononacl = NULL;
+ view->sortlist = NULL;
+ view->transferacl = NULL;
+ view->notifyacl = NULL;
+ view->updateacl = NULL;
+ view->upfwdacl = NULL;
+ view->requestixfr = ISC_TRUE;
+ view->provideixfr = ISC_TRUE;
+ view->maxcachettl = 7 * 24 * 3600;
+ view->maxncachettl = 3 * 3600;
+ view->dstport = 53;
+ view->preferred_glue = 0;
+ view->flush = ISC_FALSE;
+ view->dlv = NULL;
+ view->maxudp = 0;
+ dns_fixedname_init(&view->dlv_fixed);
+
+ result = dns_order_create(view->mctx, &view->order);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_dynkeys;
+
+ result = dns_peerlist_new(view->mctx, &view->peers);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_order;
+
+ result = dns_aclenv_init(view->mctx, &view->aclenv);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_peerlist;
+
+ ISC_LINK_INIT(view, link);
+ ISC_EVENT_INIT(&view->resevent, sizeof(view->resevent), 0, NULL,
+ DNS_EVENT_VIEWRESSHUTDOWN, resolver_shutdown,
+ view, NULL, NULL, NULL);
+ ISC_EVENT_INIT(&view->adbevent, sizeof(view->adbevent), 0, NULL,
+ DNS_EVENT_VIEWADBSHUTDOWN, adb_shutdown,
+ view, NULL, NULL, NULL);
+ ISC_EVENT_INIT(&view->reqevent, sizeof(view->reqevent), 0, NULL,
+ DNS_EVENT_VIEWREQSHUTDOWN, req_shutdown,
+ view, NULL, NULL, NULL);
+ view->magic = DNS_VIEW_MAGIC;
+
+ *viewp = view;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup_peerlist:
+ dns_peerlist_detach(&view->peers);
+
+ cleanup_order:
+ dns_order_detach(&view->order);
+
+ cleanup_dynkeys:
+ dns_tsigkeyring_destroy(&view->dynamickeys);
+
+ cleanup_references:
+ isc_refcount_destroy(&view->references);
+
+ cleanup_fwdtable:
+ dns_fwdtable_destroy(&view->fwdtable);
+
+ cleanup_trustedkeys:
+ dns_keytable_detach(&view->trustedkeys);
+
+ cleanup_secroots:
+ dns_keytable_detach(&view->secroots);
+
+ cleanup_zt:
+ dns_zt_detach(&view->zonetable);
+
+ cleanup_mutex:
+ DESTROYLOCK(&view->lock);
+
+ cleanup_name:
+ isc_mem_free(mctx, view->name);
+
+ cleanup_view:
+ isc_mem_put(mctx, view, sizeof(*view));
+
+ return (result);
+}
+
+static inline void
+destroy(dns_view_t *view) {
+ REQUIRE(!ISC_LINK_LINKED(view, link));
+ REQUIRE(isc_refcount_current(&view->references) == 0);
+ REQUIRE(view->weakrefs == 0);
+ REQUIRE(RESSHUTDOWN(view));
+ REQUIRE(ADBSHUTDOWN(view));
+ REQUIRE(REQSHUTDOWN(view));
+
+ if (view->order != NULL)
+ dns_order_detach(&view->order);
+ if (view->peers != NULL)
+ dns_peerlist_detach(&view->peers);
+ if (view->dynamickeys != NULL)
+ dns_tsigkeyring_destroy(&view->dynamickeys);
+ if (view->statickeys != NULL)
+ dns_tsigkeyring_destroy(&view->statickeys);
+ if (view->adb != NULL)
+ dns_adb_detach(&view->adb);
+ if (view->resolver != NULL)
+ dns_resolver_detach(&view->resolver);
+ if (view->acache != NULL) {
+ if (view->cachedb != NULL)
+ dns_acache_putdb(view->acache, view->cachedb);
+ dns_acache_detach(&view->acache);
+ }
+ if (view->requestmgr != NULL)
+ dns_requestmgr_detach(&view->requestmgr);
+ if (view->task != NULL)
+ isc_task_detach(&view->task);
+ if (view->hints != NULL)
+ dns_db_detach(&view->hints);
+ if (view->dlzdatabase != NULL)
+ dns_dlzdestroy(&view->dlzdatabase);
+ if (view->cachedb != NULL)
+ dns_db_detach(&view->cachedb);
+ if (view->cache != NULL)
+ dns_cache_detach(&view->cache);
+ if (view->matchclients != NULL)
+ dns_acl_detach(&view->matchclients);
+ if (view->matchdestinations != NULL)
+ dns_acl_detach(&view->matchdestinations);
+ if (view->queryacl != NULL)
+ dns_acl_detach(&view->queryacl);
+ if (view->queryonacl != NULL)
+ dns_acl_detach(&view->queryonacl);
+ if (view->recursionacl != NULL)
+ dns_acl_detach(&view->recursionacl);
+ if (view->recursiononacl != NULL)
+ dns_acl_detach(&view->recursiononacl);
+ if (view->sortlist != NULL)
+ dns_acl_detach(&view->sortlist);
+ if (view->transferacl != NULL)
+ dns_acl_detach(&view->transferacl);
+ if (view->notifyacl != NULL)
+ dns_acl_detach(&view->notifyacl);
+ if (view->updateacl != NULL)
+ dns_acl_detach(&view->updateacl);
+ if (view->upfwdacl != NULL)
+ dns_acl_detach(&view->upfwdacl);
+ if (view->delonly != NULL) {
+ dns_name_t *name;
+ int i;
+
+ for (i = 0; i < DNS_VIEW_DELONLYHASH; i++) {
+ name = ISC_LIST_HEAD(view->delonly[i]);
+ while (name != NULL) {
+ ISC_LIST_UNLINK(view->delonly[i], name, link);
+ dns_name_free(name, view->mctx);
+ isc_mem_put(view->mctx, name, sizeof(*name));
+ name = ISC_LIST_HEAD(view->delonly[i]);
+ }
+ }
+ isc_mem_put(view->mctx, view->delonly, sizeof(dns_namelist_t) *
+ DNS_VIEW_DELONLYHASH);
+ view->delonly = NULL;
+ }
+ if (view->rootexclude != NULL) {
+ dns_name_t *name;
+ int i;
+
+ for (i = 0; i < DNS_VIEW_DELONLYHASH; i++) {
+ name = ISC_LIST_HEAD(view->rootexclude[i]);
+ while (name != NULL) {
+ ISC_LIST_UNLINK(view->rootexclude[i],
+ name, link);
+ dns_name_free(name, view->mctx);
+ isc_mem_put(view->mctx, name, sizeof(*name));
+ name = ISC_LIST_HEAD(view->rootexclude[i]);
+ }
+ }
+ isc_mem_put(view->mctx, view->rootexclude,
+ sizeof(dns_namelist_t) * DNS_VIEW_DELONLYHASH);
+ view->rootexclude = NULL;
+ }
+ if (view->resstats != NULL)
+ dns_stats_detach(&view->resstats);
+ if (view->resquerystats != NULL)
+ dns_stats_detach(&view->resquerystats);
+ dns_keytable_detach(&view->trustedkeys);
+ dns_keytable_detach(&view->secroots);
+ dns_fwdtable_destroy(&view->fwdtable);
+ dns_aclenv_destroy(&view->aclenv);
+ DESTROYLOCK(&view->lock);
+ isc_refcount_destroy(&view->references);
+ isc_mem_free(view->mctx, view->name);
+ isc_mem_put(view->mctx, view, sizeof(*view));
+}
+
+/*
+ * Return true iff 'view' may be freed.
+ * The caller must be holding the view lock.
+ */
+static isc_boolean_t
+all_done(dns_view_t *view) {
+
+ if (isc_refcount_current(&view->references) == 0 &&
+ view->weakrefs == 0 &&
+ RESSHUTDOWN(view) && ADBSHUTDOWN(view) && REQSHUTDOWN(view))
+ return (ISC_TRUE);
+
+ return (ISC_FALSE);
+}
+
+void
+dns_view_attach(dns_view_t *source, dns_view_t **targetp) {
+
+ REQUIRE(DNS_VIEW_VALID(source));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ isc_refcount_increment(&source->references, NULL);
+
+ *targetp = source;
+}
+
+static void
+view_flushanddetach(dns_view_t **viewp, isc_boolean_t flush) {
+ dns_view_t *view;
+ unsigned int refs;
+ isc_boolean_t done = ISC_FALSE;
+
+ REQUIRE(viewp != NULL);
+ view = *viewp;
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ if (flush)
+ view->flush = ISC_TRUE;
+ isc_refcount_decrement(&view->references, &refs);
+ if (refs == 0) {
+ LOCK(&view->lock);
+ if (!RESSHUTDOWN(view))
+ dns_resolver_shutdown(view->resolver);
+ if (!ADBSHUTDOWN(view))
+ dns_adb_shutdown(view->adb);
+ if (!REQSHUTDOWN(view))
+ dns_requestmgr_shutdown(view->requestmgr);
+ if (view->acache != NULL)
+ dns_acache_shutdown(view->acache);
+ if (view->flush)
+ dns_zt_flushanddetach(&view->zonetable);
+ else
+ dns_zt_detach(&view->zonetable);
+ done = all_done(view);
+ UNLOCK(&view->lock);
+ }
+
+ *viewp = NULL;
+
+ if (done)
+ destroy(view);
+}
+
+void
+dns_view_flushanddetach(dns_view_t **viewp) {
+ view_flushanddetach(viewp, ISC_TRUE);
+}
+
+void
+dns_view_detach(dns_view_t **viewp) {
+ view_flushanddetach(viewp, ISC_FALSE);
+}
+
+static isc_result_t
+dialup(dns_zone_t *zone, void *dummy) {
+ UNUSED(dummy);
+ dns_zone_dialup(zone);
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_view_dialup(dns_view_t *view) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ (void)dns_zt_apply(view->zonetable, ISC_FALSE, dialup, NULL);
+}
+
+void
+dns_view_weakattach(dns_view_t *source, dns_view_t **targetp) {
+
+ REQUIRE(DNS_VIEW_VALID(source));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ LOCK(&source->lock);
+ source->weakrefs++;
+ UNLOCK(&source->lock);
+
+ *targetp = source;
+}
+
+void
+dns_view_weakdetach(dns_view_t **viewp) {
+ dns_view_t *view;
+ isc_boolean_t done = ISC_FALSE;
+
+ REQUIRE(viewp != NULL);
+ view = *viewp;
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ LOCK(&view->lock);
+
+ INSIST(view->weakrefs > 0);
+ view->weakrefs--;
+ done = all_done(view);
+
+ UNLOCK(&view->lock);
+
+ *viewp = NULL;
+
+ if (done)
+ destroy(view);
+}
+
+static void
+resolver_shutdown(isc_task_t *task, isc_event_t *event) {
+ dns_view_t *view = event->ev_arg;
+ isc_boolean_t done;
+
+ REQUIRE(event->ev_type == DNS_EVENT_VIEWRESSHUTDOWN);
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(view->task == task);
+
+ UNUSED(task);
+
+ LOCK(&view->lock);
+
+ view->attributes |= DNS_VIEWATTR_RESSHUTDOWN;
+ done = all_done(view);
+
+ UNLOCK(&view->lock);
+
+ isc_event_free(&event);
+
+ if (done)
+ destroy(view);
+}
+
+static void
+adb_shutdown(isc_task_t *task, isc_event_t *event) {
+ dns_view_t *view = event->ev_arg;
+ isc_boolean_t done;
+
+ REQUIRE(event->ev_type == DNS_EVENT_VIEWADBSHUTDOWN);
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(view->task == task);
+
+ UNUSED(task);
+
+ LOCK(&view->lock);
+
+ view->attributes |= DNS_VIEWATTR_ADBSHUTDOWN;
+ done = all_done(view);
+
+ UNLOCK(&view->lock);
+
+ isc_event_free(&event);
+
+ if (done)
+ destroy(view);
+}
+
+static void
+req_shutdown(isc_task_t *task, isc_event_t *event) {
+ dns_view_t *view = event->ev_arg;
+ isc_boolean_t done;
+
+ REQUIRE(event->ev_type == DNS_EVENT_VIEWREQSHUTDOWN);
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(view->task == task);
+
+ UNUSED(task);
+
+ LOCK(&view->lock);
+
+ view->attributes |= DNS_VIEWATTR_REQSHUTDOWN;
+ done = all_done(view);
+
+ UNLOCK(&view->lock);
+
+ isc_event_free(&event);
+
+ if (done)
+ destroy(view);
+}
+
+isc_result_t
+dns_view_createresolver(dns_view_t *view,
+ isc_taskmgr_t *taskmgr, unsigned int ntasks,
+ isc_socketmgr_t *socketmgr,
+ isc_timermgr_t *timermgr,
+ unsigned int options,
+ dns_dispatchmgr_t *dispatchmgr,
+ dns_dispatch_t *dispatchv4,
+ dns_dispatch_t *dispatchv6)
+{
+ isc_result_t result;
+ isc_event_t *event;
+ isc_mem_t *mctx = NULL;
+
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(!view->frozen);
+ REQUIRE(view->resolver == NULL);
+
+ result = isc_task_create(taskmgr, 0, &view->task);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ isc_task_setname(view->task, "view", view);
+
+ result = dns_resolver_create(view, taskmgr, ntasks, socketmgr,
+ timermgr, options, dispatchmgr,
+ dispatchv4, dispatchv6,
+ &view->resolver);
+ if (result != ISC_R_SUCCESS) {
+ isc_task_detach(&view->task);
+ return (result);
+ }
+ event = &view->resevent;
+ dns_resolver_whenshutdown(view->resolver, view->task, &event);
+ view->attributes &= ~DNS_VIEWATTR_RESSHUTDOWN;
+
+ result = isc_mem_create(0, 0, &mctx);
+ if (result != ISC_R_SUCCESS) {
+ dns_resolver_shutdown(view->resolver);
+ return (result);
+ }
+
+ result = dns_adb_create(mctx, view, timermgr, taskmgr, &view->adb);
+ isc_mem_setname(mctx, "ADB", NULL);
+ isc_mem_detach(&mctx);
+ if (result != ISC_R_SUCCESS) {
+ dns_resolver_shutdown(view->resolver);
+ return (result);
+ }
+ event = &view->adbevent;
+ dns_adb_whenshutdown(view->adb, view->task, &event);
+ view->attributes &= ~DNS_VIEWATTR_ADBSHUTDOWN;
+
+ result = dns_requestmgr_create(view->mctx, timermgr, socketmgr,
+ dns_resolver_taskmgr(view->resolver),
+ dns_resolver_dispatchmgr(view->resolver),
+ dns_resolver_dispatchv4(view->resolver),
+ dns_resolver_dispatchv6(view->resolver),
+ &view->requestmgr);
+ if (result != ISC_R_SUCCESS) {
+ dns_adb_shutdown(view->adb);
+ dns_resolver_shutdown(view->resolver);
+ return (result);
+ }
+ event = &view->reqevent;
+ dns_requestmgr_whenshutdown(view->requestmgr, view->task, &event);
+ view->attributes &= ~DNS_VIEWATTR_REQSHUTDOWN;
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_view_setcache(dns_view_t *view, dns_cache_t *cache) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(!view->frozen);
+
+ if (view->cache != NULL) {
+ if (view->acache != NULL)
+ dns_acache_putdb(view->acache, view->cachedb);
+ dns_db_detach(&view->cachedb);
+ dns_cache_detach(&view->cache);
+ }
+ dns_cache_attach(cache, &view->cache);
+ dns_cache_attachdb(cache, &view->cachedb);
+ INSIST(DNS_DB_VALID(view->cachedb));
+
+ if (view->acache != NULL)
+ dns_acache_setdb(view->acache, view->cachedb);
+}
+
+void
+dns_view_sethints(dns_view_t *view, dns_db_t *hints) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(!view->frozen);
+ REQUIRE(view->hints == NULL);
+ REQUIRE(dns_db_iszone(hints));
+
+ dns_db_attach(hints, &view->hints);
+}
+
+void
+dns_view_setkeyring(dns_view_t *view, dns_tsig_keyring_t *ring) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(ring != NULL);
+ if (view->statickeys != NULL)
+ dns_tsigkeyring_destroy(&view->statickeys);
+ view->statickeys = ring;
+}
+
+void
+dns_view_setdstport(dns_view_t *view, in_port_t dstport) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ view->dstport = dstport;
+}
+
+isc_result_t
+dns_view_addzone(dns_view_t *view, dns_zone_t *zone) {
+ isc_result_t result;
+
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(!view->frozen);
+
+ result = dns_zt_mount(view->zonetable, zone);
+
+ return (result);
+}
+
+void
+dns_view_freeze(dns_view_t *view) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(!view->frozen);
+
+ if (view->resolver != NULL) {
+ INSIST(view->cachedb != NULL);
+ dns_resolver_freeze(view->resolver);
+ }
+ view->frozen = ISC_TRUE;
+}
+
+isc_result_t
+dns_view_findzone(dns_view_t *view, dns_name_t *name, dns_zone_t **zonep) {
+ isc_result_t result;
+
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ result = dns_zt_find(view->zonetable, name, 0, NULL, zonep);
+ if (result == DNS_R_PARTIALMATCH) {
+ dns_zone_detach(zonep);
+ result = ISC_R_NOTFOUND;
+ }
+
+ return (result);
+}
+
+isc_result_t
+dns_view_find(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
+ isc_stdtime_t now, unsigned int options, isc_boolean_t use_hints,
+ dns_db_t **dbp, dns_dbnode_t **nodep, dns_name_t *foundname,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+ isc_result_t result;
+ dns_db_t *db, *zdb;
+ dns_dbnode_t *node, *znode;
+ isc_boolean_t is_cache;
+ dns_rdataset_t zrdataset, zsigrdataset;
+ dns_zone_t *zone;
+
+ /*
+ * Find an rdataset whose owner name is 'name', and whose type is
+ * 'type'.
+ */
+
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(view->frozen);
+ REQUIRE(type != dns_rdatatype_rrsig);
+ REQUIRE(rdataset != NULL); /* XXXBEW - remove this */
+ REQUIRE(nodep == NULL || *nodep == NULL);
+
+ /*
+ * Initialize.
+ */
+ dns_rdataset_init(&zrdataset);
+ dns_rdataset_init(&zsigrdataset);
+ zdb = NULL;
+ znode = NULL;
+
+ /*
+ * Find a database to answer the query.
+ */
+ zone = NULL;
+ db = NULL;
+ node = NULL;
+ result = dns_zt_find(view->zonetable, name, 0, NULL, &zone);
+ if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
+ result = dns_zone_getdb(zone, &db);
+ if (result != ISC_R_SUCCESS && view->cachedb != NULL)
+ dns_db_attach(view->cachedb, &db);
+ else if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ } else if (result == ISC_R_NOTFOUND && view->cachedb != NULL)
+ dns_db_attach(view->cachedb, &db);
+ else
+ goto cleanup;
+
+ is_cache = dns_db_iscache(db);
+
+ db_find:
+ /*
+ * Now look for an answer in the database.
+ */
+ result = dns_db_find(db, name, NULL, type, options,
+ now, &node, foundname, rdataset, sigrdataset);
+
+ if (result == DNS_R_DELEGATION ||
+ result == ISC_R_NOTFOUND) {
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (!is_cache) {
+ dns_db_detach(&db);
+ if (view->cachedb != NULL) {
+ /*
+ * Either the answer is in the cache, or we
+ * don't know it.
+ */
+ is_cache = ISC_TRUE;
+ dns_db_attach(view->cachedb, &db);
+ goto db_find;
+ }
+ } else {
+ /*
+ * We don't have the data in the cache. If we've got
+ * glue from the zone, use it.
+ */
+ if (dns_rdataset_isassociated(&zrdataset)) {
+ dns_rdataset_clone(&zrdataset, rdataset);
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(&zsigrdataset))
+ dns_rdataset_clone(&zsigrdataset,
+ sigrdataset);
+ result = DNS_R_GLUE;
+ if (db != NULL)
+ dns_db_detach(&db);
+ dns_db_attach(zdb, &db);
+ dns_db_attachnode(db, znode, &node);
+ goto cleanup;
+ }
+ }
+ /*
+ * We don't know the answer.
+ */
+ result = ISC_R_NOTFOUND;
+ } else if (result == DNS_R_GLUE) {
+ if (view->cachedb != NULL) {
+ /*
+ * We found an answer, but the cache may be better.
+ * Remember what we've got and go look in the cache.
+ */
+ is_cache = ISC_TRUE;
+ dns_rdataset_clone(rdataset, &zrdataset);
+ dns_rdataset_disassociate(rdataset);
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset)) {
+ dns_rdataset_clone(sigrdataset, &zsigrdataset);
+ dns_rdataset_disassociate(sigrdataset);
+ }
+ dns_db_attach(db, &zdb);
+ dns_db_attachnode(zdb, node, &znode);
+ dns_db_detachnode(db, &node);
+ dns_db_detach(&db);
+ dns_db_attach(view->cachedb, &db);
+ goto db_find;
+ }
+ /*
+ * Otherwise, the glue is the best answer.
+ */
+ result = ISC_R_SUCCESS;
+ }
+
+ if (result == ISC_R_NOTFOUND && use_hints && view->hints != NULL) {
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ if (db != NULL) {
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ dns_db_detach(&db);
+ }
+ result = dns_db_find(view->hints, name, NULL, type, options,
+ now, &node, foundname,
+ rdataset, sigrdataset);
+ if (result == ISC_R_SUCCESS || result == DNS_R_GLUE) {
+ /*
+ * We just used a hint. Let the resolver know it
+ * should consider priming.
+ */
+ dns_resolver_prime(view->resolver);
+ dns_db_attach(view->hints, &db);
+ result = DNS_R_HINT;
+ } else if (result == DNS_R_NXRRSET) {
+ dns_db_attach(view->hints, &db);
+ result = DNS_R_HINTNXRRSET;
+ } else if (result == DNS_R_NXDOMAIN)
+ result = ISC_R_NOTFOUND;
+
+ /*
+ * Cleanup if non-standard hints are used.
+ */
+ if (db == NULL && node != NULL)
+ dns_db_detachnode(view->hints, &node);
+ }
+
+ cleanup:
+ if (dns_rdataset_isassociated(&zrdataset)) {
+ dns_rdataset_disassociate(&zrdataset);
+ if (dns_rdataset_isassociated(&zsigrdataset))
+ dns_rdataset_disassociate(&zsigrdataset);
+ }
+
+ if (zdb != NULL) {
+ if (znode != NULL)
+ dns_db_detachnode(zdb, &znode);
+ dns_db_detach(&zdb);
+ }
+
+ if (db != NULL) {
+ if (node != NULL) {
+ if (nodep != NULL)
+ *nodep = node;
+ else
+ dns_db_detachnode(db, &node);
+ }
+ if (dbp != NULL)
+ *dbp = db;
+ else
+ dns_db_detach(&db);
+ } else
+ INSIST(node == NULL);
+
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+
+ return (result);
+}
+
+isc_result_t
+dns_view_simplefind(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
+ isc_stdtime_t now, unsigned int options,
+ isc_boolean_t use_hints,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+ isc_result_t result;
+ dns_fixedname_t foundname;
+
+ dns_fixedname_init(&foundname);
+ result = dns_view_find(view, name, type, now, options, use_hints,
+ NULL, NULL, dns_fixedname_name(&foundname),
+ rdataset, sigrdataset);
+ if (result == DNS_R_NXDOMAIN) {
+ /*
+ * The rdataset and sigrdataset of the relevant NSEC record
+ * may be returned, but the caller cannot use them because
+ * foundname is not returned by this simplified API. We
+ * disassociate them here to prevent any misuse by the caller.
+ */
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ } else if (result != ISC_R_SUCCESS &&
+ result != DNS_R_GLUE &&
+ result != DNS_R_HINT &&
+ result != DNS_R_NCACHENXDOMAIN &&
+ result != DNS_R_NCACHENXRRSET &&
+ result != DNS_R_NXRRSET &&
+ result != DNS_R_HINTNXRRSET &&
+ result != ISC_R_NOTFOUND) {
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ result = ISC_R_NOTFOUND;
+ }
+
+ return (result);
+}
+
+isc_result_t
+dns_view_findzonecut(dns_view_t *view, dns_name_t *name, dns_name_t *fname,
+ isc_stdtime_t now, unsigned int options,
+ isc_boolean_t use_hints,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+ return(dns_view_findzonecut2(view, name, fname, now, options,
+ use_hints, ISC_TRUE,
+ rdataset, sigrdataset));
+}
+
+isc_result_t
+dns_view_findzonecut2(dns_view_t *view, dns_name_t *name, dns_name_t *fname,
+ isc_stdtime_t now, unsigned int options,
+ isc_boolean_t use_hints, isc_boolean_t use_cache,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+ isc_result_t result;
+ dns_db_t *db;
+ isc_boolean_t is_cache, use_zone, try_hints;
+ dns_zone_t *zone;
+ dns_name_t *zfname;
+ dns_rdataset_t zrdataset, zsigrdataset;
+ dns_fixedname_t zfixedname;
+
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(view->frozen);
+
+ db = NULL;
+ zone = NULL;
+ use_zone = ISC_FALSE;
+ try_hints = ISC_FALSE;
+ zfname = NULL;
+
+ /*
+ * Initialize.
+ */
+ dns_fixedname_init(&zfixedname);
+ dns_rdataset_init(&zrdataset);
+ dns_rdataset_init(&zsigrdataset);
+
+ /*
+ * Find the right database.
+ */
+ result = dns_zt_find(view->zonetable, name, 0, NULL, &zone);
+ if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
+ result = dns_zone_getdb(zone, &db);
+ if (result == ISC_R_NOTFOUND) {
+ /*
+ * We're not directly authoritative for this query name, nor
+ * is it a subdomain of any zone for which we're
+ * authoritative.
+ */
+ if (use_cache && view->cachedb != NULL) {
+ /*
+ * We have a cache; try it.
+ */
+ dns_db_attach(view->cachedb, &db);
+ } else {
+ /*
+ * Maybe we have hints...
+ */
+ try_hints = ISC_TRUE;
+ goto finish;
+ }
+ } else if (result != ISC_R_SUCCESS) {
+ /*
+ * Something is broken.
+ */
+ goto cleanup;
+ }
+ is_cache = dns_db_iscache(db);
+
+ db_find:
+ /*
+ * Look for the zonecut.
+ */
+ if (!is_cache) {
+ result = dns_db_find(db, name, NULL, dns_rdatatype_ns, options,
+ now, NULL, fname, rdataset, sigrdataset);
+ if (result == DNS_R_DELEGATION)
+ result = ISC_R_SUCCESS;
+ else if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ if (use_cache && view->cachedb != NULL && db != view->hints) {
+ /*
+ * We found an answer, but the cache may be better.
+ */
+ zfname = dns_fixedname_name(&zfixedname);
+ result = dns_name_copy(fname, zfname, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ dns_rdataset_clone(rdataset, &zrdataset);
+ dns_rdataset_disassociate(rdataset);
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset)) {
+ dns_rdataset_clone(sigrdataset, &zsigrdataset);
+ dns_rdataset_disassociate(sigrdataset);
+ }
+ dns_db_detach(&db);
+ dns_db_attach(view->cachedb, &db);
+ is_cache = ISC_TRUE;
+ goto db_find;
+ }
+ } else {
+ result = dns_db_findzonecut(db, name, options, now, NULL,
+ fname, rdataset, sigrdataset);
+ if (result == ISC_R_SUCCESS) {
+ if (zfname != NULL &&
+ !dns_name_issubdomain(fname, zfname)) {
+ /*
+ * We found a zonecut in the cache, but our
+ * zone delegation is better.
+ */
+ use_zone = ISC_TRUE;
+ }
+ } else if (result == ISC_R_NOTFOUND) {
+ if (zfname != NULL) {
+ /*
+ * We didn't find anything in the cache, but we
+ * have a zone delegation, so use it.
+ */
+ use_zone = ISC_TRUE;
+ } else {
+ /*
+ * Maybe we have hints...
+ */
+ try_hints = ISC_TRUE;
+ }
+ } else {
+ /*
+ * Something bad happened.
+ */
+ goto cleanup;
+ }
+ }
+
+ finish:
+ if (use_zone) {
+ if (dns_rdataset_isassociated(rdataset)) {
+ dns_rdataset_disassociate(rdataset);
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ }
+ result = dns_name_copy(zfname, fname, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ dns_rdataset_clone(&zrdataset, rdataset);
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(&zrdataset))
+ dns_rdataset_clone(&zsigrdataset, sigrdataset);
+ } else if (try_hints && use_hints && view->hints != NULL) {
+ /*
+ * We've found nothing so far, but we have hints.
+ */
+ result = dns_db_find(view->hints, dns_rootname, NULL,
+ dns_rdatatype_ns, 0, now, NULL, fname,
+ rdataset, NULL);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * We can't even find the hints for the root
+ * nameservers!
+ */
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ result = ISC_R_NOTFOUND;
+ }
+ }
+
+ cleanup:
+ if (dns_rdataset_isassociated(&zrdataset)) {
+ dns_rdataset_disassociate(&zrdataset);
+ if (dns_rdataset_isassociated(&zsigrdataset))
+ dns_rdataset_disassociate(&zsigrdataset);
+ }
+ if (db != NULL)
+ dns_db_detach(&db);
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+
+ return (result);
+}
+
+isc_result_t
+dns_viewlist_find(dns_viewlist_t *list, const char *name,
+ dns_rdataclass_t rdclass, dns_view_t **viewp)
+{
+ dns_view_t *view;
+
+ REQUIRE(list != NULL);
+
+ for (view = ISC_LIST_HEAD(*list);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link)) {
+ if (strcmp(view->name, name) == 0 && view->rdclass == rdclass)
+ break;
+ }
+ if (view == NULL)
+ return (ISC_R_NOTFOUND);
+
+ dns_view_attach(view, viewp);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_viewlist_findzone(dns_viewlist_t *list, dns_name_t *name,
+ isc_boolean_t allclasses, dns_rdataclass_t rdclass,
+ dns_zone_t **zonep)
+{
+ dns_view_t *view;
+ isc_result_t result;
+ dns_zone_t *zone1 = NULL, *zone2 = NULL;
+ dns_zone_t **zp = NULL;;
+
+ REQUIRE(list != NULL);
+ for (view = ISC_LIST_HEAD(*list);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link)) {
+ if (allclasses == ISC_FALSE && view->rdclass != rdclass)
+ continue;
+
+ /*
+ * If the zone is defined in more than one view,
+ * treat it as not found.
+ */
+ zp = (zone1 == NULL) ? &zone1 : &zone2;
+ result = dns_zt_find(view->zonetable, name, 0, NULL, zp);
+ INSIST(result == ISC_R_SUCCESS ||
+ result == ISC_R_NOTFOUND ||
+ result == DNS_R_PARTIALMATCH);
+
+ /* Treat a partial match as no match */
+ if (result == DNS_R_PARTIALMATCH) {
+ dns_zone_detach(zp);
+ result = ISC_R_NOTFOUND;
+ }
+
+ if (zone2 != NULL) {
+ dns_zone_detach(&zone1);
+ dns_zone_detach(&zone2);
+ return (ISC_R_NOTFOUND);
+ }
+ }
+
+ if (zone1 != NULL) {
+ dns_zone_attach(zone1, zonep);
+ dns_zone_detach(&zone1);
+ return (ISC_R_SUCCESS);
+ }
+
+ return (ISC_R_NOTFOUND);
+}
+
+isc_result_t
+dns_view_load(dns_view_t *view, isc_boolean_t stop) {
+
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ return (dns_zt_load(view->zonetable, stop));
+}
+
+isc_result_t
+dns_view_loadnew(dns_view_t *view, isc_boolean_t stop) {
+
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ return (dns_zt_loadnew(view->zonetable, stop));
+}
+
+isc_result_t
+dns_view_gettsig(dns_view_t *view, dns_name_t *keyname, dns_tsigkey_t **keyp)
+{
+ isc_result_t result;
+ REQUIRE(keyp != NULL && *keyp == NULL);
+
+ result = dns_tsigkey_find(keyp, keyname, NULL,
+ view->statickeys);
+ if (result == ISC_R_NOTFOUND)
+ result = dns_tsigkey_find(keyp, keyname, NULL,
+ view->dynamickeys);
+ return (result);
+}
+
+isc_result_t
+dns_view_getpeertsig(dns_view_t *view, isc_netaddr_t *peeraddr,
+ dns_tsigkey_t **keyp)
+{
+ isc_result_t result;
+ dns_name_t *keyname = NULL;
+ dns_peer_t *peer = NULL;
+
+ result = dns_peerlist_peerbyaddr(view->peers, peeraddr, &peer);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_peer_getkey(peer, &keyname);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ return (dns_view_gettsig(view, keyname, keyp));
+}
+
+isc_result_t
+dns_view_checksig(dns_view_t *view, isc_buffer_t *source, dns_message_t *msg) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(source != NULL);
+
+ return (dns_tsig_verify(source, msg, view->statickeys,
+ view->dynamickeys));
+}
+
+isc_result_t
+dns_view_dumpdbtostream(dns_view_t *view, FILE *fp) {
+ isc_result_t result;
+
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ (void)fprintf(fp, ";\n; Cache dump of view '%s'\n;\n", view->name);
+ result = dns_master_dumptostream(view->mctx, view->cachedb, NULL,
+ &dns_master_style_cache, fp);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ dns_adb_dump(view->adb, fp);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_view_flushcache(dns_view_t *view) {
+ isc_result_t result;
+
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ if (view->cachedb == NULL)
+ return (ISC_R_SUCCESS);
+ result = dns_cache_flush(view->cache);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (view->acache != NULL)
+ dns_acache_putdb(view->acache, view->cachedb);
+ dns_db_detach(&view->cachedb);
+ dns_cache_attachdb(view->cache, &view->cachedb);
+ if (view->acache != NULL)
+ dns_acache_setdb(view->acache, view->cachedb);
+
+ dns_adb_flush(view->adb);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_view_flushname(dns_view_t *view, dns_name_t *name) {
+
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ if (view->adb != NULL)
+ dns_adb_flushname(view->adb, name);
+ if (view->cache == NULL)
+ return (ISC_R_SUCCESS);
+ return (dns_cache_flushname(view->cache, name));
+}
+
+isc_result_t
+dns_view_adddelegationonly(dns_view_t *view, dns_name_t *name) {
+ isc_result_t result;
+ dns_name_t *new;
+ isc_uint32_t hash;
+
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ if (view->delonly == NULL) {
+ view->delonly = isc_mem_get(view->mctx,
+ sizeof(dns_namelist_t) *
+ DNS_VIEW_DELONLYHASH);
+ if (view->delonly == NULL)
+ return (ISC_R_NOMEMORY);
+ for (hash = 0; hash < DNS_VIEW_DELONLYHASH; hash++)
+ ISC_LIST_INIT(view->delonly[hash]);
+ }
+ hash = dns_name_hash(name, ISC_FALSE) % DNS_VIEW_DELONLYHASH;
+ new = ISC_LIST_HEAD(view->delonly[hash]);
+ while (new != NULL && !dns_name_equal(new, name))
+ new = ISC_LIST_NEXT(new, link);
+ if (new != NULL)
+ return (ISC_R_SUCCESS);
+ new = isc_mem_get(view->mctx, sizeof(*new));
+ if (new == NULL)
+ return (ISC_R_NOMEMORY);
+ dns_name_init(new, NULL);
+ result = dns_name_dup(name, view->mctx, new);
+ if (result == ISC_R_SUCCESS)
+ ISC_LIST_APPEND(view->delonly[hash], new, link);
+ else
+ isc_mem_put(view->mctx, new, sizeof(*new));
+ return (result);
+}
+
+isc_result_t
+dns_view_excludedelegationonly(dns_view_t *view, dns_name_t *name) {
+ isc_result_t result;
+ dns_name_t *new;
+ isc_uint32_t hash;
+
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ if (view->rootexclude == NULL) {
+ view->rootexclude = isc_mem_get(view->mctx,
+ sizeof(dns_namelist_t) *
+ DNS_VIEW_DELONLYHASH);
+ if (view->rootexclude == NULL)
+ return (ISC_R_NOMEMORY);
+ for (hash = 0; hash < DNS_VIEW_DELONLYHASH; hash++)
+ ISC_LIST_INIT(view->rootexclude[hash]);
+ }
+ hash = dns_name_hash(name, ISC_FALSE) % DNS_VIEW_DELONLYHASH;
+ new = ISC_LIST_HEAD(view->rootexclude[hash]);
+ while (new != NULL && !dns_name_equal(new, name))
+ new = ISC_LIST_NEXT(new, link);
+ if (new != NULL)
+ return (ISC_R_SUCCESS);
+ new = isc_mem_get(view->mctx, sizeof(*new));
+ if (new == NULL)
+ return (ISC_R_NOMEMORY);
+ dns_name_init(new, NULL);
+ result = dns_name_dup(name, view->mctx, new);
+ if (result == ISC_R_SUCCESS)
+ ISC_LIST_APPEND(view->rootexclude[hash], new, link);
+ else
+ isc_mem_put(view->mctx, new, sizeof(*new));
+ return (result);
+}
+
+isc_boolean_t
+dns_view_isdelegationonly(dns_view_t *view, dns_name_t *name) {
+ dns_name_t *new;
+ isc_uint32_t hash;
+
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ if (!view->rootdelonly && view->delonly == NULL)
+ return (ISC_FALSE);
+
+ hash = dns_name_hash(name, ISC_FALSE) % DNS_VIEW_DELONLYHASH;
+ if (view->rootdelonly && dns_name_countlabels(name) <= 2) {
+ if (view->rootexclude == NULL)
+ return (ISC_TRUE);
+ new = ISC_LIST_HEAD(view->rootexclude[hash]);
+ while (new != NULL && !dns_name_equal(new, name))
+ new = ISC_LIST_NEXT(new, link);
+ if (new == NULL)
+ return (ISC_TRUE);
+ }
+
+ if (view->delonly == NULL)
+ return (ISC_FALSE);
+
+ new = ISC_LIST_HEAD(view->delonly[hash]);
+ while (new != NULL && !dns_name_equal(new, name))
+ new = ISC_LIST_NEXT(new, link);
+ if (new == NULL)
+ return (ISC_FALSE);
+ return (ISC_TRUE);
+}
+
+void
+dns_view_setrootdelonly(dns_view_t *view, isc_boolean_t value) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ view->rootdelonly = value;
+}
+
+isc_boolean_t
+dns_view_getrootdelonly(dns_view_t *view) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ return (view->rootdelonly);
+}
+
+isc_result_t
+dns_view_freezezones(dns_view_t *view, isc_boolean_t value) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ return (dns_zt_freezezones(view->zonetable, value));
+}
+
+void
+dns_view_setresstats(dns_view_t *view, dns_stats_t *stats) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(!view->frozen);
+ REQUIRE(view->resstats == NULL);
+
+ dns_stats_attach(stats, &view->resstats);
+}
+
+void
+dns_view_getresstats(dns_view_t *view, dns_stats_t **statsp) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(statsp != NULL && *statsp == NULL);
+
+ if (view->resstats != NULL)
+ dns_stats_attach(view->resstats, statsp);
+}
+
+void
+dns_view_setresquerystats(dns_view_t *view, dns_stats_t *stats) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(!view->frozen);
+ REQUIRE(view->resquerystats == NULL);
+
+ dns_stats_attach(stats, &view->resquerystats);
+}
+
+void
+dns_view_getresquerystats(dns_view_t *view, dns_stats_t **statsp) {
+ REQUIRE(DNS_VIEW_VALID(view));
+ REQUIRE(statsp != NULL && *statsp == NULL);
+
+ if (view->resquerystats != NULL)
+ dns_stats_attach(view->resquerystats, statsp);
+}
diff --git a/lib/dns/win32/DLLMain.c b/lib/dns/win32/DLLMain.c
new file mode 100644
index 0000000..7c1e129
--- /dev/null
+++ b/lib/dns/win32/DLLMain.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: DLLMain.c,v 1.6 2007/06/18 23:47:44 tbox Exp $ */
+
+#include <windows.h>
+#include <signal.h>
+
+/*
+ * Called when we enter the DLL
+ */
+__declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hinstDLL,
+ DWORD fdwReason, LPVOID lpvReserved)
+{
+ switch (fdwReason) {
+ /*
+ * The DLL is loading due to process
+ * initialization or a call to LoadLibrary.
+ */
+ case DLL_PROCESS_ATTACH:
+ break;
+
+ /* The attached process creates a new thread. */
+ case DLL_THREAD_ATTACH:
+ break;
+
+ /* The thread of the attached process terminates. */
+ case DLL_THREAD_DETACH:
+ break;
+
+ /*
+ * The DLL is unloading from a process due to
+ * process termination or a call to FreeLibrary.
+ */
+ case DLL_PROCESS_DETACH:
+ break;
+
+ default:
+ break;
+ }
+ return (TRUE);
+}
+
diff --git a/lib/dns/win32/gen.dsp b/lib/dns/win32/gen.dsp
new file mode 100644
index 0000000..1d0fc34
--- /dev/null
+++ b/lib/dns/win32/gen.dsp
@@ -0,0 +1,107 @@
+# Microsoft Developer Studio Project File - Name="gen" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=gen - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "gen.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "gen.mak" CFG="gen - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "gen - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "gen - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "gen - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /I "./" /I "../../../" /I "../../../lib/isc/win32" /I "../../../lib/isc/win32/include" /I "../../../lib/isc/include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "LIBISC_EXPORTS" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 user32.lib advapi32.lib /nologo /subsystem:console /machine:I386 /out:"../gen.exe"
+
+!ELSEIF "$(CFG)" == "gen - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "./" /I "../../../" /I "../../../lib/isc/win32" /I "../../../lib/isc/win32/include" /I "../../../lib/isc/include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "LIBISC_EXPORTS" /FR /FD /GZ /c
+# SUBTRACT CPP /X /YX
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 user32.lib advapi32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../gen.exe" /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "gen - Win32 Release"
+# Name "gen - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\gen.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE="..\gen-win32.h"
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/lib/dns/win32/gen.dsw b/lib/dns/win32/gen.dsw
new file mode 100644
index 0000000..e44f589
--- /dev/null
+++ b/lib/dns/win32/gen.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "gen"=".\gen.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/lib/dns/win32/gen.mak b/lib/dns/win32/gen.mak
new file mode 100644
index 0000000..5800af1
--- /dev/null
+++ b/lib/dns/win32/gen.mak
@@ -0,0 +1,267 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on gen.dsp
+!IF "$(CFG)" == ""
+CFG=gen - Win32 Debug
+!MESSAGE No configuration specified. Defaulting to gen - Win32 Debug.
+!ENDIF
+
+!IF "$(CFG)" != "gen - Win32 Release" && "$(CFG)" != "gen - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "gen.mak" CFG="gen - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "gen - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "gen - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "gen - Win32 Release"
+_VC_MANIFEST_INC=0
+_VC_MANIFEST_BASENAME=__VC80
+!ELSE
+_VC_MANIFEST_INC=1
+_VC_MANIFEST_BASENAME=__VC80.Debug
+!ENDIF
+
+####################################################
+# Specifying name of temporary resource file used only in incremental builds:
+
+!if "$(_VC_MANIFEST_INC)" == "1"
+_VC_MANIFEST_AUTO_RES=$(_VC_MANIFEST_BASENAME).auto.res
+!else
+_VC_MANIFEST_AUTO_RES=
+!endif
+
+####################################################
+# _VC_MANIFEST_EMBED_EXE - command to embed manifest in EXE:
+
+!if "$(_VC_MANIFEST_INC)" == "1"
+
+#MT_SPECIAL_RETURN=1090650113
+#MT_SPECIAL_SWITCH=-notify_resource_update
+MT_SPECIAL_RETURN=0
+MT_SPECIAL_SWITCH=
+_VC_MANIFEST_EMBED_EXE= \
+if exist $@.manifest mt.exe -manifest $@.manifest -out:$(_VC_MANIFEST_BASENAME).auto.manifest $(MT_SPECIAL_SWITCH) & \
+if "%ERRORLEVEL%" == "$(MT_SPECIAL_RETURN)" \
+rc /r $(_VC_MANIFEST_BASENAME).auto.rc & \
+link $** /out:$@ $(LFLAGS)
+
+!else
+
+_VC_MANIFEST_EMBED_EXE= \
+if exist $@.manifest mt.exe -manifest $@.manifest -outputresource:$@;1
+
+!endif
+
+####################################################
+# _VC_MANIFEST_EMBED_DLL - command to embed manifest in DLL:
+
+!if "$(_VC_MANIFEST_INC)" == "1"
+
+#MT_SPECIAL_RETURN=1090650113
+#MT_SPECIAL_SWITCH=-notify_resource_update
+MT_SPECIAL_RETURN=0
+MT_SPECIAL_SWITCH=
+_VC_MANIFEST_EMBED_EXE= \
+if exist $@.manifest mt.exe -manifest $@.manifest -out:$(_VC_MANIFEST_BASENAME).auto.manifest $(MT_SPECIAL_SWITCH) & \
+if "%ERRORLEVEL%" == "$(MT_SPECIAL_RETURN)" \
+rc /r $(_VC_MANIFEST_BASENAME).auto.rc & \
+link $** /out:$@ $(LFLAGS)
+
+!else
+
+_VC_MANIFEST_EMBED_EXE= \
+if exist $@.manifest mt.exe -manifest $@.manifest -outputresource:$@;2
+
+!endif
+####################################################
+# _VC_MANIFEST_CLEAN - command to clean resources files generated temporarily:
+
+!if "$(_VC_MANIFEST_INC)" == "1"
+
+_VC_MANIFEST_CLEAN=-del $(_VC_MANIFEST_BASENAME).auto.res \
+ $(_VC_MANIFEST_BASENAME).auto.rc \
+ $(_VC_MANIFEST_BASENAME).auto.manifest
+
+!else
+
+_VC_MANIFEST_CLEAN=
+
+!endif
+
+!IF "$(CFG)" == "gen - Win32 Release"
+
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL : "..\gen.exe"
+
+
+CLEAN :
+ -@erase "$(INTDIR)\gen.obj"
+ -@erase "$(INTDIR)\vc60.idb"
+ -@erase "..\gen.exe"
+ -@$(_VC_MANIFEST_CLEAN)
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP_PROJ=/nologo /ML /W3 /GX /O2 /I "./" /I "../../../" /I "../../../lib/isc/win32" /I "../../../lib/isc/win32/include" /I "../../../lib/isc/include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "LIBISC_EXPORTS" /Fp"$(INTDIR)\gen.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\gen.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=user32.lib advapi32.lib /nologo /subsystem:console /incremental:no /pdb:"$(OUTDIR)\gen.pdb" /machine:I386 /out:"../gen.exe"
+LINK32_OBJS= \
+ "$(INTDIR)\gen.obj"
+
+"..\gen.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+ $(_VC_MANIFEST_EMBED_EXE)
+
+!ELSEIF "$(CFG)" == "gen - Win32 Debug"
+
+OUTDIR=.\Debug
+INTDIR=.\Debug
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+ALL : "..\gen.exe" "$(OUTDIR)\gen.bsc"
+
+
+CLEAN :
+ -@erase "$(INTDIR)\gen.obj"
+ -@erase "$(INTDIR)\gen.sbr"
+ -@erase "$(INTDIR)\vc60.idb"
+ -@erase "$(INTDIR)\vc60.pdb"
+ -@erase "$(OUTDIR)\gen.bsc"
+ -@erase "$(OUTDIR)\gen.pdb"
+ -@erase "..\gen.exe"
+ -@erase "..\gen.ilk"
+ -@$(_VC_MANIFEST_CLEAN)
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP_PROJ=/nologo /MLd /W3 /Gm /GX /ZI /Od /I "./" /I "../../../" /I "../../../lib/isc/win32" /I "../../../lib/isc/win32/include" /I "../../../lib/isc/include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "LIBISC_EXPORTS" /FR"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\gen.bsc"
+BSC32_SBRS= \
+ "$(INTDIR)\gen.sbr"
+
+"$(OUTDIR)\gen.bsc" : "$(OUTDIR)" $(BSC32_SBRS)
+ $(BSC32) @<<
+ $(BSC32_FLAGS) $(BSC32_SBRS)
+<<
+
+LINK32=link.exe
+LINK32_FLAGS=user32.lib advapi32.lib /nologo /subsystem:console /incremental:yes /pdb:"$(OUTDIR)\gen.pdb" /debug /machine:I386 /out:"../gen.exe" /pdbtype:sept
+LINK32_OBJS= \
+ "$(INTDIR)\gen.obj"
+
+"..\gen.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+ $(_VC_MANIFEST_EMBED_EXE)
+
+!ENDIF
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+
+!IF "$(NO_EXTERNAL_DEPS)" != "1"
+!IF EXISTS("gen.dep")
+!INCLUDE "gen.dep"
+!ELSE
+!MESSAGE Warning: cannot find "gen.dep"
+!ENDIF
+!ENDIF
+
+
+!IF "$(CFG)" == "gen - Win32 Release" || "$(CFG)" == "gen - Win32 Debug"
+SOURCE=..\gen.c
+
+!IF "$(CFG)" == "gen - Win32 Release"
+
+
+"$(INTDIR)\gen.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "gen - Win32 Debug"
+
+
+"$(INTDIR)\gen.obj" "$(INTDIR)\gen.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+
+!ENDIF
+
+####################################################
+# Commands to generate initial empty manifest file and the RC file
+# that references it, and for generating the .res file:
+
+$(_VC_MANIFEST_BASENAME).auto.res : $(_VC_MANIFEST_BASENAME).auto.rc
+
+$(_VC_MANIFEST_BASENAME).auto.rc : $(_VC_MANIFEST_BASENAME).auto.manifest
+ type <<$@
+#include <winuser.h>
+1RT_MANIFEST"$(_VC_MANIFEST_BASENAME).auto.manifest"
+<< KEEP
+
+$(_VC_MANIFEST_BASENAME).auto.manifest :
+ type <<$@
+<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
+<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
+</assembly>
+<< KEEP
diff --git a/lib/dns/win32/libdns.def b/lib/dns/win32/libdns.def
new file mode 100644
index 0000000..d961b53
--- /dev/null
+++ b/lib/dns/win32/libdns.def
@@ -0,0 +1,861 @@
+LIBRARY libdns
+
+; Exported Functions
+EXPORTS
+
+dns_acache_attach
+dns_acache_attachentry
+dns_acache_cancelentry
+dns_acache_create
+dns_acache_createentry
+dns_acache_detach
+dns_acache_detachentry
+dns_acache_getentry
+dns_acache_putdb
+dns_acache_setcachesize
+dns_acache_setcleaninginterval
+dns_acache_setdb
+dns_acache_setentry
+dns_acache_shutdown
+dns_acl_any
+dns_acl_attach
+dns_acl_create
+dns_acl_detach
+dns_acl_isany
+dns_acl_isinsecure
+dns_acl_isnone
+dns_acl_match
+dns_acl_merge
+dns_acl_none
+dns_aclelement_match
+dns_aclenv_copy
+dns_aclenv_destroy
+dns_aclenv_init
+dns_adb_adjustsrtt
+dns_adb_attach
+dns_adb_cancelfind
+dns_adb_changeflags
+dns_adb_create
+dns_adb_createfind
+dns_adb_destroyfind
+dns_adb_detach
+dns_adb_dump
+dns_adb_dumpfind
+dns_adb_findaddrinfo
+dns_adb_flush
+dns_adb_freeaddrinfo
+dns_adb_marklame
+dns_adb_setadbsize
+dns_adb_shutdown
+dns_adb_whenshutdown
+dns_byaddr_cancel
+dns_byaddr_create
+dns_byaddr_createptrname
+dns_byaddr_createptrname2
+dns_byaddr_destroy
+dns_cache_attach
+dns_cache_attachdb
+dns_cache_clean
+dns_cache_create
+dns_cache_detach
+dns_cache_dump
+dns_cache_flush
+dns_cache_load
+dns_cache_setcachesize
+dns_cache_setcleaninginterval
+dns_cache_setfilename
+dns_cert_fromtext
+dns_cert_totext
+dns_compress_add
+dns_compress_findglobal
+dns_compress_getedns
+dns_compress_getmethods
+dns_compress_init
+dns_compress_invalidate
+dns_compress_rollback
+dns_compress_setmethods
+dns_compress_setsensitive
+dns_counter_fromtext
+dns_db_addrdataset
+dns_db_allrdatasets
+dns_db_attach
+dns_db_attachnode
+dns_db_attachversion
+dns_db_beginload
+dns_db_class
+dns_db_closeversion
+dns_db_create
+dns_db_createiterator
+dns_db_createsoatuple
+dns_db_currentversion
+dns_db_deleterdataset
+dns_db_detach
+dns_db_detachnode
+dns_db_diff
+dns_db_dump
+dns_db_endload
+dns_db_expirenode
+dns_db_find
+dns_db_findnode
+dns_db_findnsec3node
+dns_db_findrdataset
+dns_db_findzonecut
+dns_db_getnsec3parameters
+dns_db_getoriginnode
+dns_db_getrrsetstats
+dns_db_getsoaserial
+dns_db_iscache
+dns_db_isdnssec
+dns_db_ispersistent
+dns_db_issecure
+dns_db_isstub
+dns_db_iszone
+dns_db_load
+dns_db_load2
+dns_db_newversion
+dns_db_nodecount
+dns_db_ondestroy
+dns_db_origin
+dns_db_overmem
+dns_db_printnode
+dns_db_register
+dns_db_subtractrdataset
+dns_db_unregister
+dns_dbiterator_current
+dns_dbiterator_destroy
+dns_dbiterator_first
+dns_dbiterator_last
+dns_dbiterator_next
+dns_dbiterator_origin
+dns_dbiterator_pause
+dns_dbiterator_prev
+dns_dbiterator_seek
+dns_dbiterator_setcleanmode
+dns_dbtable_add
+dns_dbtable_adddefault
+dns_dbtable_attach
+dns_dbtable_create
+dns_dbtable_detach
+dns_dbtable_find
+dns_dbtable_getdefault
+dns_dbtable_remove
+dns_dbtable_removedefault
+dns_decompress_edns
+dns_decompress_getmethods
+dns_decompress_init
+dns_decompress_invalidate
+dns_decompress_setmethods
+dns_decompress_type
+dns_diff_append
+dns_diff_appendminimal
+dns_diff_apply
+dns_diff_applysilently
+dns_diff_clear
+dns_diff_init
+dns_diff_load
+dns_diff_print
+dns_diff_sort
+dns_difftuple_copy
+dns_difftuple_create
+dns_difftuple_free
+dns_dispatch_addresponse
+dns_dispatch_attach
+dns_dispatch_cancel
+dns_dispatch_changeattributes
+dns_dispatch_createtcp
+dns_dispatch_detach
+dns_dispatch_getlocaladdress
+dns_dispatch_getsocket
+dns_dispatch_getudp
+dns_dispatch_importrecv
+dns_dispatch_removeresponse
+dns_dispatch_starttcp
+dns_dispatchmgr_create
+dns_dispatchmgr_destroy
+dns_dispatchmgr_getblackhole
+dns_dispatchmgr_setavailports
+dns_dispatchmgr_setblackhole
+dns_dispatchmgr_setblackportlist
+dns_dispatchmgr_setstats
+dns_dlzallowzonexfr
+dns_dlzcreate
+dns_dlzdestroy
+dns_dlzfindzone
+dns_dlzregister
+dns_dlzstrtoargv
+dns_dlzunregister
+dns_dnssec_findzonekeys
+dns_dnssec_findzonekeys2
+dns_dnssec_keyfromrdata
+dns_dnssec_sign
+dns_dnssec_signmessage
+dns_dnssec_verify
+dns_dnssec_verify2
+dns_dnssec_verifymessage
+dns_ds_buildrdata
+dns_ds_digest_supported
+dns_dumpctx_detach
+dns_fwdtable_add
+dns_fwdtable_create
+dns_fwdtable_destroy
+dns_fwdtable_find
+dns_generalstats_create
+dns_generalstats_dump
+dns_generalstats_increment
+dns_iptable_addprefix
+dns_iptable_attach
+dns_iptable_create
+dns_iptable_detach
+dns_iptable_merge
+dns_journal_begin_transaction
+dns_journal_commit
+dns_journal_current_rr
+dns_journal_destroy
+dns_journal_first_rr
+dns_journal_first_serial
+dns_journal_iter_init
+dns_journal_last_serial
+dns_journal_next_rr
+dns_journal_open
+dns_journal_print
+dns_journal_rollforward
+dns_journal_write_transaction
+dns_journal_writediff
+dns_keyflags_fromtext
+dns_keynode_key
+dns_keytable_add
+dns_keytable_attach
+dns_keytable_create
+dns_keytable_detach
+dns_keytable_detachkeynode
+dns_keytable_finddeepestmatch
+dns_keytable_findkeynode
+dns_keytable_findnextkeynode
+dns_keytable_issecuredomain
+dns_lib_initmsgcat
+dns_loadctx_attach
+dns_loadctx_cancel
+dns_loadctx_detach
+dns_log_init
+dns_log_setcontext
+dns_lookup_cancel
+dns_lookup_create
+dns_lookup_destroy
+dns_master_dump
+dns_master_dumpnode
+dns_master_dumpnodetostream
+dns_master_dumptostream
+dns_master_dumptostream2
+dns_master_dumptostreaminc
+dns_master_loadbuffer
+dns_master_loadbufferinc
+dns_master_loadfile
+dns_master_loadfileinc
+dns_master_loadstream
+dns_master_loadstreaminc
+dns_master_questiontotext
+dns_master_rdatasettotext
+dns_master_stylecreate
+dns_master_styledestroy
+dns_message_addname
+dns_message_checksig
+dns_message_create
+dns_message_currentname
+dns_message_destroy
+dns_message_find
+dns_message_findname
+dns_message_findtype
+dns_message_firstname
+dns_message_getopt
+dns_message_getquerytsig
+dns_message_getrawmessage
+dns_message_getsig0
+dns_message_getsig0key
+dns_message_gettempname
+dns_message_gettempoffsets
+dns_message_gettemprdata
+dns_message_gettemprdatalist
+dns_message_gettemprdataset
+dns_message_gettimeadjust
+dns_message_gettsig
+dns_message_gettsigkey
+dns_message_movename
+dns_message_nextname
+dns_message_parse
+dns_message_peekheader
+dns_message_pseudosectiontotext
+dns_message_puttempname
+dns_message_puttemprdata
+dns_message_puttemprdatalist
+dns_message_puttemprdataset
+dns_message_rechecksig
+dns_message_removename
+dns_message_renderbegin
+dns_message_renderchangebuffer
+dns_message_renderend
+dns_message_renderheader
+dns_message_renderrelease
+dns_message_renderreserve
+dns_message_renderreset
+dns_message_rendersection
+dns_message_reply
+dns_message_reset
+dns_message_resetsig
+dns_message_sectiontotext
+dns_message_setopt
+dns_message_setquerytsig
+dns_message_setsig0key
+dns_message_setsortorder
+dns_message_settimeadjust
+dns_message_settsigkey
+dns_message_signer
+dns_message_takebuffer
+dns_message_totext
+dns_name_clone
+dns_name_compare
+dns_name_concatenate
+dns_name_copy
+dns_name_countlabels
+dns_name_destroy
+dns_name_digest
+dns_name_downcase
+dns_name_dup
+dns_name_dupwithoffsets
+dns_name_dynamic
+dns_name_equal
+dns_name_format
+dns_name_free
+dns_name_fromregion
+dns_name_fromtext
+dns_name_fromwire
+dns_name_fullcompare
+dns_name_getlabel
+dns_name_getlabelsequence
+dns_name_hasbuffer
+dns_name_hash
+dns_name_init
+dns_name_internalwildcard
+dns_name_invalidate
+dns_name_isabsolute
+dns_name_issubdomain
+dns_name_iswildcard
+dns_name_matcheswildcard
+dns_name_print
+dns_name_rdatacompare
+dns_name_reset
+dns_name_setbuffer
+dns_name_settotextfilter
+dns_name_split
+dns_name_tofilenametext
+dns_name_toregion
+dns_name_totext
+dns_name_towire
+dns_ncache_add
+dns_ncache_getrdataset
+dns_ncache_towire
+dns_nsec3_active
+dns_nsec3_addnsec3
+dns_nsec3_addnsec3s
+dns_nsec3_buildrdata
+dns_nsec3_delnsec3
+dns_nsec3_delnsec3s
+dns_nsec3_hashlength
+dns_nsec3_hashname
+dns_nsec3_maxiterations
+dns_nsec3_supportedhash
+dns_nsec3_typepresent
+dns_nsec_build
+dns_nsec_buildrdata
+dns_nsec_nseconly
+dns_nsec_typepresent
+dns_opcode_totext
+dns_opcodestats_create
+dns_opcodestats_dump
+dns_opcodestats_increment
+dns_order_add
+dns_order_attach
+dns_order_create
+dns_order_detach
+dns_order_find
+dns_peer_attach
+dns_peer_detach
+dns_peer_getbogus
+dns_peer_getkey
+dns_peer_getmaxudp
+dns_peer_getprovideixfr
+dns_peer_getrequestixfr
+dns_peer_getsupportedns
+dns_peer_gettransferformat
+dns_peer_gettransfers
+dns_peer_new
+dns_peer_newprefix
+dns_peer_setbogus
+dns_peer_setkey
+dns_peer_setkeybycharp
+dns_peer_setmaxudp
+dns_peer_setnotifysource
+dns_peer_setprovideixfr
+dns_peer_setquerysource
+dns_peer_setrequestixfr
+dns_peer_setrequestnsid
+dns_peer_setsupportedns
+dns_peer_settransferformat
+dns_peer_settransfers
+dns_peer_settransfersource
+dns_peer_setudpsize
+dns_peerlist_addpeer
+dns_peerlist_attach
+dns_peerlist_currpeer
+dns_peerlist_detach
+dns_peerlist_new
+dns_peerlist_peerbyaddr
+dns_portlist_add
+dns_portlist_create
+dns_portlist_detach
+dns_rbt_addname
+dns_rbt_addnode
+dns_rbt_create
+dns_rbt_deletename
+dns_rbt_deletenode
+dns_rbt_destroy
+dns_rbt_findname
+dns_rbt_findnode
+dns_rbt_formatnodename
+dns_rbt_fullnamefromnode
+dns_rbt_namefromnode
+dns_rbt_nodecount
+dns_rbt_printall
+dns_rbtnodechain_current
+dns_rbtnodechain_first
+dns_rbtnodechain_init
+dns_rbtnodechain_invalidate
+dns_rbtnodechain_last
+dns_rbtnodechain_next
+dns_rbtnodechain_prev
+dns_rbtnodechain_reset
+dns_rcode_fromtext
+dns_rcode_totext
+dns_rdata_additionaldata
+dns_rdata_checkowner
+dns_rdata_clone
+dns_rdata_compare
+dns_rdata_covers
+dns_rdata_digest
+dns_rdata_freestruct
+dns_rdata_fromregion
+dns_rdata_fromstruct
+dns_rdata_fromtext
+dns_rdata_fromwire
+dns_rdata_init
+dns_rdata_reset
+dns_rdata_tofmttext
+dns_rdata_toregion
+dns_rdata_tostruct
+dns_rdata_totext
+dns_rdata_towire
+dns_rdatacallbacks_init
+dns_rdatacallbacks_init_stdio
+dns_rdataclass_format
+dns_rdataclass_fromtext
+dns_rdataclass_ismeta
+dns_rdataclass_totext
+dns_rdatalist_init
+dns_rdatalist_tordataset
+dns_rdataset_additionaldata
+dns_rdataset_clone
+dns_rdataset_count
+dns_rdataset_current
+dns_rdataset_disassociate
+dns_rdataset_first
+dns_rdataset_getadditional
+dns_rdataset_getclosest
+dns_rdataset_getnoqname
+dns_rdataset_init
+dns_rdataset_invalidate
+dns_rdataset_isassociated
+dns_rdataset_makequestion
+dns_rdataset_next
+dns_rdataset_putadditional
+dns_rdataset_setadditional
+dns_rdataset_totext
+dns_rdataset_towire
+dns_rdataset_towiresorted
+dns_rdatasetiter_current
+dns_rdatasetiter_destroy
+dns_rdatasetiter_first
+dns_rdatasetiter_next
+dns_rdatasetstats_dump
+dns_rdataslab_equal
+dns_rdataslab_fromrdataset
+dns_rdataslab_merge
+dns_rdataslab_size
+dns_rdataslab_subtract
+dns_rdatatype_atparent
+dns_rdatatype_attributes
+dns_rdatatype_format
+dns_rdatatype_fromtext
+dns_rdatatype_isdnssec
+dns_rdatatype_isknown
+dns_rdatatype_ismeta
+dns_rdatatype_issingleton
+dns_rdatatype_iszonecutauth
+dns_rdatatype_notquestion
+dns_rdatatype_questiononly
+dns_rdatatype_totext
+dns_rdatatypestats_create
+dns_rdatatypestats_dump
+dns_rdatatypestats_increment
+dns_request_cancel
+dns_request_create
+dns_request_createraw
+dns_request_createvia
+dns_request_createvia3
+dns_request_destroy
+dns_request_getresponse
+dns_request_usedtcp
+dns_requestmgr_attach
+dns_requestmgr_create
+dns_requestmgr_detach
+dns_requestmgr_shutdown
+dns_requestmgr_whenshutdown
+dns_resolver_addalternate
+dns_resolver_algorithm_supported
+dns_resolver_attach
+dns_resolver_cancelfetch
+dns_resolver_create
+dns_resolver_createfetch
+dns_resolver_createfetch2
+dns_resolver_destroyfetch
+dns_resolver_detach
+dns_resolver_disable_algorithm
+dns_resolver_dispatchmgr
+dns_resolver_dispatchv4
+dns_resolver_dispatchv6
+dns_resolver_freeze
+dns_resolver_getlamettl
+dns_resolver_getoptions
+dns_resolver_getudpsize
+dns_resolver_getzeronosoattl
+dns_resolver_nrunning
+dns_resolver_prime
+dns_resolver_reset_algorithms
+dns_resolver_resetmustbesecure
+dns_resolver_setclientsperquery
+dns_resolver_setlamettl
+dns_resolver_setmustbesecure
+dns_resolver_setudpsize
+dns_resolver_setzeronosoattl
+dns_resolver_shutdown
+dns_resolver_socketmgr
+dns_resolver_taskmgr
+dns_resolver_whenshutdown
+dns_result_register
+dns_result_torcode
+dns_result_totext
+dns_rootns_create
+dns_sdb_putnamedrr
+dns_sdb_putrdata
+dns_sdb_putrr
+dns_sdb_putsoa
+dns_sdb_register
+dns_sdb_unregister
+dns_sdlz_putnamedrr
+dns_sdlz_putrr
+dns_sdlz_putsoa
+dns_sdlzregister
+dns_sdlzunregister
+dns_secalg_fromtext
+dns_secalg_totext
+dns_secproto_fromtext
+dns_secproto_totext
+dns_soa_getminimum
+dns_soa_getserial
+dns_soa_setserial
+dns_ssutable_addrule
+dns_ssutable_attach
+dns_ssutable_checkrules
+dns_ssutable_create
+dns_ssutable_detach
+dns_stats_alloccounters
+dns_stats_detach
+dns_stats_freecounters
+dns_tcpmsg_cancelread
+dns_tcpmsg_init
+dns_tcpmsg_invalidate
+dns_tcpmsg_keepbuffer
+dns_tcpmsg_readmessage
+dns_tcpmsg_setmaxsize
+dns_time32_fromtext
+dns_time32_totext
+dns_time64_fromtext
+dns_time64_totext
+dns_timer_setidle
+dns_tkey_builddeletequery
+dns_tkey_builddhquery
+dns_tkey_buildgssquery
+dns_tkey_processdeleteresponse
+dns_tkey_processdhresponse
+dns_tkey_processgssresponse
+dns_tkey_processquery
+dns_tkeyctx_create
+dns_tkeyctx_destroy
+dns_tsig_sign
+dns_tsig_verify
+dns_tsigkey_attach
+dns_tsigkey_create
+dns_tsigkey_createfromkey
+dns_tsigkey_detach
+dns_tsigkey_find
+dns_tsigkey_setdeleted
+dns_tsigkeyring_create
+dns_tsigkeyring_destroy
+dns_tsigrcode_fromtext
+dns_tsigrcode_totext
+dns_ttl_fromtext
+dns_ttl_totext
+dns_validator_cancel
+dns_validator_create
+dns_validator_destroy
+dns_validator_send
+dns_view_adddelegationonly
+dns_view_addzone
+dns_view_attach
+dns_view_checksig
+dns_view_create
+dns_view_createresolver
+dns_view_detach
+dns_view_dialup
+dns_view_dumpdbtostream
+dns_view_excludedelegationonly
+dns_view_find
+dns_view_findzone
+dns_view_findzonecut
+dns_view_flushanddetach
+dns_view_flushcache
+dns_view_flushname
+dns_view_freeze
+dns_view_freezezones
+dns_view_getpeertsig
+dns_view_getresquerystats
+dns_view_getresstats
+dns_view_gettsig
+dns_view_load
+dns_view_loadnew
+dns_view_setcache
+dns_view_setdstport
+dns_view_sethints
+dns_view_setkeyring
+dns_view_setresquerystats
+dns_view_setresstats
+dns_view_setrootdelonly
+dns_view_simplefind
+dns_view_weakattach
+dns_view_weakdetach
+dns_viewlist_find
+dns_viewlist_findzone
+dns_xfrin_attach
+dns_xfrin_create
+dns_xfrin_detach
+dns_xfrin_shutdown
+dns_zone_attach
+dns_zone_checknames
+dns_zone_clearforwardacl
+dns_zone_clearnotifyacl
+dns_zone_clearqueryacl
+dns_zone_clearupdateacl
+dns_zone_clearxfracl
+dns_zone_create
+dns_zone_detach
+dns_zone_dialup
+dns_zone_dump
+dns_zone_dumptostream
+dns_zone_dumptostream2
+dns_zone_expire
+dns_zone_first
+dns_zone_flush
+dns_zone_forcereload
+dns_zone_forwardupdate
+dns_zone_fulldumptostream
+dns_zone_getchecknames
+dns_zone_getclass
+dns_zone_getdb
+dns_zone_getdbtype
+dns_zone_getfile
+dns_zone_getforwardacl
+dns_zone_getidlein
+dns_zone_getidleout
+dns_zone_getjournal
+dns_zone_getjournalsize
+dns_zone_getkeydirectory
+dns_zone_getmaxxfrin
+dns_zone_getmaxxfrout
+dns_zone_getmctx
+dns_zone_getmgr
+dns_zone_getnotifyacl
+dns_zone_getnotifysrc4
+dns_zone_getnotifysrc6
+dns_zone_getoptions
+dns_zone_getorigin
+dns_zone_getprivatetype
+dns_zone_getqueryacl
+dns_zone_getrequeststats
+dns_zone_getsigresigninginterval
+dns_zone_getsigvalidityinterval
+dns_zone_getssutable
+dns_zone_getstatscounters
+dns_zone_gettask
+dns_zone_gettype
+dns_zone_getupdateacl
+dns_zone_getupdatedisabled
+dns_zone_getview
+dns_zone_getxfracl
+dns_zone_getxfrsource4
+dns_zone_getxfrsource6
+dns_zone_getzeronosoattl
+dns_zone_iattach
+dns_zone_idetach
+dns_zone_isforced
+dns_zone_load
+dns_zone_log
+dns_zone_maintenance
+dns_zone_markdirty
+dns_zone_name
+dns_zone_next
+dns_zone_notify
+dns_zone_notifyreceive
+dns_zone_refresh
+dns_zone_replacedb
+dns_zone_setacache
+dns_zone_setalsonotify
+dns_zone_setaltxfrsource4
+dns_zone_setaltxfrsource6
+dns_zone_setcheckmx
+dns_zone_setchecknames
+dns_zone_setcheckns
+dns_zone_setchecksrv
+dns_zone_setclass
+dns_zone_setdbtype
+dns_zone_setdialup
+dns_zone_setfile
+dns_zone_setfile2
+dns_zone_setflag
+dns_zone_setforwardacl
+dns_zone_setidlein
+dns_zone_setidleout
+dns_zone_setisself
+dns_zone_setjournal
+dns_zone_setjournalsize
+dns_zone_setkeydirectory
+dns_zone_setmasters
+dns_zone_setmasterswithkeys
+dns_zone_setmaxrefreshtime
+dns_zone_setmaxretrytime
+dns_zone_setmaxxfrin
+dns_zone_setmaxxfrout
+dns_zone_setminrefreshtime
+dns_zone_setminretrytime
+dns_zone_setnodes
+dns_zone_setnotifyacl
+dns_zone_setnotifydelay
+dns_zone_setnotifysrc4
+dns_zone_setnotifysrc6
+dns_zone_setnotifytype
+dns_zone_setoption
+dns_zone_setorigin
+dns_zone_setprivatetype
+dns_zone_setqueryacl
+dns_zone_setqueryonacl
+dns_zone_setrequeststats
+dns_zone_setsignatures
+dns_zone_setsigresigninginterval
+dns_zone_setsigvalidityinterval
+dns_zone_setssutable
+dns_zone_setstatistics
+dns_zone_setstats
+dns_zone_settask
+dns_zone_settype
+dns_zone_setupdateacl
+dns_zone_setupdatedisabled
+dns_zone_setview
+dns_zone_setxfracl
+dns_zone_setxfrsource4
+dns_zone_setxfrsource6
+dns_zone_setzeronosoattl
+dns_zone_signwithkey
+dns_zone_unload
+dns_zonekey_iszonekey
+dns_zonemgr_attach
+dns_zonemgr_create
+dns_zonemgr_detach
+dns_zonemgr_forcemaint
+dns_zonemgr_getcount
+dns_zonemgr_getiolimit
+dns_zonemgr_getserialqueryrate
+dns_zonemgr_getttransfersin
+dns_zonemgr_getttransfersperns
+dns_zonemgr_managezone
+dns_zonemgr_releasezone
+dns_zonemgr_resumexfrs
+dns_zonemgr_setiolimit
+dns_zonemgr_setserialqueryrate
+dns_zonemgr_settransfersin
+dns_zonemgr_settransfersperns
+dns_zonemgr_shutdown
+dns_zt_apply
+dns_zt_attach
+dns_zt_create
+dns_zt_detach
+dns_zt_find
+dns_zt_flushanddetach
+dns_zt_load
+dns_zt_mount
+dns_zt_unmount
+dst_algorithm_supported
+dst_context_adddata
+dst_context_create
+dst_context_destroy
+dst_context_sign
+dst_context_verify
+dst_gssapi_acceptctx
+dst_gssapi_acquirecred
+dst_gssapi_initctx
+dst_key_alg
+dst_key_buildfilename
+dst_key_class
+dst_key_compare
+dst_key_computesecret
+dst_key_flags
+dst_key_free
+dst_key_frombuffer
+dst_key_fromdns
+dst_key_fromfile
+dst_key_fromgssapi
+dst_key_fromlabel
+dst_key_fromnamedfile
+dst_key_generate
+dst_key_id
+dst_key_isnullkey
+dst_key_isprivate
+dst_key_iszonekey
+dst_key_name
+dst_key_paramcompare
+dst_key_proto
+dst_key_secretsize
+dst_key_setbits
+dst_key_sigsize
+dst_key_size
+dst_key_tobuffer
+dst_key_todns
+dst_key_tofile
+dst_lib_destroy
+dst_lib_init
+dst_lib_initmsgcat
+dst_region_computeid
+dst_result_register
+dst_result_totext
+
+; Exported Data
+
+EXPORTS
+
+dns_master_style_full DATA
diff --git a/lib/dns/win32/libdns.dsp b/lib/dns/win32/libdns.dsp
new file mode 100644
index 0000000..5399b19
--- /dev/null
+++ b/lib/dns/win32/libdns.dsp
@@ -0,0 +1,737 @@
+# Microsoft Developer Studio Project File - Name="libdns" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=libdns - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "libdns.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "libdns.mak" CFG="libdns - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "libdns - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "libdns - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "libdns_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../../../../openssl-0.9.8d/inc32/openssl/include" /I "./" /I "../../../" /I "include" /I "../include" /I "../../isc/win32" /I "../../isc/win32/include" /I "../../isc/include" /I "../../isc/noatomic/include" /I "../../../../openssl-0.9.8d/inc32" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "__STDC__" /D "_MBCS" /D "_USRDLL" /D "USE_MD5" /D "OPENSSL" /D "DST_USE_PRIVATE_OPENSSL" /D "LIBDNS_EXPORTS" /YX /FD /c
+# SUBTRACT CPP /X
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 user32.lib advapi32.lib ws2_32.lib ../../isc/win32/Release/libisc.lib ../../../../openssl-0.9.8d/out32dll/libeay32.lib /nologo /dll /machine:I386 /out:"../../../Build/Release/libdns.dll"
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "libdns_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "./" /I "../../../" /I "include" /I "../include" /I "../../isc/win32" /I "../../isc/win32/include" /I "../../isc/include" /I "../../isc/noatomic/include" /I "../../../../openssl-0.9.8d/inc32" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "__STDC__" /D "_MBCS" /D "_USRDLL" /D "USE_MD5" /D "OPENSSL" /D "DST_USE_PRIVATE_OPENSSL" /D "LIBDNS_EXPORTS" /FR /YX /FD /GZ /c
+# SUBTRACT CPP /X
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 user32.lib advapi32.lib ws2_32.lib ../../isc/win32/debug/libisc.lib ../../../../openssl-0.9.8d/out32dll/libeay32.lib /nologo /dll /map /debug /machine:I386 /out:"../../../Build/Debug/libdns.dll" /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "libdns - Win32 Release"
+# Name "libdns - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\include\dns\acache.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\acl.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\adb.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\bit.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\byaddr.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\cache.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\callbacks.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\cert.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\code.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\compress.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\db.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\dbiterator.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\dbtable.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\diff.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\dispatch.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\dlz.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\dnssec.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\ds.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\enumclass.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\enumtype.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\events.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\fixedname.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\forward.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\iptable.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\journal.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\keyflags.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\keytable.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\keyvalues.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\lib.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\log.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\lookup.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\master.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\masterdump.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\message.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\name.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\ncache.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\nsec.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\nsec3.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\order.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\peer.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\portlist.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\rbt.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\rbtdb.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\rbtdb64.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\rcode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\rdata.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\rdataclass.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\rdatalist.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\rdataset.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\rdatasetiter.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\rdataslab.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\rdatastruct.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\rdatatype.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\request.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\resolver.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\result.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\rootns.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\sdb.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\sdlz.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\secalg.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\secproto.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\soa.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\ssu.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\stats.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\tcpmsg.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\time.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\timer.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\tkey.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\tsig.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\ttl.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\types.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\validator.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\version.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\view.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\xfrin.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\zone.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\zonekey.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\dns\zt.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# Begin Group "Main Dns Lib"
+
+# PROP Default_Filter "c"
+# Begin Source File
+
+SOURCE=..\acache.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\acl.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\adb.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\byaddr.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\cache.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\callbacks.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\compress.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\db.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\dbiterator.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\dbtable.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\diff.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\dispatch.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\dlz.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\DLLMain.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\dnssec.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\ds.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\forward.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\iptable.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\journal.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\keytable.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\lib.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\log.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\lookup.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\master.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\masterdump.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\message.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\name.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\ncache.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\nsec.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\nsec3.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\order.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\peer.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\portlist.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\rbt.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\rbtdb.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\rbtdb64.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\rcode.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\rdata.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\rdatalist.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\rdataset.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\rdatasetiter.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\rdataslab.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\request.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\resolver.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\result.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\rootns.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\sdb.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\soa.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\sdlz.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\ssu.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\stats.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\tcpmsg.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\time.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\timer.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\tkey.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\tsig.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\ttl.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\validator.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\version.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\view.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\xfrin.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\zone.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\zonekey.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\zt.c
+# End Source File
+# End Group
+# Begin Group "dst"
+
+# PROP Default_Filter "c"
+# Begin Source File
+
+SOURCE=..\dst_api.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\dst_lib.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\dst_parse.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\dst_result.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\gssapi_link.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\gssapictx.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\spnego.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\hmac_link.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\key.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\openssl_link.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\openssldh_link.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\openssldsa_link.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\opensslrsa_link.c
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\libdns.def
+# End Source File
+# End Target
+# End Project
diff --git a/lib/dns/win32/libdns.dsw b/lib/dns/win32/libdns.dsw
new file mode 100644
index 0000000..424a2cb
--- /dev/null
+++ b/lib/dns/win32/libdns.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "libdns"=".\libdns.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/lib/dns/win32/libdns.mak b/lib/dns/win32/libdns.mak
new file mode 100644
index 0000000..db50d26
--- /dev/null
+++ b/lib/dns/win32/libdns.mak
@@ -0,0 +1,2188 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on libdns.dsp
+!IF "$(CFG)" == ""
+CFG=libdns - Win32 Debug
+!MESSAGE No configuration specified. Defaulting to libdns - Win32 Debug.
+!ENDIF
+
+!IF "$(CFG)" != "libdns - Win32 Release" && "$(CFG)" != "libdns - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "libdns.mak" CFG="libdns - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "libdns - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "libdns - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+_VC_MANIFEST_INC=0
+_VC_MANIFEST_BASENAME=__VC80
+!ELSE
+_VC_MANIFEST_INC=1
+_VC_MANIFEST_BASENAME=__VC80.Debug
+!ENDIF
+
+####################################################
+# Specifying name of temporary resource file used only in incremental builds:
+
+!if "$(_VC_MANIFEST_INC)" == "1"
+_VC_MANIFEST_AUTO_RES=$(_VC_MANIFEST_BASENAME).auto.res
+!else
+_VC_MANIFEST_AUTO_RES=
+!endif
+
+####################################################
+# _VC_MANIFEST_EMBED_EXE - command to embed manifest in EXE:
+
+!if "$(_VC_MANIFEST_INC)" == "1"
+
+#MT_SPECIAL_RETURN=1090650113
+#MT_SPECIAL_SWITCH=-notify_resource_update
+MT_SPECIAL_RETURN=0
+MT_SPECIAL_SWITCH=
+_VC_MANIFEST_EMBED_EXE= \
+if exist $@.manifest mt.exe -manifest $@.manifest -out:$(_VC_MANIFEST_BASENAME).auto.manifest $(MT_SPECIAL_SWITCH) & \
+if "%ERRORLEVEL%" == "$(MT_SPECIAL_RETURN)" \
+rc /r $(_VC_MANIFEST_BASENAME).auto.rc & \
+link $** /out:$@ $(LFLAGS)
+
+!else
+
+_VC_MANIFEST_EMBED_EXE= \
+if exist $@.manifest mt.exe -manifest $@.manifest -outputresource:$@;1
+
+!endif
+
+####################################################
+# _VC_MANIFEST_EMBED_DLL - command to embed manifest in DLL:
+
+!if "$(_VC_MANIFEST_INC)" == "1"
+
+#MT_SPECIAL_RETURN=1090650113
+#MT_SPECIAL_SWITCH=-notify_resource_update
+MT_SPECIAL_RETURN=0
+MT_SPECIAL_SWITCH=
+_VC_MANIFEST_EMBED_EXE= \
+if exist $@.manifest mt.exe -manifest $@.manifest -out:$(_VC_MANIFEST_BASENAME).auto.manifest $(MT_SPECIAL_SWITCH) & \
+if "%ERRORLEVEL%" == "$(MT_SPECIAL_RETURN)" \
+rc /r $(_VC_MANIFEST_BASENAME).auto.rc & \
+link $** /out:$@ $(LFLAGS)
+
+!else
+
+_VC_MANIFEST_EMBED_EXE= \
+if exist $@.manifest mt.exe -manifest $@.manifest -outputresource:$@;2
+
+!endif
+####################################################
+# _VC_MANIFEST_CLEAN - command to clean resources files generated temporarily:
+
+!if "$(_VC_MANIFEST_INC)" == "1"
+
+_VC_MANIFEST_CLEAN=-del $(_VC_MANIFEST_BASENAME).auto.res \
+ $(_VC_MANIFEST_BASENAME).auto.rc \
+ $(_VC_MANIFEST_BASENAME).auto.manifest
+
+!else
+
+_VC_MANIFEST_CLEAN=
+
+!endif
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+OUTDIR=.\Release
+INTDIR=.\Release
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "..\..\..\Build\Release\libdns.dll"
+
+!ELSE
+
+ALL : "libisc - Win32 Release" "..\..\..\Build\Release\libdns.dll"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libisc - Win32 ReleaseCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\acache.obj"
+ -@erase "$(INTDIR)\acl.obj"
+ -@erase "$(INTDIR)\adb.obj"
+ -@erase "$(INTDIR)\byaddr.obj"
+ -@erase "$(INTDIR)\cache.obj"
+ -@erase "$(INTDIR)\callbacks.obj"
+ -@erase "$(INTDIR)\compress.obj"
+ -@erase "$(INTDIR)\db.obj"
+ -@erase "$(INTDIR)\dbiterator.obj"
+ -@erase "$(INTDIR)\dbtable.obj"
+ -@erase "$(INTDIR)\diff.obj"
+ -@erase "$(INTDIR)\dispatch.obj"
+ -@erase "$(INTDIR)\dlz.obj"
+ -@erase "$(INTDIR)\DLLMain.obj"
+ -@erase "$(INTDIR)\dnssec.obj"
+ -@erase "$(INTDIR)\ds.obj"
+ -@erase "$(INTDIR)\dst_api.obj"
+ -@erase "$(INTDIR)\dst_lib.obj"
+ -@erase "$(INTDIR)\dst_parse.obj"
+ -@erase "$(INTDIR)\dst_result.obj"
+ -@erase "$(INTDIR)\forward.obj"
+ -@erase "$(INTDIR)\gssapi_link.obj"
+ -@erase "$(INTDIR)\gssapictx.obj"
+ -@erase "$(INTDIR)\spnego.obj"
+ -@erase "$(INTDIR)\hmac_link.obj"
+ -@erase "$(INTDIR)\iptable.obj"
+ -@erase "$(INTDIR)\journal.obj"
+ -@erase "$(INTDIR)\key.obj"
+ -@erase "$(INTDIR)\keytable.obj"
+ -@erase "$(INTDIR)\lib.obj"
+ -@erase "$(INTDIR)\log.obj"
+ -@erase "$(INTDIR)\lookup.obj"
+ -@erase "$(INTDIR)\master.obj"
+ -@erase "$(INTDIR)\masterdump.obj"
+ -@erase "$(INTDIR)\message.obj"
+ -@erase "$(INTDIR)\name.obj"
+ -@erase "$(INTDIR)\ncache.obj"
+ -@erase "$(INTDIR)\nsec.obj"
+ -@erase "$(INTDIR)\nsec3.obj"
+ -@erase "$(INTDIR)\openssl_link.obj"
+ -@erase "$(INTDIR)\openssldh_link.obj"
+ -@erase "$(INTDIR)\openssldsa_link.obj"
+ -@erase "$(INTDIR)\opensslrsa_link.obj"
+ -@erase "$(INTDIR)\order.obj"
+ -@erase "$(INTDIR)\peer.obj"
+ -@erase "$(INTDIR)\portlist.obj"
+ -@erase "$(INTDIR)\rbt.obj"
+ -@erase "$(INTDIR)\rbtdb.obj"
+ -@erase "$(INTDIR)\rbtdb64.obj"
+ -@erase "$(INTDIR)\rcode.obj"
+ -@erase "$(INTDIR)\rdata.obj"
+ -@erase "$(INTDIR)\rdatalist.obj"
+ -@erase "$(INTDIR)\rdataset.obj"
+ -@erase "$(INTDIR)\rdatasetiter.obj"
+ -@erase "$(INTDIR)\rdataslab.obj"
+ -@erase "$(INTDIR)\request.obj"
+ -@erase "$(INTDIR)\resolver.obj"
+ -@erase "$(INTDIR)\result.obj"
+ -@erase "$(INTDIR)\rootns.obj"
+ -@erase "$(INTDIR)\sdb.obj"
+ -@erase "$(INTDIR)\sdlz.obj"
+ -@erase "$(INTDIR)\soa.obj"
+ -@erase "$(INTDIR)\ssu.obj"
+ -@erase "$(INTDIR)\stats.obj"
+ -@erase "$(INTDIR)\tcpmsg.obj"
+ -@erase "$(INTDIR)\time.obj"
+ -@erase "$(INTDIR)\timer.obj"
+ -@erase "$(INTDIR)\tkey.obj"
+ -@erase "$(INTDIR)\tsig.obj"
+ -@erase "$(INTDIR)\ttl.obj"
+ -@erase "$(INTDIR)\validator.obj"
+ -@erase "$(INTDIR)\vc60.idb"
+ -@erase "$(INTDIR)\version.obj"
+ -@erase "$(INTDIR)\view.obj"
+ -@erase "$(INTDIR)\xfrin.obj"
+ -@erase "$(INTDIR)\zone.obj"
+ -@erase "$(INTDIR)\zonekey.obj"
+ -@erase "$(INTDIR)\zt.obj"
+ -@erase "$(OUTDIR)\libdns.exp"
+ -@erase "$(OUTDIR)\libdns.lib"
+ -@erase "..\..\..\Build\Release\libdns.dll"
+ -@$(_VC_MANIFEST_CLEAN)
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MD /W3 /GX /O2 /I "../../../../../openssl-0.9.8d/inc32/openssl/include" /I "./" /I "../../../" /I "include" /I "../include" /I "../../isc/win32" /I "../../isc/win32/include" /I "../../isc/include" /I "../../../lib/isc/noatomic/include" /I "../../../../openssl-0.9.8d/inc32" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "__STDC__" /D "_MBCS" /D "_USRDLL" /D "USE_MD5" /D "OPENSSL" /D "DST_USE_PRIVATE_OPENSSL" /D "LIBDNS_EXPORTS" /Fp"$(INTDIR)\libdns.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32
+RSC=rc.exe
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\libdns.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=user32.lib advapi32.lib ws2_32.lib ../../isc/win32/Release/libisc.lib ../../../../openssl-0.9.8d/out32dll/libeay32.lib /nologo /dll /incremental:no /pdb:"$(OUTDIR)\libdns.pdb" /machine:I386 /def:".\libdns.def" /out:"../../../Build/Release/libdns.dll" /implib:"$(OUTDIR)\libdns.lib"
+DEF_FILE= \
+ ".\libdns.def"
+LINK32_OBJS= \
+ "$(INTDIR)\acache.obj" \
+ "$(INTDIR)\acl.obj" \
+ "$(INTDIR)\adb.obj" \
+ "$(INTDIR)\byaddr.obj" \
+ "$(INTDIR)\cache.obj" \
+ "$(INTDIR)\callbacks.obj" \
+ "$(INTDIR)\compress.obj" \
+ "$(INTDIR)\db.obj" \
+ "$(INTDIR)\dbiterator.obj" \
+ "$(INTDIR)\dbtable.obj" \
+ "$(INTDIR)\diff.obj" \
+ "$(INTDIR)\dispatch.obj" \
+ "$(INTDIR)\dlz.obj" \
+ "$(INTDIR)\DLLMain.obj" \
+ "$(INTDIR)\dnssec.obj" \
+ "$(INTDIR)\ds.obj" \
+ "$(INTDIR)\forward.obj" \
+ "$(INTDIR)\iptable.obj" \
+ "$(INTDIR)\journal.obj" \
+ "$(INTDIR)\keytable.obj" \
+ "$(INTDIR)\lib.obj" \
+ "$(INTDIR)\log.obj" \
+ "$(INTDIR)\lookup.obj" \
+ "$(INTDIR)\master.obj" \
+ "$(INTDIR)\masterdump.obj" \
+ "$(INTDIR)\message.obj" \
+ "$(INTDIR)\name.obj" \
+ "$(INTDIR)\ncache.obj" \
+ "$(INTDIR)\nsec.obj" \
+ "$(INTDIR)\nsec3.obj" \
+ "$(INTDIR)\order.obj" \
+ "$(INTDIR)\peer.obj" \
+ "$(INTDIR)\portlist.obj" \
+ "$(INTDIR)\rbt.obj" \
+ "$(INTDIR)\rbtdb.obj" \
+ "$(INTDIR)\rbtdb64.obj" \
+ "$(INTDIR)\rcode.obj" \
+ "$(INTDIR)\rdata.obj" \
+ "$(INTDIR)\rdatalist.obj" \
+ "$(INTDIR)\rdataset.obj" \
+ "$(INTDIR)\rdatasetiter.obj" \
+ "$(INTDIR)\rdataslab.obj" \
+ "$(INTDIR)\request.obj" \
+ "$(INTDIR)\resolver.obj" \
+ "$(INTDIR)\result.obj" \
+ "$(INTDIR)\rootns.obj" \
+ "$(INTDIR)\sdb.obj" \
+ "$(INTDIR)\sdlz.obj" \
+ "$(INTDIR)\soa.obj" \
+ "$(INTDIR)\ssu.obj" \
+ "$(INTDIR)\stats.obj" \
+ "$(INTDIR)\tcpmsg.obj" \
+ "$(INTDIR)\time.obj" \
+ "$(INTDIR)\timer.obj" \
+ "$(INTDIR)\tkey.obj" \
+ "$(INTDIR)\tsig.obj" \
+ "$(INTDIR)\ttl.obj" \
+ "$(INTDIR)\validator.obj" \
+ "$(INTDIR)\version.obj" \
+ "$(INTDIR)\view.obj" \
+ "$(INTDIR)\xfrin.obj" \
+ "$(INTDIR)\zone.obj" \
+ "$(INTDIR)\zonekey.obj" \
+ "$(INTDIR)\zt.obj" \
+ "$(INTDIR)\dst_api.obj" \
+ "$(INTDIR)\dst_lib.obj" \
+ "$(INTDIR)\dst_parse.obj" \
+ "$(INTDIR)\dst_result.obj" \
+ "$(INTDIR)\gssapi_link.obj" \
+ "$(INTDIR)\gssapictx.obj" \
+ "$(INTDIR)\spnego.obj" \
+ "$(INTDIR)\hmac_link.obj" \
+ "$(INTDIR)\key.obj" \
+ "$(INTDIR)\openssl_link.obj" \
+ "$(INTDIR)\openssldh_link.obj" \
+ "$(INTDIR)\openssldsa_link.obj" \
+ "$(INTDIR)\opensslrsa_link.obj" \
+ "..\..\isc\win32\Release\libisc.lib"
+
+"..\..\..\Build\Release\libdns.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+ $(_VC_MANIFEST_EMBED_DLL)
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+OUTDIR=.\Debug
+INTDIR=.\Debug
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "..\..\..\Build\Debug\libdns.dll" "$(OUTDIR)\libdns.bsc"
+
+!ELSE
+
+ALL : "libisc - Win32 Debug" "..\..\..\Build\Debug\libdns.dll" "$(OUTDIR)\libdns.bsc"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libisc - Win32 DebugCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\acache.obj"
+ -@erase "$(INTDIR)\acache.sbr"
+ -@erase "$(INTDIR)\acl.obj"
+ -@erase "$(INTDIR)\acl.sbr"
+ -@erase "$(INTDIR)\adb.obj"
+ -@erase "$(INTDIR)\adb.sbr"
+ -@erase "$(INTDIR)\byaddr.obj"
+ -@erase "$(INTDIR)\byaddr.sbr"
+ -@erase "$(INTDIR)\cache.obj"
+ -@erase "$(INTDIR)\cache.sbr"
+ -@erase "$(INTDIR)\callbacks.obj"
+ -@erase "$(INTDIR)\callbacks.sbr"
+ -@erase "$(INTDIR)\compress.obj"
+ -@erase "$(INTDIR)\compress.sbr"
+ -@erase "$(INTDIR)\db.obj"
+ -@erase "$(INTDIR)\db.sbr"
+ -@erase "$(INTDIR)\dbiterator.obj"
+ -@erase "$(INTDIR)\dbiterator.sbr"
+ -@erase "$(INTDIR)\dbtable.obj"
+ -@erase "$(INTDIR)\dbtable.sbr"
+ -@erase "$(INTDIR)\diff.obj"
+ -@erase "$(INTDIR)\diff.sbr"
+ -@erase "$(INTDIR)\dispatch.obj"
+ -@erase "$(INTDIR)\dispatch.sbr"
+ -@erase "$(INTDIR)\dlz.obj"
+ -@erase "$(INTDIR)\dlz.sbr"
+ -@erase "$(INTDIR)\DLLMain.obj"
+ -@erase "$(INTDIR)\DLLMain.sbr"
+ -@erase "$(INTDIR)\dnssec.obj"
+ -@erase "$(INTDIR)\dnssec.sbr"
+ -@erase "$(INTDIR)\ds.obj"
+ -@erase "$(INTDIR)\ds.sbr"
+ -@erase "$(INTDIR)\dst_api.obj"
+ -@erase "$(INTDIR)\dst_api.sbr"
+ -@erase "$(INTDIR)\dst_lib.obj"
+ -@erase "$(INTDIR)\dst_lib.sbr"
+ -@erase "$(INTDIR)\dst_parse.obj"
+ -@erase "$(INTDIR)\dst_parse.sbr"
+ -@erase "$(INTDIR)\dst_result.obj"
+ -@erase "$(INTDIR)\dst_result.sbr"
+ -@erase "$(INTDIR)\forward.obj"
+ -@erase "$(INTDIR)\forward.sbr"
+ -@erase "$(INTDIR)\gssapi_link.obj"
+ -@erase "$(INTDIR)\gssapi_link.sbr"
+ -@erase "$(INTDIR)\gssapictx.obj"
+ -@erase "$(INTDIR)\gssapictx.sbr"
+ -@erase "$(INTDIR)\spnego.obj"
+ -@erase "$(INTDIR)\spnego.sbr"
+ -@erase "$(INTDIR)\hmac_link.obj"
+ -@erase "$(INTDIR)\hmac_link.sbr"
+ -@erase "$(INTDIR)\iptable.obj"
+ -@erase "$(INTDIR)\iptable.sbr"
+ -@erase "$(INTDIR)\journal.obj"
+ -@erase "$(INTDIR)\journal.sbr"
+ -@erase "$(INTDIR)\key.obj"
+ -@erase "$(INTDIR)\key.sbr"
+ -@erase "$(INTDIR)\keytable.obj"
+ -@erase "$(INTDIR)\keytable.sbr"
+ -@erase "$(INTDIR)\lib.obj"
+ -@erase "$(INTDIR)\lib.sbr"
+ -@erase "$(INTDIR)\log.obj"
+ -@erase "$(INTDIR)\log.sbr"
+ -@erase "$(INTDIR)\lookup.obj"
+ -@erase "$(INTDIR)\lookup.sbr"
+ -@erase "$(INTDIR)\master.obj"
+ -@erase "$(INTDIR)\master.sbr"
+ -@erase "$(INTDIR)\masterdump.obj"
+ -@erase "$(INTDIR)\masterdump.sbr"
+ -@erase "$(INTDIR)\message.obj"
+ -@erase "$(INTDIR)\message.sbr"
+ -@erase "$(INTDIR)\name.obj"
+ -@erase "$(INTDIR)\name.sbr"
+ -@erase "$(INTDIR)\ncache.obj"
+ -@erase "$(INTDIR)\ncache.sbr"
+ -@erase "$(INTDIR)\nsec.obj"
+ -@erase "$(INTDIR)\nsec.sbr"
+ -@erase "$(INTDIR)\nsec3.obj"
+ -@erase "$(INTDIR)\nsec3.sbr"
+ -@erase "$(INTDIR)\openssl_link.obj"
+ -@erase "$(INTDIR)\openssl_link.sbr"
+ -@erase "$(INTDIR)\openssldh_link.obj"
+ -@erase "$(INTDIR)\openssldh_link.sbr"
+ -@erase "$(INTDIR)\openssldsa_link.obj"
+ -@erase "$(INTDIR)\openssldsa_link.sbr"
+ -@erase "$(INTDIR)\opensslrsa_link.obj"
+ -@erase "$(INTDIR)\opensslrsa_link.sbr"
+ -@erase "$(INTDIR)\order.obj"
+ -@erase "$(INTDIR)\order.sbr"
+ -@erase "$(INTDIR)\peer.obj"
+ -@erase "$(INTDIR)\peer.sbr"
+ -@erase "$(INTDIR)\portlist.obj"
+ -@erase "$(INTDIR)\portlist.sbr"
+ -@erase "$(INTDIR)\rbt.obj"
+ -@erase "$(INTDIR)\rbt.sbr"
+ -@erase "$(INTDIR)\rbtdb.obj"
+ -@erase "$(INTDIR)\rbtdb.sbr"
+ -@erase "$(INTDIR)\rbtdb64.obj"
+ -@erase "$(INTDIR)\rbtdb64.sbr"
+ -@erase "$(INTDIR)\rcode.obj"
+ -@erase "$(INTDIR)\rcode.sbr"
+ -@erase "$(INTDIR)\rdata.obj"
+ -@erase "$(INTDIR)\rdata.sbr"
+ -@erase "$(INTDIR)\rdatalist.obj"
+ -@erase "$(INTDIR)\rdatalist.sbr"
+ -@erase "$(INTDIR)\rdataset.obj"
+ -@erase "$(INTDIR)\rdataset.sbr"
+ -@erase "$(INTDIR)\rdatasetiter.obj"
+ -@erase "$(INTDIR)\rdatasetiter.sbr"
+ -@erase "$(INTDIR)\rdataslab.obj"
+ -@erase "$(INTDIR)\rdataslab.sbr"
+ -@erase "$(INTDIR)\request.obj"
+ -@erase "$(INTDIR)\request.sbr"
+ -@erase "$(INTDIR)\resolver.obj"
+ -@erase "$(INTDIR)\resolver.sbr"
+ -@erase "$(INTDIR)\result.obj"
+ -@erase "$(INTDIR)\result.sbr"
+ -@erase "$(INTDIR)\rootns.obj"
+ -@erase "$(INTDIR)\rootns.sbr"
+ -@erase "$(INTDIR)\sdb.obj"
+ -@erase "$(INTDIR)\sdb.sbr"
+ -@erase "$(INTDIR)\sdlz.obj"
+ -@erase "$(INTDIR)\sdlz.sbr"
+ -@erase "$(INTDIR)\soa.obj"
+ -@erase "$(INTDIR)\soa.sbr"
+ -@erase "$(INTDIR)\ssu.obj"
+ -@erase "$(INTDIR)\ssu.sbr"
+ -@erase "$(INTDIR)\stats.obj"
+ -@erase "$(INTDIR)\stats.sbr"
+ -@erase "$(INTDIR)\tcpmsg.obj"
+ -@erase "$(INTDIR)\tcpmsg.sbr"
+ -@erase "$(INTDIR)\time.obj"
+ -@erase "$(INTDIR)\time.sbr"
+ -@erase "$(INTDIR)\timer.obj"
+ -@erase "$(INTDIR)\timer.sbr"
+ -@erase "$(INTDIR)\tkey.obj"
+ -@erase "$(INTDIR)\tkey.sbr"
+ -@erase "$(INTDIR)\tsig.obj"
+ -@erase "$(INTDIR)\tsig.sbr"
+ -@erase "$(INTDIR)\ttl.obj"
+ -@erase "$(INTDIR)\ttl.sbr"
+ -@erase "$(INTDIR)\validator.obj"
+ -@erase "$(INTDIR)\validator.sbr"
+ -@erase "$(INTDIR)\vc60.idb"
+ -@erase "$(INTDIR)\vc60.pdb"
+ -@erase "$(INTDIR)\version.obj"
+ -@erase "$(INTDIR)\version.sbr"
+ -@erase "$(INTDIR)\view.obj"
+ -@erase "$(INTDIR)\view.sbr"
+ -@erase "$(INTDIR)\xfrin.obj"
+ -@erase "$(INTDIR)\xfrin.sbr"
+ -@erase "$(INTDIR)\zone.obj"
+ -@erase "$(INTDIR)\zone.sbr"
+ -@erase "$(INTDIR)\zonekey.obj"
+ -@erase "$(INTDIR)\zonekey.sbr"
+ -@erase "$(INTDIR)\zt.obj"
+ -@erase "$(INTDIR)\zt.sbr"
+ -@erase "$(OUTDIR)\libdns.bsc"
+ -@erase "$(OUTDIR)\libdns.exp"
+ -@erase "$(OUTDIR)\libdns.lib"
+ -@erase "$(OUTDIR)\libdns.map"
+ -@erase "$(OUTDIR)\libdns.pdb"
+ -@erase "..\..\..\Build\Debug\libdns.dll"
+ -@erase "..\..\..\Build\Debug\libdns.ilk"
+ -@$(_VC_MANIFEST_CLEAN)
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MDd /W3 /Gm /GX /ZI /Od /I "./" /I "../../../" /I "include" /I "../include" /I "../../isc/win32" /I "../../isc/win32/include" /I "../../isc/include" /I "../../../lib/isc/noatomic/include" /I "../../../../openssl-0.9.8d/inc32" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "__STDC__" /D "_MBCS" /D "_USRDLL" /D "USE_MD5" /D "OPENSSL" /D "DST_USE_PRIVATE_OPENSSL" /D "LIBDNS_EXPORTS" /FR"$(INTDIR)\\" /Fp"$(INTDIR)\libdns.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32
+RSC=rc.exe
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\libdns.bsc"
+BSC32_SBRS= \
+ "$(INTDIR)\acache.sbr" \
+ "$(INTDIR)\acl.sbr" \
+ "$(INTDIR)\adb.sbr" \
+ "$(INTDIR)\byaddr.sbr" \
+ "$(INTDIR)\cache.sbr" \
+ "$(INTDIR)\callbacks.sbr" \
+ "$(INTDIR)\compress.sbr" \
+ "$(INTDIR)\db.sbr" \
+ "$(INTDIR)\dbiterator.sbr" \
+ "$(INTDIR)\dbtable.sbr" \
+ "$(INTDIR)\diff.sbr" \
+ "$(INTDIR)\dispatch.sbr" \
+ "$(INTDIR)\dlz.sbr" \
+ "$(INTDIR)\DLLMain.sbr" \
+ "$(INTDIR)\dnssec.sbr" \
+ "$(INTDIR)\ds.sbr" \
+ "$(INTDIR)\forward.sbr" \
+ "$(INTDIR)\iptable.sbr" \
+ "$(INTDIR)\journal.sbr" \
+ "$(INTDIR)\keytable.sbr" \
+ "$(INTDIR)\lib.sbr" \
+ "$(INTDIR)\log.sbr" \
+ "$(INTDIR)\lookup.sbr" \
+ "$(INTDIR)\master.sbr" \
+ "$(INTDIR)\masterdump.sbr" \
+ "$(INTDIR)\message.sbr" \
+ "$(INTDIR)\name.sbr" \
+ "$(INTDIR)\ncache.sbr" \
+ "$(INTDIR)\nsec.sbr" \
+ "$(INTDIR)\nsec3.sbr" \
+ "$(INTDIR)\order.sbr" \
+ "$(INTDIR)\peer.sbr" \
+ "$(INTDIR)\portlist.sbr" \
+ "$(INTDIR)\rbt.sbr" \
+ "$(INTDIR)\rbtdb.sbr" \
+ "$(INTDIR)\rbtdb64.sbr" \
+ "$(INTDIR)\rcode.sbr" \
+ "$(INTDIR)\rdata.sbr" \
+ "$(INTDIR)\rdatalist.sbr" \
+ "$(INTDIR)\rdataset.sbr" \
+ "$(INTDIR)\rdatasetiter.sbr" \
+ "$(INTDIR)\rdataslab.sbr" \
+ "$(INTDIR)\request.sbr" \
+ "$(INTDIR)\resolver.sbr" \
+ "$(INTDIR)\result.sbr" \
+ "$(INTDIR)\rootns.sbr" \
+ "$(INTDIR)\sdb.sbr" \
+ "$(INTDIR)\sdlz.sbr" \
+ "$(INTDIR)\soa.sbr" \
+ "$(INTDIR)\ssu.sbr" \
+ "$(INTDIR)\stats.sbr" \
+ "$(INTDIR)\tcpmsg.sbr" \
+ "$(INTDIR)\time.sbr" \
+ "$(INTDIR)\timer.sbr" \
+ "$(INTDIR)\tkey.sbr" \
+ "$(INTDIR)\tsig.sbr" \
+ "$(INTDIR)\ttl.sbr" \
+ "$(INTDIR)\validator.sbr" \
+ "$(INTDIR)\version.sbr" \
+ "$(INTDIR)\view.sbr" \
+ "$(INTDIR)\xfrin.sbr" \
+ "$(INTDIR)\zone.sbr" \
+ "$(INTDIR)\zonekey.sbr" \
+ "$(INTDIR)\zt.sbr" \
+ "$(INTDIR)\dst_api.sbr" \
+ "$(INTDIR)\dst_lib.sbr" \
+ "$(INTDIR)\dst_parse.sbr" \
+ "$(INTDIR)\dst_result.sbr" \
+ "$(INTDIR)\gssapi_link.sbr" \
+ "$(INTDIR)\gssapictx.sbr" \
+ "$(INTDIR)\spnego.sbr" \
+ "$(INTDIR)\hmac_link.sbr" \
+ "$(INTDIR)\key.sbr" \
+ "$(INTDIR)\openssl_link.sbr" \
+ "$(INTDIR)\openssldh_link.sbr" \
+ "$(INTDIR)\openssldsa_link.sbr" \
+ "$(INTDIR)\opensslrsa_link.sbr"
+
+"$(OUTDIR)\libdns.bsc" : "$(OUTDIR)" $(BSC32_SBRS)
+ $(BSC32) @<<
+ $(BSC32_FLAGS) $(BSC32_SBRS)
+<<
+
+LINK32=link.exe
+LINK32_FLAGS=user32.lib advapi32.lib ws2_32.lib ../../isc/win32/debug/libisc.lib ../../../../openssl-0.9.8d/out32dll/libeay32.lib /nologo /dll /incremental:yes /pdb:"$(OUTDIR)\libdns.pdb" /map:"$(INTDIR)\libdns.map" /debug /machine:I386 /def:".\libdns.def" /out:"../../../Build/Debug/libdns.dll" /implib:"$(OUTDIR)\libdns.lib" /pdbtype:sept
+DEF_FILE= \
+ ".\libdns.def"
+LINK32_OBJS= \
+ "$(INTDIR)\acache.obj" \
+ "$(INTDIR)\acl.obj" \
+ "$(INTDIR)\adb.obj" \
+ "$(INTDIR)\byaddr.obj" \
+ "$(INTDIR)\cache.obj" \
+ "$(INTDIR)\callbacks.obj" \
+ "$(INTDIR)\compress.obj" \
+ "$(INTDIR)\db.obj" \
+ "$(INTDIR)\dbiterator.obj" \
+ "$(INTDIR)\dbtable.obj" \
+ "$(INTDIR)\diff.obj" \
+ "$(INTDIR)\dispatch.obj" \
+ "$(INTDIR)\dlz.obj" \
+ "$(INTDIR)\DLLMain.obj" \
+ "$(INTDIR)\dnssec.obj" \
+ "$(INTDIR)\ds.obj" \
+ "$(INTDIR)\forward.obj" \
+ "$(INTDIR)\iptable.obj" \
+ "$(INTDIR)\journal.obj" \
+ "$(INTDIR)\keytable.obj" \
+ "$(INTDIR)\lib.obj" \
+ "$(INTDIR)\log.obj" \
+ "$(INTDIR)\lookup.obj" \
+ "$(INTDIR)\master.obj" \
+ "$(INTDIR)\masterdump.obj" \
+ "$(INTDIR)\message.obj" \
+ "$(INTDIR)\name.obj" \
+ "$(INTDIR)\ncache.obj" \
+ "$(INTDIR)\nsec.obj" \
+ "$(INTDIR)\nsec3.obj" \
+ "$(INTDIR)\order.obj" \
+ "$(INTDIR)\peer.obj" \
+ "$(INTDIR)\portlist.obj" \
+ "$(INTDIR)\rbt.obj" \
+ "$(INTDIR)\rbtdb.obj" \
+ "$(INTDIR)\rbtdb64.obj" \
+ "$(INTDIR)\rcode.obj" \
+ "$(INTDIR)\rdata.obj" \
+ "$(INTDIR)\rdatalist.obj" \
+ "$(INTDIR)\rdataset.obj" \
+ "$(INTDIR)\rdatasetiter.obj" \
+ "$(INTDIR)\rdataslab.obj" \
+ "$(INTDIR)\request.obj" \
+ "$(INTDIR)\resolver.obj" \
+ "$(INTDIR)\result.obj" \
+ "$(INTDIR)\rootns.obj" \
+ "$(INTDIR)\sdb.obj" \
+ "$(INTDIR)\sdlz.obj" \
+ "$(INTDIR)\soa.obj" \
+ "$(INTDIR)\ssu.obj" \
+ "$(INTDIR)\stats.obj" \
+ "$(INTDIR)\tcpmsg.obj" \
+ "$(INTDIR)\time.obj" \
+ "$(INTDIR)\timer.obj" \
+ "$(INTDIR)\tkey.obj" \
+ "$(INTDIR)\tsig.obj" \
+ "$(INTDIR)\ttl.obj" \
+ "$(INTDIR)\validator.obj" \
+ "$(INTDIR)\version.obj" \
+ "$(INTDIR)\view.obj" \
+ "$(INTDIR)\xfrin.obj" \
+ "$(INTDIR)\zone.obj" \
+ "$(INTDIR)\zonekey.obj" \
+ "$(INTDIR)\zt.obj" \
+ "$(INTDIR)\dst_api.obj" \
+ "$(INTDIR)\dst_lib.obj" \
+ "$(INTDIR)\dst_parse.obj" \
+ "$(INTDIR)\dst_result.obj" \
+ "$(INTDIR)\gssapi_link.obj" \
+ "$(INTDIR)\gssapictx.obj" \
+ "$(INTDIR)\spnego.obj" \
+ "$(INTDIR)\hmac_link.obj" \
+ "$(INTDIR)\key.obj" \
+ "$(INTDIR)\openssl_link.obj" \
+ "$(INTDIR)\openssldh_link.obj" \
+ "$(INTDIR)\openssldsa_link.obj" \
+ "$(INTDIR)\opensslrsa_link.obj" \
+ "..\..\isc\win32\Debug\libisc.lib"
+
+"..\..\..\Build\Debug\libdns.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+ $(_VC_MANIFEST_EMBED_DLL)
+
+!ENDIF
+
+
+!IF "$(NO_EXTERNAL_DEPS)" != "1"
+!IF EXISTS("libdns.dep")
+!INCLUDE "libdns.dep"
+!ELSE
+!MESSAGE Warning: cannot find "libdns.dep"
+!ENDIF
+!ENDIF
+
+
+!IF "$(CFG)" == "libdns - Win32 Release" || "$(CFG)" == "libdns - Win32 Debug"
+SOURCE=..\acache.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\acache.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\acache.obj" "$(INTDIR)\acache.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\acl.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\acl.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\acl.obj" "$(INTDIR)\acl.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\adb.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\adb.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\adb.obj" "$(INTDIR)\adb.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\byaddr.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\byaddr.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\byaddr.obj" "$(INTDIR)\byaddr.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\cache.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\cache.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\cache.obj" "$(INTDIR)\cache.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\callbacks.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\callbacks.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\callbacks.obj" "$(INTDIR)\callbacks.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\compress.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\compress.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\compress.obj" "$(INTDIR)\compress.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\db.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\db.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\db.obj" "$(INTDIR)\db.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\dbiterator.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\dbiterator.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\dbiterator.obj" "$(INTDIR)\dbiterator.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\dbtable.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\dbtable.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\dbtable.obj" "$(INTDIR)\dbtable.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\diff.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\diff.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\diff.obj" "$(INTDIR)\diff.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\dispatch.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+CPP_SWITCHES=/nologo /MD /W3 /GX /O2 /I "../../../../../openssl-0.9.8d/inc32/openssl/include" /I "./" /I "../../../" /I "include" /I "../include" /I "../../isc/win32" /I "../../isc/win32/include" /I "../../isc/include" /I "../../isc/noatomic/include" /I "../../../../openssl-0.9.8d/inc32" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "__STDC__" /D "_MBCS" /D "_USRDLL" /D "USE_MD5" /D "OPENSSL" /D "DST_USE_PRIVATE_OPENSSL" /D "LIBDNS_EXPORTS" /Fp"$(INTDIR)\libdns.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
+
+"$(INTDIR)\dispatch.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+CPP_SWITCHES=/nologo /MDd /W3 /Gm /GX /ZI /Od /I "./" /I "../../../" /I "include" /I "../include" /I "../../isc/win32" /I "../../isc/win32/include" /I "../../isc/include" /I "../../isc/noatomic/include" /I "../../../../openssl-0.9.8d/inc32" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "__STDC__" /D "_MBCS" /D "_USRDLL" /D "USE_MD5" /D "OPENSSL" /D "DST_USE_PRIVATE_OPENSSL" /D "LIBDNS_EXPORTS" /FR"$(INTDIR)\\" /Fp"$(INTDIR)\libdns.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c
+
+"$(INTDIR)\dispatch.obj" "$(INTDIR)\dispatch.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ENDIF
+
+SOURCE=..\dlz.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\dlz.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+"$(INTDIR)\dlz.obj" "$(INTDIR)\dlz.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=.\DLLMain.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\DLLMain.obj" : $(SOURCE) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\DLLMain.obj" "$(INTDIR)\DLLMain.sbr" : $(SOURCE) "$(INTDIR)"
+
+
+!ENDIF
+
+SOURCE=..\dnssec.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\dnssec.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\dnssec.obj" "$(INTDIR)\dnssec.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\ds.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\ds.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\ds.obj" "$(INTDIR)\ds.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\forward.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\forward.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\forward.obj" "$(INTDIR)\forward.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\iptable.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\iptable.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\iptable.obj" "$(INTDIR)\iptable.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\journal.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\journal.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\journal.obj" "$(INTDIR)\journal.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\keytable.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\keytable.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\keytable.obj" "$(INTDIR)\keytable.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\lib.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\lib.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\lib.obj" "$(INTDIR)\lib.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\log.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\log.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\log.obj" "$(INTDIR)\log.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\lookup.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\lookup.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\lookup.obj" "$(INTDIR)\lookup.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\master.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\master.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\master.obj" "$(INTDIR)\master.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\masterdump.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\masterdump.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\masterdump.obj" "$(INTDIR)\masterdump.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\message.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\message.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\message.obj" "$(INTDIR)\message.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\name.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\name.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\name.obj" "$(INTDIR)\name.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\ncache.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\ncache.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\ncache.obj" "$(INTDIR)\ncache.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\nsec.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\nsec.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\nsec.obj" "$(INTDIR)\nsec.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\nsec3.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\nsec3.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\nsec3.obj" "$(INTDIR)\nsec3.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\order.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\order.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\order.obj" "$(INTDIR)\order.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\peer.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\peer.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\peer.obj" "$(INTDIR)\peer.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+
+SOURCE=..\portlist.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\portlist.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\portlist.obj" "$(INTDIR)\portlist.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\rbt.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\rbt.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\rbt.obj" "$(INTDIR)\rbt.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\rbtdb.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\rbtdb.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\rbtdb.obj" "$(INTDIR)\rbtdb.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\rbtdb64.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\rbtdb64.obj" : $(SOURCE) "$(INTDIR)" "..\rbtdb.c"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\rbtdb64.obj" "$(INTDIR)\rbtdb64.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\rcode.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\rcode.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\rcode.obj" "$(INTDIR)\rcode.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\rdata.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\rdata.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\rdata.obj" "$(INTDIR)\rdata.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\rdatalist.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\rdatalist.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\rdatalist.obj" "$(INTDIR)\rdatalist.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\rdataset.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\rdataset.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\rdataset.obj" "$(INTDIR)\rdataset.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\rdatasetiter.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\rdatasetiter.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\rdatasetiter.obj" "$(INTDIR)\rdatasetiter.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\rdataslab.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\rdataslab.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\rdataslab.obj" "$(INTDIR)\rdataslab.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\request.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\request.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\request.obj" "$(INTDIR)\request.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\resolver.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\resolver.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\resolver.obj" "$(INTDIR)\resolver.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\result.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\result.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\result.obj" "$(INTDIR)\result.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\rootns.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\rootns.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\rootns.obj" "$(INTDIR)\rootns.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\sdb.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\sdb.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\sdb.obj" "$(INTDIR)\sdb.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\sdlz.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\sdlz.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\sdlz.obj" "$(INTDIR)\sdlz.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\soa.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\soa.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\soa.obj" "$(INTDIR)\soa.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\ssu.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\ssu.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\ssu.obj" "$(INTDIR)\ssu.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\stats.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\stats.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\stats.obj" "$(INTDIR)\stats.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\tcpmsg.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\tcpmsg.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\tcpmsg.obj" "$(INTDIR)\tcpmsg.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\time.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\time.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\time.obj" "$(INTDIR)\time.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\timer.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\timer.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\timer.obj" "$(INTDIR)\timer.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\tkey.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\tkey.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\tkey.obj" "$(INTDIR)\tkey.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\tsig.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\tsig.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\tsig.obj" "$(INTDIR)\tsig.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\ttl.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\ttl.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\ttl.obj" "$(INTDIR)\ttl.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\validator.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\validator.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\validator.obj" "$(INTDIR)\validator.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=.\version.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\version.obj" : $(SOURCE) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\version.obj" "$(INTDIR)\version.sbr" : $(SOURCE) "$(INTDIR)"
+
+
+!ENDIF
+
+SOURCE=..\view.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\view.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\view.obj" "$(INTDIR)\view.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\xfrin.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\xfrin.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\xfrin.obj" "$(INTDIR)\xfrin.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\zone.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\zone.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\zone.obj" "$(INTDIR)\zone.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\zonekey.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\zonekey.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\zonekey.obj" "$(INTDIR)\zonekey.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\zt.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\zt.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\zt.obj" "$(INTDIR)\zt.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\dst_api.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\dst_api.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\dst_api.obj" "$(INTDIR)\dst_api.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\dst_lib.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\dst_lib.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\dst_lib.obj" "$(INTDIR)\dst_lib.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\dst_parse.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\dst_parse.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\dst_parse.obj" "$(INTDIR)\dst_parse.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\dst_result.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\dst_result.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\dst_result.obj" "$(INTDIR)\dst_result.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\gssapi_link.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\gssapi_link.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\gssapi_link.obj" "$(INTDIR)\gssapi_link.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\gssapictx.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\gssapictx.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\gssapictx.obj" "$(INTDIR)\gssapictx.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\spnego.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\spnego.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\spnego.obj" "$(INTDIR)\spnego.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\hmac_link.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\hmac_link.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\hmac_link.obj" "$(INTDIR)\hmac_link.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\key.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\key.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\key.obj" "$(INTDIR)\key.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\openssl_link.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\openssl_link.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\openssl_link.obj" "$(INTDIR)\openssl_link.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\openssldh_link.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\openssldh_link.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\openssldh_link.obj" "$(INTDIR)\openssldh_link.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\openssldsa_link.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\openssldsa_link.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\openssldsa_link.obj" "$(INTDIR)\openssldsa_link.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\opensslrsa_link.c
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\opensslrsa_link.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\opensslrsa_link.obj" "$(INTDIR)\opensslrsa_link.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+!IF "$(CFG)" == "libdns - Win32 Release"
+
+"libisc - Win32 Release" :
+ cd "..\..\isc\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisc.mak" CFG="libisc - Win32 Release"
+ cd "..\..\dns\win32"
+
+"libisc - Win32 ReleaseCLEAN" :
+ cd "..\..\isc\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisc.mak" CFG="libisc - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\dns\win32"
+
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
+
+"libisc - Win32 Debug" :
+ cd "..\..\isc\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisc.mak" CFG="libisc - Win32 Debug"
+ cd "..\..\dns\win32"
+
+"libisc - Win32 DebugCLEAN" :
+ cd "..\..\isc\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisc.mak" CFG="libisc - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\dns\win32"
+
+!ENDIF
+
+
+!ENDIF
+
+####################################################
+# Commands to generate initial empty manifest file and the RC file
+# that references it, and for generating the .res file:
+
+$(_VC_MANIFEST_BASENAME).auto.res : $(_VC_MANIFEST_BASENAME).auto.rc
+
+$(_VC_MANIFEST_BASENAME).auto.rc : $(_VC_MANIFEST_BASENAME).auto.manifest
+ type <<$@
+#include <winuser.h>
+1RT_MANIFEST"$(_VC_MANIFEST_BASENAME).auto.manifest"
+<< KEEP
+
+$(_VC_MANIFEST_BASENAME).auto.manifest :
+ type <<$@
+<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
+<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
+</assembly>
+<< KEEP
diff --git a/lib/dns/win32/version.c b/lib/dns/win32/version.c
new file mode 100644
index 0000000..a570183
--- /dev/null
+++ b/lib/dns/win32/version.c
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: version.c,v 1.6 2007/06/19 23:47:17 tbox Exp $ */
+
+#include <versions.h>
+
+#include <dns/version.h>
+
+LIBDNS_EXTERNAL_DATA const char dns_version[] = VERSION;
+
+LIBDNS_EXTERNAL_DATA const unsigned int dns_libinterface = LIBINTERFACE;
+LIBDNS_EXTERNAL_DATA const unsigned int dns_librevision = LIBREVISION;
+LIBDNS_EXTERNAL_DATA const unsigned int dns_libage = LIBAGE;
diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c
new file mode 100644
index 0000000..4e3d2c3
--- /dev/null
+++ b/lib/dns/xfrin.c
@@ -0,0 +1,1532 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: xfrin.c,v 1.166 2008/09/25 04:12:39 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/random.h>
+#include <isc/string.h> /* Required for HP/UX (and others?) */
+#include <isc/task.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/diff.h>
+#include <dns/events.h>
+#include <dns/journal.h>
+#include <dns/log.h>
+#include <dns/message.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/result.h>
+#include <dns/soa.h>
+#include <dns/tcpmsg.h>
+#include <dns/timer.h>
+#include <dns/tsig.h>
+#include <dns/view.h>
+#include <dns/xfrin.h>
+#include <dns/zone.h>
+
+#include <dst/dst.h>
+
+/*
+ * Incoming AXFR and IXFR.
+ */
+
+/*%
+ * It would be non-sensical (or at least obtuse) to use FAIL() with an
+ * ISC_R_SUCCESS code, but the test is there to keep the Solaris compiler
+ * from complaining about "end-of-loop code not reached".
+ */
+#define FAIL(code) \
+ do { result = (code); \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+
+#define CHECK(op) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+
+/*%
+ * The states of the *XFR state machine. We handle both IXFR and AXFR
+ * with a single integrated state machine because they cannot be distinguished
+ * immediately - an AXFR response to an IXFR request can only be detected
+ * when the first two (2) response RRs have already been received.
+ */
+typedef enum {
+ XFRST_SOAQUERY,
+ XFRST_GOTSOA,
+ XFRST_INITIALSOA,
+ XFRST_FIRSTDATA,
+ XFRST_IXFR_DELSOA,
+ XFRST_IXFR_DEL,
+ XFRST_IXFR_ADDSOA,
+ XFRST_IXFR_ADD,
+ XFRST_AXFR,
+ XFRST_END
+} xfrin_state_t;
+
+/*%
+ * Incoming zone transfer context.
+ */
+
+struct dns_xfrin_ctx {
+ unsigned int magic;
+ isc_mem_t *mctx;
+ dns_zone_t *zone;
+
+ int refcount;
+
+ isc_task_t *task;
+ isc_timer_t *timer;
+ isc_socketmgr_t *socketmgr;
+
+ int connects; /*%< Connect in progress */
+ int sends; /*%< Send in progress */
+ int recvs; /*%< Receive in progress */
+ isc_boolean_t shuttingdown;
+
+ dns_name_t name; /*%< Name of zone to transfer */
+ dns_rdataclass_t rdclass;
+
+ isc_boolean_t checkid;
+ dns_messageid_t id;
+
+ /*%
+ * Requested transfer type (dns_rdatatype_axfr or
+ * dns_rdatatype_ixfr). The actual transfer type
+ * may differ due to IXFR->AXFR fallback.
+ */
+ dns_rdatatype_t reqtype;
+
+ isc_sockaddr_t masteraddr;
+ isc_sockaddr_t sourceaddr;
+ isc_socket_t *socket;
+
+ /*% Buffer for IXFR/AXFR request message */
+ isc_buffer_t qbuffer;
+ unsigned char qbuffer_data[512];
+
+ /*% Incoming reply TCP message */
+ dns_tcpmsg_t tcpmsg;
+ isc_boolean_t tcpmsg_valid;
+
+ dns_db_t *db;
+ dns_dbversion_t *ver;
+ dns_diff_t diff; /*%< Pending database changes */
+ int difflen; /*%< Number of pending tuples */
+
+ xfrin_state_t state;
+ isc_uint32_t end_serial;
+ isc_boolean_t is_ixfr;
+
+ unsigned int nmsg; /*%< Number of messages recvd */
+ unsigned int nrecs; /*%< Number of records recvd */
+ isc_uint64_t nbytes; /*%< Number of bytes received */
+
+ isc_time_t start; /*%< Start time of the transfer */
+ isc_time_t end; /*%< End time of the transfer */
+
+ dns_tsigkey_t *tsigkey; /*%< Key used to create TSIG */
+ isc_buffer_t *lasttsig; /*%< The last TSIG */
+ dst_context_t *tsigctx; /*%< TSIG verification context */
+ unsigned int sincetsig; /*%< recvd since the last TSIG */
+ dns_xfrindone_t done;
+
+ /*%
+ * AXFR- and IXFR-specific data. Only one is used at a time
+ * according to the is_ixfr flag, so this could be a union,
+ * but keeping them separate makes it a bit simpler to clean
+ * things up when destroying the context.
+ */
+ struct {
+ dns_addrdatasetfunc_t add_func;
+ dns_dbload_t *add_private;
+ } axfr;
+
+ struct {
+ isc_uint32_t request_serial;
+ isc_uint32_t current_serial;
+ dns_journal_t *journal;
+
+ } ixfr;
+};
+
+#define XFRIN_MAGIC ISC_MAGIC('X', 'f', 'r', 'I')
+#define VALID_XFRIN(x) ISC_MAGIC_VALID(x, XFRIN_MAGIC)
+
+/**************************************************************************/
+/*
+ * Forward declarations.
+ */
+
+static isc_result_t
+xfrin_create(isc_mem_t *mctx,
+ dns_zone_t *zone,
+ dns_db_t *db,
+ isc_task_t *task,
+ isc_timermgr_t *timermgr,
+ isc_socketmgr_t *socketmgr,
+ dns_name_t *zonename,
+ dns_rdataclass_t rdclass,
+ dns_rdatatype_t reqtype,
+ isc_sockaddr_t *masteraddr,
+ isc_sockaddr_t *sourceaddr,
+ dns_tsigkey_t *tsigkey,
+ dns_xfrin_ctx_t **xfrp);
+
+static isc_result_t axfr_init(dns_xfrin_ctx_t *xfr);
+static isc_result_t axfr_makedb(dns_xfrin_ctx_t *xfr, dns_db_t **dbp);
+static isc_result_t axfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op,
+ dns_name_t *name, dns_ttl_t ttl,
+ dns_rdata_t *rdata);
+static isc_result_t axfr_apply(dns_xfrin_ctx_t *xfr);
+static isc_result_t axfr_commit(dns_xfrin_ctx_t *xfr);
+
+static isc_result_t ixfr_init(dns_xfrin_ctx_t *xfr);
+static isc_result_t ixfr_apply(dns_xfrin_ctx_t *xfr);
+static isc_result_t ixfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op,
+ dns_name_t *name, dns_ttl_t ttl,
+ dns_rdata_t *rdata);
+static isc_result_t ixfr_commit(dns_xfrin_ctx_t *xfr);
+
+static isc_result_t xfr_rr(dns_xfrin_ctx_t *xfr, dns_name_t *name,
+ isc_uint32_t ttl, dns_rdata_t *rdata);
+
+static isc_result_t xfrin_start(dns_xfrin_ctx_t *xfr);
+
+static void xfrin_connect_done(isc_task_t *task, isc_event_t *event);
+static isc_result_t xfrin_send_request(dns_xfrin_ctx_t *xfr);
+static void xfrin_send_done(isc_task_t *task, isc_event_t *event);
+static void xfrin_sendlen_done(isc_task_t *task, isc_event_t *event);
+static void xfrin_recv_done(isc_task_t *task, isc_event_t *event);
+static void xfrin_timeout(isc_task_t *task, isc_event_t *event);
+
+static void maybe_free(dns_xfrin_ctx_t *xfr);
+
+static void
+xfrin_fail(dns_xfrin_ctx_t *xfr, isc_result_t result, const char *msg);
+static isc_result_t
+render(dns_message_t *msg, isc_mem_t *mctx, isc_buffer_t *buf);
+
+static void
+xfrin_logv(int level, const char *zonetext, isc_sockaddr_t *masteraddr,
+ const char *fmt, va_list ap)
+ ISC_FORMAT_PRINTF(4, 0);
+
+static void
+xfrin_log1(int level, const char *zonetext, isc_sockaddr_t *masteraddr,
+ const char *fmt, ...)
+ ISC_FORMAT_PRINTF(4, 5);
+
+static void
+xfrin_log(dns_xfrin_ctx_t *xfr, int level, const char *fmt, ...)
+ ISC_FORMAT_PRINTF(3, 4);
+
+/**************************************************************************/
+/*
+ * AXFR handling
+ */
+
+static isc_result_t
+axfr_init(dns_xfrin_ctx_t *xfr) {
+ isc_result_t result;
+
+ xfr->is_ixfr = ISC_FALSE;
+
+ if (xfr->db != NULL)
+ dns_db_detach(&xfr->db);
+
+ CHECK(axfr_makedb(xfr, &xfr->db));
+ CHECK(dns_db_beginload(xfr->db, &xfr->axfr.add_func,
+ &xfr->axfr.add_private));
+ result = ISC_R_SUCCESS;
+ failure:
+ return (result);
+}
+
+static isc_result_t
+axfr_makedb(dns_xfrin_ctx_t *xfr, dns_db_t **dbp) {
+ return (dns_db_create(xfr->mctx, /* XXX */
+ "rbt", /* XXX guess */
+ &xfr->name,
+ dns_dbtype_zone,
+ xfr->rdclass,
+ 0, NULL, /* XXX guess */
+ dbp));
+}
+
+static isc_result_t
+axfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op,
+ dns_name_t *name, dns_ttl_t ttl, dns_rdata_t *rdata)
+{
+ isc_result_t result;
+
+ dns_difftuple_t *tuple = NULL;
+
+ CHECK(dns_zone_checknames(xfr->zone, name, rdata));
+ CHECK(dns_difftuple_create(xfr->diff.mctx, op,
+ name, ttl, rdata, &tuple));
+ dns_diff_append(&xfr->diff, &tuple);
+ if (++xfr->difflen > 100)
+ CHECK(axfr_apply(xfr));
+ result = ISC_R_SUCCESS;
+ failure:
+ return (result);
+}
+
+/*
+ * Store a set of AXFR RRs in the database.
+ */
+static isc_result_t
+axfr_apply(dns_xfrin_ctx_t *xfr) {
+ isc_result_t result;
+
+ CHECK(dns_diff_load(&xfr->diff,
+ xfr->axfr.add_func, xfr->axfr.add_private));
+ xfr->difflen = 0;
+ dns_diff_clear(&xfr->diff);
+ result = ISC_R_SUCCESS;
+ failure:
+ return (result);
+}
+
+static isc_result_t
+axfr_commit(dns_xfrin_ctx_t *xfr) {
+ isc_result_t result;
+
+ CHECK(axfr_apply(xfr));
+ CHECK(dns_db_endload(xfr->db, &xfr->axfr.add_private));
+ CHECK(dns_zone_replacedb(xfr->zone, xfr->db, ISC_TRUE));
+
+ result = ISC_R_SUCCESS;
+ failure:
+ return (result);
+}
+
+/**************************************************************************/
+/*
+ * IXFR handling
+ */
+
+static isc_result_t
+ixfr_init(dns_xfrin_ctx_t *xfr) {
+ isc_result_t result;
+ char *journalfile;
+
+ if (xfr->reqtype != dns_rdatatype_ixfr) {
+ xfrin_log(xfr, ISC_LOG_ERROR,
+ "got incremental response to AXFR request");
+ return (DNS_R_FORMERR);
+ }
+
+ xfr->is_ixfr = ISC_TRUE;
+ INSIST(xfr->db != NULL);
+ xfr->difflen = 0;
+
+ journalfile = dns_zone_getjournal(xfr->zone);
+ if (journalfile != NULL)
+ CHECK(dns_journal_open(xfr->mctx, journalfile,
+ ISC_TRUE, &xfr->ixfr.journal));
+
+ result = ISC_R_SUCCESS;
+ failure:
+ return (result);
+}
+
+static isc_result_t
+ixfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op,
+ dns_name_t *name, dns_ttl_t ttl, dns_rdata_t *rdata)
+{
+ isc_result_t result;
+
+ dns_difftuple_t *tuple = NULL;
+ if (op == DNS_DIFFOP_ADD)
+ CHECK(dns_zone_checknames(xfr->zone, name, rdata));
+ CHECK(dns_difftuple_create(xfr->diff.mctx, op,
+ name, ttl, rdata, &tuple));
+ dns_diff_append(&xfr->diff, &tuple);
+ if (++xfr->difflen > 100)
+ CHECK(ixfr_apply(xfr));
+ result = ISC_R_SUCCESS;
+ failure:
+ return (result);
+}
+
+/*
+ * Apply a set of IXFR changes to the database.
+ */
+static isc_result_t
+ixfr_apply(dns_xfrin_ctx_t *xfr) {
+ isc_result_t result;
+
+ if (xfr->ver == NULL) {
+ CHECK(dns_db_newversion(xfr->db, &xfr->ver));
+ if (xfr->ixfr.journal != NULL)
+ CHECK(dns_journal_begin_transaction(xfr->ixfr.journal));
+ }
+ CHECK(dns_diff_apply(&xfr->diff, xfr->db, xfr->ver));
+ if (xfr->ixfr.journal != NULL) {
+ result = dns_journal_writediff(xfr->ixfr.journal, &xfr->diff);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ }
+ dns_diff_clear(&xfr->diff);
+ xfr->difflen = 0;
+ result = ISC_R_SUCCESS;
+ failure:
+ return (result);
+}
+
+static isc_result_t
+ixfr_commit(dns_xfrin_ctx_t *xfr) {
+ isc_result_t result;
+
+ CHECK(ixfr_apply(xfr));
+ if (xfr->ver != NULL) {
+ /* XXX enter ready-to-commit state here */
+ if (xfr->ixfr.journal != NULL)
+ CHECK(dns_journal_commit(xfr->ixfr.journal));
+ dns_db_closeversion(xfr->db, &xfr->ver, ISC_TRUE);
+ dns_zone_markdirty(xfr->zone);
+ }
+ result = ISC_R_SUCCESS;
+ failure:
+ return (result);
+}
+
+/**************************************************************************/
+/*
+ * Common AXFR/IXFR protocol code
+ */
+
+/*
+ * Handle a single incoming resource record according to the current
+ * state.
+ */
+static isc_result_t
+xfr_rr(dns_xfrin_ctx_t *xfr, dns_name_t *name, isc_uint32_t ttl,
+ dns_rdata_t *rdata)
+{
+ isc_result_t result;
+
+ xfr->nrecs++;
+
+ if (rdata->type == dns_rdatatype_none ||
+ dns_rdatatype_ismeta(rdata->type))
+ FAIL(DNS_R_FORMERR);
+
+ redo:
+ switch (xfr->state) {
+ case XFRST_SOAQUERY:
+ if (rdata->type != dns_rdatatype_soa) {
+ xfrin_log(xfr, ISC_LOG_ERROR,
+ "non-SOA response to SOA query");
+ FAIL(DNS_R_FORMERR);
+ }
+ xfr->end_serial = dns_soa_getserial(rdata);
+ if (!DNS_SERIAL_GT(xfr->end_serial, xfr->ixfr.request_serial) &&
+ !dns_zone_isforced(xfr->zone)) {
+ xfrin_log(xfr, ISC_LOG_DEBUG(3),
+ "requested serial %u, "
+ "master has %u, not updating",
+ xfr->ixfr.request_serial, xfr->end_serial);
+ FAIL(DNS_R_UPTODATE);
+ }
+ xfr->state = XFRST_GOTSOA;
+ break;
+
+ case XFRST_GOTSOA:
+ /*
+ * Skip other records in the answer section.
+ */
+ break;
+
+ case XFRST_INITIALSOA:
+ if (rdata->type != dns_rdatatype_soa) {
+ xfrin_log(xfr, ISC_LOG_ERROR,
+ "first RR in zone transfer must be SOA");
+ FAIL(DNS_R_FORMERR);
+ }
+ /*
+ * Remember the serial number in the initial SOA.
+ * We need it to recognize the end of an IXFR.
+ */
+ xfr->end_serial = dns_soa_getserial(rdata);
+ if (xfr->reqtype == dns_rdatatype_ixfr &&
+ ! DNS_SERIAL_GT(xfr->end_serial, xfr->ixfr.request_serial)
+ && !dns_zone_isforced(xfr->zone))
+ {
+ /*
+ * This must be the single SOA record that is
+ * sent when the current version on the master
+ * is not newer than the version in the request.
+ */
+ xfrin_log(xfr, ISC_LOG_DEBUG(3),
+ "requested serial %u, "
+ "master has %u, not updating",
+ xfr->ixfr.request_serial, xfr->end_serial);
+ FAIL(DNS_R_UPTODATE);
+ }
+ if (xfr->reqtype == dns_rdatatype_axfr)
+ xfr->checkid = ISC_FALSE;
+ xfr->state = XFRST_FIRSTDATA;
+ break;
+
+ case XFRST_FIRSTDATA:
+ /*
+ * If the transfer begins with one SOA record, it is an AXFR,
+ * if it begins with two SOAs, it is an IXFR.
+ */
+ if (xfr->reqtype == dns_rdatatype_ixfr &&
+ rdata->type == dns_rdatatype_soa &&
+ xfr->ixfr.request_serial == dns_soa_getserial(rdata)) {
+ xfrin_log(xfr, ISC_LOG_DEBUG(3),
+ "got incremental response");
+ CHECK(ixfr_init(xfr));
+ xfr->state = XFRST_IXFR_DELSOA;
+ } else {
+ xfrin_log(xfr, ISC_LOG_DEBUG(3),
+ "got nonincremental response");
+ CHECK(axfr_init(xfr));
+ xfr->state = XFRST_AXFR;
+ }
+ goto redo;
+
+ case XFRST_IXFR_DELSOA:
+ INSIST(rdata->type == dns_rdatatype_soa);
+ CHECK(ixfr_putdata(xfr, DNS_DIFFOP_DEL, name, ttl, rdata));
+ xfr->state = XFRST_IXFR_DEL;
+ break;
+
+ case XFRST_IXFR_DEL:
+ if (rdata->type == dns_rdatatype_soa) {
+ isc_uint32_t soa_serial = dns_soa_getserial(rdata);
+ xfr->state = XFRST_IXFR_ADDSOA;
+ xfr->ixfr.current_serial = soa_serial;
+ goto redo;
+ }
+ CHECK(ixfr_putdata(xfr, DNS_DIFFOP_DEL, name, ttl, rdata));
+ break;
+
+ case XFRST_IXFR_ADDSOA:
+ INSIST(rdata->type == dns_rdatatype_soa);
+ CHECK(ixfr_putdata(xfr, DNS_DIFFOP_ADD, name, ttl, rdata));
+ xfr->state = XFRST_IXFR_ADD;
+ break;
+
+ case XFRST_IXFR_ADD:
+ if (rdata->type == dns_rdatatype_soa) {
+ isc_uint32_t soa_serial = dns_soa_getserial(rdata);
+ if (soa_serial == xfr->end_serial) {
+ CHECK(ixfr_commit(xfr));
+ xfr->state = XFRST_END;
+ break;
+ } else if (soa_serial != xfr->ixfr.current_serial) {
+ xfrin_log(xfr, ISC_LOG_ERROR,
+ "IXFR out of sync: "
+ "expected serial %u, got %u",
+ xfr->ixfr.current_serial, soa_serial);
+ FAIL(DNS_R_FORMERR);
+ } else {
+ CHECK(ixfr_commit(xfr));
+ xfr->state = XFRST_IXFR_DELSOA;
+ goto redo;
+ }
+ }
+ if (rdata->type == dns_rdatatype_ns &&
+ dns_name_iswildcard(name))
+ FAIL(DNS_R_INVALIDNS);
+ CHECK(ixfr_putdata(xfr, DNS_DIFFOP_ADD, name, ttl, rdata));
+ break;
+
+ case XFRST_AXFR:
+ /*
+ * Old BINDs sent cross class A records for non IN classes.
+ */
+ if (rdata->type == dns_rdatatype_a &&
+ rdata->rdclass != xfr->rdclass &&
+ xfr->rdclass != dns_rdataclass_in)
+ break;
+ CHECK(axfr_putdata(xfr, DNS_DIFFOP_ADD, name, ttl, rdata));
+ if (rdata->type == dns_rdatatype_soa) {
+ CHECK(axfr_commit(xfr));
+ xfr->state = XFRST_END;
+ break;
+ }
+ break;
+ case XFRST_END:
+ FAIL(DNS_R_EXTRADATA);
+ default:
+ INSIST(0);
+ break;
+ }
+ result = ISC_R_SUCCESS;
+ failure:
+ return (result);
+}
+
+isc_result_t
+dns_xfrin_create(dns_zone_t *zone, dns_rdatatype_t xfrtype,
+ isc_sockaddr_t *masteraddr, dns_tsigkey_t *tsigkey,
+ isc_mem_t *mctx, isc_timermgr_t *timermgr,
+ isc_socketmgr_t *socketmgr, isc_task_t *task,
+ dns_xfrindone_t done, dns_xfrin_ctx_t **xfrp)
+{
+ isc_sockaddr_t sourceaddr;
+
+ switch (isc_sockaddr_pf(masteraddr)) {
+ case PF_INET:
+ sourceaddr = *dns_zone_getxfrsource4(zone);
+ break;
+ case PF_INET6:
+ sourceaddr = *dns_zone_getxfrsource6(zone);
+ break;
+ default:
+ INSIST(0);
+ }
+
+ return(dns_xfrin_create2(zone, xfrtype, masteraddr, &sourceaddr,
+ tsigkey, mctx, timermgr, socketmgr,
+ task, done, xfrp));
+}
+
+isc_result_t
+dns_xfrin_create2(dns_zone_t *zone, dns_rdatatype_t xfrtype,
+ isc_sockaddr_t *masteraddr, isc_sockaddr_t *sourceaddr,
+ dns_tsigkey_t *tsigkey, isc_mem_t *mctx,
+ isc_timermgr_t *timermgr, isc_socketmgr_t *socketmgr,
+ isc_task_t *task, dns_xfrindone_t done, dns_xfrin_ctx_t **xfrp)
+{
+ dns_name_t *zonename = dns_zone_getorigin(zone);
+ dns_xfrin_ctx_t *xfr = NULL;
+ isc_result_t result;
+ dns_db_t *db = NULL;
+
+ REQUIRE(xfrp != NULL && *xfrp == NULL);
+
+ (void)dns_zone_getdb(zone, &db);
+
+ if (xfrtype == dns_rdatatype_soa || xfrtype == dns_rdatatype_ixfr)
+ REQUIRE(db != NULL);
+
+ CHECK(xfrin_create(mctx, zone, db, task, timermgr, socketmgr, zonename,
+ dns_zone_getclass(zone), xfrtype, masteraddr,
+ sourceaddr, tsigkey, &xfr));
+
+ CHECK(xfrin_start(xfr));
+
+ xfr->done = done;
+ xfr->refcount++;
+ *xfrp = xfr;
+
+ failure:
+ if (db != NULL)
+ dns_db_detach(&db);
+ if (result != ISC_R_SUCCESS) {
+ char zonetext[DNS_NAME_MAXTEXT+32];
+ dns_zone_name(zone, zonetext, sizeof(zonetext));
+ xfrin_log1(ISC_LOG_ERROR, zonetext, masteraddr,
+ "zone transfer setup failed");
+ }
+ return (result);
+}
+
+void
+dns_xfrin_shutdown(dns_xfrin_ctx_t *xfr) {
+ if (! xfr->shuttingdown)
+ xfrin_fail(xfr, ISC_R_CANCELED, "shut down");
+}
+
+void
+dns_xfrin_attach(dns_xfrin_ctx_t *source, dns_xfrin_ctx_t **target) {
+ REQUIRE(target != NULL && *target == NULL);
+ source->refcount++;
+ *target = source;
+}
+
+void
+dns_xfrin_detach(dns_xfrin_ctx_t **xfrp) {
+ dns_xfrin_ctx_t *xfr = *xfrp;
+ INSIST(xfr->refcount > 0);
+ xfr->refcount--;
+ maybe_free(xfr);
+ *xfrp = NULL;
+}
+
+static void
+xfrin_cancelio(dns_xfrin_ctx_t *xfr) {
+ if (xfr->connects > 0) {
+ isc_socket_cancel(xfr->socket, xfr->task,
+ ISC_SOCKCANCEL_CONNECT);
+ } else if (xfr->recvs > 0) {
+ dns_tcpmsg_cancelread(&xfr->tcpmsg);
+ } else if (xfr->sends > 0) {
+ isc_socket_cancel(xfr->socket, xfr->task,
+ ISC_SOCKCANCEL_SEND);
+ }
+}
+
+static void
+xfrin_reset(dns_xfrin_ctx_t *xfr) {
+ REQUIRE(VALID_XFRIN(xfr));
+
+ xfrin_log(xfr, ISC_LOG_INFO, "resetting");
+
+ xfrin_cancelio(xfr);
+
+ if (xfr->socket != NULL)
+ isc_socket_detach(&xfr->socket);
+
+ if (xfr->lasttsig != NULL)
+ isc_buffer_free(&xfr->lasttsig);
+
+ dns_diff_clear(&xfr->diff);
+ xfr->difflen = 0;
+
+ if (xfr->ixfr.journal != NULL)
+ dns_journal_destroy(&xfr->ixfr.journal);
+
+ if (xfr->axfr.add_private != NULL) {
+ (void)dns_db_endload(xfr->db, &xfr->axfr.add_private);
+ xfr->axfr.add_func = NULL;
+ }
+
+ if (xfr->tcpmsg_valid) {
+ dns_tcpmsg_invalidate(&xfr->tcpmsg);
+ xfr->tcpmsg_valid = ISC_FALSE;
+ }
+
+ if (xfr->ver != NULL)
+ dns_db_closeversion(xfr->db, &xfr->ver, ISC_FALSE);
+}
+
+
+static void
+xfrin_fail(dns_xfrin_ctx_t *xfr, isc_result_t result, const char *msg) {
+ if (result != DNS_R_UPTODATE) {
+ xfrin_log(xfr, ISC_LOG_ERROR, "%s: %s",
+ msg, isc_result_totext(result));
+ if (xfr->is_ixfr)
+ /* Pass special result code to force AXFR retry */
+ result = DNS_R_BADIXFR;
+ }
+ xfrin_cancelio(xfr);
+ /*
+ * Close the journal.
+ */
+ if (xfr->ixfr.journal != NULL)
+ dns_journal_destroy(&xfr->ixfr.journal);
+ if (xfr->done != NULL) {
+ (xfr->done)(xfr->zone, result);
+ xfr->done = NULL;
+ }
+ xfr->shuttingdown = ISC_TRUE;
+ maybe_free(xfr);
+}
+
+static isc_result_t
+xfrin_create(isc_mem_t *mctx,
+ dns_zone_t *zone,
+ dns_db_t *db,
+ isc_task_t *task,
+ isc_timermgr_t *timermgr,
+ isc_socketmgr_t *socketmgr,
+ dns_name_t *zonename,
+ dns_rdataclass_t rdclass,
+ dns_rdatatype_t reqtype,
+ isc_sockaddr_t *masteraddr,
+ isc_sockaddr_t *sourceaddr,
+ dns_tsigkey_t *tsigkey,
+ dns_xfrin_ctx_t **xfrp)
+{
+ dns_xfrin_ctx_t *xfr = NULL;
+ isc_result_t result;
+ isc_uint32_t tmp;
+
+ xfr = isc_mem_get(mctx, sizeof(*xfr));
+ if (xfr == NULL)
+ return (ISC_R_NOMEMORY);
+ xfr->mctx = mctx;
+ xfr->refcount = 0;
+ xfr->zone = NULL;
+ dns_zone_iattach(zone, &xfr->zone);
+ xfr->task = NULL;
+ isc_task_attach(task, &xfr->task);
+ xfr->timer = NULL;
+ xfr->socketmgr = socketmgr;
+ xfr->done = NULL;
+
+ xfr->connects = 0;
+ xfr->sends = 0;
+ xfr->recvs = 0;
+ xfr->shuttingdown = ISC_FALSE;
+
+ dns_name_init(&xfr->name, NULL);
+ xfr->rdclass = rdclass;
+ isc_random_get(&tmp);
+ xfr->checkid = ISC_TRUE;
+ xfr->id = (isc_uint16_t)(tmp & 0xffff);
+ xfr->reqtype = reqtype;
+
+ /* sockaddr */
+ xfr->socket = NULL;
+ /* qbuffer */
+ /* qbuffer_data */
+ /* tcpmsg */
+ xfr->tcpmsg_valid = ISC_FALSE;
+
+ xfr->db = NULL;
+ if (db != NULL)
+ dns_db_attach(db, &xfr->db);
+ xfr->ver = NULL;
+ dns_diff_init(xfr->mctx, &xfr->diff);
+ xfr->difflen = 0;
+
+ if (reqtype == dns_rdatatype_soa)
+ xfr->state = XFRST_SOAQUERY;
+ else
+ xfr->state = XFRST_INITIALSOA;
+ /* end_serial */
+
+ xfr->nmsg = 0;
+ xfr->nrecs = 0;
+ xfr->nbytes = 0;
+ isc_time_now(&xfr->start);
+
+ xfr->tsigkey = NULL;
+ if (tsigkey != NULL)
+ dns_tsigkey_attach(tsigkey, &xfr->tsigkey);
+ xfr->lasttsig = NULL;
+ xfr->tsigctx = NULL;
+ xfr->sincetsig = 0;
+ xfr->is_ixfr = ISC_FALSE;
+
+ /* ixfr.request_serial */
+ /* ixfr.current_serial */
+ xfr->ixfr.journal = NULL;
+
+ xfr->axfr.add_func = NULL;
+ xfr->axfr.add_private = NULL;
+
+ CHECK(dns_name_dup(zonename, mctx, &xfr->name));
+
+ CHECK(isc_timer_create(timermgr, isc_timertype_inactive, NULL, NULL,
+ task, xfrin_timeout, xfr, &xfr->timer));
+ CHECK(dns_timer_setidle(xfr->timer,
+ dns_zone_getmaxxfrin(xfr->zone),
+ dns_zone_getidlein(xfr->zone),
+ ISC_FALSE));
+
+ xfr->masteraddr = *masteraddr;
+
+ INSIST(isc_sockaddr_pf(masteraddr) == isc_sockaddr_pf(sourceaddr));
+ xfr->sourceaddr = *sourceaddr;
+ isc_sockaddr_setport(&xfr->sourceaddr, 0);
+
+ isc_buffer_init(&xfr->qbuffer, xfr->qbuffer_data,
+ sizeof(xfr->qbuffer_data));
+
+ xfr->magic = XFRIN_MAGIC;
+ *xfrp = xfr;
+ return (ISC_R_SUCCESS);
+
+ failure:
+ if (xfr->timer != NULL)
+ isc_timer_detach(&xfr->timer);
+ if (dns_name_dynamic(&xfr->name))
+ dns_name_free(&xfr->name, xfr->mctx);
+ if (xfr->tsigkey != NULL)
+ dns_tsigkey_detach(&xfr->tsigkey);
+ if (xfr->db != NULL)
+ dns_db_detach(&xfr->db);
+ isc_task_detach(&xfr->task);
+ dns_zone_idetach(&xfr->zone);
+ isc_mem_put(mctx, xfr, sizeof(*xfr));
+
+ return (result);
+}
+
+static isc_result_t
+xfrin_start(dns_xfrin_ctx_t *xfr) {
+ isc_result_t result;
+ CHECK(isc_socket_create(xfr->socketmgr,
+ isc_sockaddr_pf(&xfr->sourceaddr),
+ isc_sockettype_tcp,
+ &xfr->socket));
+ isc_socket_setname(xfr->socket, "xfrin", NULL);
+#ifndef BROKEN_TCP_BIND_BEFORE_CONNECT
+ CHECK(isc_socket_bind(xfr->socket, &xfr->sourceaddr,
+ ISC_SOCKET_REUSEADDRESS));
+#endif
+ CHECK(isc_socket_connect(xfr->socket, &xfr->masteraddr, xfr->task,
+ xfrin_connect_done, xfr));
+ xfr->connects++;
+ return (ISC_R_SUCCESS);
+ failure:
+ xfrin_fail(xfr, result, "failed setting up socket");
+ return (result);
+}
+
+/* XXX the resolver could use this, too */
+
+static isc_result_t
+render(dns_message_t *msg, isc_mem_t *mctx, isc_buffer_t *buf) {
+ dns_compress_t cctx;
+ isc_boolean_t cleanup_cctx = ISC_FALSE;
+ isc_result_t result;
+
+ CHECK(dns_compress_init(&cctx, -1, mctx));
+ cleanup_cctx = ISC_TRUE;
+ CHECK(dns_message_renderbegin(msg, &cctx, buf));
+ CHECK(dns_message_rendersection(msg, DNS_SECTION_QUESTION, 0));
+ CHECK(dns_message_rendersection(msg, DNS_SECTION_ANSWER, 0));
+ CHECK(dns_message_rendersection(msg, DNS_SECTION_AUTHORITY, 0));
+ CHECK(dns_message_rendersection(msg, DNS_SECTION_ADDITIONAL, 0));
+ CHECK(dns_message_renderend(msg));
+ result = ISC_R_SUCCESS;
+ failure:
+ if (cleanup_cctx)
+ dns_compress_invalidate(&cctx);
+ return (result);
+}
+
+/*
+ * A connection has been established.
+ */
+static void
+xfrin_connect_done(isc_task_t *task, isc_event_t *event) {
+ isc_socket_connev_t *cev = (isc_socket_connev_t *) event;
+ dns_xfrin_ctx_t *xfr = (dns_xfrin_ctx_t *) event->ev_arg;
+ isc_result_t result = cev->result;
+ char sourcetext[ISC_SOCKADDR_FORMATSIZE];
+ isc_sockaddr_t sockaddr;
+
+ REQUIRE(VALID_XFRIN(xfr));
+
+ UNUSED(task);
+
+ INSIST(event->ev_type == ISC_SOCKEVENT_CONNECT);
+ isc_event_free(&event);
+
+ xfr->connects--;
+ if (xfr->shuttingdown) {
+ maybe_free(xfr);
+ return;
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ dns_zonemgr_t * zmgr = dns_zone_getmgr(xfr->zone);
+ isc_time_t now;
+
+ if (zmgr != NULL) {
+ TIME_NOW(&now);
+ dns_zonemgr_unreachableadd(zmgr, &xfr->masteraddr,
+ &xfr->sourceaddr, &now);
+ }
+ goto failure;
+ }
+
+ result = isc_socket_getsockname(xfr->socket, &sockaddr);
+ if (result == ISC_R_SUCCESS) {
+ isc_sockaddr_format(&sockaddr, sourcetext, sizeof(sourcetext));
+ } else
+ strcpy(sourcetext, "<UNKNOWN>");
+ xfrin_log(xfr, ISC_LOG_INFO, "connected using %s", sourcetext);
+
+ dns_tcpmsg_init(xfr->mctx, xfr->socket, &xfr->tcpmsg);
+ xfr->tcpmsg_valid = ISC_TRUE;
+
+ CHECK(xfrin_send_request(xfr));
+ failure:
+ if (result != ISC_R_SUCCESS)
+ xfrin_fail(xfr, result, "failed to connect");
+}
+
+/*
+ * Convert a tuple into a dns_name_t suitable for inserting
+ * into the given dns_message_t.
+ */
+static isc_result_t
+tuple2msgname(dns_difftuple_t *tuple, dns_message_t *msg, dns_name_t **target)
+{
+ isc_result_t result;
+ dns_rdata_t *rdata = NULL;
+ dns_rdatalist_t *rdl = NULL;
+ dns_rdataset_t *rds = NULL;
+ dns_name_t *name = NULL;
+
+ REQUIRE(target != NULL && *target == NULL);
+
+ CHECK(dns_message_gettemprdata(msg, &rdata));
+ dns_rdata_init(rdata);
+ dns_rdata_clone(&tuple->rdata, rdata);
+
+ CHECK(dns_message_gettemprdatalist(msg, &rdl));
+ dns_rdatalist_init(rdl);
+ rdl->type = tuple->rdata.type;
+ rdl->rdclass = tuple->rdata.rdclass;
+ rdl->ttl = tuple->ttl;
+ ISC_LIST_APPEND(rdl->rdata, rdata, link);
+
+ CHECK(dns_message_gettemprdataset(msg, &rds));
+ dns_rdataset_init(rds);
+ CHECK(dns_rdatalist_tordataset(rdl, rds));
+
+ CHECK(dns_message_gettempname(msg, &name));
+ dns_name_init(name, NULL);
+ dns_name_clone(&tuple->name, name);
+ ISC_LIST_APPEND(name->list, rds, link);
+
+ *target = name;
+ return (ISC_R_SUCCESS);
+
+ failure:
+
+ if (rds != NULL) {
+ dns_rdataset_disassociate(rds);
+ dns_message_puttemprdataset(msg, &rds);
+ }
+ if (rdl != NULL) {
+ ISC_LIST_UNLINK(rdl->rdata, rdata, link);
+ dns_message_puttemprdatalist(msg, &rdl);
+ }
+ if (rdata != NULL)
+ dns_message_puttemprdata(msg, &rdata);
+
+ return (result);
+}
+
+
+/*
+ * Build an *XFR request and send its length prefix.
+ */
+static isc_result_t
+xfrin_send_request(dns_xfrin_ctx_t *xfr) {
+ isc_result_t result;
+ isc_region_t region;
+ isc_region_t lregion;
+ dns_rdataset_t *qrdataset = NULL;
+ dns_message_t *msg = NULL;
+ unsigned char length[2];
+ dns_difftuple_t *soatuple = NULL;
+ dns_name_t *qname = NULL;
+ dns_dbversion_t *ver = NULL;
+ dns_name_t *msgsoaname = NULL;
+
+ /* Create the request message */
+ CHECK(dns_message_create(xfr->mctx, DNS_MESSAGE_INTENTRENDER, &msg));
+ CHECK(dns_message_settsigkey(msg, xfr->tsigkey));
+
+ /* Create a name for the question section. */
+ CHECK(dns_message_gettempname(msg, &qname));
+ dns_name_init(qname, NULL);
+ dns_name_clone(&xfr->name, qname);
+
+ /* Formulate the question and attach it to the question name. */
+ CHECK(dns_message_gettemprdataset(msg, &qrdataset));
+ dns_rdataset_init(qrdataset);
+ dns_rdataset_makequestion(qrdataset, xfr->rdclass, xfr->reqtype);
+ ISC_LIST_APPEND(qname->list, qrdataset, link);
+ qrdataset = NULL;
+
+ dns_message_addname(msg, qname, DNS_SECTION_QUESTION);
+ qname = NULL;
+
+ if (xfr->reqtype == dns_rdatatype_ixfr) {
+ /* Get the SOA and add it to the authority section. */
+ /* XXX is using the current version the right thing? */
+ dns_db_currentversion(xfr->db, &ver);
+ CHECK(dns_db_createsoatuple(xfr->db, ver, xfr->mctx,
+ DNS_DIFFOP_EXISTS, &soatuple));
+ xfr->ixfr.request_serial = dns_soa_getserial(&soatuple->rdata);
+ xfr->ixfr.current_serial = xfr->ixfr.request_serial;
+ xfrin_log(xfr, ISC_LOG_DEBUG(3),
+ "requesting IXFR for serial %u",
+ xfr->ixfr.request_serial);
+
+ CHECK(tuple2msgname(soatuple, msg, &msgsoaname));
+ dns_message_addname(msg, msgsoaname, DNS_SECTION_AUTHORITY);
+ } else if (xfr->reqtype == dns_rdatatype_soa)
+ CHECK(dns_db_getsoaserial(xfr->db, NULL,
+ &xfr->ixfr.request_serial));
+
+ xfr->checkid = ISC_TRUE;
+ xfr->id++;
+ xfr->nmsg = 0;
+ xfr->nrecs = 0;
+ xfr->nbytes = 0;
+ isc_time_now(&xfr->start);
+ msg->id = xfr->id;
+ if (xfr->tsigctx != NULL)
+ dst_context_destroy(&xfr->tsigctx);
+
+ CHECK(render(msg, xfr->mctx, &xfr->qbuffer));
+
+ /*
+ * Free the last tsig, if there is one.
+ */
+ if (xfr->lasttsig != NULL)
+ isc_buffer_free(&xfr->lasttsig);
+
+ /*
+ * Save the query TSIG and don't let message_destroy free it.
+ */
+ CHECK(dns_message_getquerytsig(msg, xfr->mctx, &xfr->lasttsig));
+
+ isc_buffer_usedregion(&xfr->qbuffer, &region);
+ INSIST(region.length <= 65535);
+
+ length[0] = region.length >> 8;
+ length[1] = region.length & 0xFF;
+ lregion.base = length;
+ lregion.length = 2;
+ CHECK(isc_socket_send(xfr->socket, &lregion, xfr->task,
+ xfrin_sendlen_done, xfr));
+ xfr->sends++;
+
+ failure:
+ if (qname != NULL)
+ dns_message_puttempname(msg, &qname);
+ if (qrdataset != NULL)
+ dns_message_puttemprdataset(msg, &qrdataset);
+ if (msg != NULL)
+ dns_message_destroy(&msg);
+ if (soatuple != NULL)
+ dns_difftuple_free(&soatuple);
+ if (ver != NULL)
+ dns_db_closeversion(xfr->db, &ver, ISC_FALSE);
+ return (result);
+}
+
+/* XXX there should be library support for sending DNS TCP messages */
+
+static void
+xfrin_sendlen_done(isc_task_t *task, isc_event_t *event) {
+ isc_socketevent_t *sev = (isc_socketevent_t *) event;
+ dns_xfrin_ctx_t *xfr = (dns_xfrin_ctx_t *) event->ev_arg;
+ isc_result_t evresult = sev->result;
+ isc_result_t result;
+ isc_region_t region;
+
+ REQUIRE(VALID_XFRIN(xfr));
+
+ UNUSED(task);
+
+ INSIST(event->ev_type == ISC_SOCKEVENT_SENDDONE);
+ isc_event_free(&event);
+
+ xfr->sends--;
+ if (xfr->shuttingdown) {
+ maybe_free(xfr);
+ return;
+ }
+
+ xfrin_log(xfr, ISC_LOG_DEBUG(3), "sent request length prefix");
+ CHECK(evresult);
+
+ isc_buffer_usedregion(&xfr->qbuffer, &region);
+ CHECK(isc_socket_send(xfr->socket, &region, xfr->task,
+ xfrin_send_done, xfr));
+ xfr->sends++;
+ failure:
+ if (result != ISC_R_SUCCESS)
+ xfrin_fail(xfr, result, "failed sending request length prefix");
+}
+
+
+static void
+xfrin_send_done(isc_task_t *task, isc_event_t *event) {
+ isc_socketevent_t *sev = (isc_socketevent_t *) event;
+ dns_xfrin_ctx_t *xfr = (dns_xfrin_ctx_t *) event->ev_arg;
+ isc_result_t result;
+
+ REQUIRE(VALID_XFRIN(xfr));
+
+ UNUSED(task);
+
+ INSIST(event->ev_type == ISC_SOCKEVENT_SENDDONE);
+
+ xfr->sends--;
+ xfrin_log(xfr, ISC_LOG_DEBUG(3), "sent request data");
+ CHECK(sev->result);
+
+ CHECK(dns_tcpmsg_readmessage(&xfr->tcpmsg, xfr->task,
+ xfrin_recv_done, xfr));
+ xfr->recvs++;
+ failure:
+ isc_event_free(&event);
+ if (result != ISC_R_SUCCESS)
+ xfrin_fail(xfr, result, "failed sending request data");
+}
+
+
+static void
+xfrin_recv_done(isc_task_t *task, isc_event_t *ev) {
+ dns_xfrin_ctx_t *xfr = (dns_xfrin_ctx_t *) ev->ev_arg;
+ isc_result_t result;
+ dns_message_t *msg = NULL;
+ dns_name_t *name;
+ dns_tcpmsg_t *tcpmsg;
+ dns_name_t *tsigowner = NULL;
+
+ REQUIRE(VALID_XFRIN(xfr));
+
+ UNUSED(task);
+
+ INSIST(ev->ev_type == DNS_EVENT_TCPMSG);
+ tcpmsg = ev->ev_sender;
+ isc_event_free(&ev);
+
+ xfr->recvs--;
+ if (xfr->shuttingdown) {
+ maybe_free(xfr);
+ return;
+ }
+
+ CHECK(tcpmsg->result);
+
+ xfrin_log(xfr, ISC_LOG_DEBUG(7), "received %u bytes",
+ tcpmsg->buffer.used);
+
+ CHECK(isc_timer_touch(xfr->timer));
+
+ CHECK(dns_message_create(xfr->mctx, DNS_MESSAGE_INTENTPARSE, &msg));
+
+ CHECK(dns_message_settsigkey(msg, xfr->tsigkey));
+ CHECK(dns_message_setquerytsig(msg, xfr->lasttsig));
+
+ msg->tsigctx = xfr->tsigctx;
+ xfr->tsigctx = NULL;
+
+ if (xfr->nmsg > 0)
+ msg->tcp_continuation = 1;
+
+ result = dns_message_parse(msg, &tcpmsg->buffer,
+ DNS_MESSAGEPARSE_PRESERVEORDER);
+
+ if (result != ISC_R_SUCCESS || msg->rcode != dns_rcode_noerror ||
+ (xfr->checkid && msg->id != xfr->id)) {
+ if (result == ISC_R_SUCCESS)
+ result = ISC_RESULTCLASS_DNSRCODE + msg->rcode; /*XXX*/
+ if (result == ISC_R_SUCCESS || result == DNS_R_NOERROR)
+ result = DNS_R_UNEXPECTEDID;
+ if (xfr->reqtype == dns_rdatatype_axfr ||
+ xfr->reqtype == dns_rdatatype_soa)
+ FAIL(result);
+ xfrin_log(xfr, ISC_LOG_DEBUG(3), "got %s, retrying with AXFR",
+ isc_result_totext(result));
+ try_axfr:
+ dns_message_destroy(&msg);
+ xfrin_reset(xfr);
+ xfr->reqtype = dns_rdatatype_soa;
+ xfr->state = XFRST_SOAQUERY;
+ (void)xfrin_start(xfr);
+ return;
+ }
+
+ /*
+ * Does the server know about IXFR? If it doesn't we will get
+ * a message with a empty answer section or a potentially a CNAME /
+ * DNAME, the later is handled by xfr_rr() which will return FORMERR
+ * if the first RR in the answer section is not a SOA record.
+ */
+ if (xfr->reqtype == dns_rdatatype_ixfr &&
+ xfr->state == XFRST_INITIALSOA &&
+ msg->counts[DNS_SECTION_ANSWER] == 0) {
+ xfrin_log(xfr, ISC_LOG_DEBUG(3),
+ "empty answer section, retrying with AXFR");
+ goto try_axfr;
+ }
+
+ if (xfr->reqtype == dns_rdatatype_soa &&
+ (msg->flags & DNS_MESSAGEFLAG_AA) == 0) {
+ FAIL(DNS_R_NOTAUTHORITATIVE);
+ }
+
+
+ result = dns_message_checksig(msg, dns_zone_getview(xfr->zone));
+ if (result != ISC_R_SUCCESS) {
+ xfrin_log(xfr, ISC_LOG_DEBUG(3), "TSIG check failed: %s",
+ isc_result_totext(result));
+ FAIL(result);
+ }
+
+ for (result = dns_message_firstname(msg, DNS_SECTION_ANSWER);
+ result == ISC_R_SUCCESS;
+ result = dns_message_nextname(msg, DNS_SECTION_ANSWER))
+ {
+ dns_rdataset_t *rds;
+
+ name = NULL;
+ dns_message_currentname(msg, DNS_SECTION_ANSWER, &name);
+ for (rds = ISC_LIST_HEAD(name->list);
+ rds != NULL;
+ rds = ISC_LIST_NEXT(rds, link))
+ {
+ for (result = dns_rdataset_first(rds);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rds))
+ {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_current(rds, &rdata);
+ CHECK(xfr_rr(xfr, name, rds->ttl, &rdata));
+ }
+ }
+ }
+ if (result != ISC_R_NOMORE)
+ goto failure;
+
+ if (dns_message_gettsig(msg, &tsigowner) != NULL) {
+ /*
+ * Reset the counter.
+ */
+ xfr->sincetsig = 0;
+
+ /*
+ * Free the last tsig, if there is one.
+ */
+ if (xfr->lasttsig != NULL)
+ isc_buffer_free(&xfr->lasttsig);
+
+ /*
+ * Update the last tsig pointer.
+ */
+ CHECK(dns_message_getquerytsig(msg, xfr->mctx,
+ &xfr->lasttsig));
+
+ } else if (dns_message_gettsigkey(msg) != NULL) {
+ xfr->sincetsig++;
+ if (xfr->sincetsig > 100 ||
+ xfr->nmsg == 0 || xfr->state == XFRST_END)
+ {
+ result = DNS_R_EXPECTEDTSIG;
+ goto failure;
+ }
+ }
+
+ /*
+ * Update the number of messages received.
+ */
+ xfr->nmsg++;
+
+ /*
+ * Update the number of bytes received.
+ */
+ xfr->nbytes += tcpmsg->buffer.used;
+
+ /*
+ * Take the context back.
+ */
+ INSIST(xfr->tsigctx == NULL);
+ xfr->tsigctx = msg->tsigctx;
+ msg->tsigctx = NULL;
+
+ dns_message_destroy(&msg);
+
+ if (xfr->state == XFRST_GOTSOA) {
+ xfr->reqtype = dns_rdatatype_axfr;
+ xfr->state = XFRST_INITIALSOA;
+ CHECK(xfrin_send_request(xfr));
+ } else if (xfr->state == XFRST_END) {
+ /*
+ * Close the journal.
+ */
+ if (xfr->ixfr.journal != NULL)
+ dns_journal_destroy(&xfr->ixfr.journal);
+ /*
+ * Inform the caller we succeeded.
+ */
+ if (xfr->done != NULL) {
+ (xfr->done)(xfr->zone, ISC_R_SUCCESS);
+ xfr->done = NULL;
+ }
+ /*
+ * We should have no outstanding events at this
+ * point, thus maybe_free() should succeed.
+ */
+ xfr->shuttingdown = ISC_TRUE;
+ maybe_free(xfr);
+ } else {
+ /*
+ * Read the next message.
+ */
+ CHECK(dns_tcpmsg_readmessage(&xfr->tcpmsg, xfr->task,
+ xfrin_recv_done, xfr));
+ xfr->recvs++;
+ }
+ return;
+
+ failure:
+ if (msg != NULL)
+ dns_message_destroy(&msg);
+ if (result != ISC_R_SUCCESS)
+ xfrin_fail(xfr, result, "failed while receiving responses");
+}
+
+static void
+xfrin_timeout(isc_task_t *task, isc_event_t *event) {
+ dns_xfrin_ctx_t *xfr = (dns_xfrin_ctx_t *) event->ev_arg;
+
+ REQUIRE(VALID_XFRIN(xfr));
+
+ UNUSED(task);
+
+ isc_event_free(&event);
+ /*
+ * This will log "giving up: timeout".
+ */
+ xfrin_fail(xfr, ISC_R_TIMEDOUT, "giving up");
+}
+
+static void
+maybe_free(dns_xfrin_ctx_t *xfr) {
+ isc_uint64_t msecs;
+ isc_uint64_t persec;
+
+ REQUIRE(VALID_XFRIN(xfr));
+
+ if (! xfr->shuttingdown || xfr->refcount != 0 ||
+ xfr->connects != 0 || xfr->sends != 0 ||
+ xfr->recvs != 0)
+ return;
+
+ /*
+ * Calculate the length of time the transfer took,
+ * and print a log message with the bytes and rate.
+ */
+ isc_time_now(&xfr->end);
+ msecs = isc_time_microdiff(&xfr->end, &xfr->start) / 1000;
+ if (msecs == 0)
+ msecs = 1;
+ persec = (xfr->nbytes * 1000) / msecs;
+ xfrin_log(xfr, ISC_LOG_INFO,
+ "Transfer completed: %d messages, %d records, "
+ "%" ISC_PRINT_QUADFORMAT "u bytes, "
+ "%u.%03u secs (%u bytes/sec)",
+ xfr->nmsg, xfr->nrecs, xfr->nbytes,
+ (unsigned int) (msecs / 1000), (unsigned int) (msecs % 1000),
+ (unsigned int) persec);
+
+ if (xfr->socket != NULL)
+ isc_socket_detach(&xfr->socket);
+
+ if (xfr->timer != NULL)
+ isc_timer_detach(&xfr->timer);
+
+ if (xfr->task != NULL)
+ isc_task_detach(&xfr->task);
+
+ if (xfr->tsigkey != NULL)
+ dns_tsigkey_detach(&xfr->tsigkey);
+
+ if (xfr->lasttsig != NULL)
+ isc_buffer_free(&xfr->lasttsig);
+
+ dns_diff_clear(&xfr->diff);
+
+ if (xfr->ixfr.journal != NULL)
+ dns_journal_destroy(&xfr->ixfr.journal);
+
+ if (xfr->axfr.add_private != NULL)
+ (void)dns_db_endload(xfr->db, &xfr->axfr.add_private);
+
+ if (xfr->tcpmsg_valid)
+ dns_tcpmsg_invalidate(&xfr->tcpmsg);
+
+ if (xfr->tsigctx != NULL)
+ dst_context_destroy(&xfr->tsigctx);
+
+ if ((xfr->name.attributes & DNS_NAMEATTR_DYNAMIC) != 0)
+ dns_name_free(&xfr->name, xfr->mctx);
+
+ if (xfr->ver != NULL)
+ dns_db_closeversion(xfr->db, &xfr->ver, ISC_FALSE);
+
+ if (xfr->db != NULL)
+ dns_db_detach(&xfr->db);
+
+ if (xfr->zone != NULL)
+ dns_zone_idetach(&xfr->zone);
+
+ isc_mem_put(xfr->mctx, xfr, sizeof(*xfr));
+}
+
+/*
+ * Log incoming zone transfer messages in a format like
+ * transfer of <zone> from <address>: <message>
+ */
+static void
+xfrin_logv(int level, const char *zonetext, isc_sockaddr_t *masteraddr,
+ const char *fmt, va_list ap)
+{
+ char mastertext[ISC_SOCKADDR_FORMATSIZE];
+ char msgtext[2048];
+
+ isc_sockaddr_format(masteraddr, mastertext, sizeof(mastertext));
+ vsnprintf(msgtext, sizeof(msgtext), fmt, ap);
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_XFER_IN,
+ DNS_LOGMODULE_XFER_IN, level,
+ "transfer of '%s' from %s: %s",
+ zonetext, mastertext, msgtext);
+}
+
+/*
+ * Logging function for use when a xfrin_ctx_t has not yet been created.
+ */
+
+static void
+xfrin_log1(int level, const char *zonetext, isc_sockaddr_t *masteraddr,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE)
+ return;
+
+ va_start(ap, fmt);
+ xfrin_logv(level, zonetext, masteraddr, fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Logging function for use when there is a xfrin_ctx_t.
+ */
+
+static void
+xfrin_log(dns_xfrin_ctx_t *xfr, int level, const char *fmt, ...)
+{
+ va_list ap;
+ char zonetext[DNS_NAME_MAXTEXT+32];
+
+ if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE)
+ return;
+
+ dns_zone_name(xfr->zone, zonetext, sizeof(zonetext));
+
+ va_start(ap, fmt);
+ xfrin_logv(level, zonetext, &xfr->masteraddr, fmt, ap);
+ va_end(ap);
+}
diff --git a/lib/dns/zone.c b/lib/dns/zone.c
new file mode 100644
index 0000000..9c53ab4
--- /dev/null
+++ b/lib/dns/zone.c
@@ -0,0 +1,11502 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: zone.c,v 1.483 2008/10/24 00:28:00 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/file.h>
+#include <isc/mutex.h>
+#include <isc/print.h>
+#include <isc/random.h>
+#include <isc/ratelimiter.h>
+#include <isc/refcount.h>
+#include <isc/rwlock.h>
+#include <isc/serial.h>
+#include <isc/stdtime.h>
+#include <isc/string.h>
+#include <isc/taskpool.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+#include <dns/acache.h>
+#include <dns/acl.h>
+#include <dns/adb.h>
+#include <dns/callbacks.h>
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/dnssec.h>
+#include <dns/events.h>
+#include <dns/journal.h>
+#include <dns/keyvalues.h>
+#include <dns/log.h>
+#include <dns/master.h>
+#include <dns/masterdump.h>
+#include <dns/message.h>
+#include <dns/name.h>
+#include <dns/nsec.h>
+#include <dns/nsec3.h>
+#include <dns/peer.h>
+#include <dns/rcode.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/rdatastruct.h>
+#include <dns/rdatatype.h>
+#include <dns/request.h>
+#include <dns/resolver.h>
+#include <dns/result.h>
+#include <dns/soa.h>
+#include <dns/ssu.h>
+#include <dns/stats.h>
+#include <dns/tsig.h>
+#include <dns/xfrin.h>
+#include <dns/zone.h>
+
+#include <dst/dst.h>
+
+#define ZONE_MAGIC ISC_MAGIC('Z', 'O', 'N', 'E')
+#define DNS_ZONE_VALID(zone) ISC_MAGIC_VALID(zone, ZONE_MAGIC)
+
+#define NOTIFY_MAGIC ISC_MAGIC('N', 't', 'f', 'y')
+#define DNS_NOTIFY_VALID(notify) ISC_MAGIC_VALID(notify, NOTIFY_MAGIC)
+
+#define STUB_MAGIC ISC_MAGIC('S', 't', 'u', 'b')
+#define DNS_STUB_VALID(stub) ISC_MAGIC_VALID(stub, STUB_MAGIC)
+
+#define ZONEMGR_MAGIC ISC_MAGIC('Z', 'm', 'g', 'r')
+#define DNS_ZONEMGR_VALID(stub) ISC_MAGIC_VALID(stub, ZONEMGR_MAGIC)
+
+#define LOAD_MAGIC ISC_MAGIC('L', 'o', 'a', 'd')
+#define DNS_LOAD_VALID(load) ISC_MAGIC_VALID(load, LOAD_MAGIC)
+
+#define FORWARD_MAGIC ISC_MAGIC('F', 'o', 'r', 'w')
+#define DNS_FORWARD_VALID(load) ISC_MAGIC_VALID(load, FORWARD_MAGIC)
+
+#define IO_MAGIC ISC_MAGIC('Z', 'm', 'I', 'O')
+#define DNS_IO_VALID(load) ISC_MAGIC_VALID(load, IO_MAGIC)
+
+/*%
+ * Ensure 'a' is at least 'min' but not more than 'max'.
+ */
+#define RANGE(a, min, max) \
+ (((a) < (min)) ? (min) : ((a) < (max) ? (a) : (max)))
+
+#define NSEC3REMOVE(x) (((x) & DNS_NSEC3FLAG_REMOVE) != 0)
+
+/*
+ * Default values.
+ */
+#define DNS_DEFAULT_IDLEIN 3600 /*%< 1 hour */
+#define DNS_DEFAULT_IDLEOUT 3600 /*%< 1 hour */
+#define MAX_XFER_TIME (2*3600) /*%< Documented default is 2 hours */
+
+#ifndef DNS_MAX_EXPIRE
+#define DNS_MAX_EXPIRE 14515200 /*%< 24 weeks */
+#endif
+
+#ifndef DNS_DUMP_DELAY
+#define DNS_DUMP_DELAY 900 /*%< 15 minutes */
+#endif
+
+typedef struct dns_notify dns_notify_t;
+typedef struct dns_stub dns_stub_t;
+typedef struct dns_load dns_load_t;
+typedef struct dns_forward dns_forward_t;
+typedef struct dns_io dns_io_t;
+typedef ISC_LIST(dns_io_t) dns_iolist_t;
+typedef struct dns_signing dns_signing_t;
+typedef ISC_LIST(dns_signing_t) dns_signinglist_t;
+typedef struct dns_nsec3chain dns_nsec3chain_t;
+typedef ISC_LIST(dns_nsec3chain_t) dns_nsec3chainlist_t;
+
+#define DNS_ZONE_CHECKLOCK
+#ifdef DNS_ZONE_CHECKLOCK
+#define LOCK_ZONE(z) \
+ do { LOCK(&(z)->lock); \
+ INSIST((z)->locked == ISC_FALSE); \
+ (z)->locked = ISC_TRUE; \
+ } while (0)
+#define UNLOCK_ZONE(z) \
+ do { (z)->locked = ISC_FALSE; UNLOCK(&(z)->lock); } while (0)
+#define LOCKED_ZONE(z) ((z)->locked)
+#else
+#define LOCK_ZONE(z) LOCK(&(z)->lock)
+#define UNLOCK_ZONE(z) UNLOCK(&(z)->lock)
+#define LOCKED_ZONE(z) ISC_TRUE
+#endif
+
+#ifdef ISC_RWLOCK_USEATOMIC
+#define ZONEDB_INITLOCK(l) isc_rwlock_init((l), 0, 0)
+#define ZONEDB_DESTROYLOCK(l) isc_rwlock_destroy(l)
+#define ZONEDB_LOCK(l, t) RWLOCK((l), (t))
+#define ZONEDB_UNLOCK(l, t) RWUNLOCK((l), (t))
+#else
+#define ZONEDB_INITLOCK(l) isc_mutex_init(l)
+#define ZONEDB_DESTROYLOCK(l) DESTROYLOCK(l)
+#define ZONEDB_LOCK(l, t) LOCK(l)
+#define ZONEDB_UNLOCK(l, t) UNLOCK(l)
+#endif
+
+struct dns_zone {
+ /* Unlocked */
+ unsigned int magic;
+ isc_mutex_t lock;
+#ifdef DNS_ZONE_CHECKLOCK
+ isc_boolean_t locked;
+#endif
+ isc_mem_t *mctx;
+ isc_refcount_t erefs;
+
+#ifdef ISC_RWLOCK_USEATOMIC
+ isc_rwlock_t dblock;
+#else
+ isc_mutex_t dblock;
+#endif
+ dns_db_t *db; /* Locked by dblock */
+
+ /* Locked */
+ dns_zonemgr_t *zmgr;
+ ISC_LINK(dns_zone_t) link; /* Used by zmgr. */
+ isc_timer_t *timer;
+ unsigned int irefs;
+ dns_name_t origin;
+ char *masterfile;
+ dns_masterformat_t masterformat;
+ char *journal;
+ isc_int32_t journalsize;
+ dns_rdataclass_t rdclass;
+ dns_zonetype_t type;
+ unsigned int flags;
+ unsigned int options;
+ unsigned int db_argc;
+ char **db_argv;
+ isc_time_t expiretime;
+ isc_time_t refreshtime;
+ isc_time_t dumptime;
+ isc_time_t loadtime;
+ isc_time_t notifytime;
+ isc_time_t resigntime;
+ isc_time_t keywarntime;
+ isc_time_t signingtime;
+ isc_time_t nsec3chaintime;
+ isc_uint32_t serial;
+ isc_uint32_t refresh;
+ isc_uint32_t retry;
+ isc_uint32_t expire;
+ isc_uint32_t minimum;
+ isc_stdtime_t key_expiry;
+ char *keydirectory;
+
+ isc_uint32_t maxrefresh;
+ isc_uint32_t minrefresh;
+ isc_uint32_t maxretry;
+ isc_uint32_t minretry;
+
+ isc_sockaddr_t *masters;
+ dns_name_t **masterkeynames;
+ isc_boolean_t *mastersok;
+ unsigned int masterscnt;
+ unsigned int curmaster;
+ isc_sockaddr_t masteraddr;
+ dns_notifytype_t notifytype;
+ isc_sockaddr_t *notify;
+ unsigned int notifycnt;
+ isc_sockaddr_t notifyfrom;
+ isc_task_t *task;
+ isc_sockaddr_t notifysrc4;
+ isc_sockaddr_t notifysrc6;
+ isc_sockaddr_t xfrsource4;
+ isc_sockaddr_t xfrsource6;
+ isc_sockaddr_t altxfrsource4;
+ isc_sockaddr_t altxfrsource6;
+ isc_sockaddr_t sourceaddr;
+ dns_xfrin_ctx_t *xfr; /* task locked */
+ dns_tsigkey_t *tsigkey; /* key used for xfr */
+ /* Access Control Lists */
+ dns_acl_t *update_acl;
+ dns_acl_t *forward_acl;
+ dns_acl_t *notify_acl;
+ dns_acl_t *query_acl;
+ dns_acl_t *queryon_acl;
+ dns_acl_t *xfr_acl;
+ isc_boolean_t update_disabled;
+ isc_boolean_t zero_no_soa_ttl;
+ dns_severity_t check_names;
+ ISC_LIST(dns_notify_t) notifies;
+ dns_request_t *request;
+ dns_loadctx_t *lctx;
+ dns_io_t *readio;
+ dns_dumpctx_t *dctx;
+ dns_io_t *writeio;
+ isc_uint32_t maxxfrin;
+ isc_uint32_t maxxfrout;
+ isc_uint32_t idlein;
+ isc_uint32_t idleout;
+ isc_event_t ctlevent;
+ dns_ssutable_t *ssutable;
+ isc_uint32_t sigvalidityinterval;
+ isc_uint32_t sigresigninginterval;
+ dns_view_t *view;
+ dns_acache_t *acache;
+ dns_checkmxfunc_t checkmx;
+ dns_checksrvfunc_t checksrv;
+ dns_checknsfunc_t checkns;
+ /*%
+ * Zones in certain states such as "waiting for zone transfer"
+ * or "zone transfer in progress" are kept on per-state linked lists
+ * in the zone manager using the 'statelink' field. The 'statelist'
+ * field points at the list the zone is currently on. It the zone
+ * is not on any such list, statelist is NULL.
+ */
+ ISC_LINK(dns_zone_t) statelink;
+ dns_zonelist_t *statelist;
+ /*%
+ * Statistics counters about zone management.
+ */
+ dns_stats_t *stats;
+ /*%
+ * Optional per-zone statistics counters. Counted outside of this
+ * module.
+ */
+ isc_boolean_t requeststats_on;
+ dns_stats_t *requeststats;
+ isc_uint32_t notifydelay;
+ dns_isselffunc_t isself;
+ void *isselfarg;
+
+ char * strnamerd;
+ char * strname;
+ char * strrdclass;
+ char * strviewname;
+
+ /*%
+ * Serial number for deferred journal compaction.
+ */
+ isc_uint32_t compact_serial;
+ /*%
+ * Keys that are signing the zone for the first time.
+ */
+ dns_signinglist_t signing;
+ dns_nsec3chainlist_t nsec3chain;
+ /*%
+ * Signing / re-signing quantum stopping parameters.
+ */
+ isc_uint32_t signatures;
+ isc_uint32_t nodes;
+ dns_rdatatype_t privatetype;
+};
+
+#define DNS_ZONE_FLAG(z,f) (ISC_TF(((z)->flags & (f)) != 0))
+#define DNS_ZONE_SETFLAG(z,f) do { \
+ INSIST(LOCKED_ZONE(z)); \
+ (z)->flags |= (f); \
+ } while (0)
+#define DNS_ZONE_CLRFLAG(z,f) do { \
+ INSIST(LOCKED_ZONE(z)); \
+ (z)->flags &= ~(f); \
+ } while (0)
+ /* XXX MPA these may need to go back into zone.h */
+#define DNS_ZONEFLG_REFRESH 0x00000001U /*%< refresh check in progress */
+#define DNS_ZONEFLG_NEEDDUMP 0x00000002U /*%< zone need consolidation */
+#define DNS_ZONEFLG_USEVC 0x00000004U /*%< use tcp for refresh query */
+#define DNS_ZONEFLG_DUMPING 0x00000008U /*%< a dump is in progress */
+#define DNS_ZONEFLG_HASINCLUDE 0x00000010U /*%< $INCLUDE in zone file */
+#define DNS_ZONEFLG_LOADED 0x00000020U /*%< database has loaded */
+#define DNS_ZONEFLG_EXITING 0x00000040U /*%< zone is being destroyed */
+#define DNS_ZONEFLG_EXPIRED 0x00000080U /*%< zone has expired */
+#define DNS_ZONEFLG_NEEDREFRESH 0x00000100U /*%< refresh check needed */
+#define DNS_ZONEFLG_UPTODATE 0x00000200U /*%< zone contents are
+ * uptodate */
+#define DNS_ZONEFLG_NEEDNOTIFY 0x00000400U /*%< need to send out notify
+ * messages */
+#define DNS_ZONEFLG_DIFFONRELOAD 0x00000800U /*%< generate a journal diff on
+ * reload */
+#define DNS_ZONEFLG_NOMASTERS 0x00001000U /*%< an attempt to refresh a
+ * zone with no masters
+ * occured */
+#define DNS_ZONEFLG_LOADING 0x00002000U /*%< load from disk in progress*/
+#define DNS_ZONEFLG_HAVETIMERS 0x00004000U /*%< timer values have been set
+ * from SOA (if not set, we
+ * are still using
+ * default timer values) */
+#define DNS_ZONEFLG_FORCEXFER 0x00008000U /*%< Force a zone xfer */
+#define DNS_ZONEFLG_NOREFRESH 0x00010000U
+#define DNS_ZONEFLG_DIALNOTIFY 0x00020000U
+#define DNS_ZONEFLG_DIALREFRESH 0x00040000U
+#define DNS_ZONEFLG_SHUTDOWN 0x00080000U
+#define DNS_ZONEFLAG_NOIXFR 0x00100000U /*%< IXFR failed, force AXFR */
+#define DNS_ZONEFLG_FLUSH 0x00200000U
+#define DNS_ZONEFLG_NOEDNS 0x00400000U
+#define DNS_ZONEFLG_USEALTXFRSRC 0x00800000U
+#define DNS_ZONEFLG_SOABEFOREAXFR 0x01000000U
+#define DNS_ZONEFLG_NEEDCOMPACT 0x02000000U
+
+#define DNS_ZONE_OPTION(z,o) (((z)->options & (o)) != 0)
+
+/* Flags for zone_load() */
+#define DNS_ZONELOADFLAG_NOSTAT 0x00000001U /* Do not stat() master files */
+
+#define UNREACH_CHACHE_SIZE 10U
+#define UNREACH_HOLD_TIME 600 /* 10 minutes */
+
+#define CHECK(op) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+
+struct dns_unreachable {
+ isc_sockaddr_t remote;
+ isc_sockaddr_t local;
+ isc_uint32_t expire;
+ isc_uint32_t last;
+};
+
+struct dns_zonemgr {
+ unsigned int magic;
+ isc_mem_t * mctx;
+ int refs; /* Locked by rwlock */
+ isc_taskmgr_t * taskmgr;
+ isc_timermgr_t * timermgr;
+ isc_socketmgr_t * socketmgr;
+ isc_taskpool_t * zonetasks;
+ isc_task_t * task;
+ isc_ratelimiter_t * rl;
+ isc_rwlock_t rwlock;
+ isc_mutex_t iolock;
+
+ /* Locked by rwlock. */
+ dns_zonelist_t zones;
+ dns_zonelist_t waiting_for_xfrin;
+ dns_zonelist_t xfrin_in_progress;
+
+ /* Configuration data. */
+ isc_uint32_t transfersin;
+ isc_uint32_t transfersperns;
+ unsigned int serialqueryrate;
+
+ /* Locked by iolock */
+ isc_uint32_t iolimit;
+ isc_uint32_t ioactive;
+ dns_iolist_t high;
+ dns_iolist_t low;
+
+ /* Locked by rwlock. */
+ /* LRU cache */
+ struct dns_unreachable unreachable[UNREACH_CHACHE_SIZE];
+};
+
+/*%
+ * Hold notify state.
+ */
+struct dns_notify {
+ unsigned int magic;
+ unsigned int flags;
+ isc_mem_t *mctx;
+ dns_zone_t *zone;
+ dns_adbfind_t *find;
+ dns_request_t *request;
+ dns_name_t ns;
+ isc_sockaddr_t dst;
+ ISC_LINK(dns_notify_t) link;
+};
+
+#define DNS_NOTIFY_NOSOA 0x0001U
+
+/*%
+ * dns_stub holds state while performing a 'stub' transfer.
+ * 'db' is the zone's 'db' or a new one if this is the initial
+ * transfer.
+ */
+
+struct dns_stub {
+ unsigned int magic;
+ isc_mem_t *mctx;
+ dns_zone_t *zone;
+ dns_db_t *db;
+ dns_dbversion_t *version;
+};
+
+/*%
+ * Hold load state.
+ */
+struct dns_load {
+ unsigned int magic;
+ isc_mem_t *mctx;
+ dns_zone_t *zone;
+ dns_db_t *db;
+ isc_time_t loadtime;
+ dns_rdatacallbacks_t callbacks;
+};
+
+/*%
+ * Hold forward state.
+ */
+struct dns_forward {
+ unsigned int magic;
+ isc_mem_t *mctx;
+ dns_zone_t *zone;
+ isc_buffer_t *msgbuf;
+ dns_request_t *request;
+ isc_uint32_t which;
+ isc_sockaddr_t addr;
+ dns_updatecallback_t callback;
+ void *callback_arg;
+};
+
+/*%
+ * Hold IO request state.
+ */
+struct dns_io {
+ unsigned int magic;
+ dns_zonemgr_t *zmgr;
+ isc_boolean_t high;
+ isc_task_t *task;
+ ISC_LINK(dns_io_t) link;
+ isc_event_t *event;
+};
+
+/*%
+ * Hold state for when we are signing a zone with a new
+ * DNSKEY as result of an update.
+ */
+struct dns_signing {
+ unsigned int magic;
+ dns_db_t *db;
+ dns_dbiterator_t *dbiterator;
+ dns_secalg_t algorithm;
+ isc_uint16_t keyid;
+ isc_boolean_t delete;
+ isc_boolean_t done;
+ ISC_LINK(dns_signing_t) link;
+};
+
+struct dns_nsec3chain {
+ unsigned int magic;
+ dns_db_t *db;
+ dns_dbiterator_t *dbiterator;
+ dns_rdata_nsec3param_t nsec3param;
+ unsigned char salt[255];
+ isc_boolean_t done;
+ isc_boolean_t seen_nsec;
+ isc_boolean_t delete_nsec;
+ isc_boolean_t save_delete_nsec;
+ ISC_LINK(dns_nsec3chain_t) link;
+};
+/*%<
+ * 'dbiterator' contains a iterator for the database. If we are creating
+ * a NSEC3 chain only the non-NSEC3 nodes will be iterated. If we are
+ * removing a NSEC3 chain then both NSEC3 and non-NSEC3 nodes will be
+ * iterated.
+ *
+ * 'nsec3param' contains the parameters of the NSEC3 chain being created
+ * or removed.
+ *
+ * 'salt' is buffer space and is referenced via 'nsec3param.salt'.
+ *
+ * 'seen_nsec' will be set to true if, while iterating the zone to create a
+ * NSEC3 chain, a NSEC record is seen.
+ *
+ * 'delete_nsec' will be set to true if, at the completion of the creation
+ * of a NSEC3 chain, 'seen_nsec' is true. If 'delete_nsec' is true then we
+ * are in the process of deleting the NSEC chain.
+ *
+ * 'save_delete_nsec' is used to store the initial state of 'delete_nsec'
+ * so it can be recovered in the event of a error.
+ */
+
+
+#define SEND_BUFFER_SIZE 2048
+
+static void zone_settimer(dns_zone_t *, isc_time_t *);
+static void cancel_refresh(dns_zone_t *);
+static void zone_debuglog(dns_zone_t *zone, const char *, int debuglevel,
+ const char *msg, ...) ISC_FORMAT_PRINTF(4, 5);
+static void notify_log(dns_zone_t *zone, int level, const char *fmt, ...)
+ ISC_FORMAT_PRINTF(3, 4);
+static void queue_xfrin(dns_zone_t *zone);
+static void zone_unload(dns_zone_t *zone);
+static void zone_expire(dns_zone_t *zone);
+static void zone_iattach(dns_zone_t *source, dns_zone_t **target);
+static void zone_idetach(dns_zone_t **zonep);
+static isc_result_t zone_replacedb(dns_zone_t *zone, dns_db_t *db,
+ isc_boolean_t dump);
+static inline void zone_attachdb(dns_zone_t *zone, dns_db_t *db);
+static inline void zone_detachdb(dns_zone_t *zone);
+static isc_result_t default_journal(dns_zone_t *zone);
+static void zone_xfrdone(dns_zone_t *zone, isc_result_t result);
+static isc_result_t zone_postload(dns_zone_t *zone, dns_db_t *db,
+ isc_time_t loadtime, isc_result_t result);
+static void zone_needdump(dns_zone_t *zone, unsigned int delay);
+static void zone_shutdown(isc_task_t *, isc_event_t *);
+static void zone_loaddone(void *arg, isc_result_t result);
+static isc_result_t zone_startload(dns_db_t *db, dns_zone_t *zone,
+ isc_time_t loadtime);
+static void zone_namerd_tostr(dns_zone_t *zone, char *buf, size_t length);
+static void zone_name_tostr(dns_zone_t *zone, char *buf, size_t length);
+static void zone_rdclass_tostr(dns_zone_t *zone, char *buf, size_t length);
+static void zone_viewname_tostr(dns_zone_t *zone, char *buf, size_t length);
+
+#if 0
+/* ondestroy example */
+static void dns_zonemgr_dbdestroyed(isc_task_t *task, isc_event_t *event);
+#endif
+
+static void refresh_callback(isc_task_t *, isc_event_t *);
+static void stub_callback(isc_task_t *, isc_event_t *);
+static void queue_soa_query(dns_zone_t *zone);
+static void soa_query(isc_task_t *, isc_event_t *);
+static void ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset,
+ dns_stub_t *stub);
+static int message_count(dns_message_t *msg, dns_section_t section,
+ dns_rdatatype_t type);
+static void notify_cancel(dns_zone_t *zone);
+static void notify_find_address(dns_notify_t *notify);
+static void notify_send(dns_notify_t *notify);
+static isc_result_t notify_createmessage(dns_zone_t *zone,
+ unsigned int flags,
+ dns_message_t **messagep);
+static void notify_done(isc_task_t *task, isc_event_t *event);
+static void notify_send_toaddr(isc_task_t *task, isc_event_t *event);
+static isc_result_t zone_dump(dns_zone_t *, isc_boolean_t);
+static void got_transfer_quota(isc_task_t *task, isc_event_t *event);
+static isc_result_t zmgr_start_xfrin_ifquota(dns_zonemgr_t *zmgr,
+ dns_zone_t *zone);
+static void zmgr_resume_xfrs(dns_zonemgr_t *zmgr, isc_boolean_t multi);
+static void zonemgr_free(dns_zonemgr_t *zmgr);
+static isc_result_t zonemgr_getio(dns_zonemgr_t *zmgr, isc_boolean_t high,
+ isc_task_t *task, isc_taskaction_t action,
+ void *arg, dns_io_t **iop);
+static void zonemgr_putio(dns_io_t **iop);
+static void zonemgr_cancelio(dns_io_t *io);
+
+static isc_result_t
+zone_get_from_db(dns_zone_t *zone, dns_db_t *db, unsigned int *nscount,
+ unsigned int *soacount, isc_uint32_t *serial,
+ isc_uint32_t *refresh, isc_uint32_t *retry,
+ isc_uint32_t *expire, isc_uint32_t *minimum,
+ unsigned int *errors);
+
+static void zone_freedbargs(dns_zone_t *zone);
+static void forward_callback(isc_task_t *task, isc_event_t *event);
+static void zone_saveunique(dns_zone_t *zone, const char *path,
+ const char *templat);
+static void zone_maintenance(dns_zone_t *zone);
+static void zone_notify(dns_zone_t *zone, isc_time_t *now);
+static void dump_done(void *arg, isc_result_t result);
+static isc_boolean_t dns_zonemgr_unreachable(dns_zonemgr_t *zmgr,
+ isc_sockaddr_t *remote,
+ isc_sockaddr_t *local,
+ isc_time_t *now);
+static isc_result_t zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm,
+ isc_uint16_t keyid, isc_boolean_t delete);
+
+#define ENTER zone_debuglog(zone, me, 1, "enter")
+
+static const unsigned int dbargc_default = 1;
+static const char *dbargv_default[] = { "rbt" };
+
+#define DNS_ZONE_JITTER_ADD(a, b, c) \
+ do { \
+ isc_interval_t _i; \
+ isc_uint32_t _j; \
+ _j = isc_random_jitter((b), (b)/4); \
+ isc_interval_set(&_i, _j, 0); \
+ if (isc_time_add((a), &_i, (c)) != ISC_R_SUCCESS) { \
+ dns_zone_log(zone, ISC_LOG_WARNING, \
+ "epoch approaching: upgrade required: " \
+ "now + %s failed", #b); \
+ isc_interval_set(&_i, _j/2, 0); \
+ (void)isc_time_add((a), &_i, (c)); \
+ } \
+ } while (0)
+
+#define DNS_ZONE_TIME_ADD(a, b, c) \
+ do { \
+ isc_interval_t _i; \
+ isc_interval_set(&_i, (b), 0); \
+ if (isc_time_add((a), &_i, (c)) != ISC_R_SUCCESS) { \
+ dns_zone_log(zone, ISC_LOG_WARNING, \
+ "epoch approaching: upgrade required: " \
+ "now + %s failed", #b); \
+ isc_interval_set(&_i, (b)/2, 0); \
+ (void)isc_time_add((a), &_i, (c)); \
+ } \
+ } while (0)
+
+/*%
+ * Increment resolver-related statistics counters. Zone must be locked.
+ */
+static inline void
+inc_stats(dns_zone_t *zone, dns_statscounter_t counter) {
+ if (zone->stats != NULL)
+ dns_generalstats_increment(zone->stats, counter);
+}
+
+/***
+ *** Public functions.
+ ***/
+
+isc_result_t
+dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) {
+ isc_result_t result;
+ dns_zone_t *zone;
+ isc_time_t now;
+
+ REQUIRE(zonep != NULL && *zonep == NULL);
+ REQUIRE(mctx != NULL);
+
+ TIME_NOW(&now);
+ zone = isc_mem_get(mctx, sizeof(*zone));
+ if (zone == NULL)
+ return (ISC_R_NOMEMORY);
+
+ zone->mctx = NULL;
+ isc_mem_attach(mctx, &zone->mctx);
+
+ result = isc_mutex_init(&zone->lock);
+ if (result != ISC_R_SUCCESS)
+ goto free_zone;
+
+ result = ZONEDB_INITLOCK(&zone->dblock);
+ if (result != ISC_R_SUCCESS)
+ goto free_mutex;
+
+ /* XXX MPA check that all elements are initialised */
+#ifdef DNS_ZONE_CHECKLOCK
+ zone->locked = ISC_FALSE;
+#endif
+ zone->db = NULL;
+ zone->zmgr = NULL;
+ ISC_LINK_INIT(zone, link);
+ result = isc_refcount_init(&zone->erefs, 1); /* Implicit attach. */
+ if (result != ISC_R_SUCCESS)
+ goto free_dblock;
+ zone->irefs = 0;
+ dns_name_init(&zone->origin, NULL);
+ zone->strnamerd = NULL;
+ zone->strname = NULL;
+ zone->strrdclass = NULL;
+ zone->strviewname = NULL;
+ zone->masterfile = NULL;
+ zone->masterformat = dns_masterformat_none;
+ zone->keydirectory = NULL;
+ zone->journalsize = -1;
+ zone->journal = NULL;
+ zone->rdclass = dns_rdataclass_none;
+ zone->type = dns_zone_none;
+ zone->flags = 0;
+ zone->options = 0;
+ zone->db_argc = 0;
+ zone->db_argv = NULL;
+ isc_time_settoepoch(&zone->expiretime);
+ isc_time_settoepoch(&zone->refreshtime);
+ isc_time_settoepoch(&zone->dumptime);
+ isc_time_settoepoch(&zone->loadtime);
+ zone->notifytime = now;
+ isc_time_settoepoch(&zone->resigntime);
+ isc_time_settoepoch(&zone->keywarntime);
+ isc_time_settoepoch(&zone->signingtime);
+ isc_time_settoepoch(&zone->nsec3chaintime);
+ zone->serial = 0;
+ zone->refresh = DNS_ZONE_DEFAULTREFRESH;
+ zone->retry = DNS_ZONE_DEFAULTRETRY;
+ zone->expire = 0;
+ zone->minimum = 0;
+ zone->maxrefresh = DNS_ZONE_MAXREFRESH;
+ zone->minrefresh = DNS_ZONE_MINREFRESH;
+ zone->maxretry = DNS_ZONE_MAXRETRY;
+ zone->minretry = DNS_ZONE_MINRETRY;
+ zone->masters = NULL;
+ zone->masterkeynames = NULL;
+ zone->mastersok = NULL;
+ zone->masterscnt = 0;
+ zone->curmaster = 0;
+ zone->notify = NULL;
+ zone->notifytype = dns_notifytype_yes;
+ zone->notifycnt = 0;
+ zone->task = NULL;
+ zone->update_acl = NULL;
+ zone->forward_acl = NULL;
+ zone->notify_acl = NULL;
+ zone->query_acl = NULL;
+ zone->queryon_acl = NULL;
+ zone->xfr_acl = NULL;
+ zone->update_disabled = ISC_FALSE;
+ zone->zero_no_soa_ttl = ISC_TRUE;
+ zone->check_names = dns_severity_ignore;
+ zone->request = NULL;
+ zone->lctx = NULL;
+ zone->readio = NULL;
+ zone->dctx = NULL;
+ zone->writeio = NULL;
+ zone->timer = NULL;
+ zone->idlein = DNS_DEFAULT_IDLEIN;
+ zone->idleout = DNS_DEFAULT_IDLEOUT;
+ ISC_LIST_INIT(zone->notifies);
+ isc_sockaddr_any(&zone->notifysrc4);
+ isc_sockaddr_any6(&zone->notifysrc6);
+ isc_sockaddr_any(&zone->xfrsource4);
+ isc_sockaddr_any6(&zone->xfrsource6);
+ isc_sockaddr_any(&zone->altxfrsource4);
+ isc_sockaddr_any6(&zone->altxfrsource6);
+ zone->xfr = NULL;
+ zone->tsigkey = NULL;
+ zone->maxxfrin = MAX_XFER_TIME;
+ zone->maxxfrout = MAX_XFER_TIME;
+ zone->ssutable = NULL;
+ zone->sigvalidityinterval = 30 * 24 * 3600;
+ zone->sigresigninginterval = 7 * 24 * 3600;
+ zone->view = NULL;
+ zone->acache = NULL;
+ zone->checkmx = NULL;
+ zone->checksrv = NULL;
+ zone->checkns = NULL;
+ ISC_LINK_INIT(zone, statelink);
+ zone->statelist = NULL;
+ zone->stats = NULL;
+ zone->requeststats_on = ISC_FALSE;
+ zone->requeststats = NULL;
+ zone->notifydelay = 5;
+ zone->isself = NULL;
+ zone->isselfarg = NULL;
+ ISC_LIST_INIT(zone->signing);
+ ISC_LIST_INIT(zone->nsec3chain);
+ zone->signatures = 10;
+ zone->nodes = 100;
+ zone->privatetype = (dns_rdatatype_t)0xffffU;
+
+ zone->magic = ZONE_MAGIC;
+
+ /* Must be after magic is set. */
+ result = dns_zone_setdbtype(zone, dbargc_default, dbargv_default);
+ if (result != ISC_R_SUCCESS)
+ goto free_erefs;
+
+ ISC_EVENT_INIT(&zone->ctlevent, sizeof(zone->ctlevent), 0, NULL,
+ DNS_EVENT_ZONECONTROL, zone_shutdown, zone, zone,
+ NULL, NULL);
+ *zonep = zone;
+ return (ISC_R_SUCCESS);
+
+ free_erefs:
+ isc_refcount_decrement(&zone->erefs, NULL);
+ isc_refcount_destroy(&zone->erefs);
+
+ free_dblock:
+ ZONEDB_DESTROYLOCK(&zone->dblock);
+
+ free_mutex:
+ DESTROYLOCK(&zone->lock);
+
+ free_zone:
+ isc_mem_putanddetach(&zone->mctx, zone, sizeof(*zone));
+ return (result);
+}
+
+/*
+ * Free a zone. Because we require that there be no more
+ * outstanding events or references, no locking is necessary.
+ */
+static void
+zone_free(dns_zone_t *zone) {
+ isc_mem_t *mctx = NULL;
+ dns_signing_t *signing;
+ dns_nsec3chain_t *nsec3chain;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(isc_refcount_current(&zone->erefs) == 0);
+ REQUIRE(zone->irefs == 0);
+ REQUIRE(!LOCKED_ZONE(zone));
+ REQUIRE(zone->timer == NULL);
+
+ /*
+ * Managed objects. Order is important.
+ */
+ if (zone->request != NULL)
+ dns_request_destroy(&zone->request); /* XXXMPA */
+ INSIST(zone->readio == NULL);
+ INSIST(zone->statelist == NULL);
+ INSIST(zone->writeio == NULL);
+
+ if (zone->task != NULL)
+ isc_task_detach(&zone->task);
+ if (zone->zmgr != NULL)
+ dns_zonemgr_releasezone(zone->zmgr, zone);
+
+ /* Unmanaged objects */
+ for (signing = ISC_LIST_HEAD(zone->signing);
+ signing != NULL;
+ signing = ISC_LIST_HEAD(zone->signing)) {
+ ISC_LIST_UNLINK(zone->signing, signing, link);
+ dns_db_detach(&signing->db);
+ dns_dbiterator_destroy(&signing->dbiterator);
+ isc_mem_put(zone->mctx, signing, sizeof *signing);
+ }
+ for (nsec3chain = ISC_LIST_HEAD(zone->nsec3chain);
+ nsec3chain != NULL;
+ nsec3chain = ISC_LIST_HEAD(zone->nsec3chain)) {
+ ISC_LIST_UNLINK(zone->nsec3chain, nsec3chain, link);
+ dns_db_detach(&nsec3chain->db);
+ dns_dbiterator_destroy(&nsec3chain->dbiterator);
+ isc_mem_put(zone->mctx, nsec3chain, sizeof *nsec3chain);
+ }
+ if (zone->masterfile != NULL)
+ isc_mem_free(zone->mctx, zone->masterfile);
+ zone->masterfile = NULL;
+ if (zone->keydirectory != NULL)
+ isc_mem_free(zone->mctx, zone->keydirectory);
+ zone->keydirectory = NULL;
+ zone->journalsize = -1;
+ if (zone->journal != NULL)
+ isc_mem_free(zone->mctx, zone->journal);
+ zone->journal = NULL;
+ if (zone->stats != NULL)
+ dns_stats_detach(&zone->stats);
+ if (zone->requeststats != NULL)
+ dns_stats_detach(&zone->requeststats);
+ if (zone->db != NULL)
+ zone_detachdb(zone);
+ if (zone->acache != NULL)
+ dns_acache_detach(&zone->acache);
+ zone_freedbargs(zone);
+ RUNTIME_CHECK(dns_zone_setmasterswithkeys(zone, NULL, NULL, 0)
+ == ISC_R_SUCCESS);
+ RUNTIME_CHECK(dns_zone_setalsonotify(zone, NULL, 0)
+ == ISC_R_SUCCESS);
+ zone->check_names = dns_severity_ignore;
+ if (zone->update_acl != NULL)
+ dns_acl_detach(&zone->update_acl);
+ if (zone->forward_acl != NULL)
+ dns_acl_detach(&zone->forward_acl);
+ if (zone->notify_acl != NULL)
+ dns_acl_detach(&zone->notify_acl);
+ if (zone->query_acl != NULL)
+ dns_acl_detach(&zone->query_acl);
+ if (zone->queryon_acl != NULL)
+ dns_acl_detach(&zone->queryon_acl);
+ if (zone->xfr_acl != NULL)
+ dns_acl_detach(&zone->xfr_acl);
+ if (dns_name_dynamic(&zone->origin))
+ dns_name_free(&zone->origin, zone->mctx);
+ if (zone->strnamerd != NULL)
+ isc_mem_free(zone->mctx, zone->strnamerd);
+ if (zone->strname != NULL)
+ isc_mem_free(zone->mctx, zone->strname);
+ if (zone->strrdclass != NULL)
+ isc_mem_free(zone->mctx, zone->strrdclass);
+ if (zone->strviewname != NULL)
+ isc_mem_free(zone->mctx, zone->strviewname);
+ if (zone->ssutable != NULL)
+ dns_ssutable_detach(&zone->ssutable);
+
+ /* last stuff */
+ ZONEDB_DESTROYLOCK(&zone->dblock);
+ DESTROYLOCK(&zone->lock);
+ isc_refcount_destroy(&zone->erefs);
+ zone->magic = 0;
+ mctx = zone->mctx;
+ isc_mem_put(mctx, zone, sizeof(*zone));
+ isc_mem_detach(&mctx);
+}
+
+/*
+ * Single shot.
+ */
+void
+dns_zone_setclass(dns_zone_t *zone, dns_rdataclass_t rdclass) {
+ char namebuf[1024];
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(rdclass != dns_rdataclass_none);
+
+ /*
+ * Test and set.
+ */
+ LOCK_ZONE(zone);
+ REQUIRE(zone->rdclass == dns_rdataclass_none ||
+ zone->rdclass == rdclass);
+ zone->rdclass = rdclass;
+
+ if (zone->strnamerd != NULL)
+ isc_mem_free(zone->mctx, zone->strnamerd);
+ if (zone->strrdclass != NULL)
+ isc_mem_free(zone->mctx, zone->strrdclass);
+
+ zone_namerd_tostr(zone, namebuf, sizeof namebuf);
+ zone->strnamerd = isc_mem_strdup(zone->mctx, namebuf);
+ zone_rdclass_tostr(zone, namebuf, sizeof namebuf);
+ zone->strrdclass = isc_mem_strdup(zone->mctx, namebuf);
+
+ UNLOCK_ZONE(zone);
+}
+
+dns_rdataclass_t
+dns_zone_getclass(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->rdclass);
+}
+
+void
+dns_zone_setnotifytype(dns_zone_t *zone, dns_notifytype_t notifytype) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ zone->notifytype = notifytype;
+ UNLOCK_ZONE(zone);
+}
+
+isc_uint32_t
+dns_zone_getserial(dns_zone_t *zone) {
+ isc_uint32_t serial;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ serial = zone->serial;
+ UNLOCK_ZONE(zone);
+
+ return (serial);
+}
+
+/*
+ * Single shot.
+ */
+void
+dns_zone_settype(dns_zone_t *zone, dns_zonetype_t type) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(type != dns_zone_none);
+
+ /*
+ * Test and set.
+ */
+ LOCK_ZONE(zone);
+ REQUIRE(zone->type == dns_zone_none || zone->type == type);
+ zone->type = type;
+ UNLOCK_ZONE(zone);
+}
+
+static void
+zone_freedbargs(dns_zone_t *zone) {
+ unsigned int i;
+
+ /* Free the old database argument list. */
+ if (zone->db_argv != NULL) {
+ for (i = 0; i < zone->db_argc; i++)
+ isc_mem_free(zone->mctx, zone->db_argv[i]);
+ isc_mem_put(zone->mctx, zone->db_argv,
+ zone->db_argc * sizeof(*zone->db_argv));
+ }
+ zone->db_argc = 0;
+ zone->db_argv = NULL;
+}
+
+isc_result_t
+dns_zone_getdbtype(dns_zone_t *zone, char ***argv, isc_mem_t *mctx) {
+ size_t size = 0;
+ unsigned int i;
+ isc_result_t result = ISC_R_SUCCESS;
+ void *mem;
+ char **tmp, *tmp2;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(argv != NULL && *argv == NULL);
+
+ LOCK_ZONE(zone);
+ size = (zone->db_argc + 1) * sizeof(char *);
+ for (i = 0; i < zone->db_argc; i++)
+ size += strlen(zone->db_argv[i]) + 1;
+ mem = isc_mem_allocate(mctx, size);
+ if (mem != NULL) {
+ tmp = mem;
+ tmp2 = mem;
+ tmp2 += (zone->db_argc + 1) * sizeof(char *);
+ for (i = 0; i < zone->db_argc; i++) {
+ *tmp++ = tmp2;
+ strcpy(tmp2, zone->db_argv[i]);
+ tmp2 += strlen(tmp2) + 1;
+ }
+ *tmp = NULL;
+ } else
+ result = ISC_R_NOMEMORY;
+ UNLOCK_ZONE(zone);
+ *argv = mem;
+ return (result);
+}
+
+isc_result_t
+dns_zone_setdbtype(dns_zone_t *zone,
+ unsigned int dbargc, const char * const *dbargv) {
+ isc_result_t result = ISC_R_SUCCESS;
+ char **new = NULL;
+ unsigned int i;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(dbargc >= 1);
+ REQUIRE(dbargv != NULL);
+
+ LOCK_ZONE(zone);
+
+ /* Set up a new database argument list. */
+ new = isc_mem_get(zone->mctx, dbargc * sizeof(*new));
+ if (new == NULL)
+ goto nomem;
+ for (i = 0; i < dbargc; i++)
+ new[i] = NULL;
+ for (i = 0; i < dbargc; i++) {
+ new[i] = isc_mem_strdup(zone->mctx, dbargv[i]);
+ if (new[i] == NULL)
+ goto nomem;
+ }
+
+ /* Free the old list. */
+ zone_freedbargs(zone);
+
+ zone->db_argc = dbargc;
+ zone->db_argv = new;
+ result = ISC_R_SUCCESS;
+ goto unlock;
+
+ nomem:
+ if (new != NULL) {
+ for (i = 0; i < dbargc; i++)
+ if (new[i] != NULL)
+ isc_mem_free(zone->mctx, new[i]);
+ isc_mem_put(zone->mctx, new, dbargc * sizeof(*new));
+ }
+ result = ISC_R_NOMEMORY;
+
+ unlock:
+ UNLOCK_ZONE(zone);
+ return (result);
+}
+
+void
+dns_zone_setview(dns_zone_t *zone, dns_view_t *view) {
+ char namebuf[1024];
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (zone->view != NULL)
+ dns_view_weakdetach(&zone->view);
+ dns_view_weakattach(view, &zone->view);
+
+ if (zone->strviewname != NULL)
+ isc_mem_free(zone->mctx, zone->strviewname);
+ if (zone->strnamerd != NULL)
+ isc_mem_free(zone->mctx, zone->strnamerd);
+
+ zone_namerd_tostr(zone, namebuf, sizeof namebuf);
+ zone->strnamerd = isc_mem_strdup(zone->mctx, namebuf);
+ zone_viewname_tostr(zone, namebuf, sizeof namebuf);
+ zone->strviewname = isc_mem_strdup(zone->mctx, namebuf);
+
+ UNLOCK_ZONE(zone);
+}
+
+
+dns_view_t *
+dns_zone_getview(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->view);
+}
+
+
+isc_result_t
+dns_zone_setorigin(dns_zone_t *zone, const dns_name_t *origin) {
+ isc_result_t result;
+ char namebuf[1024];
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(origin != NULL);
+
+ LOCK_ZONE(zone);
+ if (dns_name_dynamic(&zone->origin)) {
+ dns_name_free(&zone->origin, zone->mctx);
+ dns_name_init(&zone->origin, NULL);
+ }
+ result = dns_name_dup(origin, zone->mctx, &zone->origin);
+
+ if (zone->strnamerd != NULL)
+ isc_mem_free(zone->mctx, zone->strnamerd);
+ if (zone->strname != NULL)
+ isc_mem_free(zone->mctx, zone->strname);
+
+ zone_namerd_tostr(zone, namebuf, sizeof namebuf);
+ zone->strnamerd = isc_mem_strdup(zone->mctx, namebuf);
+ zone_name_tostr(zone, namebuf, sizeof namebuf);
+ zone->strname = isc_mem_strdup(zone->mctx, namebuf);
+
+ UNLOCK_ZONE(zone);
+ return (result);
+}
+
+void
+dns_zone_setacache(dns_zone_t *zone, dns_acache_t *acache) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(acache != NULL);
+
+ LOCK_ZONE(zone);
+ if (zone->acache != NULL)
+ dns_acache_detach(&zone->acache);
+ dns_acache_attach(acache, &zone->acache);
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+ if (zone->db != NULL) {
+ isc_result_t result;
+
+ /*
+ * If the zone reuses an existing DB, the DB needs to be
+ * set in the acache explicitly. We can safely ignore the
+ * case where the DB is already set. If other error happens,
+ * the acache will not work effectively.
+ */
+ result = dns_acache_setdb(acache, zone->db);
+ if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "dns_acache_setdb() failed: %s",
+ isc_result_totext(result));
+ }
+ }
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+ UNLOCK_ZONE(zone);
+}
+
+static isc_result_t
+dns_zone_setstring(dns_zone_t *zone, char **field, const char *value) {
+ char *copy;
+
+ if (value != NULL) {
+ copy = isc_mem_strdup(zone->mctx, value);
+ if (copy == NULL)
+ return (ISC_R_NOMEMORY);
+ } else {
+ copy = NULL;
+ }
+
+ if (*field != NULL)
+ isc_mem_free(zone->mctx, *field);
+
+ *field = copy;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_zone_setfile(dns_zone_t *zone, const char *file) {
+ return (dns_zone_setfile2(zone, file, dns_masterformat_text));
+}
+
+isc_result_t
+dns_zone_setfile2(dns_zone_t *zone, const char *file,
+ dns_masterformat_t format) {
+ isc_result_t result = ISC_R_SUCCESS;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ result = dns_zone_setstring(zone, &zone->masterfile, file);
+ if (result == ISC_R_SUCCESS) {
+ zone->masterformat = format;
+ result = default_journal(zone);
+ }
+ UNLOCK_ZONE(zone);
+
+ return (result);
+}
+
+const char *
+dns_zone_getfile(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->masterfile);
+}
+
+static isc_result_t
+default_journal(dns_zone_t *zone) {
+ isc_result_t result;
+ char *journal;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(LOCKED_ZONE(zone));
+
+ if (zone->masterfile != NULL) {
+ /* Calculate string length including '\0'. */
+ int len = strlen(zone->masterfile) + sizeof(".jnl");
+ journal = isc_mem_allocate(zone->mctx, len);
+ if (journal == NULL)
+ return (ISC_R_NOMEMORY);
+ strcpy(journal, zone->masterfile);
+ strcat(journal, ".jnl");
+ } else {
+ journal = NULL;
+ }
+ result = dns_zone_setstring(zone, &zone->journal, journal);
+ if (journal != NULL)
+ isc_mem_free(zone->mctx, journal);
+ return (result);
+}
+
+isc_result_t
+dns_zone_setjournal(dns_zone_t *zone, const char *journal) {
+ isc_result_t result = ISC_R_SUCCESS;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ result = dns_zone_setstring(zone, &zone->journal, journal);
+ UNLOCK_ZONE(zone);
+
+ return (result);
+}
+
+char *
+dns_zone_getjournal(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->journal);
+}
+
+/*
+ * Return true iff the zone is "dynamic", in the sense that the zone's
+ * master file (if any) is written by the server, rather than being
+ * updated manually and read by the server.
+ *
+ * This is true for slave zones, stub zones, and zones that allow
+ * dynamic updates either by having an update policy ("ssutable")
+ * or an "allow-update" ACL with a value other than exactly "{ none; }".
+ */
+static isc_boolean_t
+zone_isdynamic(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (ISC_TF(zone->type == dns_zone_slave ||
+ zone->type == dns_zone_stub ||
+ (!zone->update_disabled && zone->ssutable != NULL) ||
+ (!zone->update_disabled && zone->update_acl != NULL &&
+ !dns_acl_isnone(zone->update_acl))));
+}
+
+
+static isc_result_t
+zone_load(dns_zone_t *zone, unsigned int flags) {
+ isc_result_t result;
+ isc_time_t now;
+ isc_time_t loadtime, filetime;
+ dns_db_t *db = NULL;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ TIME_NOW(&now);
+
+ INSIST(zone->type != dns_zone_none);
+
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADING)) {
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+
+ if (zone->db != NULL && zone->masterfile == NULL) {
+ /*
+ * The zone has no master file configured, but it already
+ * has a database. It could be the built-in
+ * version.bind. CH zone, a zone with a persistent
+ * database being reloaded, or maybe a zone that
+ * used to have a master file but whose configuration
+ * was changed so that it no longer has one. Do nothing.
+ */
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+
+ if (zone->db != NULL && zone_isdynamic(zone)) {
+ /*
+ * This is a slave, stub, or dynamically updated
+ * zone being reloaded. Do nothing - the database
+ * we already have is guaranteed to be up-to-date.
+ */
+ if (zone->type == dns_zone_master)
+ result = DNS_R_DYNAMIC;
+ else
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+
+
+ /*
+ * Store the current time before the zone is loaded, so that if the
+ * file changes between the time of the load and the time that
+ * zone->loadtime is set, then the file will still be reloaded
+ * the next time dns_zone_load is called.
+ */
+ TIME_NOW(&loadtime);
+
+ /*
+ * Don't do the load if the file that stores the zone is older
+ * than the last time the zone was loaded. If the zone has not
+ * been loaded yet, zone->loadtime will be the epoch.
+ */
+ if (zone->masterfile != NULL) {
+ /*
+ * The file is already loaded. If we are just doing a
+ * "rndc reconfig", we are done.
+ */
+ if (!isc_time_isepoch(&zone->loadtime) &&
+ (flags & DNS_ZONELOADFLAG_NOSTAT) != 0) {
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+
+ result = isc_file_getmodtime(zone->masterfile, &filetime);
+ if (result == ISC_R_SUCCESS) {
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED) &&
+ !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_HASINCLUDE) &&
+ isc_time_compare(&filetime, &zone->loadtime) <= 0) {
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "skipping load: master file "
+ "older than last load");
+ result = DNS_R_UPTODATE;
+ goto cleanup;
+ }
+ loadtime = filetime;
+ }
+ }
+
+ INSIST(zone->db_argc >= 1);
+
+ /*
+ * Built in zones don't need to be reloaded.
+ */
+ if (zone->type == dns_zone_master &&
+ strcmp(zone->db_argv[0], "_builtin") == 0 &&
+ DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED)) {
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+
+ if ((zone->type == dns_zone_slave || zone->type == dns_zone_stub) &&
+ (strcmp(zone->db_argv[0], "rbt") == 0 ||
+ strcmp(zone->db_argv[0], "rbt64") == 0)) {
+ if (zone->masterfile == NULL ||
+ !isc_file_exists(zone->masterfile)) {
+ if (zone->masterfile != NULL) {
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "no master file");
+ }
+ zone->refreshtime = now;
+ if (zone->task != NULL)
+ zone_settimer(zone, &now);
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+ }
+
+ dns_zone_log(zone, ISC_LOG_DEBUG(1), "starting load");
+
+ result = dns_db_create(zone->mctx, zone->db_argv[0],
+ &zone->origin, (zone->type == dns_zone_stub) ?
+ dns_dbtype_stub : dns_dbtype_zone,
+ zone->rdclass,
+ zone->db_argc - 1, zone->db_argv + 1,
+ &db);
+
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "loading zone: creating database: %s",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+ dns_db_settask(db, zone->task);
+
+ if (! dns_db_ispersistent(db)) {
+ if (zone->masterfile != NULL) {
+ result = zone_startload(db, zone, loadtime);
+ } else {
+ result = DNS_R_NOMASTERFILE;
+ if (zone->type == dns_zone_master) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "loading zone: "
+ "no master file configured");
+ goto cleanup;
+ }
+ dns_zone_log(zone, ISC_LOG_INFO, "loading zone: "
+ "no master file configured: continuing");
+ }
+ }
+
+ if (result == DNS_R_CONTINUE) {
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADING);
+ goto cleanup;
+ }
+
+ result = zone_postload(zone, db, loadtime, result);
+
+ cleanup:
+ UNLOCK_ZONE(zone);
+ if (db != NULL)
+ dns_db_detach(&db);
+ return (result);
+}
+
+isc_result_t
+dns_zone_load(dns_zone_t *zone) {
+ return (zone_load(zone, 0));
+}
+
+isc_result_t
+dns_zone_loadnew(dns_zone_t *zone) {
+ return (zone_load(zone, DNS_ZONELOADFLAG_NOSTAT));
+}
+
+static unsigned int
+get_master_options(dns_zone_t *zone) {
+ unsigned int options;
+
+ options = DNS_MASTER_ZONE;
+ if (zone->type == dns_zone_slave)
+ options |= DNS_MASTER_SLAVE;
+ if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKNS))
+ options |= DNS_MASTER_CHECKNS;
+ if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_FATALNS))
+ options |= DNS_MASTER_FATALNS;
+ if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKNAMES))
+ options |= DNS_MASTER_CHECKNAMES;
+ if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKNAMESFAIL))
+ options |= DNS_MASTER_CHECKNAMESFAIL;
+ if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKMX))
+ options |= DNS_MASTER_CHECKMX;
+ if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKMXFAIL))
+ options |= DNS_MASTER_CHECKMXFAIL;
+ if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKWILDCARD))
+ options |= DNS_MASTER_CHECKWILDCARD;
+ if (zone->type == dns_zone_master &&
+ (zone->update_acl != NULL || zone->ssutable != NULL))
+ options |= DNS_MASTER_RESIGN;
+ return (options);
+}
+
+static void
+zone_gotreadhandle(isc_task_t *task, isc_event_t *event) {
+ dns_load_t *load = event->ev_arg;
+ isc_result_t result = ISC_R_SUCCESS;
+ unsigned int options;
+
+ REQUIRE(DNS_LOAD_VALID(load));
+
+ if ((event->ev_attributes & ISC_EVENTATTR_CANCELED) != 0)
+ result = ISC_R_CANCELED;
+ isc_event_free(&event);
+ if (result == ISC_R_CANCELED)
+ goto fail;
+
+ options = get_master_options(load->zone);
+
+ result = dns_master_loadfileinc3(load->zone->masterfile,
+ dns_db_origin(load->db),
+ dns_db_origin(load->db),
+ load->zone->rdclass,
+ options,
+ load->zone->sigresigninginterval,
+ &load->callbacks, task,
+ zone_loaddone, load,
+ &load->zone->lctx, load->zone->mctx,
+ load->zone->masterformat);
+ if (result != ISC_R_SUCCESS && result != DNS_R_CONTINUE &&
+ result != DNS_R_SEENINCLUDE)
+ goto fail;
+ return;
+
+ fail:
+ zone_loaddone(load, result);
+}
+
+static void
+zone_gotwritehandle(isc_task_t *task, isc_event_t *event) {
+ const char me[] = "zone_gotwritehandle";
+ dns_zone_t *zone = event->ev_arg;
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_dbversion_t *version = NULL;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ INSIST(task == zone->task);
+ ENTER;
+
+ if ((event->ev_attributes & ISC_EVENTATTR_CANCELED) != 0)
+ result = ISC_R_CANCELED;
+ isc_event_free(&event);
+ if (result == ISC_R_CANCELED)
+ goto fail;
+
+ LOCK_ZONE(zone);
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+ dns_db_currentversion(zone->db, &version);
+ result = dns_master_dumpinc2(zone->mctx, zone->db, version,
+ &dns_master_style_default,
+ zone->masterfile, zone->task, dump_done,
+ zone, &zone->dctx, zone->masterformat);
+ dns_db_closeversion(zone->db, &version, ISC_FALSE);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+ UNLOCK_ZONE(zone);
+ if (result != DNS_R_CONTINUE)
+ goto fail;
+ return;
+
+ fail:
+ dump_done(zone, result);
+}
+
+static isc_result_t
+zone_startload(dns_db_t *db, dns_zone_t *zone, isc_time_t loadtime) {
+ dns_load_t *load;
+ isc_result_t result;
+ isc_result_t tresult;
+ unsigned int options;
+
+ options = get_master_options(zone);
+
+ if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_MANYERRORS))
+ options |= DNS_MASTER_MANYERRORS;
+
+ if (zone->zmgr != NULL && zone->db != NULL && zone->task != NULL) {
+ load = isc_mem_get(zone->mctx, sizeof(*load));
+ if (load == NULL)
+ return (ISC_R_NOMEMORY);
+
+ load->mctx = NULL;
+ load->zone = NULL;
+ load->db = NULL;
+ load->loadtime = loadtime;
+ load->magic = LOAD_MAGIC;
+
+ isc_mem_attach(zone->mctx, &load->mctx);
+ zone_iattach(zone, &load->zone);
+ dns_db_attach(db, &load->db);
+ dns_rdatacallbacks_init(&load->callbacks);
+ result = dns_db_beginload(db, &load->callbacks.add,
+ &load->callbacks.add_private);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = zonemgr_getio(zone->zmgr, ISC_TRUE, zone->task,
+ zone_gotreadhandle, load,
+ &zone->readio);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * We can't report multiple errors so ignore
+ * the result of dns_db_endload().
+ */
+ (void)dns_db_endload(load->db,
+ &load->callbacks.add_private);
+ goto cleanup;
+ } else
+ result = DNS_R_CONTINUE;
+ } else {
+ dns_rdatacallbacks_t callbacks;
+
+ dns_rdatacallbacks_init(&callbacks);
+ result = dns_db_beginload(db, &callbacks.add,
+ &callbacks.add_private);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = dns_master_loadfile3(zone->masterfile, &zone->origin,
+ &zone->origin, zone->rdclass,
+ options, zone->sigresigninginterval,
+ &callbacks, zone->mctx,
+ zone->masterformat);
+ tresult = dns_db_endload(db, &callbacks.add_private);
+ if (result == ISC_R_SUCCESS)
+ result = tresult;
+ }
+
+ return (result);
+
+ cleanup:
+ load->magic = 0;
+ dns_db_detach(&load->db);
+ zone_idetach(&load->zone);
+ isc_mem_detach(&load->mctx);
+ isc_mem_put(zone->mctx, load, sizeof(*load));
+ return (result);
+}
+
+static isc_boolean_t
+zone_check_mx(dns_zone_t *zone, dns_db_t *db, dns_name_t *name,
+ dns_name_t *owner)
+{
+ isc_result_t result;
+ char ownerbuf[DNS_NAME_FORMATSIZE];
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char altbuf[DNS_NAME_FORMATSIZE];
+ dns_fixedname_t fixed;
+ dns_name_t *foundname;
+ int level;
+
+ /*
+ * Outside of zone.
+ */
+ if (!dns_name_issubdomain(name, &zone->origin)) {
+ if (zone->checkmx != NULL)
+ return ((zone->checkmx)(zone, name, owner));
+ return (ISC_TRUE);
+ }
+
+ if (zone->type == dns_zone_master)
+ level = ISC_LOG_ERROR;
+ else
+ level = ISC_LOG_WARNING;
+
+ dns_fixedname_init(&fixed);
+ foundname = dns_fixedname_name(&fixed);
+
+ result = dns_db_find(db, name, NULL, dns_rdatatype_a,
+ 0, 0, NULL, foundname, NULL, NULL);
+ if (result == ISC_R_SUCCESS)
+ return (ISC_TRUE);
+
+ if (result == DNS_R_NXRRSET) {
+ result = dns_db_find(db, name, NULL, dns_rdatatype_aaaa,
+ 0, 0, NULL, foundname, NULL, NULL);
+ if (result == ISC_R_SUCCESS)
+ return (ISC_TRUE);
+ }
+
+ dns_name_format(owner, ownerbuf, sizeof ownerbuf);
+ dns_name_format(name, namebuf, sizeof namebuf);
+ if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN ||
+ result == DNS_R_EMPTYNAME) {
+ dns_zone_log(zone, level,
+ "%s/MX '%s' has no address records (A or AAAA)",
+ ownerbuf, namebuf);
+ /* XXX950 make fatal for 9.5.0. */
+ return (ISC_TRUE);
+ }
+
+ if (result == DNS_R_CNAME) {
+ if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_WARNMXCNAME) ||
+ DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IGNOREMXCNAME))
+ level = ISC_LOG_WARNING;
+ if (!DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IGNOREMXCNAME))
+ dns_zone_log(zone, level,
+ "%s/MX '%s' is a CNAME (illegal)",
+ ownerbuf, namebuf);
+ return ((level == ISC_LOG_WARNING) ? ISC_TRUE : ISC_FALSE);
+ }
+
+ if (result == DNS_R_DNAME) {
+ if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_WARNMXCNAME) ||
+ DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IGNOREMXCNAME))
+ level = ISC_LOG_WARNING;
+ if (!DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IGNOREMXCNAME)) {
+ dns_name_format(foundname, altbuf, sizeof altbuf);
+ dns_zone_log(zone, level, "%s/MX '%s' is below a DNAME"
+ " '%s' (illegal)", ownerbuf, namebuf,
+ altbuf);
+ }
+ return ((level == ISC_LOG_WARNING) ? ISC_TRUE : ISC_FALSE);
+ }
+
+ if (zone->checkmx != NULL && result == DNS_R_DELEGATION)
+ return ((zone->checkmx)(zone, name, owner));
+
+ return (ISC_TRUE);
+}
+
+static isc_boolean_t
+zone_check_srv(dns_zone_t *zone, dns_db_t *db, dns_name_t *name,
+ dns_name_t *owner)
+{
+ isc_result_t result;
+ char ownerbuf[DNS_NAME_FORMATSIZE];
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char altbuf[DNS_NAME_FORMATSIZE];
+ dns_fixedname_t fixed;
+ dns_name_t *foundname;
+ int level;
+
+ /*
+ * "." means the services does not exist.
+ */
+ if (dns_name_equal(name, dns_rootname))
+ return (ISC_TRUE);
+
+ /*
+ * Outside of zone.
+ */
+ if (!dns_name_issubdomain(name, &zone->origin)) {
+ if (zone->checksrv != NULL)
+ return ((zone->checksrv)(zone, name, owner));
+ return (ISC_TRUE);
+ }
+
+ if (zone->type == dns_zone_master)
+ level = ISC_LOG_ERROR;
+ else
+ level = ISC_LOG_WARNING;
+
+ dns_fixedname_init(&fixed);
+ foundname = dns_fixedname_name(&fixed);
+
+ result = dns_db_find(db, name, NULL, dns_rdatatype_a,
+ 0, 0, NULL, foundname, NULL, NULL);
+ if (result == ISC_R_SUCCESS)
+ return (ISC_TRUE);
+
+ if (result == DNS_R_NXRRSET) {
+ result = dns_db_find(db, name, NULL, dns_rdatatype_aaaa,
+ 0, 0, NULL, foundname, NULL, NULL);
+ if (result == ISC_R_SUCCESS)
+ return (ISC_TRUE);
+ }
+
+ dns_name_format(owner, ownerbuf, sizeof ownerbuf);
+ dns_name_format(name, namebuf, sizeof namebuf);
+ if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN ||
+ result == DNS_R_EMPTYNAME) {
+ dns_zone_log(zone, level,
+ "%s/SRV '%s' has no address records (A or AAAA)",
+ ownerbuf, namebuf);
+ /* XXX950 make fatal for 9.5.0. */
+ return (ISC_TRUE);
+ }
+
+ if (result == DNS_R_CNAME) {
+ if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_WARNSRVCNAME) ||
+ DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IGNORESRVCNAME))
+ level = ISC_LOG_WARNING;
+ if (!DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IGNORESRVCNAME))
+ dns_zone_log(zone, level,
+ "%s/SRV '%s' is a CNAME (illegal)",
+ ownerbuf, namebuf);
+ return ((level == ISC_LOG_WARNING) ? ISC_TRUE : ISC_FALSE);
+ }
+
+ if (result == DNS_R_DNAME) {
+ if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_WARNSRVCNAME) ||
+ DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IGNORESRVCNAME))
+ level = ISC_LOG_WARNING;
+ if (!DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IGNORESRVCNAME)) {
+ dns_name_format(foundname, altbuf, sizeof altbuf);
+ dns_zone_log(zone, level, "%s/SRV '%s' is below a "
+ "DNAME '%s' (illegal)", ownerbuf, namebuf,
+ altbuf);
+ }
+ return ((level == ISC_LOG_WARNING) ? ISC_TRUE : ISC_FALSE);
+ }
+
+ if (zone->checksrv != NULL && result == DNS_R_DELEGATION)
+ return ((zone->checksrv)(zone, name, owner));
+
+ return (ISC_TRUE);
+}
+
+static isc_boolean_t
+zone_check_glue(dns_zone_t *zone, dns_db_t *db, dns_name_t *name,
+ dns_name_t *owner)
+{
+ isc_boolean_t answer = ISC_TRUE;
+ isc_result_t result, tresult;
+ char ownerbuf[DNS_NAME_FORMATSIZE];
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char altbuf[DNS_NAME_FORMATSIZE];
+ dns_fixedname_t fixed;
+ dns_name_t *foundname;
+ dns_rdataset_t a;
+ dns_rdataset_t aaaa;
+ int level;
+
+ /*
+ * Outside of zone.
+ */
+ if (!dns_name_issubdomain(name, &zone->origin)) {
+ if (zone->checkns != NULL)
+ return ((zone->checkns)(zone, name, owner, NULL, NULL));
+ return (ISC_TRUE);
+ }
+
+ if (zone->type == dns_zone_master)
+ level = ISC_LOG_ERROR;
+ else
+ level = ISC_LOG_WARNING;
+
+ dns_fixedname_init(&fixed);
+ foundname = dns_fixedname_name(&fixed);
+ dns_rdataset_init(&a);
+ dns_rdataset_init(&aaaa);
+
+ result = dns_db_find(db, name, NULL, dns_rdatatype_a,
+ DNS_DBFIND_GLUEOK, 0, NULL,
+ foundname, &a, NULL);
+
+ if (result == ISC_R_SUCCESS) {
+ dns_rdataset_disassociate(&a);
+ return (ISC_TRUE);
+ } else if (result == DNS_R_DELEGATION)
+ dns_rdataset_disassociate(&a);
+
+ if (result == DNS_R_NXRRSET || result == DNS_R_DELEGATION ||
+ result == DNS_R_GLUE) {
+ tresult = dns_db_find(db, name, NULL, dns_rdatatype_aaaa,
+ DNS_DBFIND_GLUEOK, 0, NULL,
+ foundname, &aaaa, NULL);
+ if (tresult == ISC_R_SUCCESS) {
+ dns_rdataset_disassociate(&aaaa);
+ return (ISC_TRUE);
+ }
+ if (tresult == DNS_R_DELEGATION)
+ dns_rdataset_disassociate(&aaaa);
+ if (result == DNS_R_GLUE || tresult == DNS_R_GLUE) {
+ /*
+ * Check glue against child zone.
+ */
+ if (zone->checkns != NULL)
+ answer = (zone->checkns)(zone, name, owner,
+ &a, &aaaa);
+ if (dns_rdataset_isassociated(&a))
+ dns_rdataset_disassociate(&a);
+ if (dns_rdataset_isassociated(&aaaa))
+ dns_rdataset_disassociate(&aaaa);
+ return (answer);
+ }
+ } else
+ tresult = result;
+
+ dns_name_format(owner, ownerbuf, sizeof ownerbuf);
+ dns_name_format(name, namebuf, sizeof namebuf);
+ if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN ||
+ result == DNS_R_EMPTYNAME || result == DNS_R_DELEGATION) {
+ const char *what;
+ isc_boolean_t required = ISC_FALSE;
+ if (dns_name_issubdomain(name, owner)) {
+ what = "REQUIRED GLUE ";
+ required = ISC_TRUE;
+ } else if (result == DNS_R_DELEGATION)
+ what = "SIBLING GLUE ";
+ else
+ what = "";
+
+ if (result != DNS_R_DELEGATION || required ||
+ DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKSIBLING)) {
+ dns_zone_log(zone, level, "%s/NS '%s' has no %s"
+ "address records (A or AAAA)",
+ ownerbuf, namebuf, what);
+ /*
+ * Log missing address record.
+ */
+ if (result == DNS_R_DELEGATION && zone->checkns != NULL)
+ (void)(zone->checkns)(zone, name, owner,
+ &a, &aaaa);
+ /* XXX950 make fatal for 9.5.0. */
+ /* answer = ISC_FALSE; */
+ }
+ } else if (result == DNS_R_CNAME) {
+ dns_zone_log(zone, level, "%s/NS '%s' is a CNAME (illegal)",
+ ownerbuf, namebuf);
+ /* XXX950 make fatal for 9.5.0. */
+ /* answer = ISC_FALSE; */
+ } else if (result == DNS_R_DNAME) {
+ dns_name_format(foundname, altbuf, sizeof altbuf);
+ dns_zone_log(zone, level,
+ "%s/NS '%s' is below a DNAME '%s' (illegal)",
+ ownerbuf, namebuf, altbuf);
+ /* XXX950 make fatal for 9.5.0. */
+ /* answer = ISC_FALSE; */
+ }
+
+ if (dns_rdataset_isassociated(&a))
+ dns_rdataset_disassociate(&a);
+ if (dns_rdataset_isassociated(&aaaa))
+ dns_rdataset_disassociate(&aaaa);
+ return (answer);
+}
+
+static isc_boolean_t
+integrity_checks(dns_zone_t *zone, dns_db_t *db) {
+ dns_dbiterator_t *dbiterator = NULL;
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t rdataset;
+ dns_fixedname_t fixed;
+ dns_fixedname_t fixedbottom;
+ dns_rdata_mx_t mx;
+ dns_rdata_ns_t ns;
+ dns_rdata_in_srv_t srv;
+ dns_rdata_t rdata;
+ dns_name_t *name;
+ dns_name_t *bottom;
+ isc_result_t result;
+ isc_boolean_t ok = ISC_TRUE;
+
+ dns_fixedname_init(&fixed);
+ name = dns_fixedname_name(&fixed);
+ dns_fixedname_init(&fixedbottom);
+ bottom = dns_fixedname_name(&fixedbottom);
+ dns_rdataset_init(&rdataset);
+ dns_rdata_init(&rdata);
+
+ result = dns_db_createiterator(db, 0, &dbiterator);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_TRUE);
+
+ result = dns_dbiterator_first(dbiterator);
+ while (result == ISC_R_SUCCESS) {
+ result = dns_dbiterator_current(dbiterator, &node, name);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ /*
+ * Is this name visible in the zone?
+ */
+ if (!dns_name_issubdomain(name, &zone->origin) ||
+ (dns_name_countlabels(bottom) > 0 &&
+ dns_name_issubdomain(name, bottom)))
+ goto next;
+
+ /*
+ * Don't check the NS records at the origin.
+ */
+ if (dns_name_equal(name, &zone->origin))
+ goto checkmx;
+
+ result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_ns,
+ 0, 0, &rdataset, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto checkmx;
+ /*
+ * Remember bottom of zone.
+ */
+ dns_name_copy(name, bottom, NULL);
+
+ result = dns_rdataset_first(&rdataset);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdataset_current(&rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &ns, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ if (!zone_check_glue(zone, db, &ns.name, name))
+ ok = ISC_FALSE;
+ dns_rdata_reset(&rdata);
+ result = dns_rdataset_next(&rdataset);
+ }
+ dns_rdataset_disassociate(&rdataset);
+
+ checkmx:
+ result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_mx,
+ 0, 0, &rdataset, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto checksrv;
+ result = dns_rdataset_first(&rdataset);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdataset_current(&rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &mx, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ if (!zone_check_mx(zone, db, &mx.mx, name))
+ ok = ISC_FALSE;
+ dns_rdata_reset(&rdata);
+ result = dns_rdataset_next(&rdataset);
+ }
+ dns_rdataset_disassociate(&rdataset);
+
+ checksrv:
+ if (zone->rdclass != dns_rdataclass_in)
+ goto next;
+ result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_srv,
+ 0, 0, &rdataset, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto next;
+ result = dns_rdataset_first(&rdataset);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdataset_current(&rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &srv, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ if (!zone_check_srv(zone, db, &srv.target, name))
+ ok = ISC_FALSE;
+ dns_rdata_reset(&rdata);
+ result = dns_rdataset_next(&rdataset);
+ }
+ dns_rdataset_disassociate(&rdataset);
+
+ next:
+ dns_db_detachnode(db, &node);
+ result = dns_dbiterator_next(dbiterator);
+ }
+
+ cleanup:
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ dns_dbiterator_destroy(&dbiterator);
+
+ return (ok);
+}
+
+/*
+ * OpenSSL verification of RSA keys with exponent 3 is known to be
+ * broken prior OpenSSL 0.9.8c/0.9.7k. Look for such keys and warn
+ * if they are in use.
+ */
+static void
+zone_check_dnskeys(dns_zone_t *zone, dns_db_t *db) {
+ dns_dbnode_t *node = NULL;
+ dns_dbversion_t *version = NULL;
+ dns_rdata_dnskey_t dnskey;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_t rdataset;
+ isc_result_t result;
+ isc_boolean_t logit, foundrsa = ISC_FALSE, foundmd5 = ISC_FALSE;
+ const char *algorithm;
+
+ result = dns_db_findnode(db, &zone->origin, ISC_FALSE, &node);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ dns_db_currentversion(db, &version);
+ dns_rdataset_init(&rdataset);
+ result = dns_db_findrdataset(db, node, version, dns_rdatatype_dnskey,
+ dns_rdatatype_none, 0, &rdataset, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset))
+ {
+ dns_rdataset_current(&rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &dnskey, NULL);
+ INSIST(result == ISC_R_SUCCESS);
+
+ if ((dnskey.algorithm == DST_ALG_RSASHA1 ||
+ dnskey.algorithm == DST_ALG_RSAMD5) &&
+ dnskey.datalen > 1 && dnskey.data[0] == 1 &&
+ dnskey.data[1] == 3)
+ {
+ if (dnskey.algorithm == DST_ALG_RSASHA1) {
+ logit = !foundrsa;
+ foundrsa = ISC_TRUE;
+ algorithm = "RSASHA1";
+ } else {
+ logit = !foundmd5;
+ foundmd5 = ISC_TRUE;
+ algorithm = "RSAMD5";
+ }
+ if (logit)
+ dns_zone_log(zone, ISC_LOG_WARNING,
+ "weak %s (%u) key found "
+ "(exponent=3)", algorithm,
+ dnskey.algorithm);
+ if (foundrsa && foundmd5)
+ break;
+ }
+ dns_rdata_reset(&rdata);
+ }
+ dns_rdataset_disassociate(&rdataset);
+
+ cleanup:
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (version != NULL)
+ dns_db_closeversion(db, &version, ISC_FALSE);
+
+}
+
+static void
+resume_signingwithkey(dns_zone_t *zone) {
+ dns_dbnode_t *node = NULL;
+ dns_dbversion_t *version = NULL;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_t rdataset;
+ isc_result_t result;
+
+ result = dns_db_findnode(zone->db, &zone->origin, ISC_FALSE, &node);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ dns_db_currentversion(zone->db, &version);
+ dns_rdataset_init(&rdataset);
+ result = dns_db_findrdataset(zone->db, node, version,
+ zone->privatetype,
+ dns_rdatatype_none, 0,
+ &rdataset, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset))
+ {
+ dns_rdataset_current(&rdataset, &rdata);
+ if (rdata.length != 5 || rdata.data[4] != 0) {
+ dns_rdata_reset(&rdata);
+ continue;
+ }
+
+ result = zone_signwithkey(zone, rdata.data[0],
+ (rdata.data[1] << 8) | rdata.data[2], ISC_TF(rdata.data[3]));
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_signwithkey failed: %s",
+ dns_result_totext(result));
+ }
+ dns_rdata_reset(&rdata);
+ }
+ dns_rdataset_disassociate(&rdataset);
+
+ cleanup:
+ if (node != NULL)
+ dns_db_detachnode(zone->db, &node);
+ if (version != NULL)
+ dns_db_closeversion(zone->db, &version, ISC_FALSE);
+
+}
+
+static isc_result_t
+zone_addnsec3chain(dns_zone_t *zone, dns_rdata_nsec3param_t *nsec3param) {
+ dns_nsec3chain_t *nsec3chain, *current;
+ isc_result_t result;
+ isc_time_t now;
+ unsigned int options = 0;
+
+ nsec3chain = isc_mem_get(zone->mctx, sizeof *nsec3chain);
+ if (nsec3chain == NULL)
+ return (ISC_R_NOMEMORY);
+
+ nsec3chain->magic = 0;
+ nsec3chain->done = ISC_FALSE;
+ nsec3chain->db = NULL;
+ nsec3chain->dbiterator = NULL;
+ nsec3chain->nsec3param.common.rdclass = nsec3param->common.rdclass;
+ nsec3chain->nsec3param.common.rdtype = nsec3param->common.rdtype;
+ nsec3chain->nsec3param.hash = nsec3param->hash;
+ nsec3chain->nsec3param.iterations = nsec3param->iterations;
+ nsec3chain->nsec3param.flags = nsec3param->flags;
+ nsec3chain->nsec3param.salt_length = nsec3param->salt_length;
+ memcpy(nsec3chain->salt, nsec3param->salt, nsec3param->salt_length);
+ nsec3chain->nsec3param.salt = nsec3chain->salt;
+ nsec3chain->seen_nsec = ISC_FALSE;
+ nsec3chain->delete_nsec = ISC_FALSE;
+ nsec3chain->save_delete_nsec = ISC_FALSE;
+
+ for (current = ISC_LIST_HEAD(zone->nsec3chain);
+ current != NULL;
+ current = ISC_LIST_NEXT(current, link)) {
+ if (current->db == zone->db &&
+ current->nsec3param.hash == nsec3param->hash &&
+ current->nsec3param.iterations == nsec3param->iterations &&
+ current->nsec3param.salt_length == nsec3param->salt_length
+ && !memcmp(current->nsec3param.salt, nsec3param->salt,
+ nsec3param->salt_length))
+ current->done = ISC_TRUE;
+ }
+
+ if (zone->db != NULL) {
+ dns_db_attach(zone->db, &nsec3chain->db);
+ if ((nsec3chain->nsec3param.flags & DNS_NSEC3FLAG_CREATE) != 0)
+ options = DNS_DB_NONSEC3;
+ result = dns_db_createiterator(nsec3chain->db, options,
+ &nsec3chain->dbiterator);
+ if (result == ISC_R_SUCCESS)
+ dns_dbiterator_first(nsec3chain->dbiterator);
+ if (result == ISC_R_SUCCESS) {
+ dns_dbiterator_pause(nsec3chain->dbiterator);
+ ISC_LIST_INITANDAPPEND(zone->nsec3chain,
+ nsec3chain, link);
+ nsec3chain = NULL;
+ if (isc_time_isepoch(&zone->nsec3chaintime)) {
+ TIME_NOW(&now);
+ zone->nsec3chaintime = now;
+ if (zone->task != NULL)
+ zone_settimer(zone, &now);
+ }
+ }
+ } else
+ result = ISC_R_NOTFOUND;
+
+ if (nsec3chain != NULL) {
+ if (nsec3chain->db != NULL)
+ dns_db_detach(&nsec3chain->db);
+ if (nsec3chain->dbiterator != NULL)
+ dns_dbiterator_destroy(&nsec3chain->dbiterator);
+ isc_mem_put(zone->mctx, nsec3chain, sizeof *nsec3chain);
+ }
+ return (result);
+}
+
+static void
+resume_addnsec3chain(dns_zone_t *zone) {
+ dns_dbnode_t *node = NULL;
+ dns_dbversion_t *version = NULL;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_t rdataset;
+ isc_result_t result;
+ dns_rdata_nsec3param_t nsec3param;
+
+ result = dns_db_findnode(zone->db, &zone->origin, ISC_FALSE, &node);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ dns_db_currentversion(zone->db, &version);
+ dns_rdataset_init(&rdataset);
+ result = dns_db_findrdataset(zone->db, node, version,
+ dns_rdatatype_nsec3param,
+ dns_rdatatype_none, 0,
+ &rdataset, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset))
+ {
+ dns_rdataset_current(&rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &nsec3param, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ if ((nsec3param.flags & DNS_NSEC3FLAG_CREATE) != 0 ||
+ (nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0) {
+ result = zone_addnsec3chain(zone, &nsec3param);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_addnsec3chain failed: %s",
+ dns_result_totext(result));
+ }
+ }
+ dns_rdata_reset(&rdata);
+ }
+ dns_rdataset_disassociate(&rdataset);
+
+ cleanup:
+ if (node != NULL)
+ dns_db_detachnode(zone->db, &node);
+ if (version != NULL)
+ dns_db_closeversion(zone->db, &version, ISC_FALSE);
+}
+
+static void
+set_resigntime(dns_zone_t *zone) {
+ dns_rdataset_t rdataset;
+ dns_fixedname_t fixed;
+ char namebuf[DNS_NAME_FORMATSIZE];
+ unsigned int resign;
+ isc_result_t result;
+ isc_uint32_t nanosecs;
+
+ dns_rdataset_init(&rdataset);
+ dns_fixedname_init(&fixed);
+ result = dns_db_getsigningtime(zone->db, &rdataset,
+ dns_fixedname_name(&fixed));
+ if (result != ISC_R_SUCCESS) {
+ isc_time_settoepoch(&zone->resigntime);
+ return;
+ }
+ resign = rdataset.resign;
+ dns_name_format(dns_fixedname_name(&fixed), namebuf, sizeof(namebuf));
+ dns_rdataset_disassociate(&rdataset);
+ isc_random_get(&nanosecs);
+ nanosecs %= 1000000000;
+ isc_time_set(&zone->resigntime, resign, nanosecs);
+}
+
+static isc_result_t
+check_nsec3param(dns_zone_t *zone, dns_db_t *db) {
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t rdataset;
+ dns_dbversion_t *version = NULL;
+ dns_rdata_nsec3param_t nsec3param;
+ isc_boolean_t ok = ISC_FALSE;
+ isc_result_t result;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_boolean_t dynamic = (zone->type == dns_zone_master) ?
+ zone_isdynamic(zone) : ISC_FALSE;
+
+ dns_rdataset_init(&rdataset);
+ result = dns_db_findnode(db, &zone->origin, ISC_FALSE, &node);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "nsec3param lookup failure: %s",
+ dns_result_totext(result));
+ return (result);
+ }
+ dns_db_currentversion(db, &version);
+
+ result = dns_db_findrdataset(db, node, version,
+ dns_rdatatype_nsec3param,
+ dns_rdatatype_none, 0, &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND) {
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "nsec3param lookup failure: %s",
+ dns_result_totext(result));
+ goto cleanup;
+ }
+
+ /*
+ * For dynamic zones we must support every algorithm so we can
+ * regenerate all the NSEC3 chains.
+ * For non-dynamic zones we only need to find a supported algorithm.
+ */
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset))
+ {
+ dns_rdataset_current(&rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &nsec3param, NULL);
+ dns_rdata_reset(&rdata);
+ INSIST(result == ISC_R_SUCCESS);
+ if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_NSEC3TESTZONE) &&
+ nsec3param.hash == DNS_NSEC3_UNKNOWNALG && !dynamic)
+ {
+ dns_zone_log(zone, ISC_LOG_WARNING,
+ "nsec3 test \"unknown\" hash algorithm found: %u",
+ nsec3param.hash);
+ ok = ISC_TRUE;
+ } else if (!dns_nsec3_supportedhash(nsec3param.hash)) {
+ if (dynamic) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "unsupported nsec3 hash algorithm"
+ " in dynamic zone: %u",
+ nsec3param.hash);
+ result = DNS_R_BADZONE;
+ /* Stop second error message. */
+ ok = ISC_TRUE;
+ break;
+ } else
+ dns_zone_log(zone, ISC_LOG_WARNING,
+ "unsupported nsec3 hash algorithm: %u",
+ nsec3param.hash);
+ } else
+ ok = ISC_TRUE;
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+
+ if (!ok) {
+ result = DNS_R_BADZONE;
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "no supported nsec3 hash algorithm");
+ }
+
+ cleanup:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ dns_db_closeversion(db, &version, ISC_FALSE);
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+static isc_result_t
+zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime,
+ isc_result_t result)
+{
+ unsigned int soacount = 0;
+ unsigned int nscount = 0;
+ unsigned int errors = 0;
+ isc_uint32_t serial, refresh, retry, expire, minimum;
+ isc_time_t now;
+ isc_boolean_t needdump = ISC_FALSE;
+ isc_boolean_t hasinclude = DNS_ZONE_FLAG(zone, DNS_ZONEFLG_HASINCLUDE);
+ unsigned int options;
+
+ TIME_NOW(&now);
+
+ /*
+ * Initiate zone transfer? We may need a error code that
+ * indicates that the "permanent" form does not exist.
+ * XXX better error feedback to log.
+ */
+ if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE) {
+ if (zone->type == dns_zone_slave ||
+ zone->type == dns_zone_stub) {
+ if (result == ISC_R_FILENOTFOUND)
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "no master file");
+ else if (result != DNS_R_NOMASTERFILE)
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "loading from master file %s "
+ "failed: %s",
+ zone->masterfile,
+ dns_result_totext(result));
+ } else
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "loading from master file %s failed: %s",
+ zone->masterfile,
+ dns_result_totext(result));
+ goto cleanup;
+ }
+
+ dns_zone_log(zone, ISC_LOG_DEBUG(2),
+ "number of nodes in database: %u",
+ dns_db_nodecount(db));
+
+ if (result == DNS_R_SEENINCLUDE)
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_HASINCLUDE);
+ else
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_HASINCLUDE);
+
+ /*
+ * Apply update log, if any, on initial load.
+ */
+ if (zone->journal != NULL &&
+ ! DNS_ZONE_OPTION(zone, DNS_ZONEOPT_NOMERGE) &&
+ ! DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED))
+ {
+ if (zone->type == dns_zone_master &&
+ (zone->update_acl != NULL || zone->ssutable != NULL))
+ options = DNS_JOURNALOPT_RESIGN;
+ else
+ options = 0;
+ result = dns_journal_rollforward(zone->mctx, db, options,
+ zone->journal);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND &&
+ result != DNS_R_UPTODATE && result != DNS_R_NOJOURNAL &&
+ result != ISC_R_RANGE) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "journal rollforward failed: %s",
+ dns_result_totext(result));
+ goto cleanup;
+ }
+ if (result == ISC_R_NOTFOUND || result == ISC_R_RANGE) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "journal rollforward failed: "
+ "journal out of sync with zone");
+ goto cleanup;
+ }
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "journal rollforward completed "
+ "successfully: %s",
+ dns_result_totext(result));
+ if (result == ISC_R_SUCCESS)
+ needdump = ISC_TRUE;
+ }
+
+ zone->loadtime = loadtime;
+
+ dns_zone_log(zone, ISC_LOG_DEBUG(1), "loaded");
+ /*
+ * Obtain ns, soa and cname counts for top of zone.
+ */
+ INSIST(db != NULL);
+ result = zone_get_from_db(zone, db, &nscount, &soacount, &serial,
+ &refresh, &retry, &expire, &minimum,
+ &errors);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "could not find NS and/or SOA records");
+ }
+
+ /*
+ * Master / Slave / Stub zones require both NS and SOA records at
+ * the top of the zone.
+ */
+
+ switch (zone->type) {
+ case dns_zone_master:
+ case dns_zone_slave:
+ case dns_zone_stub:
+ if (soacount != 1) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "has %d SOA records", soacount);
+ result = DNS_R_BADZONE;
+ }
+ if (nscount == 0) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "has no NS records");
+ result = DNS_R_BADZONE;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ if (zone->type == dns_zone_master && errors != 0) {
+ result = DNS_R_BADZONE;
+ goto cleanup;
+ }
+ if (zone->type != dns_zone_stub) {
+ result = check_nsec3param(zone, db);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+ if (zone->type == dns_zone_master &&
+ DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKINTEGRITY) &&
+ !integrity_checks(zone, db)) {
+ result = DNS_R_BADZONE;
+ goto cleanup;
+ }
+
+ if (zone->db != NULL) {
+ /*
+ * This is checked in zone_replacedb() for slave zones
+ * as they don't reload from disk.
+ */
+ if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IXFRFROMDIFFS) &&
+ !isc_serial_gt(serial, zone->serial)) {
+ isc_uint32_t serialmin, serialmax;
+
+ INSIST(zone->type == dns_zone_master);
+
+ serialmin = (zone->serial + 1) & 0xffffffffU;
+ serialmax = (zone->serial + 0x7fffffffU) &
+ 0xffffffffU;
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "ixfr-from-differences: "
+ "new serial (%u) out of range "
+ "[%u - %u]", serial, serialmin,
+ serialmax);
+ result = DNS_R_BADZONE;
+ goto cleanup;
+ } else if (!isc_serial_ge(serial, zone->serial))
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone serial has gone backwards");
+ else if (serial == zone->serial && !hasinclude)
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone serial unchanged. "
+ "zone may fail to transfer "
+ "to slaves.");
+ }
+
+ if (zone->type == dns_zone_master &&
+ (zone->update_acl != NULL || zone->ssutable != NULL) &&
+ zone->sigresigninginterval < (3 * refresh) &&
+ dns_db_issecure(db))
+ {
+ dns_zone_log(zone, ISC_LOG_WARNING,
+ "sig-re-signing-interval less than "
+ "3 * refresh.");
+ }
+
+ zone->serial = serial;
+ zone->refresh = RANGE(refresh,
+ zone->minrefresh, zone->maxrefresh);
+ zone->retry = RANGE(retry,
+ zone->minretry, zone->maxretry);
+ zone->expire = RANGE(expire, zone->refresh + zone->retry,
+ DNS_MAX_EXPIRE);
+ zone->minimum = minimum;
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_HAVETIMERS);
+
+ if (zone->type == dns_zone_slave ||
+ zone->type == dns_zone_stub) {
+ isc_time_t t;
+ isc_uint32_t delay;
+
+ result = isc_file_getmodtime(zone->journal, &t);
+ if (result != ISC_R_SUCCESS)
+ result = isc_file_getmodtime(zone->masterfile,
+ &t);
+ if (result == ISC_R_SUCCESS)
+ DNS_ZONE_TIME_ADD(&t, zone->expire,
+ &zone->expiretime);
+ else
+ DNS_ZONE_TIME_ADD(&now, zone->retry,
+ &zone->expiretime);
+
+ delay = isc_random_jitter(zone->retry,
+ (zone->retry * 3) / 4);
+ DNS_ZONE_TIME_ADD(&now, delay, &zone->refreshtime);
+ if (isc_time_compare(&zone->refreshtime,
+ &zone->expiretime) >= 0)
+ zone->refreshtime = now;
+ }
+ break;
+ default:
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "unexpected zone type %d", zone->type);
+ result = ISC_R_UNEXPECTED;
+ goto cleanup;
+ }
+
+ /*
+ * Check for weak DNSKEY's.
+ */
+ if (zone->type == dns_zone_master)
+ zone_check_dnskeys(zone, db);
+
+#if 0
+ /* destroy notification example. */
+ {
+ isc_event_t *e = isc_event_allocate(zone->mctx, NULL,
+ DNS_EVENT_DBDESTROYED,
+ dns_zonemgr_dbdestroyed,
+ zone,
+ sizeof(isc_event_t));
+ dns_db_ondestroy(db, zone->task, &e);
+ }
+#endif
+
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_write);
+ if (zone->db != NULL) {
+ result = zone_replacedb(zone, db, ISC_FALSE);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ } else {
+ zone_attachdb(zone, db);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write);
+ DNS_ZONE_SETFLAG(zone,
+ DNS_ZONEFLG_LOADED|DNS_ZONEFLG_NEEDNOTIFY);
+ }
+ result = ISC_R_SUCCESS;
+ if (needdump)
+ zone_needdump(zone, DNS_DUMP_DELAY);
+ if (zone->task != NULL) {
+ if (zone->type == dns_zone_master) {
+ set_resigntime(zone);
+ resume_signingwithkey(zone);
+ resume_addnsec3chain(zone);
+ }
+ zone_settimer(zone, &now);
+ }
+
+ if (! dns_db_ispersistent(db))
+ dns_zone_log(zone, ISC_LOG_INFO, "loaded serial %u%s",
+ zone->serial,
+ dns_db_issecure(db) ? " (signed)" : "");
+
+ return (result);
+
+ cleanup:
+ if (zone->type == dns_zone_slave ||
+ zone->type == dns_zone_stub) {
+ if (zone->journal != NULL)
+ zone_saveunique(zone, zone->journal, "jn-XXXXXXXX");
+ if (zone->masterfile != NULL)
+ zone_saveunique(zone, zone->masterfile, "db-XXXXXXXX");
+
+ /* Mark the zone for immediate refresh. */
+ zone->refreshtime = now;
+ if (zone->task != NULL)
+ zone_settimer(zone, &now);
+ result = ISC_R_SUCCESS;
+ }
+ return (result);
+}
+
+static isc_boolean_t
+exit_check(dns_zone_t *zone) {
+
+ REQUIRE(LOCKED_ZONE(zone));
+
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_SHUTDOWN) &&
+ zone->irefs == 0)
+ {
+ /*
+ * DNS_ZONEFLG_SHUTDOWN can only be set if erefs == 0.
+ */
+ INSIST(isc_refcount_current(&zone->erefs) == 0);
+ return (ISC_TRUE);
+ }
+ return (ISC_FALSE);
+}
+
+static isc_boolean_t
+zone_check_ns(dns_zone_t *zone, dns_db_t *db, dns_name_t *name) {
+ isc_result_t result;
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char altbuf[DNS_NAME_FORMATSIZE];
+ dns_fixedname_t fixed;
+ dns_name_t *foundname;
+ int level;
+
+ if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_NOCHECKNS))
+ return (ISC_TRUE);
+
+ if (zone->type == dns_zone_master)
+ level = ISC_LOG_ERROR;
+ else
+ level = ISC_LOG_WARNING;
+
+ dns_fixedname_init(&fixed);
+ foundname = dns_fixedname_name(&fixed);
+
+ result = dns_db_find(db, name, NULL, dns_rdatatype_a,
+ 0, 0, NULL, foundname, NULL, NULL);
+ if (result == ISC_R_SUCCESS)
+ return (ISC_TRUE);
+
+ if (result == DNS_R_NXRRSET) {
+ result = dns_db_find(db, name, NULL, dns_rdatatype_aaaa,
+ 0, 0, NULL, foundname, NULL, NULL);
+ if (result == ISC_R_SUCCESS)
+ return (ISC_TRUE);
+ }
+
+ dns_name_format(name, namebuf, sizeof namebuf);
+ if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN ||
+ result == DNS_R_EMPTYNAME) {
+ dns_zone_log(zone, level,
+ "NS '%s' has no address records (A or AAAA)",
+ namebuf);
+ /* XXX950 Make fatal ISC_FALSE for 9.5.0. */
+ return (ISC_TRUE);
+ }
+
+ if (result == DNS_R_CNAME) {
+ dns_zone_log(zone, level, "NS '%s' is a CNAME (illegal)",
+ namebuf);
+ /* XXX950 Make fatal ISC_FALSE for 9.5.0. */
+ return (ISC_TRUE);
+ }
+
+ if (result == DNS_R_DNAME) {
+ dns_name_format(foundname, altbuf, sizeof altbuf);
+ dns_zone_log(zone, level,
+ "NS '%s' is below a DNAME '%s' (illegal)",
+ namebuf, altbuf);
+ /* XXX950 Make fatal ISC_FALSE for 9.5.0. */
+ return (ISC_TRUE);
+ }
+
+ return (ISC_TRUE);
+}
+
+static isc_result_t
+zone_count_ns_rr(dns_zone_t *zone, dns_db_t *db, dns_dbnode_t *node,
+ dns_dbversion_t *version, unsigned int *nscount,
+ unsigned int *errors)
+{
+ isc_result_t result;
+ unsigned int count = 0;
+ unsigned int ecount = 0;
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata;
+ dns_rdata_ns_t ns;
+
+ dns_rdataset_init(&rdataset);
+ result = dns_db_findrdataset(db, node, version, dns_rdatatype_ns,
+ dns_rdatatype_none, 0, &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND)
+ goto success;
+ if (result != ISC_R_SUCCESS)
+ goto invalidate_rdataset;
+
+ result = dns_rdataset_first(&rdataset);
+ while (result == ISC_R_SUCCESS) {
+ if (errors != NULL && zone->rdclass == dns_rdataclass_in &&
+ (zone->type == dns_zone_master ||
+ zone->type == dns_zone_slave)) {
+ dns_rdata_init(&rdata);
+ dns_rdataset_current(&rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &ns, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ if (dns_name_issubdomain(&ns.name, &zone->origin) &&
+ !zone_check_ns(zone, db, &ns.name))
+ ecount++;
+ }
+ count++;
+ result = dns_rdataset_next(&rdataset);
+ }
+ dns_rdataset_disassociate(&rdataset);
+
+ success:
+ if (nscount != NULL)
+ *nscount = count;
+ if (errors != NULL)
+ *errors = ecount;
+
+ result = ISC_R_SUCCESS;
+
+ invalidate_rdataset:
+ dns_rdataset_invalidate(&rdataset);
+
+ return (result);
+}
+
+static isc_result_t
+zone_load_soa_rr(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ unsigned int *soacount,
+ isc_uint32_t *serial, isc_uint32_t *refresh,
+ isc_uint32_t *retry, isc_uint32_t *expire,
+ isc_uint32_t *minimum)
+{
+ isc_result_t result;
+ unsigned int count;
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_soa_t soa;
+
+ dns_rdataset_init(&rdataset);
+ result = dns_db_findrdataset(db, node, version, dns_rdatatype_soa,
+ dns_rdatatype_none, 0, &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND) {
+ if (soacount != NULL)
+ *soacount = 0;
+ if (serial != NULL)
+ *serial = 0;
+ if (refresh != NULL)
+ *refresh = 0;
+ if (retry != NULL)
+ *retry = 0;
+ if (expire != NULL)
+ *expire = 0;
+ if (minimum != NULL)
+ *minimum = 0;
+ result = ISC_R_SUCCESS;
+ goto invalidate_rdataset;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto invalidate_rdataset;
+
+ count = 0;
+ result = dns_rdataset_first(&rdataset);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdata_init(&rdata);
+ dns_rdataset_current(&rdataset, &rdata);
+ count++;
+ if (count == 1) {
+ result = dns_rdata_tostruct(&rdata, &soa, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ }
+
+ result = dns_rdataset_next(&rdataset);
+ dns_rdata_reset(&rdata);
+ }
+ dns_rdataset_disassociate(&rdataset);
+
+ if (soacount != NULL)
+ *soacount = count;
+
+ if (count > 0) {
+ if (serial != NULL)
+ *serial = soa.serial;
+ if (refresh != NULL)
+ *refresh = soa.refresh;
+ if (retry != NULL)
+ *retry = soa.retry;
+ if (expire != NULL)
+ *expire = soa.expire;
+ if (minimum != NULL)
+ *minimum = soa.minimum;
+ }
+
+ result = ISC_R_SUCCESS;
+
+ invalidate_rdataset:
+ dns_rdataset_invalidate(&rdataset);
+
+ return (result);
+}
+
+/*
+ * zone must be locked.
+ */
+static isc_result_t
+zone_get_from_db(dns_zone_t *zone, dns_db_t *db, unsigned int *nscount,
+ unsigned int *soacount, isc_uint32_t *serial,
+ isc_uint32_t *refresh, isc_uint32_t *retry,
+ isc_uint32_t *expire, isc_uint32_t *minimum,
+ unsigned int *errors)
+{
+ dns_dbversion_t *version;
+ isc_result_t result;
+ isc_result_t answer = ISC_R_SUCCESS;
+ dns_dbnode_t *node;
+
+ REQUIRE(db != NULL);
+ REQUIRE(zone != NULL);
+
+ version = NULL;
+ dns_db_currentversion(db, &version);
+
+ node = NULL;
+ result = dns_db_findnode(db, &zone->origin, ISC_FALSE, &node);
+ if (result != ISC_R_SUCCESS) {
+ answer = result;
+ goto closeversion;
+ }
+
+ if (nscount != NULL || errors != NULL) {
+ result = zone_count_ns_rr(zone, db, node, version,
+ nscount, errors);
+ if (result != ISC_R_SUCCESS)
+ answer = result;
+ }
+
+ if (soacount != NULL || serial != NULL || refresh != NULL
+ || retry != NULL || expire != NULL || minimum != NULL) {
+ result = zone_load_soa_rr(db, node, version, soacount,
+ serial, refresh, retry, expire,
+ minimum);
+ if (result != ISC_R_SUCCESS)
+ answer = result;
+ }
+
+ dns_db_detachnode(db, &node);
+ closeversion:
+ dns_db_closeversion(db, &version, ISC_FALSE);
+
+ return (answer);
+}
+
+void
+dns_zone_attach(dns_zone_t *source, dns_zone_t **target) {
+ REQUIRE(DNS_ZONE_VALID(source));
+ REQUIRE(target != NULL && *target == NULL);
+ isc_refcount_increment(&source->erefs, NULL);
+ *target = source;
+}
+
+void
+dns_zone_detach(dns_zone_t **zonep) {
+ dns_zone_t *zone;
+ unsigned int refs;
+ isc_boolean_t free_now = ISC_FALSE;
+
+ REQUIRE(zonep != NULL && DNS_ZONE_VALID(*zonep));
+
+ zone = *zonep;
+
+ isc_refcount_decrement(&zone->erefs, &refs);
+
+ if (refs == 0) {
+ LOCK_ZONE(zone);
+ /*
+ * We just detached the last external reference.
+ */
+ if (zone->task != NULL) {
+ /*
+ * This zone is being managed. Post
+ * its control event and let it clean
+ * up synchronously in the context of
+ * its task.
+ */
+ isc_event_t *ev = &zone->ctlevent;
+ isc_task_send(zone->task, &ev);
+ } else {
+ /*
+ * This zone is not being managed; it has
+ * no task and can have no outstanding
+ * events. Free it immediately.
+ */
+ /*
+ * Unmanaged zones should not have non-null views;
+ * we have no way of detaching from the view here
+ * without causing deadlock because this code is called
+ * with the view already locked.
+ */
+ INSIST(zone->view == NULL);
+ free_now = ISC_TRUE;
+ }
+ UNLOCK_ZONE(zone);
+ }
+ *zonep = NULL;
+ if (free_now)
+ zone_free(zone);
+}
+
+void
+dns_zone_iattach(dns_zone_t *source, dns_zone_t **target) {
+ REQUIRE(DNS_ZONE_VALID(source));
+ REQUIRE(target != NULL && *target == NULL);
+ LOCK_ZONE(source);
+ zone_iattach(source, target);
+ UNLOCK_ZONE(source);
+}
+
+static void
+zone_iattach(dns_zone_t *source, dns_zone_t **target) {
+
+ /*
+ * 'source' locked by caller.
+ */
+ REQUIRE(LOCKED_ZONE(source));
+ REQUIRE(DNS_ZONE_VALID(source));
+ REQUIRE(target != NULL && *target == NULL);
+ INSIST(source->irefs + isc_refcount_current(&source->erefs) > 0);
+ source->irefs++;
+ INSIST(source->irefs != 0);
+ *target = source;
+}
+
+static void
+zone_idetach(dns_zone_t **zonep) {
+ dns_zone_t *zone;
+
+ /*
+ * 'zone' locked by caller.
+ */
+ REQUIRE(zonep != NULL && DNS_ZONE_VALID(*zonep));
+ zone = *zonep;
+ REQUIRE(LOCKED_ZONE(*zonep));
+ *zonep = NULL;
+
+ INSIST(zone->irefs > 0);
+ zone->irefs--;
+ INSIST(zone->irefs + isc_refcount_current(&zone->erefs) > 0);
+}
+
+void
+dns_zone_idetach(dns_zone_t **zonep) {
+ dns_zone_t *zone;
+ isc_boolean_t free_needed;
+
+ REQUIRE(zonep != NULL && DNS_ZONE_VALID(*zonep));
+ zone = *zonep;
+ *zonep = NULL;
+
+ LOCK_ZONE(zone);
+ INSIST(zone->irefs > 0);
+ zone->irefs--;
+ free_needed = exit_check(zone);
+ UNLOCK_ZONE(zone);
+ if (free_needed)
+ zone_free(zone);
+}
+
+isc_mem_t *
+dns_zone_getmctx(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->mctx);
+}
+
+dns_zonemgr_t *
+dns_zone_getmgr(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->zmgr);
+}
+
+void
+dns_zone_setflag(dns_zone_t *zone, unsigned int flags, isc_boolean_t value) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (value)
+ DNS_ZONE_SETFLAG(zone, flags);
+ else
+ DNS_ZONE_CLRFLAG(zone, flags);
+ UNLOCK_ZONE(zone);
+}
+
+void
+dns_zone_setoption(dns_zone_t *zone, unsigned int option, isc_boolean_t value)
+{
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (value)
+ zone->options |= option;
+ else
+ zone->options &= ~option;
+ UNLOCK_ZONE(zone);
+}
+
+unsigned int
+dns_zone_getoptions(dns_zone_t *zone) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->options);
+}
+
+isc_result_t
+dns_zone_setxfrsource4(dns_zone_t *zone, const isc_sockaddr_t *xfrsource) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ zone->xfrsource4 = *xfrsource;
+ UNLOCK_ZONE(zone);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_sockaddr_t *
+dns_zone_getxfrsource4(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ return (&zone->xfrsource4);
+}
+
+isc_result_t
+dns_zone_setxfrsource6(dns_zone_t *zone, const isc_sockaddr_t *xfrsource) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ zone->xfrsource6 = *xfrsource;
+ UNLOCK_ZONE(zone);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_sockaddr_t *
+dns_zone_getxfrsource6(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ return (&zone->xfrsource6);
+}
+
+isc_result_t
+dns_zone_setaltxfrsource4(dns_zone_t *zone,
+ const isc_sockaddr_t *altxfrsource)
+{
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ zone->altxfrsource4 = *altxfrsource;
+ UNLOCK_ZONE(zone);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_sockaddr_t *
+dns_zone_getaltxfrsource4(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ return (&zone->altxfrsource4);
+}
+
+isc_result_t
+dns_zone_setaltxfrsource6(dns_zone_t *zone,
+ const isc_sockaddr_t *altxfrsource)
+{
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ zone->altxfrsource6 = *altxfrsource;
+ UNLOCK_ZONE(zone);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_sockaddr_t *
+dns_zone_getaltxfrsource6(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ return (&zone->altxfrsource6);
+}
+
+isc_result_t
+dns_zone_setnotifysrc4(dns_zone_t *zone, const isc_sockaddr_t *notifysrc) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ zone->notifysrc4 = *notifysrc;
+ UNLOCK_ZONE(zone);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_sockaddr_t *
+dns_zone_getnotifysrc4(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ return (&zone->notifysrc4);
+}
+
+isc_result_t
+dns_zone_setnotifysrc6(dns_zone_t *zone, const isc_sockaddr_t *notifysrc) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ zone->notifysrc6 = *notifysrc;
+ UNLOCK_ZONE(zone);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_sockaddr_t *
+dns_zone_getnotifysrc6(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ return (&zone->notifysrc6);
+}
+
+isc_result_t
+dns_zone_setalsonotify(dns_zone_t *zone, const isc_sockaddr_t *notify,
+ isc_uint32_t count)
+{
+ isc_sockaddr_t *new;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(count == 0 || notify != NULL);
+
+ LOCK_ZONE(zone);
+ if (zone->notify != NULL) {
+ isc_mem_put(zone->mctx, zone->notify,
+ zone->notifycnt * sizeof(*new));
+ zone->notify = NULL;
+ zone->notifycnt = 0;
+ }
+ if (count != 0) {
+ new = isc_mem_get(zone->mctx, count * sizeof(*new));
+ if (new == NULL) {
+ UNLOCK_ZONE(zone);
+ return (ISC_R_NOMEMORY);
+ }
+ memcpy(new, notify, count * sizeof(*new));
+ zone->notify = new;
+ zone->notifycnt = count;
+ }
+ UNLOCK_ZONE(zone);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_zone_setmasters(dns_zone_t *zone, const isc_sockaddr_t *masters,
+ isc_uint32_t count)
+{
+ isc_result_t result;
+
+ result = dns_zone_setmasterswithkeys(zone, masters, NULL, count);
+ return (result);
+}
+
+static isc_boolean_t
+same_masters(const isc_sockaddr_t *old, const isc_sockaddr_t *new,
+ isc_uint32_t count)
+{
+ unsigned int i;
+
+ for (i = 0; i < count; i++)
+ if (!isc_sockaddr_equal(&old[i], &new[i]))
+ return (ISC_FALSE);
+ return (ISC_TRUE);
+}
+
+static isc_boolean_t
+same_keynames(dns_name_t **old, dns_name_t **new, isc_uint32_t count) {
+ unsigned int i;
+
+ if (old == NULL && new == NULL)
+ return (ISC_TRUE);
+ if (old == NULL || new == NULL)
+ return (ISC_FALSE);
+
+ for (i = 0; i < count; i++) {
+ if (old[i] == NULL && new[i] == NULL)
+ continue;
+ if (old[i] == NULL || new[i] == NULL ||
+ !dns_name_equal(old[i], new[i]))
+ return (ISC_FALSE);
+ }
+ return (ISC_TRUE);
+}
+
+isc_result_t
+dns_zone_setmasterswithkeys(dns_zone_t *zone,
+ const isc_sockaddr_t *masters,
+ dns_name_t **keynames,
+ isc_uint32_t count)
+{
+ isc_sockaddr_t *new;
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_name_t **newname;
+ isc_boolean_t *newok;
+ unsigned int i;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(count == 0 || masters != NULL);
+ if (keynames != NULL) {
+ REQUIRE(count != 0);
+ }
+
+ LOCK_ZONE(zone);
+ /*
+ * The refresh code assumes that 'masters' wouldn't change under it.
+ * If it will change then kill off any current refresh in progress
+ * and update the masters info. If it won't change then we can just
+ * unlock and exit.
+ */
+ if (count != zone->masterscnt ||
+ !same_masters(zone->masters, masters, count) ||
+ !same_keynames(zone->masterkeynames, keynames, count)) {
+ if (zone->request != NULL)
+ dns_request_cancel(zone->request);
+ } else
+ goto unlock;
+ if (zone->masters != NULL) {
+ isc_mem_put(zone->mctx, zone->masters,
+ zone->masterscnt * sizeof(*new));
+ zone->masters = NULL;
+ }
+ if (zone->masterkeynames != NULL) {
+ for (i = 0; i < zone->masterscnt; i++) {
+ if (zone->masterkeynames[i] != NULL) {
+ dns_name_free(zone->masterkeynames[i],
+ zone->mctx);
+ isc_mem_put(zone->mctx,
+ zone->masterkeynames[i],
+ sizeof(dns_name_t));
+ zone->masterkeynames[i] = NULL;
+ }
+ }
+ isc_mem_put(zone->mctx, zone->masterkeynames,
+ zone->masterscnt * sizeof(dns_name_t *));
+ zone->masterkeynames = NULL;
+ }
+ if (zone->mastersok != NULL) {
+ isc_mem_put(zone->mctx, zone->mastersok,
+ zone->masterscnt * sizeof(isc_boolean_t));
+ zone->mastersok = NULL;
+ }
+ zone->masterscnt = 0;
+ /*
+ * If count == 0, don't allocate any space for masters, mastersok or
+ * keynames so internally, those pointers are NULL if count == 0
+ */
+ if (count == 0)
+ goto unlock;
+
+ /*
+ * masters must countain count elements!
+ */
+ new = isc_mem_get(zone->mctx, count * sizeof(*new));
+ if (new == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto unlock;
+ }
+ memcpy(new, masters, count * sizeof(*new));
+
+ /*
+ * Similarly for mastersok.
+ */
+ newok = isc_mem_get(zone->mctx, count * sizeof(*newok));
+ if (newok == NULL) {
+ result = ISC_R_NOMEMORY;
+ isc_mem_put(zone->mctx, new, count * sizeof(*new));
+ goto unlock;
+ };
+ for (i = 0; i < count; i++)
+ newok[i] = ISC_FALSE;
+
+ /*
+ * if keynames is non-NULL, it must contain count elements!
+ */
+ newname = NULL;
+ if (keynames != NULL) {
+ newname = isc_mem_get(zone->mctx, count * sizeof(*newname));
+ if (newname == NULL) {
+ result = ISC_R_NOMEMORY;
+ isc_mem_put(zone->mctx, new, count * sizeof(*new));
+ isc_mem_put(zone->mctx, newok, count * sizeof(*newok));
+ goto unlock;
+ }
+ for (i = 0; i < count; i++)
+ newname[i] = NULL;
+ for (i = 0; i < count; i++) {
+ if (keynames[i] != NULL) {
+ newname[i] = isc_mem_get(zone->mctx,
+ sizeof(dns_name_t));
+ if (newname[i] == NULL)
+ goto allocfail;
+ dns_name_init(newname[i], NULL);
+ result = dns_name_dup(keynames[i], zone->mctx,
+ newname[i]);
+ if (result != ISC_R_SUCCESS) {
+ allocfail:
+ for (i = 0; i < count; i++)
+ if (newname[i] != NULL)
+ dns_name_free(
+ newname[i],
+ zone->mctx);
+ isc_mem_put(zone->mctx, new,
+ count * sizeof(*new));
+ isc_mem_put(zone->mctx, newok,
+ count * sizeof(*newok));
+ isc_mem_put(zone->mctx, newname,
+ count * sizeof(*newname));
+ goto unlock;
+ }
+ }
+ }
+ }
+
+ /*
+ * Everything is ok so attach to the zone.
+ */
+ zone->masters = new;
+ zone->mastersok = newok;
+ zone->masterkeynames = newname;
+ zone->masterscnt = count;
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NOMASTERS);
+
+ unlock:
+ UNLOCK_ZONE(zone);
+ return (result);
+}
+
+isc_result_t
+dns_zone_getdb(dns_zone_t *zone, dns_db_t **dpb) {
+ isc_result_t result = ISC_R_SUCCESS;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+ if (zone->db == NULL)
+ result = DNS_R_NOTLOADED;
+ else
+ dns_db_attach(zone->db, dpb);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+
+ return (result);
+}
+
+/*
+ * Co-ordinates the starting of routine jobs.
+ */
+
+void
+dns_zone_maintenance(dns_zone_t *zone) {
+ const char me[] = "dns_zone_maintenance";
+ isc_time_t now;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ ENTER;
+
+ LOCK_ZONE(zone);
+ TIME_NOW(&now);
+ zone_settimer(zone, &now);
+ UNLOCK_ZONE(zone);
+}
+
+static inline isc_boolean_t
+was_dumping(dns_zone_t *zone) {
+ isc_boolean_t dumping;
+
+ REQUIRE(LOCKED_ZONE(zone));
+
+ dumping = DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DUMPING);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_DUMPING);
+ if (!dumping) {
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NEEDDUMP);
+ isc_time_settoepoch(&zone->dumptime);
+ }
+ return (dumping);
+}
+
+#define MAXZONEKEYS 10
+
+static isc_result_t
+do_one_tuple(dns_difftuple_t **tuple, dns_db_t *db, dns_dbversion_t *ver,
+ dns_diff_t *diff)
+{
+ dns_diff_t temp_diff;
+ isc_result_t result;
+
+ /*
+ * Create a singleton diff.
+ */
+ dns_diff_init(diff->mctx, &temp_diff);
+ temp_diff.resign = diff->resign;
+ ISC_LIST_APPEND(temp_diff.tuples, *tuple, link);
+
+ /*
+ * Apply it to the database.
+ */
+ result = dns_diff_apply(&temp_diff, db, ver);
+ ISC_LIST_UNLINK(temp_diff.tuples, *tuple, link);
+ if (result != ISC_R_SUCCESS) {
+ dns_difftuple_free(tuple);
+ return (result);
+ }
+
+ /*
+ * Merge it into the current pending journal entry.
+ */
+ dns_diff_appendminimal(diff, tuple);
+
+ /*
+ * Do not clear temp_diff.
+ */
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+increment_soa_serial(dns_db_t *db, dns_dbversion_t *ver,
+ dns_diff_t *diff, isc_mem_t *mctx)
+{
+ dns_difftuple_t *deltuple = NULL;
+ dns_difftuple_t *addtuple = NULL;
+ isc_uint32_t serial;
+ isc_result_t result;
+
+ CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_DEL, &deltuple));
+ CHECK(dns_difftuple_copy(deltuple, &addtuple));
+ addtuple->op = DNS_DIFFOP_ADD;
+
+ serial = dns_soa_getserial(&addtuple->rdata);
+
+ /* RFC1982 */
+ serial = (serial + 1) & 0xFFFFFFFF;
+ if (serial == 0)
+ serial = 1;
+
+ dns_soa_setserial(serial, &addtuple->rdata);
+ CHECK(do_one_tuple(&deltuple, db, ver, diff));
+ CHECK(do_one_tuple(&addtuple, db, ver, diff));
+ result = ISC_R_SUCCESS;
+
+ failure:
+ if (addtuple != NULL)
+ dns_difftuple_free(&addtuple);
+ if (deltuple != NULL)
+ dns_difftuple_free(&deltuple);
+ return (result);
+}
+
+static isc_result_t
+update_one_rr(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff,
+ dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl,
+ dns_rdata_t *rdata)
+{
+ dns_difftuple_t *tuple = NULL;
+ isc_result_t result;
+ result = dns_difftuple_create(diff->mctx, op,
+ name, ttl, rdata, &tuple);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ return (do_one_tuple(&tuple, db, ver, diff));
+}
+
+static isc_boolean_t
+ksk_sanity(dns_db_t *db, dns_dbversion_t *ver) {
+ isc_boolean_t ret = ISC_FALSE;
+ isc_boolean_t have_ksk = ISC_FALSE, have_nonksk = ISC_FALSE;
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_dnskey_t dnskey;
+
+ dns_rdataset_init(&rdataset);
+ CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node));
+ CHECK(dns_db_findrdataset(db, node, ver, dns_rdatatype_dnskey, 0, 0,
+ &rdataset, NULL));
+ CHECK(dns_rdataset_first(&rdataset));
+ while (result == ISC_R_SUCCESS && (!have_ksk || !have_nonksk)) {
+ dns_rdataset_current(&rdataset, &rdata);
+ CHECK(dns_rdata_tostruct(&rdata, &dnskey, NULL));
+ if ((dnskey.flags & (DNS_KEYFLAG_OWNERMASK|DNS_KEYTYPE_NOAUTH))
+ == DNS_KEYOWNER_ZONE) {
+ if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0)
+ have_ksk = ISC_TRUE;
+ else
+ have_nonksk = ISC_TRUE;
+ }
+ dns_rdata_reset(&rdata);
+ result = dns_rdataset_next(&rdataset);
+ }
+ if (have_ksk && have_nonksk)
+ ret = ISC_TRUE;
+ failure:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (ret);
+}
+
+static isc_result_t
+find_zone_keys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver,
+ isc_mem_t *mctx, unsigned int maxkeys,
+ dst_key_t **keys, unsigned int *nkeys)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ const char *directory = dns_zone_getkeydirectory(zone);
+ CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node));
+ result = dns_dnssec_findzonekeys2(db, ver, node, dns_db_origin(db),
+ directory, mctx, maxkeys, keys,
+ nkeys);
+ if (result == ISC_R_NOTFOUND)
+ result = ISC_R_SUCCESS;
+ failure:
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+static isc_result_t
+offline(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff, dns_name_t *name,
+ dns_ttl_t ttl, dns_rdata_t *rdata)
+{
+ isc_result_t result;
+
+ if ((rdata->flags & DNS_RDATA_OFFLINE) != 0)
+ return (ISC_R_SUCCESS);
+ result = update_one_rr(db, ver, diff, DNS_DIFFOP_DELRESIGN,
+ name, ttl, rdata);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ rdata->flags |= DNS_RDATA_OFFLINE;
+ result = update_one_rr(db, ver, diff, DNS_DIFFOP_ADDRESIGN,
+ name, ttl, rdata);
+ return (result);
+}
+
+static void
+set_key_expiry_warning(dns_zone_t *zone, isc_stdtime_t when, isc_stdtime_t now)
+{
+ unsigned int delta;
+
+ zone->key_expiry = when;
+ if (when <= now) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "DNSKEY RRSIG(s) have expired");
+ isc_time_settoepoch(&zone->keywarntime);
+ } else if (when < now + 7 * 24 * 3600) {
+ dns_zone_log(zone, ISC_LOG_WARNING,
+ "DNSKEY RRSIG(s) will expire at %u",
+ when); /* XXXMPA convert to date. */
+ delta = when - now;
+ delta--; /* loop prevention */
+ delta /= 24 * 3600; /* to whole days */
+ delta *= 24 * 3600; /* to seconds */
+ isc_time_set(&zone->keywarntime, when - delta, 0);
+ } else {
+ dns_zone_log(zone, ISC_LOG_NOTICE, /* XXMPA ISC_LOG_DEBUG(1) */
+ "setting keywarntime to %u - 7 days",
+ when); /* XXXMPA convert to date. */
+ isc_time_set(&zone->keywarntime, when - 7 * 24 * 3600, 0);
+ }
+}
+
+/*
+ * Delete expired RRsigs and any RRsigs we are about to re-sign.
+ * See also update.c:del_keysigs().
+ */
+static isc_result_t
+del_sigs(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ dns_rdatatype_t type, dns_diff_t *diff, dst_key_t **keys,
+ unsigned int nkeys, isc_stdtime_t now)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ unsigned int i;
+ dns_rdata_rrsig_t rrsig;
+ isc_boolean_t found;
+ isc_stdtime_t warn = 0, maybe = 0;
+
+ dns_rdataset_init(&rdataset);
+
+ if (type == dns_rdatatype_nsec3)
+ result = dns_db_findnsec3node(db, name, ISC_FALSE, &node);
+ else
+ result = dns_db_findnode(db, name, ISC_FALSE, &node);
+ if (result == ISC_R_NOTFOUND)
+ return (ISC_R_SUCCESS);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ result = dns_db_findrdataset(db, node, ver, dns_rdatatype_rrsig, type,
+ (isc_stdtime_t) 0, &rdataset, NULL);
+ dns_db_detachnode(db, &node);
+
+ if (result == ISC_R_NOTFOUND)
+ return (ISC_R_SUCCESS);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset)) {
+ dns_rdataset_current(&rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &rrsig, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ if (type != dns_rdatatype_dnskey) {
+ result = update_one_rr(db, ver, diff,
+ DNS_DIFFOP_DEL, name,
+ rdataset.ttl, &rdata);
+ dns_rdata_reset(&rdata);
+ if (result != ISC_R_SUCCESS)
+ break;
+ continue;
+ }
+
+ /*
+ * RRSIG(DNSKEY) requires special processing.
+ */
+ found = ISC_FALSE;
+ for (i = 0; i < nkeys; i++) {
+ if (rrsig.algorithm == dst_key_alg(keys[i]) &&
+ rrsig.keyid == dst_key_id(keys[i])) {
+ found = ISC_TRUE;
+ /*
+ * Mark offline RRSIG(DNSKEY).
+ * We want the earliest offline expire time
+ * iff there is a new offline signature.
+ */
+ if (!dst_key_isprivate(keys[i])) {
+ if (warn != 0 &&
+ warn > rrsig.timeexpire)
+ warn = rrsig.timeexpire;
+ if (rdata.flags & DNS_RDATA_OFFLINE) {
+ if (maybe == 0 ||
+ maybe > rrsig.timeexpire)
+ maybe =
+ rrsig.timeexpire;
+ break;
+ }
+ if (warn == 0)
+ warn = maybe;
+ if (warn == 0 ||
+ warn > rrsig.timeexpire)
+ warn = rrsig.timeexpire;
+ result = offline(db, ver, diff, name,
+ rdataset.ttl, &rdata);
+ break;
+ }
+ result = update_one_rr(db, ver, diff,
+ DNS_DIFFOP_DEL,
+ name, rdataset.ttl,
+ &rdata);
+ break;
+ }
+ }
+ /*
+ * If there is not a matching DNSKEY then
+ * delete the RRSIG.
+ */
+ if (!found)
+ result = update_one_rr(db, ver, diff, DNS_DIFFOP_DEL,
+ name, rdataset.ttl, &rdata);
+ dns_rdata_reset(&rdata);
+ if (result != ISC_R_SUCCESS)
+ break;
+ }
+ dns_rdataset_disassociate(&rdataset);
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ if (warn != 0)
+ set_key_expiry_warning(zone, warn, now);
+ failure:
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+static isc_result_t
+add_sigs(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ dns_rdatatype_t type, dns_diff_t *diff, dst_key_t **keys,
+ unsigned int nkeys, isc_mem_t *mctx, isc_stdtime_t inception,
+ isc_stdtime_t expire, isc_boolean_t check_ksk)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t rdataset;
+ dns_rdata_t sig_rdata = DNS_RDATA_INIT;
+ unsigned char data[1024]; /* XXX */
+ isc_buffer_t buffer;
+ unsigned int i;
+
+ dns_rdataset_init(&rdataset);
+ isc_buffer_init(&buffer, data, sizeof(data));
+
+ if (type == dns_rdatatype_nsec3)
+ result = dns_db_findnsec3node(db, name, ISC_FALSE, &node);
+ else
+ result = dns_db_findnode(db, name, ISC_FALSE, &node);
+ if (result == ISC_R_NOTFOUND)
+ return (ISC_R_SUCCESS);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ result = dns_db_findrdataset(db, node, ver, type, 0,
+ (isc_stdtime_t) 0, &rdataset, NULL);
+ dns_db_detachnode(db, &node);
+ if (result == ISC_R_NOTFOUND)
+ return (ISC_R_SUCCESS);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ for (i = 0; i < nkeys; i++) {
+ if (check_ksk && type != dns_rdatatype_dnskey &&
+ (dst_key_flags(keys[i]) & DNS_KEYFLAG_KSK) != 0)
+ continue;
+ if (!dst_key_isprivate(keys[i]))
+ continue;
+ /* Calculate the signature, creating a RRSIG RDATA. */
+ CHECK(dns_dnssec_sign(name, &rdataset, keys[i],
+ &inception, &expire,
+ mctx, &buffer, &sig_rdata));
+ /* Update the database and journal with the RRSIG. */
+ /* XXX inefficient - will cause dataset merging */
+ CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_ADDRESIGN,
+ name, rdataset.ttl, &sig_rdata));
+ dns_rdata_reset(&sig_rdata);
+ }
+
+ failure:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+static void
+zone_resigninc(dns_zone_t *zone) {
+ const char *journalfile;
+ dns_db_t *db = NULL;
+ dns_dbversion_t *version = NULL;
+ dns_diff_t sig_diff;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ dns_rdataset_t rdataset;
+ dns_rdatatype_t covers;
+ dst_key_t *zone_keys[MAXZONEKEYS];
+ isc_boolean_t check_ksk;
+ isc_result_t result;
+ isc_stdtime_t now, inception, soaexpire, expire, stop;
+ isc_uint32_t jitter;
+ unsigned int i;
+ unsigned int nkeys = 0;
+ unsigned int resign;
+
+ dns_rdataset_init(&rdataset);
+ dns_fixedname_init(&fixed);
+ dns_diff_init(zone->mctx, &sig_diff);
+ sig_diff.resign = zone->sigresigninginterval;
+
+ /*
+ * Updates are disabled. Pause for 5 minutes.
+ */
+ if (zone->update_disabled) {
+ result = ISC_R_FAILURE;
+ goto failure;
+ }
+
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+ dns_db_attach(zone->db, &db);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+
+ result = dns_db_newversion(db, &version);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_resigninc:dns_db_newversion -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ result = find_zone_keys(zone, db, version, zone->mctx, MAXZONEKEYS,
+ zone_keys, &nkeys);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_resigninc:find_zone_keys -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ isc_stdtime_get(&now);
+ inception = now - 3600; /* Allow for clock skew. */
+ soaexpire = now + dns_zone_getsigvalidityinterval(zone);
+ /*
+ * Spread out signatures over time if they happen to be
+ * clumped. We don't do this for each add_sigs() call as
+ * we still want some clustering to occur.
+ */
+ isc_random_get(&jitter);
+ expire = soaexpire - jitter % 3600;
+ stop = now + 5;
+
+ check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK);
+ if (check_ksk)
+ check_ksk = ksk_sanity(db, version);
+
+ name = dns_fixedname_name(&fixed);
+ result = dns_db_getsigningtime(db, &rdataset, name);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_resigninc:dns_db_getsigningtime -> %s\n",
+ dns_result_totext(result));
+ }
+
+ i = 0;
+ while (result == ISC_R_SUCCESS) {
+ resign = rdataset.resign;
+ covers = rdataset.covers;
+ /*
+ * Stop if we hit the SOA as that means we have walked the
+ * entire zone. The SOA record should always be the most
+ * recent signature.
+ */
+ /* XXXMPA increase number of RRsets signed pre call */
+ if (covers == dns_rdatatype_soa || i++ > zone->signatures ||
+ resign > stop) {
+ /*
+ * Ensure that we don't loop resigning the SOA.
+ */
+ if (covers == dns_rdatatype_soa)
+ dns_db_resigned(db, &rdataset, version);
+ dns_rdataset_disassociate(&rdataset);
+ break;
+ }
+
+ dns_db_resigned(db, &rdataset, version);
+ dns_rdataset_disassociate(&rdataset);
+
+ result = del_sigs(zone, db, version, name, covers, &sig_diff,
+ zone_keys, nkeys, now);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_resigninc:del_sigs -> %s\n",
+ dns_result_totext(result));
+ break;
+ }
+ result = add_sigs(db, version, name, covers, &sig_diff,
+ zone_keys, nkeys, zone->mctx, inception,
+ expire, check_ksk);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_resigninc:add_sigs -> %s\n",
+ dns_result_totext(result));
+ break;
+ }
+ result = dns_db_getsigningtime(db, &rdataset,
+ dns_fixedname_name(&fixed));
+ if (nkeys == 0 && result == ISC_R_NOTFOUND) {
+ result = ISC_R_SUCCESS;
+ break;
+ }
+ if (result != ISC_R_SUCCESS)
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_resigninc:dns_db_getsigningtime -> %s\n",
+ dns_result_totext(result));
+ }
+
+ if (result != ISC_R_NOMORE && result != ISC_R_SUCCESS)
+ goto failure;
+
+ result = del_sigs(zone, db, version, &zone->origin, dns_rdatatype_soa,
+ &sig_diff, zone_keys, nkeys, now);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_resigninc:del_sigs -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ result = increment_soa_serial(db, version, &sig_diff, zone->mctx);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_resigninc:increment_soa_serial -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ /*
+ * Generate maximum life time signatures so that the above loop
+ * termination is sensible.
+ */
+ result = add_sigs(db, version, &zone->origin, dns_rdatatype_soa,
+ &sig_diff, zone_keys, nkeys, zone->mctx, inception,
+ soaexpire, check_ksk);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_resigninc:add_sigs -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ journalfile = dns_zone_getjournal(zone);
+ if (journalfile != NULL) {
+ dns_journal_t *journal = NULL;
+ result = dns_journal_open(zone->mctx, journalfile,
+ ISC_TRUE, &journal);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_resigninc:dns_journal_open -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ result = dns_journal_write_transaction(journal, &sig_diff);
+ dns_journal_destroy(&journal);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_resigninc:dns_journal_write_transaction -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+ }
+
+ /*
+ * Everything has succeeded. Commit the changes.
+ */
+ dns_db_closeversion(db, &version, ISC_TRUE);
+
+ failure:
+ dns_diff_clear(&sig_diff);
+ for (i = 0; i < nkeys; i++)
+ dst_key_free(&zone_keys[i]);
+ if (version != NULL) {
+ dns_db_closeversion(zone->db, &version, ISC_FALSE);
+ dns_db_detach(&db);
+ } else if (db != NULL)
+ dns_db_detach(&db);
+ if (result == ISC_R_SUCCESS) {
+ set_resigntime(zone);
+ LOCK_ZONE(zone);
+ zone_needdump(zone, DNS_DUMP_DELAY);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY);
+ UNLOCK_ZONE(zone);
+ } else {
+ /*
+ * Something failed. Retry in 5 minutes.
+ */
+ isc_interval_t ival;
+ isc_interval_set(&ival, 300, 0);
+ isc_time_nowplusinterval(&zone->resigntime, &ival);
+ }
+}
+
+static isc_result_t
+next_active(dns_db_t *db, dns_dbversion_t *version, dns_name_t *oldname,
+ dns_name_t *newname, isc_boolean_t bottom)
+{
+ isc_result_t result;
+ dns_dbiterator_t *dbit = NULL;
+ dns_rdatasetiter_t *rdsit = NULL;
+ dns_dbnode_t *node = NULL;
+
+ CHECK(dns_db_createiterator(db, DNS_DB_NONSEC3, &dbit));
+ CHECK(dns_dbiterator_seek(dbit, oldname));
+ do {
+ result = dns_dbiterator_next(dbit);
+ if (result == ISC_R_NOMORE)
+ CHECK(dns_dbiterator_first(dbit));
+ CHECK(dns_dbiterator_current(dbit, &node, newname));
+ if (bottom && dns_name_issubdomain(newname, oldname) &&
+ !dns_name_equal(newname, oldname)) {
+ dns_db_detachnode(db, &node);
+ continue;
+ }
+ /*
+ * Is this node empty?
+ */
+ CHECK(dns_db_allrdatasets(db, node, version, 0, &rdsit));
+ result = dns_rdatasetiter_first(rdsit);
+ dns_db_detachnode(db, &node);
+ dns_rdatasetiter_destroy(&rdsit);
+ if (result != ISC_R_NOMORE)
+ break;
+ } while (1);
+ failure:
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (dbit != NULL)
+ dns_dbiterator_destroy(&dbit);
+ return (result);
+}
+
+static void
+set_bit(unsigned char *array, unsigned int index) {
+ unsigned int shift, mask;
+
+ shift = 7 - (index % 8);
+ mask = 1 << shift;
+
+ array[index / 8] |= mask;
+}
+
+static isc_boolean_t
+signed_with_key(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ dns_rdatatype_t type, dst_key_t *key)
+{
+ isc_result_t result;
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_rrsig_t rrsig;
+
+ dns_rdataset_init(&rdataset);
+ result = dns_db_findrdataset(db, node, version, dns_rdatatype_rrsig,
+ type, 0, &rdataset, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_FALSE);
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset)) {
+ dns_rdataset_current(&rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &rrsig, NULL);
+ INSIST(result == ISC_R_SUCCESS);
+ if (rrsig.algorithm == dst_key_alg(key) &&
+ rrsig.keyid == dst_key_id(key)) {
+ dns_rdataset_disassociate(&rdataset);
+ return (ISC_TRUE);
+ }
+ dns_rdata_reset(&rdata);
+ }
+ dns_rdataset_disassociate(&rdataset);
+ return (ISC_FALSE);
+}
+
+static isc_result_t
+add_nsec(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
+ dns_dbnode_t *node, dns_ttl_t ttl, isc_boolean_t bottom,
+ dns_diff_t *diff)
+{
+ dns_fixedname_t fixed;
+ dns_name_t *next;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_result_t result;
+ unsigned char nsecbuffer[DNS_NSEC_BUFFERSIZE];
+
+ dns_fixedname_init(&fixed);
+ next = dns_fixedname_name(&fixed);
+
+ CHECK(next_active(db, version, name, next, bottom));
+ CHECK(dns_nsec_buildrdata(db, version, node, next, nsecbuffer,
+ &rdata));
+ if (dns_name_equal(dns_db_origin(db), name)) {
+ /*
+ * Set the OPT bit to indicate that this is a
+ * partially secure zone.
+ */
+ isc_region_t region;
+
+ dns_rdata_toregion(&rdata, &region);
+ dns_name_fromregion(next, &region);
+ isc_region_consume(&region, next->length);
+ INSIST(region.length > (2 + dns_rdatatype_opt / 8) &&
+ region.base[0] == 0 &&
+ region.base[1] > dns_rdatatype_opt / 8);
+ set_bit(region.base + 2, dns_rdatatype_opt);
+ }
+ CHECK(update_one_rr(db, version, diff, DNS_DIFFOP_ADD, name, ttl,
+ &rdata));
+ failure:
+ return (result);
+}
+
+static isc_result_t
+sign_a_node(dns_db_t *db, dns_name_t *name, dns_dbnode_t *node,
+ dns_dbversion_t *version, isc_boolean_t build_nsec3,
+ isc_boolean_t build_nsec, dst_key_t *key,
+ isc_stdtime_t inception, isc_stdtime_t expire,
+ unsigned int minimum, isc_boolean_t is_ksk,
+ isc_boolean_t *delegation, dns_diff_t *diff,
+ isc_int32_t *signatures, isc_mem_t *mctx)
+{
+ isc_result_t result;
+ dns_rdatasetiter_t *iterator = NULL;
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_buffer_t buffer;
+ unsigned char data[1024];
+ isc_boolean_t seen_soa, seen_ns, seen_rr, seen_dname, seen_nsec,
+ seen_nsec3, seen_ds;
+ isc_boolean_t bottom;
+
+ result = dns_db_allrdatasets(db, node, version, 0, &iterator);
+ if (result != ISC_R_SUCCESS) {
+ if (result == ISC_R_NOTFOUND)
+ result = ISC_R_SUCCESS;
+ return (result);
+ }
+ dns_rdataset_init(&rdataset);
+ isc_buffer_init(&buffer, data, sizeof(data));
+ seen_rr = seen_soa = seen_ns = seen_dname = seen_nsec =
+ seen_nsec3 = seen_ds = ISC_FALSE;
+ for (result = dns_rdatasetiter_first(iterator);
+ result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(iterator)) {
+ dns_rdatasetiter_current(iterator, &rdataset);
+ if (rdataset.type == dns_rdatatype_soa)
+ seen_soa = ISC_TRUE;
+ else if (rdataset.type == dns_rdatatype_ns)
+ seen_ns = ISC_TRUE;
+ else if (rdataset.type == dns_rdatatype_ds)
+ seen_ds = ISC_TRUE;
+ else if (rdataset.type == dns_rdatatype_dname)
+ seen_dname = ISC_TRUE;
+ else if (rdataset.type == dns_rdatatype_nsec)
+ seen_nsec = ISC_TRUE;
+ else if (rdataset.type == dns_rdatatype_nsec3)
+ seen_nsec3 = ISC_TRUE;
+ seen_rr = ISC_TRUE;
+ dns_rdataset_disassociate(&rdataset);
+ }
+ if (result != ISC_R_NOMORE)
+ goto failure;
+ if (seen_ns && !seen_soa)
+ *delegation = ISC_TRUE;
+ /*
+ * Going from insecure to NSEC3.
+ * Don't generate NSEC3 records for NSEC3 records.
+ */
+ if (build_nsec3 && !seen_nsec3 && seen_rr) {
+ isc_boolean_t unsecure = !seen_ds && seen_ns && !seen_soa;
+ CHECK(dns_nsec3_addnsec3s(db, version, name, minimum,
+ unsecure, diff));
+ (*signatures)--;
+ }
+ /*
+ * Going from insecure to NSEC.
+ * Don't generate NSEC records for NSEC3 records.
+ */
+ if (build_nsec && !seen_nsec3 && !seen_nsec && seen_rr) {
+ /* Build and add NSEC. */
+ bottom = (seen_ns && !seen_soa) || seen_dname;
+ CHECK(add_nsec(db, version, name, node, minimum, bottom, diff));
+ /* Count a NSEC generation as a signature generation. */
+ (*signatures)--;
+ }
+ result = dns_rdatasetiter_first(iterator);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdatasetiter_current(iterator, &rdataset);
+ if (rdataset.type == dns_rdatatype_soa ||
+ rdataset.type == dns_rdatatype_rrsig)
+ goto next_rdataset;
+ if (is_ksk && rdataset.type != dns_rdatatype_dnskey)
+ goto next_rdataset;
+ if (*delegation && !dns_rdatatype_atparent(rdataset.type))
+ goto next_rdataset;
+ if (signed_with_key(db, node, version, rdataset.type, key))
+ goto next_rdataset;
+ /* Calculate the signature, creating a RRSIG RDATA. */
+ CHECK(dns_dnssec_sign(name, &rdataset, key, &inception,
+ &expire, mctx, &buffer, &rdata));
+ /* Update the database and journal with the RRSIG. */
+ /* XXX inefficient - will cause dataset merging */
+ CHECK(update_one_rr(db, version, diff, DNS_DIFFOP_ADDRESIGN,
+ name, rdataset.ttl, &rdata));
+ dns_rdata_reset(&rdata);
+ (*signatures)--;
+ next_rdataset:
+ dns_rdataset_disassociate(&rdataset);
+ result = dns_rdatasetiter_next(iterator);
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ if (seen_dname)
+ *delegation = ISC_TRUE;
+failure:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ if (iterator != NULL)
+ dns_rdatasetiter_destroy(&iterator);
+ return (result);
+}
+
+static isc_result_t
+updatesecure(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
+ dns_ttl_t minimum, isc_boolean_t *secureupdated, dns_diff_t *diff)
+{
+ isc_result_t result;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ unsigned char nsecbuffer[DNS_NSEC_BUFFERSIZE];
+ dns_rdataset_t rdataset;
+ dns_rdata_nsec_t nsec;
+ dns_dbnode_t *node = NULL;
+
+ /*
+ * Check to see if the OPT bit has already been cleared.
+ */
+ CHECK(dns_db_getoriginnode(db, &node));
+ dns_rdataset_init(&rdataset);
+ CHECK(dns_db_findrdataset(db, node, version, dns_rdatatype_nsec,
+ dns_rdatatype_none, 0, &rdataset, NULL));
+ CHECK(dns_rdataset_first(&rdataset));
+ dns_rdataset_current(&rdataset, &rdata);
+
+ /*
+ * Find the NEXT name for building the new record.
+ */
+ CHECK(dns_rdata_tostruct(&rdata, &nsec, NULL));
+
+ /*
+ * Delete the old NSEC record.
+ */
+ CHECK(update_one_rr(db, version, diff, DNS_DIFFOP_DEL, name, minimum,
+ &rdata));
+ dns_rdata_reset(&rdata);
+
+ /*
+ * Add the new NSEC record.
+ */
+ CHECK(dns_nsec_buildrdata(db, version, node, &nsec.next, nsecbuffer,
+ &rdata));
+ CHECK(update_one_rr(db, version, diff, DNS_DIFFOP_ADD, name, minimum,
+ &rdata));
+ dns_rdata_reset(&rdata);
+
+ if (secureupdated != NULL)
+ *secureupdated = ISC_TRUE;
+
+ failure:
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ return (result);
+}
+
+static isc_result_t
+updatesignwithkey(dns_signing_t *signing, dns_dbversion_t *version,
+ dns_name_t *name, dns_rdatatype_t privatetype,
+ dns_diff_t *diff)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ unsigned char data[5];
+ isc_boolean_t seen_done = ISC_FALSE;
+
+ dns_rdataset_init(&rdataset);
+ result = dns_db_getoriginnode(signing->db, &node);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ result = dns_db_findrdataset(signing->db, node, version, privatetype,
+ dns_rdatatype_none, 0, &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND) {
+ result = ISC_R_SUCCESS;
+ goto failure;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset)) {
+ dns_rdataset_current(&rdataset, &rdata);
+ if (rdata.length != 5 ||
+ rdata.data[0] != signing->algorithm ||
+ rdata.data[1] != ((signing->keyid >> 8) & 0xff) ||
+ rdata.data[2] != (signing->keyid & 0xff)) {
+ dns_rdata_reset(&rdata);
+ continue;
+ }
+ if (!signing->delete && rdata.data[4] != 0)
+ seen_done = ISC_TRUE;
+ else
+ CHECK(update_one_rr(signing->db, version, diff,
+ DNS_DIFFOP_DEL, name, rdataset.ttl, &rdata));
+ dns_rdata_reset(&rdata);
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ if (!signing->delete && !seen_done) {
+
+ data[0] = signing->algorithm;
+ data[1] = (signing->keyid >> 8) & 0xff;
+ data[2] = signing->keyid & 0xff;
+ data[3] = 0;
+ data[4] = 1;
+ rdata.length = sizeof(data);
+ rdata.data = data;
+ rdata.type = privatetype;
+ rdata.rdclass = dns_db_class(signing->db);
+ CHECK(update_one_rr(signing->db, version, diff, DNS_DIFFOP_ADD,
+ name, rdataset.ttl, &rdata));
+ }
+ failure:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ if (node != NULL)
+ dns_db_detachnode(signing->db, &node);
+ return (result);
+}
+
+static isc_result_t
+fixup_nsec3param(dns_db_t *db, dns_dbversion_t *ver, dns_nsec3chain_t *chain,
+ isc_boolean_t active, dns_diff_t *diff)
+{
+ dns_dbnode_t *node = NULL;
+ dns_name_t *name = dns_db_origin(db);
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_t rdataset;
+ dns_rdata_nsec3param_t nsec3param;
+ isc_result_t result;
+ isc_buffer_t buffer;
+ unsigned char parambuf[DNS_NSEC3PARAM_BUFFERSIZE];
+ dns_ttl_t ttl = 0;
+
+ dns_rdataset_init(&rdataset);
+
+ result = dns_db_getoriginnode(db, &node);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param,
+ 0, 0, &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND)
+ goto add;
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ /*
+ * Preserve the existing ttl.
+ */
+ ttl = rdataset.ttl;
+
+ /*
+ * Delete all NSEC3PARAM records which match that in nsec3chain.
+ */
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset)) {
+
+ dns_rdataset_current(&rdataset, &rdata);
+ CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL));
+
+ if (nsec3param.hash != chain->nsec3param.hash ||
+ (active && nsec3param.flags != 0) ||
+ nsec3param.iterations != chain->nsec3param.iterations ||
+ nsec3param.salt_length != chain->nsec3param.salt_length ||
+ memcmp(nsec3param.salt, chain->nsec3param.salt,
+ nsec3param.salt_length)) {
+ dns_rdata_reset(&rdata);
+ continue;
+ }
+
+ CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_DEL,
+ name, rdataset.ttl, &rdata));
+ dns_rdata_reset(&rdata);
+ }
+ if (result != ISC_R_NOMORE)
+ goto failure;
+
+ add:
+ if ((chain->nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0) {
+ result = ISC_R_SUCCESS;
+ goto failure;
+ }
+
+ /*
+ * Add a NSEC3PARAM record which matches that in nsec3chain but
+ * with all flags bits cleared.
+ *
+ * Note: we do not clear chain->nsec3param.flags as this change
+ * may be reversed.
+ */
+ isc_buffer_init(&buffer, &parambuf, sizeof(parambuf));
+ CHECK(dns_rdata_fromstruct(&rdata, dns_db_class(db),
+ dns_rdatatype_nsec3param,
+ &chain->nsec3param, &buffer));
+ rdata.data[1] = 0; /* Clear flag bits. */
+ CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_ADD, name, ttl, &rdata));
+
+ failure:
+ dns_db_detachnode(db, &node);
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ return (result);
+}
+
+static isc_result_t
+delete_nsec(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node,
+ dns_name_t *name, dns_diff_t *diff)
+{
+ dns_rdataset_t rdataset;
+ isc_result_t result;
+
+ dns_rdataset_init(&rdataset);
+
+ result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec,
+ 0, 0, &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND)
+ return (ISC_R_SUCCESS);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset)) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ dns_rdataset_current(&rdataset, &rdata);
+ CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_DEL, name,
+ rdataset.ttl, &rdata));
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ failure:
+ dns_rdataset_disassociate(&rdataset);
+ return (result);
+}
+
+static isc_result_t
+deletematchingnsec3(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node,
+ dns_name_t *name, const dns_rdata_nsec3param_t *param,
+ dns_diff_t *diff)
+{
+ dns_rdataset_t rdataset;
+ dns_rdata_nsec3_t nsec3;
+ isc_result_t result;
+
+ dns_rdataset_init(&rdataset);
+ result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3,
+ 0, 0, &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND)
+ return (ISC_R_SUCCESS);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset)) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ dns_rdataset_current(&rdataset, &rdata);
+ CHECK(dns_rdata_tostruct(&rdata, &nsec3, NULL));
+ if (nsec3.hash != param->hash ||
+ nsec3.iterations != param->iterations ||
+ nsec3.salt_length != param->salt_length ||
+ memcmp(nsec3.salt, param->salt, nsec3.salt_length))
+ continue;
+ CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_DEL, name,
+ rdataset.ttl, &rdata));
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ failure:
+ dns_rdataset_disassociate(&rdataset);
+ return (result);
+}
+
+static isc_result_t
+need_nsec_chain(dns_db_t *db, dns_dbversion_t *ver,
+ const dns_rdata_nsec3param_t *param,
+ isc_boolean_t *answer, isc_boolean_t *updatensec)
+{
+ dns_dbnode_t *node = NULL;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_nsec3param_t myparam;
+ dns_rdataset_t rdataset;
+ isc_result_t result;
+
+ *answer = ISC_FALSE;
+
+ result = dns_db_getoriginnode(db, &node);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ dns_rdataset_init(&rdataset);
+ result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec,
+ 0, 0, &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND)
+ goto check_nsec3param;
+
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ CHECK(dns_rdataset_first(&rdataset));
+ dns_rdataset_current(&rdataset, &rdata);
+
+ if (!dns_nsec_typepresent(&rdata, dns_rdatatype_opt)) {
+ /*
+ * We have a complete NSEC chain. Signal to update
+ * the apex NSEC record.
+ */
+ *updatensec = ISC_TRUE;
+ goto failure;
+ }
+ dns_rdataset_disassociate(&rdataset);
+ dns_rdata_reset(&rdata);
+
+ check_nsec3param:
+ result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param,
+ 0, 0, &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND) {
+ *answer = ISC_TRUE;
+ dns_db_detachnode(db, &node);
+ return (ISC_R_SUCCESS);
+ }
+ if (result != ISC_R_SUCCESS) {
+ dns_db_detachnode(db, &node);
+ return (result);
+ }
+
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset)) {
+ dns_rdataset_current(&rdataset, &rdata);
+ CHECK(dns_rdata_tostruct(&rdata, &myparam, NULL));
+ dns_rdata_reset(&rdata);
+ /*
+ * Ignore any NSEC3PARAM removals.
+ */
+ if (NSEC3REMOVE(myparam.flags))
+ continue;
+ /*
+ * Ignore the chain that we are in the process of deleting.
+ */
+ if (myparam.hash == param->hash &&
+ myparam.iterations == param->iterations &&
+ myparam.salt_length == param->salt_length &&
+ !memcmp(myparam.salt, param->salt, myparam.salt_length))
+ continue;
+ /*
+ * Found an active NSEC3 chain.
+ */
+ break;
+ }
+ if (result == ISC_R_NOMORE) {
+ *answer = ISC_TRUE;
+ result = ISC_R_SUCCESS;
+ }
+
+ failure:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+/*
+ * Incrementally build and sign a new NSEC3 chain using the parameters
+ * requested.
+ */
+static void
+zone_nsec3chain(dns_zone_t *zone) {
+ const char *journalfile;
+ dns_db_t *db = NULL;
+ dns_dbnode_t *node = NULL;
+ dns_dbversion_t *version = NULL;
+ dns_diff_t sig_diff;
+ dns_diff_t nsec_diff;
+ dns_diff_t nsec3_diff;
+ dns_diff_t param_diff;
+ dns_fixedname_t fixed;
+ dns_fixedname_t nextfixed;
+ dns_name_t *name, *nextname;
+ dns_rdataset_t rdataset;
+ dns_nsec3chain_t *nsec3chain = NULL, *nextnsec3chain;
+ dns_nsec3chainlist_t cleanup;
+ dst_key_t *zone_keys[MAXZONEKEYS];
+ isc_int32_t signatures;
+ isc_boolean_t check_ksk, is_ksk;
+ isc_boolean_t delegation;
+ isc_boolean_t first;
+ isc_result_t result;
+ isc_stdtime_t now, inception, soaexpire, expire, stop;
+ isc_uint32_t jitter;
+ unsigned int i;
+ unsigned int nkeys = 0;
+ isc_uint32_t nodes;
+ isc_boolean_t unsecure = ISC_FALSE;
+ isc_boolean_t seen_soa, seen_ns, seen_dname, seen_ds;
+ isc_boolean_t seen_nsec, seen_nsec3, seen_rr;
+ dns_rdatasetiter_t *iterator = NULL;
+ dns_difftuple_t *tuple;
+ isc_boolean_t buildnsecchain;
+ isc_boolean_t updatensec = ISC_FALSE;
+
+ dns_rdataset_init(&rdataset);
+ dns_fixedname_init(&fixed);
+ name = dns_fixedname_name(&fixed);
+ dns_fixedname_init(&nextfixed);
+ nextname = dns_fixedname_name(&nextfixed);
+ dns_diff_init(zone->mctx, &param_diff);
+ dns_diff_init(zone->mctx, &nsec3_diff);
+ dns_diff_init(zone->mctx, &nsec_diff);
+ dns_diff_init(zone->mctx, &sig_diff);
+ sig_diff.resign = zone->sigresigninginterval;
+ ISC_LIST_INIT(cleanup);
+
+ /*
+ * Updates are disabled. Pause for 5 minutes.
+ */
+ if (zone->update_disabled) {
+ result = ISC_R_FAILURE;
+ goto failure;
+ }
+
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+ dns_db_attach(zone->db, &db);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+
+ result = dns_db_newversion(db, &version);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_nsec3chain:dns_db_newversion -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ result = find_zone_keys(zone, db, version, zone->mctx,
+ MAXZONEKEYS, zone_keys, &nkeys);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_nsec3chain:find_zone_keys -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ isc_stdtime_get(&now);
+ inception = now - 3600; /* Allow for clock skew. */
+ soaexpire = now + dns_zone_getsigvalidityinterval(zone);
+
+ /*
+ * Spread out signatures over time if they happen to be
+ * clumped. We don't do this for each add_sigs() call as
+ * we still want some clustering to occur.
+ */
+ isc_random_get(&jitter);
+ expire = soaexpire - jitter % 3600;
+ stop = now + 5;
+
+ check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK);
+ if (check_ksk)
+ check_ksk = ksk_sanity(db, version);
+
+ /*
+ * We keep pulling nodes off each interator in turn until
+ * we have no more nodes to pull off or we reach the limits
+ * for this quantum.
+ */
+ nodes = zone->nodes;
+ signatures = zone->signatures;
+ LOCK_ZONE(zone);
+ nsec3chain = ISC_LIST_HEAD(zone->nsec3chain);
+ UNLOCK_ZONE(zone);
+ first = ISC_TRUE;
+
+ if (nsec3chain != NULL)
+ nsec3chain->save_delete_nsec = nsec3chain->delete_nsec;
+ /*
+ * Generate new NSEC3 chains first.
+ */
+ while (nsec3chain != NULL && nodes-- > 0 && signatures > 0) {
+ LOCK_ZONE(zone);
+ nextnsec3chain = ISC_LIST_NEXT(nsec3chain, link);
+
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+ if (nsec3chain->done || nsec3chain->db != zone->db) {
+ ISC_LIST_UNLINK(zone->nsec3chain, nsec3chain, link);
+ ISC_LIST_APPEND(cleanup, nsec3chain, link);
+ }
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+ UNLOCK_ZONE(zone);
+ if (ISC_LIST_TAIL(cleanup) == nsec3chain)
+ goto next_addchain;
+
+ /*
+ * Possible future db.
+ */
+ if (nsec3chain->db != db) {
+ goto next_addchain;
+ }
+
+ if (NSEC3REMOVE(nsec3chain->nsec3param.flags))
+ goto next_addchain;
+
+ is_ksk = ISC_FALSE;
+ delegation = ISC_FALSE;
+ dns_dbiterator_current(nsec3chain->dbiterator, &node, name);
+
+ if (nsec3chain->delete_nsec) {
+ delegation = ISC_FALSE;
+ dns_dbiterator_pause(nsec3chain->dbiterator);
+ CHECK(delete_nsec(db, version, node, name, &nsec_diff));
+ goto next_addnode;
+ }
+ /*
+ * On the first pass we need to check if the current node
+ * has not been obscured.
+ */
+ delegation = ISC_FALSE;
+ unsecure = ISC_FALSE;
+ if (first) {
+ dns_fixedname_t ffound;
+ dns_name_t *found;
+ dns_fixedname_init(&ffound);
+ found = dns_fixedname_name(&ffound);
+ result = dns_db_find(db, name, version,
+ dns_rdatatype_soa,
+ DNS_DBFIND_NOWILD, 0, NULL, found,
+ NULL, NULL);
+ if ((result == DNS_R_DELEGATION ||
+ result == DNS_R_DNAME) &&
+ !dns_name_equal(name, found)) {
+ /*
+ * Remember the obscuring name so that
+ * we skip all obscured names.
+ */
+ dns_name_copy(found, name, NULL);
+ delegation = ISC_TRUE;
+ goto next_addnode;
+ }
+ }
+
+ /*
+ * Check to see if this is a bottom of zone node.
+ */
+ result = dns_db_allrdatasets(db, node, version, 0, &iterator);
+ if (result == ISC_R_NOTFOUND) /* Empty node? */
+ goto next_addnode;
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ seen_soa = seen_ns = seen_dname = seen_ds = seen_nsec =
+ ISC_FALSE;
+ for (result = dns_rdatasetiter_first(iterator);
+ result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(iterator)) {
+ dns_rdatasetiter_current(iterator, &rdataset);
+ INSIST(rdataset.type != dns_rdatatype_nsec3);
+ if (rdataset.type == dns_rdatatype_soa)
+ seen_soa = ISC_TRUE;
+ else if (rdataset.type == dns_rdatatype_ns)
+ seen_ns = ISC_TRUE;
+ else if (rdataset.type == dns_rdatatype_dname)
+ seen_dname = ISC_TRUE;
+ else if (rdataset.type == dns_rdatatype_ds)
+ seen_ds = ISC_TRUE;
+ else if (rdataset.type == dns_rdatatype_nsec)
+ seen_nsec = ISC_TRUE;
+ dns_rdataset_disassociate(&rdataset);
+ }
+ dns_rdatasetiter_destroy(&iterator);
+ /*
+ * Is there a NSEC chain than needs to be cleaned up?
+ */
+ if (seen_nsec)
+ nsec3chain->seen_nsec = ISC_TRUE;
+ if (seen_ns && !seen_soa && !seen_ds)
+ unsecure = ISC_TRUE;
+ if ((seen_ns && !seen_soa) || seen_dname)
+ delegation = ISC_TRUE;
+
+ /*
+ * Process one node.
+ */
+ dns_dbiterator_pause(nsec3chain->dbiterator);
+ CHECK(dns_nsec3_addnsec3(db, version, name,
+ &nsec3chain->nsec3param,
+ zone->minimum, unsecure, &nsec3_diff));
+ /*
+ * Treat each call to dns_nsec3_addnsec3() as if it's cost is
+ * two signatures. Additionally there will, in general, be
+ * two signature generated below.
+ *
+ * If we are only changing the optout flag the cost is half
+ * that of the cost of generating a completely new chain.
+ */
+ signatures -= 4;
+
+ /*
+ * Go onto next node.
+ */
+ next_addnode:
+ first = ISC_FALSE;
+ dns_db_detachnode(db, &node);
+ do {
+ result = dns_dbiterator_next(nsec3chain->dbiterator);
+
+ if (result == ISC_R_NOMORE && nsec3chain->delete_nsec) {
+ CHECK(fixup_nsec3param(db, version, nsec3chain,
+ ISC_FALSE, &param_diff));
+ LOCK_ZONE(zone);
+ ISC_LIST_UNLINK(zone->nsec3chain, nsec3chain,
+ link);
+ UNLOCK_ZONE(zone);
+ ISC_LIST_APPEND(cleanup, nsec3chain, link);
+ goto next_addchain;
+ }
+ if (result == ISC_R_NOMORE) {
+ dns_dbiterator_pause(nsec3chain->dbiterator);
+ if (nsec3chain->seen_nsec) {
+ CHECK(fixup_nsec3param(db, version,
+ nsec3chain,
+ ISC_TRUE,
+ &param_diff));
+ nsec3chain->delete_nsec = ISC_TRUE;
+ goto same_addchain;
+ }
+ CHECK(fixup_nsec3param(db, version, nsec3chain,
+ ISC_FALSE, &param_diff));
+ LOCK_ZONE(zone);
+ ISC_LIST_UNLINK(zone->nsec3chain, nsec3chain,
+ link);
+ UNLOCK_ZONE(zone);
+ ISC_LIST_APPEND(cleanup, nsec3chain, link);
+ goto next_addchain;
+ } else if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_nsec3chain:"
+ "dns_dbiterator_next -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ } else if (delegation) {
+ dns_dbiterator_current(nsec3chain->dbiterator,
+ &node, nextname);
+ dns_db_detachnode(db, &node);
+ if (!dns_name_issubdomain(nextname, name))
+ break;
+ } else
+ break;
+ } while (1);
+ continue;
+
+ same_addchain:
+ CHECK(dns_dbiterator_first(nsec3chain->dbiterator));
+ first = ISC_TRUE;
+ continue;
+
+ next_addchain:
+ dns_dbiterator_pause(nsec3chain->dbiterator);
+ nsec3chain = nextnsec3chain;
+ first = ISC_TRUE;
+ if (nsec3chain != NULL)
+ nsec3chain->save_delete_nsec = nsec3chain->delete_nsec;
+ }
+
+ /*
+ * Process removals.
+ */
+ LOCK_ZONE(zone);
+ nsec3chain = ISC_LIST_HEAD(zone->nsec3chain);
+ UNLOCK_ZONE(zone);
+ first = ISC_TRUE;
+ buildnsecchain = ISC_FALSE;
+ while (nsec3chain != NULL && nodes-- > 0 && signatures > 0) {
+ LOCK_ZONE(zone);
+ nextnsec3chain = ISC_LIST_NEXT(nsec3chain, link);
+ UNLOCK_ZONE(zone);
+
+ if (nsec3chain->db != db)
+ goto next_removechain;
+
+ if (!NSEC3REMOVE(nsec3chain->nsec3param.flags))
+ goto next_removechain;
+
+ /*
+ * Work out if we need to build a NSEC chain as a consequence
+ * of removing this NSEC3 chain.
+ */
+ if (first && !updatensec &&
+ (nsec3chain->nsec3param.flags & DNS_NSEC3FLAG_NONSEC) == 0)
+ CHECK(need_nsec_chain(db, version,
+ &nsec3chain->nsec3param,
+ &buildnsecchain, &updatensec));
+
+ dns_dbiterator_current(nsec3chain->dbiterator, &node, name);
+ delegation = ISC_FALSE;
+
+ if (!buildnsecchain) {
+ /*
+ * Delete the NSECPARAM record that matches this chain.
+ */
+ if (first)
+ CHECK(fixup_nsec3param(db, version, nsec3chain,
+ ISC_TRUE, &param_diff));
+
+ /*
+ * Delete the NSEC3 records.
+ */
+ CHECK(deletematchingnsec3(db, version, node, name,
+ &nsec3chain->nsec3param,
+ &nsec3_diff));
+ goto next_removenode;
+ }
+
+ if (first) {
+ dns_fixedname_t ffound;
+ dns_name_t *found;
+ dns_fixedname_init(&ffound);
+ found = dns_fixedname_name(&ffound);
+ result = dns_db_find(db, name, version,
+ dns_rdatatype_soa,
+ DNS_DBFIND_NOWILD, 0, NULL, found,
+ NULL, NULL);
+ if ((result == DNS_R_DELEGATION ||
+ result == DNS_R_DNAME) &&
+ !dns_name_equal(name, found)) {
+ /*
+ * Remember the obscuring name so that
+ * we skip all obscured names.
+ */
+ dns_name_copy(found, name, NULL);
+ delegation = ISC_TRUE;
+ goto next_removenode;
+ }
+ }
+
+ /*
+ * Check to see if this is a bottom of zone node.
+ */
+ result = dns_db_allrdatasets(db, node, version, 0, &iterator);
+ if (result == ISC_R_NOTFOUND) /* Empty node? */
+ goto next_removenode;
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ seen_soa = seen_ns = seen_dname = seen_nsec3 = seen_nsec =
+ seen_rr = ISC_FALSE;
+ for (result = dns_rdatasetiter_first(iterator);
+ result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(iterator)) {
+ dns_rdatasetiter_current(iterator, &rdataset);
+ if (rdataset.type == dns_rdatatype_soa)
+ seen_soa = ISC_TRUE;
+ else if (rdataset.type == dns_rdatatype_ns)
+ seen_ns = ISC_TRUE;
+ else if (rdataset.type == dns_rdatatype_dname)
+ seen_dname = ISC_TRUE;
+ else if (rdataset.type == dns_rdatatype_nsec)
+ seen_nsec = ISC_TRUE;
+ else if (rdataset.type == dns_rdatatype_nsec3)
+ seen_nsec3 = ISC_TRUE;
+ seen_rr = ISC_TRUE;
+ dns_rdataset_disassociate(&rdataset);
+ }
+ dns_rdatasetiter_destroy(&iterator);
+
+ if (!seen_rr || seen_nsec3 || seen_nsec)
+ goto next_removenode;
+ if ((seen_ns && !seen_soa) || seen_dname)
+ delegation = ISC_TRUE;
+
+ CHECK(add_nsec(db, version, name, node, zone->minimum,
+ delegation, &nsec_diff));
+
+ next_removenode:
+ first = ISC_FALSE;
+ dns_db_detachnode(db, &node);
+ do {
+ result = dns_dbiterator_next(nsec3chain->dbiterator);
+ if (result == ISC_R_NOMORE && buildnsecchain) {
+ /*
+ * The NSEC chain should now be built.
+ * We can now remove the NSEC3 chain.
+ */
+ updatensec = ISC_TRUE;
+ goto same_removechain;
+ }
+ if (result == ISC_R_NOMORE) {
+ LOCK_ZONE(zone);
+ ISC_LIST_UNLINK(zone->nsec3chain, nsec3chain,
+ link);
+ UNLOCK_ZONE(zone);
+ ISC_LIST_APPEND(cleanup, nsec3chain, link);
+ dns_dbiterator_pause(nsec3chain->dbiterator);
+ CHECK(fixup_nsec3param(db, version, nsec3chain,
+ ISC_FALSE, &param_diff));
+ goto next_removechain;
+ } else if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_nsec3chain:"
+ "dns_dbiterator_next -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ } else if (delegation) {
+ dns_dbiterator_current(nsec3chain->dbiterator,
+ &node, nextname);
+ dns_db_detachnode(db, &node);
+ if (!dns_name_issubdomain(nextname, name))
+ break;
+ } else
+ break;
+ } while (1);
+ continue;
+
+ same_removechain:
+ CHECK(dns_dbiterator_first(nsec3chain->dbiterator));
+ buildnsecchain = ISC_FALSE;
+ first = ISC_TRUE;
+ continue;
+
+ next_removechain:
+ dns_dbiterator_pause(nsec3chain->dbiterator);
+ nsec3chain = nextnsec3chain;
+ first = ISC_TRUE;
+ }
+
+ /*
+ * Add / update signatures for the NSEC3 records.
+ */
+ for (tuple = ISC_LIST_HEAD(nsec3_diff.tuples);
+ tuple != NULL;
+ tuple = ISC_LIST_HEAD(nsec3_diff.tuples)) {
+ /*
+ * We have changed the NSEC3 RRset above so we need to update
+ * the signatures.
+ */
+ result = del_sigs(zone, db, version, &tuple->name,
+ dns_rdatatype_nsec3, &sig_diff,
+ zone_keys, nkeys, now);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_nsec3chain:del_sigs -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+ result = add_sigs(db, version, &tuple->name,
+ dns_rdatatype_nsec3, &sig_diff, zone_keys,
+ nkeys, zone->mctx, inception, expire,
+ check_ksk);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_nsec3chain:add_sigs -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ do {
+ dns_difftuple_t *next = ISC_LIST_NEXT(tuple, link);
+ while (next != NULL &&
+ !dns_name_equal(&tuple->name, &next->name))
+ next = ISC_LIST_NEXT(next, link);
+ ISC_LIST_UNLINK(nsec3_diff.tuples, tuple, link);
+ dns_diff_appendminimal(&sig_diff, &tuple);
+ INSIST(tuple == NULL);
+ tuple = next;
+ } while (tuple != NULL);
+ }
+
+ for (tuple = ISC_LIST_HEAD(param_diff.tuples);
+ tuple != NULL;
+ tuple = ISC_LIST_HEAD(param_diff.tuples)) {
+ /*
+ * We have changed the NSEC3PARAM RRset above so we need to
+ * update the signatures.
+ */
+ result = del_sigs(zone, db, version, &tuple->name,
+ dns_rdatatype_nsec3param, &sig_diff,
+ zone_keys, nkeys, now);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_nsec3chain:del_sigs -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+ result = add_sigs(db, version, &tuple->name,
+ dns_rdatatype_nsec3param, &sig_diff,
+ zone_keys, nkeys, zone->mctx, inception,
+ expire, check_ksk);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_nsec3chain:add_sigs -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+ ISC_LIST_UNLINK(param_diff.tuples, tuple, link);
+ dns_diff_appendminimal(&sig_diff, &tuple);
+ INSIST(tuple == NULL);
+ }
+
+ if (updatensec)
+ CHECK(updatesecure(db, version, &zone->origin, zone->minimum,
+ NULL, &nsec_diff));
+
+ for (tuple = ISC_LIST_HEAD(nsec_diff.tuples);
+ tuple != NULL;
+ tuple = ISC_LIST_HEAD(nsec_diff.tuples)) {
+ result = del_sigs(zone, db, version, &tuple->name,
+ dns_rdatatype_nsec, &sig_diff,
+ zone_keys, nkeys, now);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_nsec3chain:del_sigs -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+ result = add_sigs(db, version, &tuple->name,
+ dns_rdatatype_nsec, &sig_diff,
+ zone_keys, nkeys, zone->mctx, inception,
+ expire, check_ksk);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_nsec3chain:add_sigs -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+ ISC_LIST_UNLINK(nsec_diff.tuples, tuple, link);
+ dns_diff_appendminimal(&sig_diff, &tuple);
+ INSIST(tuple == NULL);
+ }
+
+ /*
+ * If we made no effective changes to the zone then we can just
+ * cleanup otherwise we need to increment the serial.
+ */
+ if (ISC_LIST_HEAD(sig_diff.tuples) == NULL)
+ goto done;
+
+ result = del_sigs(zone, db, version, &zone->origin, dns_rdatatype_soa,
+ &sig_diff, zone_keys, nkeys, now);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:"
+ "del_sigs -> %s\n", dns_result_totext(result));
+ goto failure;
+ }
+
+ result = increment_soa_serial(db, version, &sig_diff, zone->mctx);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:"
+ "increment_soa_serial -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ result = add_sigs(db, version, &zone->origin, dns_rdatatype_soa,
+ &sig_diff, zone_keys, nkeys, zone->mctx, inception,
+ soaexpire, check_ksk);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:"
+ "add_sigs -> %s\n", dns_result_totext(result));
+ goto failure;
+ }
+
+ journalfile = dns_zone_getjournal(zone);
+ if (journalfile != NULL) {
+ dns_journal_t *journal = NULL;
+ result = dns_journal_open(zone->mctx, journalfile,
+ ISC_TRUE, &journal);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:"
+ "dns_journal_open -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ result = dns_journal_write_transaction(journal, &sig_diff);
+ dns_journal_destroy(&journal);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:"
+ "dns_journal_write_transaction -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+ }
+
+ LOCK_ZONE(zone);
+ zone_needdump(zone, DNS_DUMP_DELAY);
+ UNLOCK_ZONE(zone);
+
+ done:
+ /*
+ * Pause all iterators so that dns_db_closeversion() can succeed.
+ */
+ LOCK_ZONE(zone);
+ for (nsec3chain = ISC_LIST_HEAD(zone->nsec3chain);
+ nsec3chain != NULL;
+ nsec3chain = ISC_LIST_NEXT(nsec3chain, link))
+ dns_dbiterator_pause(nsec3chain->dbiterator);
+ UNLOCK_ZONE(zone);
+
+ /*
+ * Everything has succeeded. Commit the changes.
+ */
+ dns_db_closeversion(db, &version, ISC_TRUE);
+
+ /*
+ * Everything succeeded so we can clean these up now.
+ */
+ nsec3chain = ISC_LIST_HEAD(cleanup);
+ while (nsec3chain != NULL) {
+ ISC_LIST_UNLINK(cleanup, nsec3chain, link);
+ dns_db_detach(&nsec3chain->db);
+ dns_dbiterator_destroy(&nsec3chain->dbiterator);
+ isc_mem_put(zone->mctx, nsec3chain, sizeof *nsec3chain);
+ nsec3chain = ISC_LIST_HEAD(cleanup);
+ }
+
+ set_resigntime(zone);
+
+ failure:
+ /*
+ * On error roll back the current nsec3chain.
+ */
+ if (result != ISC_R_SUCCESS && nsec3chain != NULL) {
+ if (nsec3chain->done) {
+ dns_db_detach(&nsec3chain->db);
+ dns_dbiterator_destroy(&nsec3chain->dbiterator);
+ isc_mem_put(zone->mctx, nsec3chain, sizeof *nsec3chain);
+ } else {
+ result = dns_dbiterator_first(nsec3chain->dbiterator);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_dbiterator_pause(nsec3chain->dbiterator);
+ nsec3chain->delete_nsec = nsec3chain->save_delete_nsec;
+ }
+ }
+
+ /*
+ * Rollback the cleanup list.
+ */
+ nsec3chain = ISC_LIST_TAIL(cleanup);
+ while (nsec3chain != NULL) {
+ ISC_LIST_UNLINK(cleanup, nsec3chain, link);
+ if (nsec3chain->done) {
+ dns_db_detach(&nsec3chain->db);
+ dns_dbiterator_destroy(&nsec3chain->dbiterator);
+ isc_mem_put(zone->mctx, nsec3chain, sizeof *nsec3chain);
+ } else {
+ LOCK_ZONE(zone);
+ ISC_LIST_PREPEND(zone->nsec3chain, nsec3chain, link);
+ UNLOCK_ZONE(zone);
+ result = dns_dbiterator_first(nsec3chain->dbiterator);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_dbiterator_pause(nsec3chain->dbiterator);
+ nsec3chain->delete_nsec = nsec3chain->save_delete_nsec;
+ }
+ nsec3chain = ISC_LIST_TAIL(cleanup);
+ }
+
+ LOCK_ZONE(zone);
+ for (nsec3chain = ISC_LIST_HEAD(zone->nsec3chain);
+ nsec3chain != NULL;
+ nsec3chain = ISC_LIST_NEXT(nsec3chain, link))
+ dns_dbiterator_pause(nsec3chain->dbiterator);
+ UNLOCK_ZONE(zone);
+
+ dns_diff_clear(&param_diff);
+ dns_diff_clear(&nsec3_diff);
+ dns_diff_clear(&nsec_diff);
+ dns_diff_clear(&sig_diff);
+
+ if (iterator != NULL)
+ dns_rdatasetiter_destroy(&iterator);
+
+ for (i = 0; i < nkeys; i++)
+ dst_key_free(&zone_keys[i]);
+
+ if (version != NULL) {
+ dns_db_closeversion(db, &version, ISC_FALSE);
+ dns_db_detach(&db);
+ } else if (db != NULL)
+ dns_db_detach(&db);
+
+ LOCK_ZONE(zone);
+ if (ISC_LIST_HEAD(zone->nsec3chain) != NULL) {
+ isc_interval_t i;
+ if (zone->update_disabled || result != ISC_R_SUCCESS)
+ isc_interval_set(&i, 60, 0); /* 1 minute */
+ else
+ isc_interval_set(&i, 0, 10000000); /* 10 ms */
+ isc_time_nowplusinterval(&zone->nsec3chaintime, &i);
+ } else
+ isc_time_settoepoch(&zone->nsec3chaintime);
+ UNLOCK_ZONE(zone);
+}
+
+static isc_result_t
+del_sig(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
+ dns_dbnode_t *node, unsigned int nkeys, dns_secalg_t algorithm,
+ isc_uint16_t keyid, dns_diff_t *diff)
+{
+ dns_rdata_rrsig_t rrsig;
+ dns_rdataset_t rdataset;
+ dns_rdatasetiter_t *iterator = NULL;
+ isc_result_t result;
+
+ result = dns_db_allrdatasets(db, node, version, 0, &iterator);
+ if (result != ISC_R_SUCCESS) {
+ if (result == ISC_R_NOTFOUND)
+ result = ISC_R_SUCCESS;
+ return (result);
+ }
+
+ dns_rdataset_init(&rdataset);
+ for (result = dns_rdatasetiter_first(iterator);
+ result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(iterator)) {
+ dns_rdatasetiter_current(iterator, &rdataset);
+ if (nkeys == 0 && rdataset.type == dns_rdatatype_nsec) {
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset)) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_current(&rdataset, &rdata);
+ CHECK(update_one_rr(db, version, diff,
+ DNS_DIFFOP_DEL, name,
+ rdataset.ttl, &rdata));
+ }
+ if (result != ISC_R_NOMORE)
+ goto failure;
+ dns_rdataset_disassociate(&rdataset);
+ continue;
+ }
+ if (rdataset.type != dns_rdatatype_rrsig) {
+ dns_rdataset_disassociate(&rdataset);
+ continue;
+ }
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset)) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_current(&rdataset, &rdata);
+ CHECK(dns_rdata_tostruct(&rdata, &rrsig, NULL));
+ if (rrsig.algorithm != algorithm ||
+ rrsig.keyid != keyid)
+ continue;
+ CHECK(update_one_rr(db, version, diff,
+ DNS_DIFFOP_DEL, name,
+ rdataset.ttl, &rdata));
+ }
+ dns_rdataset_disassociate(&rdataset);
+ if (result != ISC_R_NOMORE)
+ break;
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ failure:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ dns_rdatasetiter_destroy(&iterator);
+ return (result);
+}
+
+/*
+ * Incrementally sign the zone using the keys requested.
+ * Builds the NSEC chain if required.
+ */
+static void
+zone_sign(dns_zone_t *zone) {
+ const char *journalfile;
+ dns_db_t *db = NULL;
+ dns_dbnode_t *node = NULL;
+ dns_dbversion_t *version = NULL;
+ dns_diff_t sig_diff;
+ dns_fixedname_t fixed;
+ dns_fixedname_t nextfixed;
+ dns_name_t *name, *nextname;
+ dns_rdataset_t rdataset;
+ dns_signing_t *signing, *nextsigning;
+ dns_signinglist_t cleanup;
+ dst_key_t *zone_keys[MAXZONEKEYS];
+ isc_int32_t signatures;
+ isc_boolean_t check_ksk, is_ksk;
+ isc_boolean_t delegation;
+ isc_boolean_t finishedakey = ISC_FALSE;
+ isc_boolean_t secureupdated = ISC_FALSE;
+ isc_boolean_t build_nsec3 = ISC_FALSE, build_nsec = ISC_FALSE;
+ isc_boolean_t first;
+ isc_result_t result;
+ isc_stdtime_t now, inception, soaexpire, expire, stop;
+ isc_uint32_t jitter;
+ unsigned int i;
+ unsigned int nkeys = 0;
+ isc_uint32_t nodes;
+
+ dns_rdataset_init(&rdataset);
+ dns_fixedname_init(&fixed);
+ name = dns_fixedname_name(&fixed);
+ dns_fixedname_init(&nextfixed);
+ nextname = dns_fixedname_name(&nextfixed);
+ dns_diff_init(zone->mctx, &sig_diff);
+ sig_diff.resign = zone->sigresigninginterval;
+ ISC_LIST_INIT(cleanup);
+
+ /*
+ * Updates are disabled. Pause for 5 minutes.
+ */
+ if (zone->update_disabled) {
+ result = ISC_R_FAILURE;
+ goto failure;
+ }
+
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+ dns_db_attach(zone->db, &db);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+
+ result = dns_db_newversion(db, &version);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_sign:dns_db_newversion -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ result = find_zone_keys(zone, db, version, zone->mctx,
+ MAXZONEKEYS, zone_keys, &nkeys);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_sign:find_zone_keys -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ isc_stdtime_get(&now);
+ inception = now - 3600; /* Allow for clock skew. */
+ soaexpire = now + dns_zone_getsigvalidityinterval(zone);
+
+ /*
+ * Spread out signatures over time if they happen to be
+ * clumped. We don't do this for each add_sigs() call as
+ * we still want some clustering to occur.
+ */
+ isc_random_get(&jitter);
+ expire = soaexpire - jitter % 3600;
+ stop = now + 5;
+
+ check_ksk = DNS_ZONE_OPTION(zone, DNS_ZONEOPT_UPDATECHECKKSK);
+ if (check_ksk)
+ check_ksk = ksk_sanity(db, version);
+
+ /*
+ * We keep pulling nodes off each interator in turn until
+ * we have no more nodes to pull off or we reach the limits
+ * for this quantum.
+ */
+ nodes = zone->nodes;
+ signatures = zone->signatures;
+ signing = ISC_LIST_HEAD(zone->signing);
+ first = ISC_TRUE;
+ /*
+ * See if we have a NSEC chain.
+ */
+ result = dns_db_getoriginnode(db, &node);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ result = dns_db_findrdataset(db, node, version, dns_rdatatype_nsec,
+ dns_rdatatype_none, 0, &rdataset, NULL);
+ dns_db_detachnode(db, &node);
+ if (result == ISC_R_SUCCESS) {
+ build_nsec = ISC_TRUE;
+ dns_rdataset_disassociate(&rdataset);
+ } else if (result != ISC_R_NOTFOUND) {
+ goto failure;
+ } else {
+ /*
+ * No NSEC chain present.
+ * See if we need to build a NSEC3 chain?
+ */
+ result = dns_nsec3_active(db, version, ISC_TRUE, &build_nsec3);
+ if (result == ISC_R_SUCCESS) {
+ if (build_nsec3)
+ build_nsec3 = ISC_FALSE;
+ else {
+ result = dns_nsec3_active(db, version,
+ ISC_FALSE,
+ &build_nsec3);
+ if (build_nsec3)
+ secureupdated = ISC_TRUE;
+ else
+ build_nsec = ISC_TRUE;
+ }
+ }
+ }
+
+ while (signing != NULL && nodes-- > 0 && signatures > 0) {
+ nextsigning = ISC_LIST_NEXT(signing, link);
+
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+ if (signing->done || signing->db != zone->db) {
+ /*
+ * The zone has been reloaded. We will have
+ * created new signings as part of the reload
+ * process so we can destroy this one.
+ */
+ ISC_LIST_UNLINK(zone->signing, signing, link);
+ ISC_LIST_APPEND(cleanup, signing, link);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+ goto next_signing;
+ }
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+
+ if (signing->db != db)
+ goto next_signing;
+
+ is_ksk = ISC_FALSE;
+ delegation = ISC_FALSE;
+
+ dns_dbiterator_current(signing->dbiterator, &node, name);
+
+ if (signing->delete) {
+ dns_dbiterator_pause(signing->dbiterator);
+ CHECK(del_sig(db, version, name, node, nkeys,
+ signing->algorithm, signing->keyid,
+ &sig_diff));
+ goto next_node;
+ }
+ /*
+ * On the first pass we need to check if the current node
+ * has not been obscured.
+ */
+ if (first) {
+ dns_fixedname_t ffound;
+ dns_name_t *found;
+ dns_fixedname_init(&ffound);
+ found = dns_fixedname_name(&ffound);
+ result = dns_db_find(db, name, version,
+ dns_rdatatype_soa,
+ DNS_DBFIND_NOWILD, 0, NULL, found,
+ NULL, NULL);
+ if ((result == DNS_R_DELEGATION ||
+ result == DNS_R_DNAME) &&
+ !dns_name_equal(name, found)) {
+ /*
+ * Remember the obscuring name so that
+ * we skip all obscured names.
+ */
+ dns_name_copy(found, name, NULL);
+ delegation = ISC_TRUE;
+ goto next_node;
+ }
+ }
+
+ /*
+ * Process one node.
+ */
+ dns_dbiterator_pause(signing->dbiterator);
+ for (i = 0; i < nkeys; i++) {
+ /*
+ * Find the key we want to sign with.
+ */
+ if (dst_key_alg(zone_keys[i]) != signing->algorithm ||
+ dst_key_id(zone_keys[i]) != signing->keyid ||
+ !dst_key_isprivate(zone_keys[i]))
+ continue;
+ /*
+ * Do we do KSK processing?
+ */
+ if (check_ksk &&
+ (dst_key_flags(zone_keys[i]) & DNS_KEYFLAG_KSK) != 0)
+ is_ksk = ISC_TRUE;
+ CHECK(sign_a_node(db, name, node, version, build_nsec3,
+ build_nsec, zone_keys[i], inception,
+ expire, zone->minimum, is_ksk,
+ &delegation, &sig_diff, &signatures,
+ zone->mctx));
+ break;
+ }
+ /*
+ * Go onto next node.
+ */
+ next_node:
+ first = ISC_FALSE;
+ dns_db_detachnode(db, &node);
+ do {
+ result = dns_dbiterator_next(signing->dbiterator);
+ if (result == ISC_R_NOMORE) {
+ ISC_LIST_UNLINK(zone->signing, signing, link);
+ ISC_LIST_APPEND(cleanup, signing, link);
+ dns_dbiterator_pause(signing->dbiterator);
+ finishedakey = ISC_TRUE;
+ if (!is_ksk && !secureupdated && nkeys != 0 &&
+ build_nsec) {
+ /*
+ * We have finished regenerating the
+ * zone with a zone signing key.
+ * The NSEC chain is now complete and
+ * there is a full set of signatures
+ * for the zone. We can now clear the
+ * OPT bit from the NSEC record.
+ */
+ result = updatesecure(db, version,
+ &zone->origin,
+ zone->minimum,
+ &secureupdated,
+ &sig_diff);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone,
+ ISC_LOG_ERROR,
+ "updatesecure -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+ }
+ result = updatesignwithkey(signing, version,
+ &zone->origin,
+ zone->privatetype,
+ &sig_diff);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "updatesignwithkey -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+ goto next_signing;
+ } else if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_sign:dns_dbiterator_next -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ } else if (delegation) {
+ dns_dbiterator_current(signing->dbiterator,
+ &node, nextname);
+ dns_db_detachnode(db, &node);
+ if (!dns_name_issubdomain(nextname, name))
+ break;
+ } else
+ break;
+ } while (1);
+ continue;
+
+ next_signing:
+ dns_dbiterator_pause(signing->dbiterator);
+ signing = nextsigning;
+ first = ISC_TRUE;
+ }
+
+ if (secureupdated) {
+ /*
+ * We have changed the NSEC RRset above so we need to update
+ * the signatures.
+ */
+ result = del_sigs(zone, db, version, &zone->origin,
+ dns_rdatatype_nsec, &sig_diff, zone_keys,
+ nkeys, now);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_sign:del_sigs -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+ result = add_sigs(db, version, &zone->origin,
+ dns_rdatatype_nsec, &sig_diff, zone_keys,
+ nkeys, zone->mctx, inception, soaexpire,
+ check_ksk);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_sign:add_sigs -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+ }
+ if (finishedakey) {
+ /*
+ * We have changed the RRset above so we need to update
+ * the signatures.
+ */
+ result = del_sigs(zone, db, version, &zone->origin,
+ zone->privatetype, &sig_diff,
+ zone_keys, nkeys, now);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_sign:del_sigs -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+ result = add_sigs(db, version, &zone->origin,
+ zone->privatetype, &sig_diff,
+ zone_keys, nkeys, zone->mctx, inception,
+ soaexpire, check_ksk);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_sign:add_sigs -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+ }
+ result = del_sigs(zone, db, version, &zone->origin, dns_rdatatype_soa,
+ &sig_diff, zone_keys, nkeys, now);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_sign:del_sigs -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ result = increment_soa_serial(db, version, &sig_diff, zone->mctx);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_sign:increment_soa_serial -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ /*
+ * Generate maximum life time signatures so that the above loop
+ * termination is sensible.
+ */
+ result = add_sigs(db, version, &zone->origin, dns_rdatatype_soa,
+ &sig_diff, zone_keys, nkeys, zone->mctx, inception,
+ soaexpire, check_ksk);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_sign:add_sigs -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ journalfile = dns_zone_getjournal(zone);
+ if (journalfile != NULL) {
+ dns_journal_t *journal = NULL;
+ result = dns_journal_open(zone->mctx, journalfile,
+ ISC_TRUE, &journal);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_sign:dns_journal_open -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+
+ result = dns_journal_write_transaction(journal, &sig_diff);
+ dns_journal_destroy(&journal);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "zone_sign:dns_journal_write_transaction -> %s\n",
+ dns_result_totext(result));
+ goto failure;
+ }
+ }
+
+
+ /*
+ * Pause all iterators so that dns_db_closeversion() can succeed.
+ */
+ for (signing = ISC_LIST_HEAD(zone->signing);
+ signing != NULL;
+ signing = ISC_LIST_NEXT(signing, link))
+ dns_dbiterator_pause(signing->dbiterator);
+
+ for (signing = ISC_LIST_HEAD(cleanup);
+ signing != NULL;
+ signing = ISC_LIST_NEXT(signing, link))
+ dns_dbiterator_pause(signing->dbiterator);
+
+ /*
+ * Everything has succeeded. Commit the changes.
+ */
+ dns_db_closeversion(db, &version, ISC_TRUE);
+
+ /*
+ * Everything succeeded so we can clean these up now.
+ */
+ signing = ISC_LIST_HEAD(cleanup);
+ while (signing != NULL) {
+ ISC_LIST_UNLINK(cleanup, signing, link);
+ dns_db_detach(&signing->db);
+ dns_dbiterator_destroy(&signing->dbiterator);
+ isc_mem_put(zone->mctx, signing, sizeof *signing);
+ signing = ISC_LIST_HEAD(cleanup);
+ }
+
+ set_resigntime(zone);
+
+ LOCK_ZONE(zone);
+ zone_needdump(zone, DNS_DUMP_DELAY);
+ UNLOCK_ZONE(zone);
+
+ failure:
+ /*
+ * Rollback the cleanup list.
+ */
+ signing = ISC_LIST_HEAD(cleanup);
+ while (signing != NULL) {
+ ISC_LIST_UNLINK(cleanup, signing, link);
+ ISC_LIST_APPEND(zone->signing, signing, link);
+ dns_dbiterator_first(signing->dbiterator);
+ dns_dbiterator_pause(signing->dbiterator);
+ signing = ISC_LIST_HEAD(cleanup);
+ }
+
+ for (signing = ISC_LIST_HEAD(zone->signing);
+ signing != NULL;
+ signing = ISC_LIST_NEXT(signing, link))
+ dns_dbiterator_pause(signing->dbiterator);
+
+ dns_diff_clear(&sig_diff);
+
+ for (i = 0; i < nkeys; i++)
+ dst_key_free(&zone_keys[i]);
+
+ if (version != NULL) {
+ dns_db_closeversion(db, &version, ISC_FALSE);
+ dns_db_detach(&db);
+ } else if (db != NULL)
+ dns_db_detach(&db);
+
+ if (ISC_LIST_HEAD(zone->signing) != NULL) {
+ isc_interval_t i;
+ if (zone->update_disabled || result != ISC_R_SUCCESS)
+ isc_interval_set(&i, 60, 0); /* 1 minute */
+ else
+ isc_interval_set(&i, 0, 10000000); /* 10 ms */
+ isc_time_nowplusinterval(&zone->signingtime, &i);
+ } else
+ isc_time_settoepoch(&zone->signingtime);
+}
+
+static void
+zone_maintenance(dns_zone_t *zone) {
+ const char me[] = "zone_maintenance";
+ isc_time_t now;
+ isc_result_t result;
+ isc_boolean_t dumping;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ ENTER;
+
+ /*
+ * Configuring the view of this zone may have
+ * failed, for example because the config file
+ * had a syntax error. In that case, the view
+ * adb or resolver, and we had better not try
+ * to do maintenance on it.
+ */
+ if (zone->view == NULL || zone->view->adb == NULL)
+ return;
+
+ TIME_NOW(&now);
+
+ /*
+ * Expire check.
+ */
+ switch (zone->type) {
+ case dns_zone_slave:
+ case dns_zone_stub:
+ LOCK_ZONE(zone);
+ if (isc_time_compare(&now, &zone->expiretime) >= 0 &&
+ DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED)) {
+ zone_expire(zone);
+ zone->refreshtime = now;
+ }
+ UNLOCK_ZONE(zone);
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * Up to date check.
+ */
+ switch (zone->type) {
+ case dns_zone_slave:
+ case dns_zone_stub:
+ if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALREFRESH) &&
+ isc_time_compare(&now, &zone->refreshtime) >= 0)
+ dns_zone_refresh(zone);
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * Do we need to consolidate the backing store?
+ */
+ switch (zone->type) {
+ case dns_zone_master:
+ case dns_zone_slave:
+ LOCK_ZONE(zone);
+ if (zone->masterfile != NULL &&
+ isc_time_compare(&now, &zone->dumptime) >= 0 &&
+ DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED) &&
+ DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDDUMP)) {
+ dumping = was_dumping(zone);
+ } else
+ dumping = ISC_TRUE;
+ UNLOCK_ZONE(zone);
+ if (!dumping) {
+ result = zone_dump(zone, ISC_TRUE); /* task locked */
+ if (result != ISC_R_SUCCESS)
+ dns_zone_log(zone, ISC_LOG_WARNING,
+ "dump failed: %s",
+ dns_result_totext(result));
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (zone->type) {
+ case dns_zone_master:
+ case dns_zone_slave:
+ /*
+ * Do we need to send out notify messages?
+ */
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY) &&
+ isc_time_compare(&now, &zone->notifytime) >= 0)
+ zone_notify(zone, &now);
+ /*
+ * Do we need to sign/resign some RRsets?
+ */
+ if (!isc_time_isepoch(&zone->signingtime) &&
+ isc_time_compare(&now, &zone->signingtime) >= 0)
+ zone_sign(zone);
+ else if (!isc_time_isepoch(&zone->resigntime) &&
+ isc_time_compare(&now, &zone->resigntime) >= 0)
+ zone_resigninc(zone);
+ else if (!isc_time_isepoch(&zone->nsec3chaintime) &&
+ isc_time_compare(&now, &zone->nsec3chaintime) >= 0)
+ zone_nsec3chain(zone);
+ /*
+ * Do we need to issue a key expiry warning.
+ */
+ if (!isc_time_isepoch(&zone->keywarntime) &&
+ isc_time_compare(&now, &zone->keywarntime) >= 0)
+ set_key_expiry_warning(zone, zone->key_expiry,
+ isc_time_seconds(&now));
+ break;
+ default:
+ break;
+ }
+ zone_settimer(zone, &now);
+}
+
+void
+dns_zone_markdirty(dns_zone_t *zone) {
+
+ LOCK_ZONE(zone);
+ set_resigntime(zone); /* XXXMPA make seperate call back */
+ zone_needdump(zone, DNS_DUMP_DELAY);
+ UNLOCK_ZONE(zone);
+}
+
+void
+dns_zone_expire(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ zone_expire(zone);
+ UNLOCK_ZONE(zone);
+}
+
+static void
+zone_expire(dns_zone_t *zone) {
+ /*
+ * 'zone' locked by caller.
+ */
+
+ REQUIRE(LOCKED_ZONE(zone));
+
+ dns_zone_log(zone, ISC_LOG_WARNING, "expired");
+
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_EXPIRED);
+ zone->refresh = DNS_ZONE_DEFAULTREFRESH;
+ zone->retry = DNS_ZONE_DEFAULTRETRY;
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_HAVETIMERS);
+ zone_unload(zone);
+}
+
+void
+dns_zone_refresh(dns_zone_t *zone) {
+ isc_interval_t i;
+ isc_uint32_t oldflags;
+ unsigned int j;
+ isc_result_t result;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING))
+ return;
+
+ /*
+ * Set DNS_ZONEFLG_REFRESH so that there is only one refresh operation
+ * in progress at a time.
+ */
+
+ LOCK_ZONE(zone);
+ oldflags = zone->flags;
+ if (zone->masterscnt == 0) {
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOMASTERS);
+ if ((oldflags & DNS_ZONEFLG_NOMASTERS) == 0)
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "cannot refresh: no masters");
+ goto unlock;
+ }
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_REFRESH);
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NOEDNS);
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_USEALTXFRSRC);
+ if ((oldflags & (DNS_ZONEFLG_REFRESH|DNS_ZONEFLG_LOADING)) != 0)
+ goto unlock;
+
+ /*
+ * Set the next refresh time as if refresh check has failed.
+ * Setting this to the retry time will do that. XXXMLG
+ * If we are successful it will be reset using zone->refresh.
+ */
+ isc_interval_set(&i, isc_random_jitter(zone->retry, zone->retry / 4),
+ 0);
+ result = isc_time_nowplusinterval(&zone->refreshtime, &i);
+ if (result |= ISC_R_SUCCESS)
+ dns_zone_log(zone, ISC_LOG_WARNING,
+ "isc_time_nowplusinterval() failed: %s",
+ dns_result_totext(result));
+
+ /*
+ * When lacking user-specified timer values from the SOA,
+ * do exponential backoff of the retry time up to a
+ * maximum of six hours.
+ */
+ if (! DNS_ZONE_FLAG(zone, DNS_ZONEFLG_HAVETIMERS))
+ zone->retry = ISC_MIN(zone->retry * 2, 6 * 3600);
+
+ zone->curmaster = 0;
+ for (j = 0; j < zone->masterscnt; j++)
+ zone->mastersok[j] = ISC_FALSE;
+ /* initiate soa query */
+ queue_soa_query(zone);
+ unlock:
+ UNLOCK_ZONE(zone);
+}
+
+isc_result_t
+dns_zone_flush(dns_zone_t *zone) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_boolean_t dumping;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_FLUSH);
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDDUMP) &&
+ zone->masterfile != NULL) {
+ result = ISC_R_ALREADYRUNNING;
+ dumping = was_dumping(zone);
+ } else
+ dumping = ISC_TRUE;
+ UNLOCK_ZONE(zone);
+ if (!dumping)
+ result = zone_dump(zone, ISC_FALSE); /* Unknown task. */
+ return (result);
+}
+
+isc_result_t
+dns_zone_dump(dns_zone_t *zone) {
+ isc_result_t result = ISC_R_ALREADYRUNNING;
+ isc_boolean_t dumping;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ dumping = was_dumping(zone);
+ UNLOCK_ZONE(zone);
+ if (!dumping)
+ result = zone_dump(zone, ISC_FALSE); /* Unknown task. */
+ return (result);
+}
+
+static void
+zone_needdump(dns_zone_t *zone, unsigned int delay) {
+ isc_time_t dumptime;
+ isc_time_t now;
+
+ /*
+ * 'zone' locked by caller
+ */
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(LOCKED_ZONE(zone));
+
+ /*
+ * Do we have a place to dump to and are we loaded?
+ */
+ if (zone->masterfile == NULL ||
+ DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED) == 0)
+ return;
+
+ TIME_NOW(&now);
+ /* add some noise */
+ DNS_ZONE_JITTER_ADD(&now, delay, &dumptime);
+
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDDUMP);
+ if (isc_time_isepoch(&zone->dumptime) ||
+ isc_time_compare(&zone->dumptime, &dumptime) > 0)
+ zone->dumptime = dumptime;
+ if (zone->task != NULL)
+ zone_settimer(zone, &now);
+}
+
+static void
+dump_done(void *arg, isc_result_t result) {
+ const char me[] = "dump_done";
+ dns_zone_t *zone = arg;
+ dns_db_t *db;
+ dns_dbversion_t *version;
+ isc_boolean_t again = ISC_FALSE;
+ isc_boolean_t compact = ISC_FALSE;
+ isc_uint32_t serial;
+ isc_result_t tresult;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ ENTER;
+
+ if (result == ISC_R_SUCCESS && zone->journal != NULL &&
+ zone->journalsize != -1) {
+
+ /*
+ * We don't own these, zone->dctx must stay valid.
+ */
+ db = dns_dumpctx_db(zone->dctx);
+ version = dns_dumpctx_version(zone->dctx);
+
+ tresult = dns_db_getsoaserial(db, version, &serial);
+ /*
+ * Note: we are task locked here so we can test
+ * zone->xfr safely.
+ */
+ if (tresult == ISC_R_SUCCESS && zone->xfr == NULL) {
+ tresult = dns_journal_compact(zone->mctx,
+ zone->journal,
+ serial,
+ zone->journalsize);
+ switch (tresult) {
+ case ISC_R_SUCCESS:
+ case ISC_R_NOSPACE:
+ case ISC_R_NOTFOUND:
+ dns_zone_log(zone, ISC_LOG_DEBUG(3),
+ "dns_journal_compact: %s",
+ dns_result_totext(tresult));
+ break;
+ default:
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "dns_journal_compact failed: %s",
+ dns_result_totext(tresult));
+ break;
+ }
+ } else if (tresult == ISC_R_SUCCESS) {
+ compact = ISC_TRUE;
+ zone->compact_serial = serial;
+ }
+ }
+
+ LOCK_ZONE(zone);
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_DUMPING);
+ if (compact)
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDCOMPACT);
+ if (result != ISC_R_SUCCESS && result != ISC_R_CANCELED) {
+ /*
+ * Try again in a short while.
+ */
+ zone_needdump(zone, DNS_DUMP_DELAY);
+ } else if (result == ISC_R_SUCCESS &&
+ DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FLUSH) &&
+ DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDDUMP) &&
+ DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED)) {
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NEEDDUMP);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_DUMPING);
+ isc_time_settoepoch(&zone->dumptime);
+ again = ISC_TRUE;
+ } else if (result == ISC_R_SUCCESS)
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_FLUSH);
+
+ if (zone->dctx != NULL)
+ dns_dumpctx_detach(&zone->dctx);
+ zonemgr_putio(&zone->writeio);
+ UNLOCK_ZONE(zone);
+ if (again)
+ (void)zone_dump(zone, ISC_FALSE);
+ dns_zone_idetach(&zone);
+}
+
+static isc_result_t
+zone_dump(dns_zone_t *zone, isc_boolean_t compact) {
+ const char me[] = "zone_dump";
+ isc_result_t result;
+ dns_dbversion_t *version = NULL;
+ isc_boolean_t again;
+ dns_db_t *db = NULL;
+ char *masterfile = NULL;
+ dns_masterformat_t masterformat = dns_masterformat_none;
+
+/*
+ * 'compact' MUST only be set if we are task locked.
+ */
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ ENTER;
+
+ redo:
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+ if (zone->db != NULL)
+ dns_db_attach(zone->db, &db);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+ LOCK_ZONE(zone);
+ if (zone->masterfile != NULL) {
+ masterfile = isc_mem_strdup(zone->mctx, zone->masterfile);
+ masterformat = zone->masterformat;
+ }
+ UNLOCK_ZONE(zone);
+ if (db == NULL) {
+ result = DNS_R_NOTLOADED;
+ goto fail;
+ }
+ if (masterfile == NULL) {
+ result = DNS_R_NOMASTERFILE;
+ goto fail;
+ }
+
+ if (compact) {
+ dns_zone_t *dummy = NULL;
+ LOCK_ZONE(zone);
+ zone_iattach(zone, &dummy);
+ result = zonemgr_getio(zone->zmgr, ISC_FALSE, zone->task,
+ zone_gotwritehandle, zone,
+ &zone->writeio);
+ if (result != ISC_R_SUCCESS)
+ zone_idetach(&dummy);
+ else
+ result = DNS_R_CONTINUE;
+ UNLOCK_ZONE(zone);
+ } else {
+ dns_db_currentversion(db, &version);
+ result = dns_master_dump2(zone->mctx, db, version,
+ &dns_master_style_default,
+ masterfile, masterformat);
+ dns_db_closeversion(db, &version, ISC_FALSE);
+ }
+ fail:
+ if (db != NULL)
+ dns_db_detach(&db);
+ if (masterfile != NULL)
+ isc_mem_free(zone->mctx, masterfile);
+ masterfile = NULL;
+
+ if (result == DNS_R_CONTINUE)
+ return (ISC_R_SUCCESS); /* XXXMPA */
+
+ again = ISC_FALSE;
+ LOCK_ZONE(zone);
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_DUMPING);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * Try again in a short while.
+ */
+ zone_needdump(zone, DNS_DUMP_DELAY);
+ } else if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FLUSH) &&
+ DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDDUMP) &&
+ DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED)) {
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NEEDDUMP);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_DUMPING);
+ isc_time_settoepoch(&zone->dumptime);
+ again = ISC_TRUE;
+ } else
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_FLUSH);
+ UNLOCK_ZONE(zone);
+ if (again)
+ goto redo;
+
+ return (result);
+}
+
+static isc_result_t
+dumptostream(dns_zone_t *zone, FILE *fd, const dns_master_style_t *style,
+ dns_masterformat_t format)
+{
+ isc_result_t result;
+ dns_dbversion_t *version = NULL;
+ dns_db_t *db = NULL;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+ if (zone->db != NULL)
+ dns_db_attach(zone->db, &db);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+ if (db == NULL)
+ return (DNS_R_NOTLOADED);
+
+ dns_db_currentversion(db, &version);
+ result = dns_master_dumptostream2(zone->mctx, db, version, style,
+ format, fd);
+ dns_db_closeversion(db, &version, ISC_FALSE);
+ dns_db_detach(&db);
+ return (result);
+}
+
+isc_result_t
+dns_zone_dumptostream2(dns_zone_t *zone, FILE *fd, dns_masterformat_t format,
+ const dns_master_style_t *style) {
+ return dumptostream(zone, fd, style, format);
+}
+
+isc_result_t
+dns_zone_dumptostream(dns_zone_t *zone, FILE *fd) {
+ return dumptostream(zone, fd, &dns_master_style_default,
+ dns_masterformat_text);
+}
+
+isc_result_t
+dns_zone_fulldumptostream(dns_zone_t *zone, FILE *fd) {
+ return dumptostream(zone, fd, &dns_master_style_full,
+ dns_masterformat_text);
+}
+
+void
+dns_zone_unload(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ zone_unload(zone);
+ UNLOCK_ZONE(zone);
+}
+
+static void
+notify_cancel(dns_zone_t *zone) {
+ dns_notify_t *notify;
+
+ /*
+ * 'zone' locked by caller.
+ */
+
+ REQUIRE(LOCKED_ZONE(zone));
+
+ for (notify = ISC_LIST_HEAD(zone->notifies);
+ notify != NULL;
+ notify = ISC_LIST_NEXT(notify, link)) {
+ if (notify->find != NULL)
+ dns_adb_cancelfind(notify->find);
+ if (notify->request != NULL)
+ dns_request_cancel(notify->request);
+ }
+}
+
+static void
+zone_unload(dns_zone_t *zone) {
+
+ /*
+ * 'zone' locked by caller.
+ */
+
+ REQUIRE(LOCKED_ZONE(zone));
+
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_write);
+ zone_detachdb(zone);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write);
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_LOADED);
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NEEDDUMP);
+}
+
+void
+dns_zone_setminrefreshtime(dns_zone_t *zone, isc_uint32_t val) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(val > 0);
+
+ zone->minrefresh = val;
+}
+
+void
+dns_zone_setmaxrefreshtime(dns_zone_t *zone, isc_uint32_t val) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(val > 0);
+
+ zone->maxrefresh = val;
+}
+
+void
+dns_zone_setminretrytime(dns_zone_t *zone, isc_uint32_t val) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(val > 0);
+
+ zone->minretry = val;
+}
+
+void
+dns_zone_setmaxretrytime(dns_zone_t *zone, isc_uint32_t val) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(val > 0);
+
+ zone->maxretry = val;
+}
+
+static isc_boolean_t
+notify_isqueued(dns_zone_t *zone, dns_name_t *name, isc_sockaddr_t *addr) {
+ dns_notify_t *notify;
+
+ for (notify = ISC_LIST_HEAD(zone->notifies);
+ notify != NULL;
+ notify = ISC_LIST_NEXT(notify, link)) {
+ if (notify->request != NULL)
+ continue;
+ if (name != NULL && dns_name_dynamic(&notify->ns) &&
+ dns_name_equal(name, &notify->ns))
+ return (ISC_TRUE);
+ if (addr != NULL && isc_sockaddr_equal(addr, &notify->dst))
+ return (ISC_TRUE);
+ }
+ return (ISC_FALSE);
+}
+
+static isc_boolean_t
+notify_isself(dns_zone_t *zone, isc_sockaddr_t *dst) {
+ dns_tsigkey_t *key = NULL;
+ isc_sockaddr_t src;
+ isc_sockaddr_t any;
+ isc_boolean_t isself;
+ isc_netaddr_t dstaddr;
+
+ if (zone->view == NULL || zone->isself == NULL)
+ return (ISC_FALSE);
+
+ switch (isc_sockaddr_pf(dst)) {
+ case PF_INET:
+ src = zone->notifysrc4;
+ isc_sockaddr_any(&any);
+ break;
+ case PF_INET6:
+ src = zone->notifysrc6;
+ isc_sockaddr_any6(&any);
+ break;
+ default:
+ return (ISC_FALSE);
+ }
+
+ /*
+ * When sending from any the kernel will assign a source address
+ * that matches the destination address.
+ */
+ if (isc_sockaddr_eqaddr(&any, &src))
+ src = *dst;
+
+ isc_netaddr_fromsockaddr(&dstaddr, dst);
+ (void)dns_view_getpeertsig(zone->view, &dstaddr, &key);
+ isself = (zone->isself)(zone->view, key, &src, dst, zone->rdclass,
+ zone->isselfarg);
+ if (key != NULL)
+ dns_tsigkey_detach(&key);
+ return (isself);
+}
+
+static void
+notify_destroy(dns_notify_t *notify, isc_boolean_t locked) {
+ isc_mem_t *mctx;
+
+ /*
+ * Caller holds zone lock.
+ */
+ REQUIRE(DNS_NOTIFY_VALID(notify));
+
+ if (notify->zone != NULL) {
+ if (!locked)
+ LOCK_ZONE(notify->zone);
+ REQUIRE(LOCKED_ZONE(notify->zone));
+ if (ISC_LINK_LINKED(notify, link))
+ ISC_LIST_UNLINK(notify->zone->notifies, notify, link);
+ if (!locked)
+ UNLOCK_ZONE(notify->zone);
+ if (locked)
+ zone_idetach(&notify->zone);
+ else
+ dns_zone_idetach(&notify->zone);
+ }
+ if (notify->find != NULL)
+ dns_adb_destroyfind(&notify->find);
+ if (notify->request != NULL)
+ dns_request_destroy(&notify->request);
+ if (dns_name_dynamic(&notify->ns))
+ dns_name_free(&notify->ns, notify->mctx);
+ mctx = notify->mctx;
+ isc_mem_put(notify->mctx, notify, sizeof(*notify));
+ isc_mem_detach(&mctx);
+}
+
+static isc_result_t
+notify_create(isc_mem_t *mctx, unsigned int flags, dns_notify_t **notifyp) {
+ dns_notify_t *notify;
+
+ REQUIRE(notifyp != NULL && *notifyp == NULL);
+
+ notify = isc_mem_get(mctx, sizeof(*notify));
+ if (notify == NULL)
+ return (ISC_R_NOMEMORY);
+
+ notify->mctx = NULL;
+ isc_mem_attach(mctx, &notify->mctx);
+ notify->flags = flags;
+ notify->zone = NULL;
+ notify->find = NULL;
+ notify->request = NULL;
+ isc_sockaddr_any(&notify->dst);
+ dns_name_init(&notify->ns, NULL);
+ ISC_LINK_INIT(notify, link);
+ notify->magic = NOTIFY_MAGIC;
+ *notifyp = notify;
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * XXXAG should check for DNS_ZONEFLG_EXITING
+ */
+static void
+process_adb_event(isc_task_t *task, isc_event_t *ev) {
+ dns_notify_t *notify;
+ isc_eventtype_t result;
+
+ UNUSED(task);
+
+ notify = ev->ev_arg;
+ REQUIRE(DNS_NOTIFY_VALID(notify));
+ INSIST(task == notify->zone->task);
+ result = ev->ev_type;
+ isc_event_free(&ev);
+ if (result == DNS_EVENT_ADBMOREADDRESSES) {
+ dns_adb_destroyfind(&notify->find);
+ notify_find_address(notify);
+ return;
+ }
+ if (result == DNS_EVENT_ADBNOMOREADDRESSES) {
+ LOCK_ZONE(notify->zone);
+ notify_send(notify);
+ UNLOCK_ZONE(notify->zone);
+ }
+ notify_destroy(notify, ISC_FALSE);
+}
+
+static void
+notify_find_address(dns_notify_t *notify) {
+ isc_result_t result;
+ unsigned int options;
+
+ REQUIRE(DNS_NOTIFY_VALID(notify));
+ options = DNS_ADBFIND_WANTEVENT | DNS_ADBFIND_INET |
+ DNS_ADBFIND_INET6 | DNS_ADBFIND_RETURNLAME;
+
+ if (notify->zone->view->adb == NULL)
+ goto destroy;
+
+ result = dns_adb_createfind(notify->zone->view->adb,
+ notify->zone->task,
+ process_adb_event, notify,
+ &notify->ns, dns_rootname, 0,
+ options, 0, NULL,
+ notify->zone->view->dstport,
+ &notify->find);
+
+ /* Something failed? */
+ if (result != ISC_R_SUCCESS)
+ goto destroy;
+
+ /* More addresses pending? */
+ if ((notify->find->options & DNS_ADBFIND_WANTEVENT) != 0)
+ return;
+
+ /* We have as many addresses as we can get. */
+ LOCK_ZONE(notify->zone);
+ notify_send(notify);
+ UNLOCK_ZONE(notify->zone);
+
+ destroy:
+ notify_destroy(notify, ISC_FALSE);
+}
+
+
+static isc_result_t
+notify_send_queue(dns_notify_t *notify) {
+ isc_event_t *e;
+ isc_result_t result;
+
+ e = isc_event_allocate(notify->mctx, NULL,
+ DNS_EVENT_NOTIFYSENDTOADDR,
+ notify_send_toaddr,
+ notify, sizeof(isc_event_t));
+ if (e == NULL)
+ return (ISC_R_NOMEMORY);
+ e->ev_arg = notify;
+ e->ev_sender = NULL;
+ result = isc_ratelimiter_enqueue(notify->zone->zmgr->rl,
+ notify->zone->task, &e);
+ if (result != ISC_R_SUCCESS)
+ isc_event_free(&e);
+ return (result);
+}
+
+static void
+notify_send_toaddr(isc_task_t *task, isc_event_t *event) {
+ dns_notify_t *notify;
+ isc_result_t result;
+ dns_message_t *message = NULL;
+ isc_netaddr_t dstip;
+ dns_tsigkey_t *key = NULL;
+ char addrbuf[ISC_SOCKADDR_FORMATSIZE];
+ isc_sockaddr_t src;
+ int timeout;
+ isc_boolean_t have_notifysource = ISC_FALSE;
+
+ notify = event->ev_arg;
+ REQUIRE(DNS_NOTIFY_VALID(notify));
+
+ UNUSED(task);
+
+ LOCK_ZONE(notify->zone);
+
+ if (DNS_ZONE_FLAG(notify->zone, DNS_ZONEFLG_LOADED) == 0) {
+ result = ISC_R_CANCELED;
+ goto cleanup;
+ }
+
+ if ((event->ev_attributes & ISC_EVENTATTR_CANCELED) != 0 ||
+ DNS_ZONE_FLAG(notify->zone, DNS_ZONEFLG_EXITING) ||
+ notify->zone->view->requestmgr == NULL ||
+ notify->zone->db == NULL) {
+ result = ISC_R_CANCELED;
+ goto cleanup;
+ }
+
+ /*
+ * The raw IPv4 address should also exist. Don't send to the
+ * mapped form.
+ */
+ if (isc_sockaddr_pf(&notify->dst) == PF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED(&notify->dst.type.sin6.sin6_addr)) {
+ isc_sockaddr_format(&notify->dst, addrbuf, sizeof(addrbuf));
+ notify_log(notify->zone, ISC_LOG_DEBUG(3),
+ "notify: ignoring IPv6 mapped IPV4 address: %s",
+ addrbuf);
+ result = ISC_R_CANCELED;
+ goto cleanup;
+ }
+
+ result = notify_createmessage(notify->zone, notify->flags, &message);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ isc_netaddr_fromsockaddr(&dstip, &notify->dst);
+ (void)dns_view_getpeertsig(notify->zone->view, &dstip, &key);
+
+ isc_sockaddr_format(&notify->dst, addrbuf, sizeof(addrbuf));
+ notify_log(notify->zone, ISC_LOG_DEBUG(3), "sending notify to %s",
+ addrbuf);
+ if (notify->zone->view->peers != NULL) {
+ dns_peer_t *peer = NULL;
+ result = dns_peerlist_peerbyaddr(notify->zone->view->peers,
+ &dstip, &peer);
+ if (result == ISC_R_SUCCESS) {
+ result = dns_peer_getnotifysource(peer, &src);
+ if (result == ISC_R_SUCCESS)
+ have_notifysource = ISC_TRUE;
+ }
+ }
+ switch (isc_sockaddr_pf(&notify->dst)) {
+ case PF_INET:
+ if (!have_notifysource)
+ src = notify->zone->notifysrc4;
+ break;
+ case PF_INET6:
+ if (!have_notifysource)
+ src = notify->zone->notifysrc6;
+ break;
+ default:
+ result = ISC_R_NOTIMPLEMENTED;
+ goto cleanup_key;
+ }
+ timeout = 15;
+ if (DNS_ZONE_FLAG(notify->zone, DNS_ZONEFLG_DIALNOTIFY))
+ timeout = 30;
+ result = dns_request_createvia2(notify->zone->view->requestmgr,
+ message, &src, &notify->dst, 0, key,
+ timeout * 3, timeout,
+ notify->zone->task, notify_done,
+ notify, &notify->request);
+ if (result == ISC_R_SUCCESS) {
+ if (isc_sockaddr_pf(&notify->dst) == AF_INET) {
+ inc_stats(notify->zone,
+ dns_zonestatscounter_notifyoutv4);
+ } else {
+ inc_stats(notify->zone,
+ dns_zonestatscounter_notifyoutv6);
+ }
+ }
+
+ cleanup_key:
+ if (key != NULL)
+ dns_tsigkey_detach(&key);
+ dns_message_destroy(&message);
+ cleanup:
+ UNLOCK_ZONE(notify->zone);
+ if (result != ISC_R_SUCCESS)
+ notify_destroy(notify, ISC_FALSE);
+ isc_event_free(&event);
+}
+
+static void
+notify_send(dns_notify_t *notify) {
+ dns_adbaddrinfo_t *ai;
+ isc_sockaddr_t dst;
+ isc_result_t result;
+ dns_notify_t *new = NULL;
+
+ /*
+ * Zone lock held by caller.
+ */
+ REQUIRE(DNS_NOTIFY_VALID(notify));
+ REQUIRE(LOCKED_ZONE(notify->zone));
+
+ for (ai = ISC_LIST_HEAD(notify->find->list);
+ ai != NULL;
+ ai = ISC_LIST_NEXT(ai, publink)) {
+ dst = ai->sockaddr;
+ if (notify_isqueued(notify->zone, NULL, &dst))
+ continue;
+ if (notify_isself(notify->zone, &dst))
+ continue;
+ new = NULL;
+ result = notify_create(notify->mctx,
+ (notify->flags & DNS_NOTIFY_NOSOA),
+ &new);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ zone_iattach(notify->zone, &new->zone);
+ ISC_LIST_APPEND(new->zone->notifies, new, link);
+ new->dst = dst;
+ result = notify_send_queue(new);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ new = NULL;
+ }
+
+ cleanup:
+ if (new != NULL)
+ notify_destroy(new, ISC_TRUE);
+}
+
+void
+dns_zone_notify(dns_zone_t *zone) {
+ isc_time_t now;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY);
+
+ TIME_NOW(&now);
+ zone_settimer(zone, &now);
+ UNLOCK_ZONE(zone);
+}
+
+static void
+zone_notify(dns_zone_t *zone, isc_time_t *now) {
+ dns_dbnode_t *node = NULL;
+ dns_db_t *zonedb = NULL;
+ dns_dbversion_t *version = NULL;
+ dns_name_t *origin = NULL;
+ dns_name_t master;
+ dns_rdata_ns_t ns;
+ dns_rdata_soa_t soa;
+ isc_uint32_t serial;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_t nsrdset;
+ dns_rdataset_t soardset;
+ isc_result_t result;
+ dns_notify_t *notify = NULL;
+ unsigned int i;
+ isc_sockaddr_t dst;
+ isc_boolean_t isqueued;
+ dns_notifytype_t notifytype;
+ unsigned int flags = 0;
+ isc_boolean_t loggednotify = ISC_FALSE;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY);
+ notifytype = zone->notifytype;
+ DNS_ZONE_TIME_ADD(now, zone->notifydelay, &zone->notifytime);
+ UNLOCK_ZONE(zone);
+
+ if (! DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED))
+ return;
+
+ if (notifytype == dns_notifytype_no)
+ return;
+
+ if (notifytype == dns_notifytype_masteronly &&
+ zone->type != dns_zone_master)
+ return;
+
+ origin = &zone->origin;
+
+ /*
+ * If the zone is dialup we are done as we don't want to send
+ * the current soa so as to force a refresh query.
+ */
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALNOTIFY))
+ flags |= DNS_NOTIFY_NOSOA;
+
+ /*
+ * Get SOA RRset.
+ */
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+ if (zone->db != NULL)
+ dns_db_attach(zone->db, &zonedb);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+ if (zonedb == NULL)
+ return;
+ dns_db_currentversion(zonedb, &version);
+ result = dns_db_findnode(zonedb, origin, ISC_FALSE, &node);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup1;
+
+ dns_rdataset_init(&soardset);
+ result = dns_db_findrdataset(zonedb, node, version, dns_rdatatype_soa,
+ dns_rdatatype_none, 0, &soardset, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup2;
+
+ /*
+ * Find serial and master server's name.
+ */
+ dns_name_init(&master, NULL);
+ result = dns_rdataset_first(&soardset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup3;
+ dns_rdataset_current(&soardset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &soa, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_rdata_reset(&rdata);
+ result = dns_name_dup(&soa.origin, zone->mctx, &master);
+ serial = soa.serial;
+ dns_rdataset_disassociate(&soardset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup3;
+
+ /*
+ * Enqueue notify requests for 'also-notify' servers.
+ */
+ LOCK_ZONE(zone);
+ for (i = 0; i < zone->notifycnt; i++) {
+ dst = zone->notify[i];
+ if (notify_isqueued(zone, NULL, &dst))
+ continue;
+ result = notify_create(zone->mctx, flags, &notify);
+ if (result != ISC_R_SUCCESS)
+ continue;
+ zone_iattach(zone, &notify->zone);
+ notify->dst = dst;
+ ISC_LIST_APPEND(zone->notifies, notify, link);
+ result = notify_send_queue(notify);
+ if (result != ISC_R_SUCCESS)
+ notify_destroy(notify, ISC_TRUE);
+ if (!loggednotify) {
+ notify_log(zone, ISC_LOG_INFO,
+ "sending notifies (serial %u)",
+ serial);
+ loggednotify = ISC_TRUE;
+ }
+ notify = NULL;
+ }
+ UNLOCK_ZONE(zone);
+
+ if (notifytype == dns_notifytype_explicit)
+ goto cleanup3;
+
+ /*
+ * Process NS RRset to generate notifies.
+ */
+
+ dns_rdataset_init(&nsrdset);
+ result = dns_db_findrdataset(zonedb, node, version, dns_rdatatype_ns,
+ dns_rdatatype_none, 0, &nsrdset, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup3;
+
+ result = dns_rdataset_first(&nsrdset);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdataset_current(&nsrdset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &ns, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_rdata_reset(&rdata);
+ /*
+ * Don't notify the master server unless explictly
+ * configured to do so.
+ */
+ if (!DNS_ZONE_OPTION(zone, DNS_ZONEOPT_NOTIFYTOSOA) &&
+ dns_name_compare(&master, &ns.name) == 0) {
+ result = dns_rdataset_next(&nsrdset);
+ continue;
+ }
+
+ if (!loggednotify) {
+ notify_log(zone, ISC_LOG_INFO,
+ "sending notifies (serial %u)",
+ serial);
+ loggednotify = ISC_TRUE;
+ }
+
+ LOCK_ZONE(zone);
+ isqueued = notify_isqueued(zone, &ns.name, NULL);
+ UNLOCK_ZONE(zone);
+ if (isqueued) {
+ result = dns_rdataset_next(&nsrdset);
+ continue;
+ }
+ result = notify_create(zone->mctx, flags, &notify);
+ if (result != ISC_R_SUCCESS)
+ continue;
+ dns_zone_iattach(zone, &notify->zone);
+ result = dns_name_dup(&ns.name, zone->mctx, &notify->ns);
+ if (result != ISC_R_SUCCESS) {
+ LOCK_ZONE(zone);
+ notify_destroy(notify, ISC_TRUE);
+ UNLOCK_ZONE(zone);
+ continue;
+ }
+ LOCK_ZONE(zone);
+ ISC_LIST_APPEND(zone->notifies, notify, link);
+ UNLOCK_ZONE(zone);
+ notify_find_address(notify);
+ notify = NULL;
+ result = dns_rdataset_next(&nsrdset);
+ }
+ dns_rdataset_disassociate(&nsrdset);
+
+ cleanup3:
+ if (dns_name_dynamic(&master))
+ dns_name_free(&master, zone->mctx);
+ cleanup2:
+ dns_db_detachnode(zonedb, &node);
+ cleanup1:
+ dns_db_closeversion(zonedb, &version, ISC_FALSE);
+ dns_db_detach(&zonedb);
+}
+
+/***
+ *** Private
+ ***/
+
+static inline isc_result_t
+save_nsrrset(dns_message_t *message, dns_name_t *name,
+ dns_db_t *db, dns_dbversion_t *version)
+{
+ dns_rdataset_t *nsrdataset = NULL;
+ dns_rdataset_t *rdataset = NULL;
+ dns_dbnode_t *node = NULL;
+ dns_rdata_ns_t ns;
+ isc_result_t result;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ /*
+ * Extract NS RRset from message.
+ */
+ result = dns_message_findname(message, DNS_SECTION_ANSWER, name,
+ dns_rdatatype_ns, dns_rdatatype_none,
+ NULL, &nsrdataset);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+
+ /*
+ * Add NS rdataset.
+ */
+ result = dns_db_findnode(db, name, ISC_TRUE, &node);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ result = dns_db_addrdataset(db, node, version, 0,
+ nsrdataset, 0, NULL);
+ dns_db_detachnode(db, &node);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ /*
+ * Add glue rdatasets.
+ */
+ for (result = dns_rdataset_first(nsrdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(nsrdataset)) {
+ dns_rdataset_current(nsrdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &ns, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_rdata_reset(&rdata);
+ if (!dns_name_issubdomain(&ns.name, name))
+ continue;
+ rdataset = NULL;
+ result = dns_message_findname(message, DNS_SECTION_ADDITIONAL,
+ &ns.name, dns_rdatatype_aaaa,
+ dns_rdatatype_none, NULL,
+ &rdataset);
+ if (result == ISC_R_SUCCESS) {
+ result = dns_db_findnode(db, &ns.name,
+ ISC_TRUE, &node);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ result = dns_db_addrdataset(db, node, version, 0,
+ rdataset, 0, NULL);
+ dns_db_detachnode(db, &node);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ }
+ rdataset = NULL;
+ result = dns_message_findname(message, DNS_SECTION_ADDITIONAL,
+ &ns.name, dns_rdatatype_a,
+ dns_rdatatype_none, NULL,
+ &rdataset);
+ if (result == ISC_R_SUCCESS) {
+ result = dns_db_findnode(db, &ns.name,
+ ISC_TRUE, &node);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ result = dns_db_addrdataset(db, node, version, 0,
+ rdataset, 0, NULL);
+ dns_db_detachnode(db, &node);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ }
+ }
+ if (result != ISC_R_NOMORE)
+ goto fail;
+
+ return (ISC_R_SUCCESS);
+
+fail:
+ return (result);
+}
+
+static void
+stub_callback(isc_task_t *task, isc_event_t *event) {
+ const char me[] = "stub_callback";
+ dns_requestevent_t *revent = (dns_requestevent_t *)event;
+ dns_stub_t *stub = NULL;
+ dns_message_t *msg = NULL;
+ dns_zone_t *zone = NULL;
+ char master[ISC_SOCKADDR_FORMATSIZE];
+ char source[ISC_SOCKADDR_FORMATSIZE];
+ isc_uint32_t nscnt, cnamecnt;
+ isc_result_t result;
+ isc_time_t now;
+ isc_boolean_t exiting = ISC_FALSE;
+ isc_interval_t i;
+ unsigned int j;
+
+ stub = revent->ev_arg;
+ INSIST(DNS_STUB_VALID(stub));
+
+ UNUSED(task);
+
+ zone = stub->zone;
+
+ ENTER;
+
+ TIME_NOW(&now);
+
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING)) {
+ zone_debuglog(zone, me, 1, "exiting");
+ exiting = ISC_TRUE;
+ goto next_master;
+ }
+
+ isc_sockaddr_format(&zone->masteraddr, master, sizeof(master));
+ isc_sockaddr_format(&zone->sourceaddr, source, sizeof(source));
+
+ if (revent->result != ISC_R_SUCCESS) {
+ if (revent->result == ISC_R_TIMEDOUT &&
+ !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NOEDNS)) {
+ LOCK_ZONE(zone);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOEDNS);
+ UNLOCK_ZONE(zone);
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "refreshing stub: timeout retrying "
+ " without EDNS master %s (source %s)",
+ master, source);
+ goto same_master;
+ }
+ dns_zonemgr_unreachableadd(zone->zmgr, &zone->masteraddr,
+ &zone->sourceaddr, &now);
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "could not refresh stub from master %s"
+ " (source %s): %s", master, source,
+ dns_result_totext(revent->result));
+ goto next_master;
+ }
+
+ result = dns_message_create(zone->mctx, DNS_MESSAGE_INTENTPARSE, &msg);
+ if (result != ISC_R_SUCCESS)
+ goto next_master;
+
+ result = dns_request_getresponse(revent->request, msg, 0);
+ if (result != ISC_R_SUCCESS)
+ goto next_master;
+
+ /*
+ * Unexpected rcode.
+ */
+ if (msg->rcode != dns_rcode_noerror) {
+ char rcode[128];
+ isc_buffer_t rb;
+
+ isc_buffer_init(&rb, rcode, sizeof(rcode));
+ (void)dns_rcode_totext(msg->rcode, &rb);
+
+ if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NOEDNS) &&
+ (msg->rcode == dns_rcode_servfail ||
+ msg->rcode == dns_rcode_notimp ||
+ msg->rcode == dns_rcode_formerr)) {
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "refreshing stub: rcode (%.*s) retrying "
+ "without EDNS master %s (source %s)",
+ (int)rb.used, rcode, master, source);
+ LOCK_ZONE(zone);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOEDNS);
+ UNLOCK_ZONE(zone);
+ goto same_master;
+ }
+
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "refreshing stub: "
+ "unexpected rcode (%.*s) from %s (source %s)",
+ (int)rb.used, rcode, master, source);
+ goto next_master;
+ }
+
+ /*
+ * We need complete messages.
+ */
+ if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0) {
+ if (dns_request_usedtcp(revent->request)) {
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "refreshing stub: truncated TCP "
+ "response from master %s (source %s)",
+ master, source);
+ goto next_master;
+ }
+ LOCK_ZONE(zone);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_USEVC);
+ UNLOCK_ZONE(zone);
+ goto same_master;
+ }
+
+ /*
+ * If non-auth log and next master.
+ */
+ if ((msg->flags & DNS_MESSAGEFLAG_AA) == 0) {
+ dns_zone_log(zone, ISC_LOG_INFO, "refreshing stub: "
+ "non-authoritative answer from "
+ "master %s (source %s)", master, source);
+ goto next_master;
+ }
+
+ /*
+ * Sanity checks.
+ */
+ cnamecnt = message_count(msg, DNS_SECTION_ANSWER, dns_rdatatype_cname);
+ nscnt = message_count(msg, DNS_SECTION_ANSWER, dns_rdatatype_ns);
+
+ if (cnamecnt != 0) {
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "refreshing stub: unexpected CNAME response "
+ "from master %s (source %s)", master, source);
+ goto next_master;
+ }
+
+ if (nscnt == 0) {
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "refreshing stub: no NS records in response "
+ "from master %s (source %s)", master, source);
+ goto next_master;
+ }
+
+ /*
+ * Save answer.
+ */
+ result = save_nsrrset(msg, &zone->origin, stub->db, stub->version);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "refreshing stub: unable to save NS records "
+ "from master %s (source %s)", master, source);
+ goto next_master;
+ }
+
+ /*
+ * Tidy up.
+ */
+ dns_db_closeversion(stub->db, &stub->version, ISC_TRUE);
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_write);
+ if (zone->db == NULL)
+ zone_attachdb(zone, stub->db);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write);
+ dns_db_detach(&stub->db);
+
+ if (zone->masterfile != NULL) {
+ dns_zone_dump(zone);
+ TIME_NOW(&zone->loadtime);
+ }
+
+ dns_message_destroy(&msg);
+ isc_event_free(&event);
+ LOCK_ZONE(zone);
+ dns_request_destroy(&zone->request);
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_REFRESH);
+ DNS_ZONE_JITTER_ADD(&now, zone->refresh, &zone->refreshtime);
+ isc_interval_set(&i, zone->expire, 0);
+ DNS_ZONE_TIME_ADD(&now, zone->expire, &zone->expiretime);
+ zone_settimer(zone, &now);
+ UNLOCK_ZONE(zone);
+ goto free_stub;
+
+ next_master:
+ if (stub->version != NULL)
+ dns_db_closeversion(stub->db, &stub->version, ISC_FALSE);
+ if (stub->db != NULL)
+ dns_db_detach(&stub->db);
+ if (msg != NULL)
+ dns_message_destroy(&msg);
+ isc_event_free(&event);
+ LOCK_ZONE(zone);
+ dns_request_destroy(&zone->request);
+ /*
+ * Skip to next failed / untried master.
+ */
+ do {
+ zone->curmaster++;
+ } while (zone->curmaster < zone->masterscnt &&
+ zone->mastersok[zone->curmaster]);
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NOEDNS);
+ if (exiting || zone->curmaster >= zone->masterscnt) {
+ isc_boolean_t done = ISC_TRUE;
+ if (!exiting &&
+ DNS_ZONE_OPTION(zone, DNS_ZONEOPT_USEALTXFRSRC) &&
+ !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_USEALTXFRSRC)) {
+ /*
+ * Did we get a good answer from all the masters?
+ */
+ for (j = 0; j < zone->masterscnt; j++)
+ if (zone->mastersok[j] == ISC_FALSE) {
+ done = ISC_FALSE;
+ break;
+ }
+ } else
+ done = ISC_TRUE;
+ if (!done) {
+ zone->curmaster = 0;
+ /*
+ * Find the next failed master.
+ */
+ while (zone->curmaster < zone->masterscnt &&
+ zone->mastersok[zone->curmaster])
+ zone->curmaster++;
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_USEALTXFRSRC);
+ } else {
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_REFRESH);
+
+ zone_settimer(zone, &now);
+ UNLOCK_ZONE(zone);
+ goto free_stub;
+ }
+ }
+ queue_soa_query(zone);
+ UNLOCK_ZONE(zone);
+ goto free_stub;
+
+ same_master:
+ if (msg != NULL)
+ dns_message_destroy(&msg);
+ isc_event_free(&event);
+ LOCK_ZONE(zone);
+ dns_request_destroy(&zone->request);
+ UNLOCK_ZONE(zone);
+ ns_query(zone, NULL, stub);
+ goto done;
+
+ free_stub:
+ stub->magic = 0;
+ dns_zone_idetach(&stub->zone);
+ INSIST(stub->db == NULL);
+ INSIST(stub->version == NULL);
+ isc_mem_put(stub->mctx, stub, sizeof(*stub));
+
+ done:
+ INSIST(event == NULL);
+ return;
+}
+
+/*
+ * An SOA query has finished (successfully or not).
+ */
+static void
+refresh_callback(isc_task_t *task, isc_event_t *event) {
+ const char me[] = "refresh_callback";
+ dns_requestevent_t *revent = (dns_requestevent_t *)event;
+ dns_zone_t *zone;
+ dns_message_t *msg = NULL;
+ isc_uint32_t soacnt, cnamecnt, soacount, nscount;
+ isc_time_t now;
+ char master[ISC_SOCKADDR_FORMATSIZE];
+ char source[ISC_SOCKADDR_FORMATSIZE];
+ dns_rdataset_t *rdataset = NULL;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_soa_t soa;
+ isc_result_t result;
+ isc_uint32_t serial;
+ unsigned int j;
+
+ zone = revent->ev_arg;
+ INSIST(DNS_ZONE_VALID(zone));
+
+ UNUSED(task);
+
+ ENTER;
+
+ /*
+ * if timeout log and next master;
+ */
+
+ isc_sockaddr_format(&zone->masteraddr, master, sizeof(master));
+ isc_sockaddr_format(&zone->sourceaddr, source, sizeof(source));
+
+ TIME_NOW(&now);
+
+ if (revent->result != ISC_R_SUCCESS) {
+ if (revent->result == ISC_R_TIMEDOUT &&
+ !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NOEDNS)) {
+ LOCK_ZONE(zone);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOEDNS);
+ UNLOCK_ZONE(zone);
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "refresh: timeout retrying without EDNS "
+ "master %s (source %s)", master, source);
+ goto same_master;
+ }
+ if (revent->result == ISC_R_TIMEDOUT &&
+ !dns_request_usedtcp(revent->request)) {
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "refresh: retry limit for "
+ "master %s exceeded (source %s)",
+ master, source);
+ /* Try with slave with TCP. */
+ if (zone->type == dns_zone_slave &&
+ DNS_ZONE_OPTION(zone, DNS_ZONEOPT_TRYTCPREFRESH)) {
+ if (!dns_zonemgr_unreachable(zone->zmgr,
+ &zone->masteraddr,
+ &zone->sourceaddr,
+ &now)) {
+ LOCK_ZONE(zone);
+ DNS_ZONE_SETFLAG(zone,
+ DNS_ZONEFLG_SOABEFOREAXFR);
+ UNLOCK_ZONE(zone);
+ goto tcp_transfer;
+ }
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "refresh: skipped tcp fallback"
+ "as master %s (source %s) is "
+ "unreachable (cached)",
+ master, source);
+ }
+ } else
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "refresh: failure trying master "
+ "%s (source %s): %s", master, source,
+ dns_result_totext(revent->result));
+ goto next_master;
+ }
+
+ result = dns_message_create(zone->mctx, DNS_MESSAGE_INTENTPARSE, &msg);
+ if (result != ISC_R_SUCCESS)
+ goto next_master;
+ result = dns_request_getresponse(revent->request, msg, 0);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "refresh: failure trying master "
+ "%s (source %s): %s", master, source,
+ dns_result_totext(result));
+ goto next_master;
+ }
+
+ /*
+ * Unexpected rcode.
+ */
+ if (msg->rcode != dns_rcode_noerror) {
+ char rcode[128];
+ isc_buffer_t rb;
+
+ isc_buffer_init(&rb, rcode, sizeof(rcode));
+ (void)dns_rcode_totext(msg->rcode, &rb);
+
+ if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NOEDNS) &&
+ (msg->rcode == dns_rcode_servfail ||
+ msg->rcode == dns_rcode_notimp ||
+ msg->rcode == dns_rcode_formerr)) {
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "refresh: rcode (%.*s) retrying without "
+ "EDNS master %s (source %s)",
+ (int)rb.used, rcode, master, source);
+ LOCK_ZONE(zone);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOEDNS);
+ UNLOCK_ZONE(zone);
+ goto same_master;
+ }
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "refresh: unexpected rcode (%.*s) from "
+ "master %s (source %s)", (int)rb.used, rcode,
+ master, source);
+ /*
+ * Perhaps AXFR/IXFR is allowed even if SOA queries arn't.
+ */
+ if (msg->rcode == dns_rcode_refused &&
+ zone->type == dns_zone_slave)
+ goto tcp_transfer;
+ goto next_master;
+ }
+
+ /*
+ * If truncated punt to zone transfer which will query again.
+ */
+ if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0) {
+ if (zone->type == dns_zone_slave) {
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "refresh: truncated UDP answer, "
+ "initiating TCP zone xfer "
+ "for master %s (source %s)",
+ master, source);
+ LOCK_ZONE(zone);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_SOABEFOREAXFR);
+ UNLOCK_ZONE(zone);
+ goto tcp_transfer;
+ } else {
+ INSIST(zone->type == dns_zone_stub);
+ if (dns_request_usedtcp(revent->request)) {
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "refresh: truncated TCP response "
+ "from master %s (source %s)",
+ master, source);
+ goto next_master;
+ }
+ LOCK_ZONE(zone);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_USEVC);
+ UNLOCK_ZONE(zone);
+ goto same_master;
+ }
+ }
+
+ /*
+ * if non-auth log and next master;
+ */
+ if ((msg->flags & DNS_MESSAGEFLAG_AA) == 0) {
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "refresh: non-authoritative answer from "
+ "master %s (source %s)", master, source);
+ goto next_master;
+ }
+
+ cnamecnt = message_count(msg, DNS_SECTION_ANSWER, dns_rdatatype_cname);
+ soacnt = message_count(msg, DNS_SECTION_ANSWER, dns_rdatatype_soa);
+ nscount = message_count(msg, DNS_SECTION_AUTHORITY, dns_rdatatype_ns);
+ soacount = message_count(msg, DNS_SECTION_AUTHORITY,
+ dns_rdatatype_soa);
+
+ /*
+ * There should not be a CNAME record at top of zone.
+ */
+ if (cnamecnt != 0) {
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "refresh: CNAME at top of zone "
+ "in master %s (source %s)", master, source);
+ goto next_master;
+ }
+
+ /*
+ * if referral log and next master;
+ */
+ if (soacnt == 0 && soacount == 0 && nscount != 0) {
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "refresh: referral response "
+ "from master %s (source %s)", master, source);
+ goto next_master;
+ }
+
+ /*
+ * if nodata log and next master;
+ */
+ if (soacnt == 0 && (nscount == 0 || soacount != 0)) {
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "refresh: NODATA response "
+ "from master %s (source %s)", master, source);
+ goto next_master;
+ }
+
+ /*
+ * Only one soa at top of zone.
+ */
+ if (soacnt != 1) {
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "refresh: answer SOA count (%d) != 1 "
+ "from master %s (source %s)",
+ soacnt, master, source);
+ goto next_master;
+ }
+ /*
+ * Extract serial
+ */
+ rdataset = NULL;
+ result = dns_message_findname(msg, DNS_SECTION_ANSWER, &zone->origin,
+ dns_rdatatype_soa, dns_rdatatype_none,
+ NULL, &rdataset);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "refresh: unable to get SOA record "
+ "from master %s (source %s)", master, source);
+ goto next_master;
+ }
+
+ result = dns_rdataset_first(rdataset);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "refresh: dns_rdataset_first() failed");
+ goto next_master;
+ }
+
+ dns_rdataset_current(rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &soa, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ serial = soa.serial;
+
+ zone_debuglog(zone, me, 1, "serial: new %u, old %u",
+ serial, zone->serial);
+ if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED) ||
+ DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FORCEXFER) ||
+ isc_serial_gt(serial, zone->serial)) {
+ if (dns_zonemgr_unreachable(zone->zmgr, &zone->masteraddr,
+ &zone->sourceaddr, &now)) {
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "refresh: skipping %s as master %s "
+ "(source %s) is unreachable (cached)",
+ zone->type == dns_zone_slave ?
+ "zone transfer" : "NS query",
+ master, source);
+ goto next_master;
+ }
+ tcp_transfer:
+ isc_event_free(&event);
+ LOCK_ZONE(zone);
+ dns_request_destroy(&zone->request);
+ UNLOCK_ZONE(zone);
+ if (zone->type == dns_zone_slave) {
+ queue_xfrin(zone);
+ } else {
+ INSIST(zone->type == dns_zone_stub);
+ ns_query(zone, rdataset, NULL);
+ }
+ if (msg != NULL)
+ dns_message_destroy(&msg);
+ } else if (isc_serial_eq(soa.serial, zone->serial)) {
+ if (zone->masterfile != NULL) {
+ result = ISC_R_FAILURE;
+ if (zone->journal != NULL)
+ result = isc_file_settime(zone->journal, &now);
+ if (result == ISC_R_SUCCESS &&
+ !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDDUMP) &&
+ !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DUMPING)) {
+ result = isc_file_settime(zone->masterfile,
+ &now);
+ } else if (result != ISC_R_SUCCESS)
+ result = isc_file_settime(zone->masterfile,
+ &now);
+ /* Someone removed the file from underneath us! */
+ if (result == ISC_R_FILENOTFOUND) {
+ LOCK_ZONE(zone);
+ zone_needdump(zone, DNS_DUMP_DELAY);
+ UNLOCK_ZONE(zone);
+ } else if (result != ISC_R_SUCCESS)
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "refresh: could not set file "
+ "modification time of '%s': %s",
+ zone->masterfile,
+ dns_result_totext(result));
+ }
+ DNS_ZONE_JITTER_ADD(&now, zone->refresh, &zone->refreshtime);
+ DNS_ZONE_TIME_ADD(&now, zone->expire, &zone->expiretime);
+ zone->mastersok[zone->curmaster] = ISC_TRUE;
+ goto next_master;
+ } else {
+ if (!DNS_ZONE_OPTION(zone, DNS_ZONEOPT_MULTIMASTER))
+ dns_zone_log(zone, ISC_LOG_INFO, "serial number (%u) "
+ "received from master %s < ours (%u)",
+ soa.serial, master, zone->serial);
+ else
+ zone_debuglog(zone, me, 1, "ahead");
+ zone->mastersok[zone->curmaster] = ISC_TRUE;
+ goto next_master;
+ }
+ if (msg != NULL)
+ dns_message_destroy(&msg);
+ goto detach;
+
+ next_master:
+ if (msg != NULL)
+ dns_message_destroy(&msg);
+ isc_event_free(&event);
+ LOCK_ZONE(zone);
+ dns_request_destroy(&zone->request);
+ /*
+ * Skip to next failed / untried master.
+ */
+ do {
+ zone->curmaster++;
+ } while (zone->curmaster < zone->masterscnt &&
+ zone->mastersok[zone->curmaster]);
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NOEDNS);
+ if (zone->curmaster >= zone->masterscnt) {
+ isc_boolean_t done = ISC_TRUE;
+ if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_USEALTXFRSRC) &&
+ !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_USEALTXFRSRC)) {
+ /*
+ * Did we get a good answer from all the masters?
+ */
+ for (j = 0; j < zone->masterscnt; j++)
+ if (zone->mastersok[j] == ISC_FALSE) {
+ done = ISC_FALSE;
+ break;
+ }
+ } else
+ done = ISC_TRUE;
+ if (!done) {
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_USEALTXFRSRC);
+ zone->curmaster = 0;
+ /*
+ * Find the next failed master.
+ */
+ while (zone->curmaster < zone->masterscnt &&
+ zone->mastersok[zone->curmaster])
+ zone->curmaster++;
+ goto requeue;
+ }
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_REFRESH);
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDREFRESH)) {
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NEEDREFRESH);
+ zone->refreshtime = now;
+ }
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_USEALTXFRSRC);
+ zone_settimer(zone, &now);
+ UNLOCK_ZONE(zone);
+ goto detach;
+ }
+
+ requeue:
+ queue_soa_query(zone);
+ UNLOCK_ZONE(zone);
+ goto detach;
+
+ same_master:
+ if (msg != NULL)
+ dns_message_destroy(&msg);
+ isc_event_free(&event);
+ LOCK_ZONE(zone);
+ dns_request_destroy(&zone->request);
+ queue_soa_query(zone);
+ UNLOCK_ZONE(zone);
+
+ detach:
+ dns_zone_idetach(&zone);
+ return;
+}
+
+static void
+queue_soa_query(dns_zone_t *zone) {
+ const char me[] = "queue_soa_query";
+ isc_event_t *e;
+ dns_zone_t *dummy = NULL;
+ isc_result_t result;
+
+ ENTER;
+ /*
+ * Locked by caller
+ */
+ REQUIRE(LOCKED_ZONE(zone));
+
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING)) {
+ cancel_refresh(zone);
+ return;
+ }
+
+ e = isc_event_allocate(zone->mctx, NULL, DNS_EVENT_ZONE,
+ soa_query, zone, sizeof(isc_event_t));
+ if (e == NULL) {
+ cancel_refresh(zone);
+ return;
+ }
+
+ /*
+ * Attach so that we won't clean up
+ * until the event is delivered.
+ */
+ zone_iattach(zone, &dummy);
+
+ e->ev_arg = zone;
+ e->ev_sender = NULL;
+ result = isc_ratelimiter_enqueue(zone->zmgr->rl, zone->task, &e);
+ if (result != ISC_R_SUCCESS) {
+ zone_idetach(&dummy);
+ isc_event_free(&e);
+ cancel_refresh(zone);
+ }
+}
+
+static inline isc_result_t
+create_query(dns_zone_t *zone, dns_rdatatype_t rdtype,
+ dns_message_t **messagep)
+{
+ dns_message_t *message = NULL;
+ dns_name_t *qname = NULL;
+ dns_rdataset_t *qrdataset = NULL;
+ isc_result_t result;
+
+ result = dns_message_create(zone->mctx, DNS_MESSAGE_INTENTRENDER,
+ &message);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ message->opcode = dns_opcode_query;
+ message->rdclass = zone->rdclass;
+
+ result = dns_message_gettempname(message, &qname);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = dns_message_gettemprdataset(message, &qrdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ /*
+ * Make question.
+ */
+ dns_name_init(qname, NULL);
+ dns_name_clone(&zone->origin, qname);
+ dns_rdataset_init(qrdataset);
+ dns_rdataset_makequestion(qrdataset, zone->rdclass, rdtype);
+ ISC_LIST_APPEND(qname->list, qrdataset, link);
+ dns_message_addname(message, qname, DNS_SECTION_QUESTION);
+
+ *messagep = message;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (qname != NULL)
+ dns_message_puttempname(message, &qname);
+ if (qrdataset != NULL)
+ dns_message_puttemprdataset(message, &qrdataset);
+ if (message != NULL)
+ dns_message_destroy(&message);
+ return (result);
+}
+
+static isc_result_t
+add_opt(dns_message_t *message, isc_uint16_t udpsize, isc_boolean_t reqnsid) {
+ dns_rdataset_t *rdataset = NULL;
+ dns_rdatalist_t *rdatalist = NULL;
+ dns_rdata_t *rdata = NULL;
+ isc_result_t result;
+
+ result = dns_message_gettemprdatalist(message, &rdatalist);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_message_gettemprdata(message, &rdata);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_message_gettemprdataset(message, &rdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ dns_rdataset_init(rdataset);
+
+ rdatalist->type = dns_rdatatype_opt;
+ rdatalist->covers = 0;
+
+ /*
+ * Set Maximum UDP buffer size.
+ */
+ rdatalist->rdclass = udpsize;
+
+ /*
+ * Set EXTENDED-RCODE, VERSION, DO and Z to 0.
+ */
+ rdatalist->ttl = 0;
+
+ /* Set EDNS options if applicable */
+ if (reqnsid) {
+ unsigned char data[4];
+ isc_buffer_t buf;
+
+ isc_buffer_init(&buf, data, sizeof(data));
+ isc_buffer_putuint16(&buf, DNS_OPT_NSID);
+ isc_buffer_putuint16(&buf, 0);
+ rdata->data = data;
+ rdata->length = sizeof(data);
+ } else {
+ rdata->data = NULL;
+ rdata->length = 0;
+ }
+
+ rdata->rdclass = rdatalist->rdclass;
+ rdata->type = rdatalist->type;
+ rdata->flags = 0;
+
+ ISC_LIST_INIT(rdatalist->rdata);
+ ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
+ RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, rdataset)
+ == ISC_R_SUCCESS);
+
+ return (dns_message_setopt(message, rdataset));
+
+ cleanup:
+ if (rdatalist != NULL)
+ dns_message_puttemprdatalist(message, &rdatalist);
+ if (rdataset != NULL)
+ dns_message_puttemprdataset(message, &rdataset);
+ if (rdata != NULL)
+ dns_message_puttemprdata(message, &rdata);
+
+ return (result);
+}
+
+static void
+soa_query(isc_task_t *task, isc_event_t *event) {
+ const char me[] = "soa_query";
+ isc_result_t result = ISC_R_FAILURE;
+ dns_message_t *message = NULL;
+ dns_zone_t *zone = event->ev_arg;
+ dns_zone_t *dummy = NULL;
+ isc_netaddr_t masterip;
+ dns_tsigkey_t *key = NULL;
+ isc_uint32_t options;
+ isc_boolean_t cancel = ISC_TRUE;
+ int timeout;
+ isc_boolean_t have_xfrsource, reqnsid;
+ isc_uint16_t udpsize = SEND_BUFFER_SIZE;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ UNUSED(task);
+
+ ENTER;
+
+ LOCK_ZONE(zone);
+ if (((event->ev_attributes & ISC_EVENTATTR_CANCELED) != 0) ||
+ DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING) ||
+ zone->view->requestmgr == NULL) {
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING))
+ cancel = ISC_FALSE;
+ goto cleanup;
+ }
+
+ /*
+ * XXX Optimisation: Create message when zone is setup and reuse.
+ */
+ result = create_query(zone, dns_rdatatype_soa, &message);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ again:
+ INSIST(zone->masterscnt > 0);
+ INSIST(zone->curmaster < zone->masterscnt);
+
+ zone->masteraddr = zone->masters[zone->curmaster];
+
+ isc_netaddr_fromsockaddr(&masterip, &zone->masteraddr);
+ /*
+ * First, look for a tsig key in the master statement, then
+ * try for a server key.
+ */
+ if ((zone->masterkeynames != NULL) &&
+ (zone->masterkeynames[zone->curmaster] != NULL)) {
+ dns_view_t *view = dns_zone_getview(zone);
+ dns_name_t *keyname = zone->masterkeynames[zone->curmaster];
+ result = dns_view_gettsig(view, keyname, &key);
+ if (result != ISC_R_SUCCESS) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_name_format(keyname, namebuf, sizeof(namebuf));
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "unable to find key: %s", namebuf);
+ }
+ }
+ if (key == NULL)
+ (void)dns_view_getpeertsig(zone->view, &masterip, &key);
+
+ have_xfrsource = ISC_FALSE;
+ reqnsid = zone->view->requestnsid;
+ if (zone->view->peers != NULL) {
+ dns_peer_t *peer = NULL;
+ isc_boolean_t edns;
+ result = dns_peerlist_peerbyaddr(zone->view->peers,
+ &masterip, &peer);
+ if (result == ISC_R_SUCCESS) {
+ result = dns_peer_getsupportedns(peer, &edns);
+ if (result == ISC_R_SUCCESS && !edns)
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOEDNS);
+ result = dns_peer_gettransfersource(peer,
+ &zone->sourceaddr);
+ if (result == ISC_R_SUCCESS)
+ have_xfrsource = ISC_TRUE;
+ if (zone->view->resolver != NULL)
+ udpsize =
+ dns_resolver_getudpsize(zone->view->resolver);
+ (void)dns_peer_getudpsize(peer, &udpsize);
+ (void)dns_peer_getrequestnsid(peer, &reqnsid);
+ }
+ }
+
+ switch (isc_sockaddr_pf(&zone->masteraddr)) {
+ case PF_INET:
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_USEALTXFRSRC)) {
+ if (isc_sockaddr_equal(&zone->altxfrsource4,
+ &zone->xfrsource4))
+ goto skip_master;
+ zone->sourceaddr = zone->altxfrsource4;
+ } else if (!have_xfrsource)
+ zone->sourceaddr = zone->xfrsource4;
+ break;
+ case PF_INET6:
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_USEALTXFRSRC)) {
+ if (isc_sockaddr_equal(&zone->altxfrsource6,
+ &zone->xfrsource6))
+ goto skip_master;
+ zone->sourceaddr = zone->altxfrsource6;
+ } else if (!have_xfrsource)
+ zone->sourceaddr = zone->xfrsource6;
+ break;
+ default:
+ result = ISC_R_NOTIMPLEMENTED;
+ goto cleanup;
+ }
+
+ options = DNS_ZONE_FLAG(zone, DNS_ZONEFLG_USEVC) ?
+ DNS_REQUESTOPT_TCP : 0;
+
+ if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NOEDNS)) {
+ result = add_opt(message, udpsize, reqnsid);
+ if (result != ISC_R_SUCCESS)
+ zone_debuglog(zone, me, 1,
+ "unable to add opt record: %s",
+ dns_result_totext(result));
+ }
+
+ zone_iattach(zone, &dummy);
+ timeout = 15;
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALREFRESH))
+ timeout = 30;
+ result = dns_request_createvia2(zone->view->requestmgr, message,
+ &zone->sourceaddr, &zone->masteraddr,
+ options, key, timeout * 3, timeout,
+ zone->task, refresh_callback, zone,
+ &zone->request);
+ if (result != ISC_R_SUCCESS) {
+ zone_idetach(&dummy);
+ zone_debuglog(zone, me, 1,
+ "dns_request_createvia2() failed: %s",
+ dns_result_totext(result));
+ goto cleanup;
+ } else {
+ if (isc_sockaddr_pf(&zone->masteraddr) == PF_INET)
+ inc_stats(zone, dns_zonestatscounter_soaoutv4);
+ else
+ inc_stats(zone, dns_zonestatscounter_soaoutv6);
+ }
+ cancel = ISC_FALSE;
+
+ cleanup:
+ if (key != NULL)
+ dns_tsigkey_detach(&key);
+ if (result != ISC_R_SUCCESS)
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_REFRESH);
+ if (message != NULL)
+ dns_message_destroy(&message);
+ if (cancel)
+ cancel_refresh(zone);
+ isc_event_free(&event);
+ UNLOCK_ZONE(zone);
+ dns_zone_idetach(&zone);
+ return;
+
+ skip_master:
+ if (key != NULL)
+ dns_tsigkey_detach(&key);
+ /*
+ * Skip to next failed / untried master.
+ */
+ do {
+ zone->curmaster++;
+ } while (zone->curmaster < zone->masterscnt &&
+ zone->mastersok[zone->curmaster]);
+ if (zone->curmaster < zone->masterscnt)
+ goto again;
+ zone->curmaster = 0;
+ goto cleanup;
+}
+
+static void
+ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) {
+ const char me[] = "ns_query";
+ isc_result_t result;
+ dns_message_t *message = NULL;
+ isc_netaddr_t masterip;
+ dns_tsigkey_t *key = NULL;
+ dns_dbnode_t *node = NULL;
+ int timeout;
+ isc_boolean_t have_xfrsource = ISC_FALSE, reqnsid;
+ isc_uint16_t udpsize = SEND_BUFFER_SIZE;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE((soardataset != NULL && stub == NULL) ||
+ (soardataset == NULL && stub != NULL));
+ REQUIRE(stub == NULL || DNS_STUB_VALID(stub));
+
+ ENTER;
+
+ LOCK_ZONE(zone);
+ if (stub == NULL) {
+ stub = isc_mem_get(zone->mctx, sizeof(*stub));
+ if (stub == NULL)
+ goto cleanup;
+ stub->magic = STUB_MAGIC;
+ stub->mctx = zone->mctx;
+ stub->zone = NULL;
+ stub->db = NULL;
+ stub->version = NULL;
+
+ /*
+ * Attach so that the zone won't disappear from under us.
+ */
+ zone_iattach(zone, &stub->zone);
+
+ /*
+ * If a db exists we will update it, otherwise we create a
+ * new one and attach it to the zone once we have the NS
+ * RRset and glue.
+ */
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+ if (zone->db != NULL) {
+ dns_db_attach(zone->db, &stub->db);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+ } else {
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+
+ INSIST(zone->db_argc >= 1);
+ result = dns_db_create(zone->mctx, zone->db_argv[0],
+ &zone->origin, dns_dbtype_stub,
+ zone->rdclass,
+ zone->db_argc - 1,
+ zone->db_argv + 1,
+ &stub->db);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "refreshing stub: "
+ "could not create "
+ "database: %s",
+ dns_result_totext(result));
+ goto cleanup;
+ }
+ dns_db_settask(stub->db, zone->task);
+ }
+
+ dns_db_newversion(stub->db, &stub->version);
+
+ /*
+ * Update SOA record.
+ */
+ result = dns_db_findnode(stub->db, &zone->origin, ISC_TRUE,
+ &node);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "refreshing stub: "
+ "dns_db_findnode() failed: %s",
+ dns_result_totext(result));
+ goto cleanup;
+ }
+
+ result = dns_db_addrdataset(stub->db, node, stub->version, 0,
+ soardataset, 0, NULL);
+ dns_db_detachnode(stub->db, &node);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "refreshing stub: "
+ "dns_db_addrdataset() failed: %s",
+ dns_result_totext(result));
+ goto cleanup;
+ }
+ }
+
+ /*
+ * XXX Optimisation: Create message when zone is setup and reuse.
+ */
+ result = create_query(zone, dns_rdatatype_ns, &message);
+
+ INSIST(zone->masterscnt > 0);
+ INSIST(zone->curmaster < zone->masterscnt);
+ zone->masteraddr = zone->masters[zone->curmaster];
+
+ isc_netaddr_fromsockaddr(&masterip, &zone->masteraddr);
+ /*
+ * First, look for a tsig key in the master statement, then
+ * try for a server key.
+ */
+ if ((zone->masterkeynames != NULL) &&
+ (zone->masterkeynames[zone->curmaster] != NULL)) {
+ dns_view_t *view = dns_zone_getview(zone);
+ dns_name_t *keyname = zone->masterkeynames[zone->curmaster];
+ result = dns_view_gettsig(view, keyname, &key);
+ if (result != ISC_R_SUCCESS) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_name_format(keyname, namebuf, sizeof(namebuf));
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "unable to find key: %s", namebuf);
+ }
+ }
+ if (key == NULL)
+ (void)dns_view_getpeertsig(zone->view, &masterip, &key);
+
+ reqnsid = zone->view->requestnsid;
+ if (zone->view->peers != NULL) {
+ dns_peer_t *peer = NULL;
+ isc_boolean_t edns;
+ result = dns_peerlist_peerbyaddr(zone->view->peers,
+ &masterip, &peer);
+ if (result == ISC_R_SUCCESS) {
+ result = dns_peer_getsupportedns(peer, &edns);
+ if (result == ISC_R_SUCCESS && !edns)
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOEDNS);
+ result = dns_peer_gettransfersource(peer,
+ &zone->sourceaddr);
+ if (result == ISC_R_SUCCESS)
+ have_xfrsource = ISC_TRUE;
+ if (zone->view->resolver != NULL)
+ udpsize =
+ dns_resolver_getudpsize(zone->view->resolver);
+ (void)dns_peer_getudpsize(peer, &udpsize);
+ (void)dns_peer_getrequestnsid(peer, &reqnsid);
+ }
+
+ }
+ if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NOEDNS)) {
+ result = add_opt(message, udpsize, reqnsid);
+ if (result != ISC_R_SUCCESS)
+ zone_debuglog(zone, me, 1,
+ "unable to add opt record: %s",
+ dns_result_totext(result));
+ }
+
+ /*
+ * Always use TCP so that we shouldn't truncate in additional section.
+ */
+ switch (isc_sockaddr_pf(&zone->masteraddr)) {
+ case PF_INET:
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_USEALTXFRSRC))
+ zone->sourceaddr = zone->altxfrsource4;
+ else if (!have_xfrsource)
+ zone->sourceaddr = zone->xfrsource4;
+ break;
+ case PF_INET6:
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_USEALTXFRSRC))
+ zone->sourceaddr = zone->altxfrsource6;
+ else if (!have_xfrsource)
+ zone->sourceaddr = zone->xfrsource6;
+ break;
+ default:
+ result = ISC_R_NOTIMPLEMENTED;
+ goto cleanup;
+ }
+ timeout = 15;
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALREFRESH))
+ timeout = 30;
+ result = dns_request_createvia2(zone->view->requestmgr, message,
+ &zone->sourceaddr, &zone->masteraddr,
+ DNS_REQUESTOPT_TCP, key, timeout * 3,
+ timeout, zone->task, stub_callback,
+ stub, &zone->request);
+ if (result != ISC_R_SUCCESS) {
+ zone_debuglog(zone, me, 1,
+ "dns_request_createvia() failed: %s",
+ dns_result_totext(result));
+ goto cleanup;
+ }
+ dns_message_destroy(&message);
+ goto unlock;
+
+ cleanup:
+ cancel_refresh(zone);
+ if (stub != NULL) {
+ stub->magic = 0;
+ if (stub->version != NULL)
+ dns_db_closeversion(stub->db, &stub->version,
+ ISC_FALSE);
+ if (stub->db != NULL)
+ dns_db_detach(&stub->db);
+ if (stub->zone != NULL)
+ zone_idetach(&stub->zone);
+ isc_mem_put(stub->mctx, stub, sizeof(*stub));
+ }
+ if (message != NULL)
+ dns_message_destroy(&message);
+ unlock:
+ if (key != NULL)
+ dns_tsigkey_detach(&key);
+ UNLOCK_ZONE(zone);
+ return;
+}
+
+/*
+ * Handle the control event. Note that although this event causes the zone
+ * to shut down, it is not a shutdown event in the sense of the task library.
+ */
+static void
+zone_shutdown(isc_task_t *task, isc_event_t *event) {
+ dns_zone_t *zone = (dns_zone_t *) event->ev_arg;
+ isc_boolean_t free_needed, linked = ISC_FALSE;
+
+ UNUSED(task);
+ REQUIRE(DNS_ZONE_VALID(zone));
+ INSIST(event->ev_type == DNS_EVENT_ZONECONTROL);
+ INSIST(isc_refcount_current(&zone->erefs) == 0);
+ zone_debuglog(zone, "zone_shutdown", 3, "shutting down");
+
+ /*
+ * Stop things being restarted after we cancel them below.
+ */
+ LOCK_ZONE(zone);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_EXITING);
+ UNLOCK_ZONE(zone);
+
+ /*
+ * If we were waiting for xfrin quota, step out of
+ * the queue.
+ * If there's no zone manager, we can't be waiting for the
+ * xfrin quota
+ */
+ if (zone->zmgr != NULL) {
+ RWLOCK(&zone->zmgr->rwlock, isc_rwlocktype_write);
+ if (zone->statelist == &zone->zmgr->waiting_for_xfrin) {
+ ISC_LIST_UNLINK(zone->zmgr->waiting_for_xfrin, zone,
+ statelink);
+ linked = ISC_TRUE;
+ zone->statelist = NULL;
+ }
+ RWUNLOCK(&zone->zmgr->rwlock, isc_rwlocktype_write);
+ }
+
+ /*
+ * In task context, no locking required. See zone_xfrdone().
+ */
+ if (zone->xfr != NULL)
+ dns_xfrin_shutdown(zone->xfr);
+
+ LOCK_ZONE(zone);
+ if (linked) {
+ INSIST(zone->irefs > 0);
+ zone->irefs--;
+ }
+ if (zone->request != NULL) {
+ dns_request_cancel(zone->request);
+ }
+
+ if (zone->readio != NULL)
+ zonemgr_cancelio(zone->readio);
+
+ if (zone->lctx != NULL)
+ dns_loadctx_cancel(zone->lctx);
+
+ if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FLUSH) ||
+ !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DUMPING)) {
+ if (zone->writeio != NULL)
+ zonemgr_cancelio(zone->writeio);
+
+ if (zone->dctx != NULL)
+ dns_dumpctx_cancel(zone->dctx);
+ }
+
+ notify_cancel(zone);
+
+ if (zone->timer != NULL) {
+ isc_timer_detach(&zone->timer);
+ INSIST(zone->irefs > 0);
+ zone->irefs--;
+ }
+
+ if (zone->view != NULL)
+ dns_view_weakdetach(&zone->view);
+
+ /*
+ * We have now canceled everything set the flag to allow exit_check()
+ * to succeed. We must not unlock between setting this flag and
+ * calling exit_check().
+ */
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_SHUTDOWN);
+ free_needed = exit_check(zone);
+ UNLOCK_ZONE(zone);
+ if (free_needed)
+ zone_free(zone);
+}
+
+static void
+zone_timer(isc_task_t *task, isc_event_t *event) {
+ const char me[] = "zone_timer";
+ dns_zone_t *zone = (dns_zone_t *)event->ev_arg;
+
+ UNUSED(task);
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ ENTER;
+
+ zone_maintenance(zone);
+
+ isc_event_free(&event);
+}
+
+static void
+zone_settimer(dns_zone_t *zone, isc_time_t *now) {
+ const char me[] = "zone_settimer";
+ isc_time_t next;
+ isc_result_t result;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING))
+ return;
+
+ isc_time_settoepoch(&next);
+
+ switch (zone->type) {
+ case dns_zone_master:
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY))
+ next = zone->notifytime;
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDDUMP) &&
+ !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DUMPING)) {
+ INSIST(!isc_time_isepoch(&zone->dumptime));
+ if (isc_time_isepoch(&next) ||
+ isc_time_compare(&zone->dumptime, &next) < 0)
+ next = zone->dumptime;
+ }
+ if (!isc_time_isepoch(&zone->resigntime)) {
+ if (isc_time_isepoch(&next) ||
+ isc_time_compare(&zone->resigntime, &next) < 0)
+ next = zone->resigntime;
+ }
+ if (!isc_time_isepoch(&zone->keywarntime)) {
+ if (isc_time_isepoch(&next) ||
+ isc_time_compare(&zone->keywarntime, &next) < 0)
+ next = zone->keywarntime;
+ }
+ if (!isc_time_isepoch(&zone->signingtime)) {
+ if (isc_time_isepoch(&next) ||
+ isc_time_compare(&zone->signingtime, &next) < 0)
+ next = zone->signingtime;
+ }
+ if (!isc_time_isepoch(&zone->nsec3chaintime)) {
+ if (isc_time_isepoch(&next) ||
+ isc_time_compare(&zone->nsec3chaintime, &next) < 0)
+ next = zone->nsec3chaintime;
+ }
+ break;
+
+ case dns_zone_slave:
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY))
+ next = zone->notifytime;
+ /*FALLTHROUGH*/
+
+ case dns_zone_stub:
+ if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_REFRESH) &&
+ !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NOMASTERS) &&
+ !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NOREFRESH) &&
+ !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADING)) {
+ INSIST(!isc_time_isepoch(&zone->refreshtime));
+ if (isc_time_isepoch(&next) ||
+ isc_time_compare(&zone->refreshtime, &next) < 0)
+ next = zone->refreshtime;
+ }
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED)) {
+ INSIST(!isc_time_isepoch(&zone->expiretime));
+ if (isc_time_isepoch(&next) ||
+ isc_time_compare(&zone->expiretime, &next) < 0)
+ next = zone->expiretime;
+ }
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDDUMP) &&
+ !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DUMPING)) {
+ INSIST(!isc_time_isepoch(&zone->dumptime));
+ if (isc_time_isepoch(&next) ||
+ isc_time_compare(&zone->dumptime, &next) < 0)
+ next = zone->dumptime;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (isc_time_isepoch(&next)) {
+ zone_debuglog(zone, me, 10, "settimer inactive");
+ result = isc_timer_reset(zone->timer, isc_timertype_inactive,
+ NULL, NULL, ISC_TRUE);
+ if (result != ISC_R_SUCCESS)
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "could not deactivate zone timer: %s",
+ isc_result_totext(result));
+ } else {
+ if (isc_time_compare(&next, now) <= 0)
+ next = *now;
+ result = isc_timer_reset(zone->timer, isc_timertype_once,
+ &next, NULL, ISC_TRUE);
+ if (result != ISC_R_SUCCESS)
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "could not reset zone timer: %s",
+ isc_result_totext(result));
+ }
+}
+
+static void
+cancel_refresh(dns_zone_t *zone) {
+ const char me[] = "cancel_refresh";
+ isc_time_t now;
+
+ /*
+ * 'zone' locked by caller.
+ */
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(LOCKED_ZONE(zone));
+
+ ENTER;
+
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_REFRESH);
+ TIME_NOW(&now);
+ zone_settimer(zone, &now);
+}
+
+static isc_result_t
+notify_createmessage(dns_zone_t *zone, unsigned int flags,
+ dns_message_t **messagep)
+{
+ dns_db_t *zonedb = NULL;
+ dns_dbnode_t *node = NULL;
+ dns_dbversion_t *version = NULL;
+ dns_message_t *message = NULL;
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ dns_name_t *tempname = NULL;
+ dns_rdata_t *temprdata = NULL;
+ dns_rdatalist_t *temprdatalist = NULL;
+ dns_rdataset_t *temprdataset = NULL;
+
+ isc_result_t result;
+ isc_region_t r;
+ isc_buffer_t *b = NULL;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(messagep != NULL && *messagep == NULL);
+
+ result = dns_message_create(zone->mctx, DNS_MESSAGE_INTENTRENDER,
+ &message);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ message->opcode = dns_opcode_notify;
+ message->flags |= DNS_MESSAGEFLAG_AA;
+ message->rdclass = zone->rdclass;
+
+ result = dns_message_gettempname(message, &tempname);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = dns_message_gettemprdataset(message, &temprdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ /*
+ * Make question.
+ */
+ dns_name_init(tempname, NULL);
+ dns_name_clone(&zone->origin, tempname);
+ dns_rdataset_init(temprdataset);
+ dns_rdataset_makequestion(temprdataset, zone->rdclass,
+ dns_rdatatype_soa);
+ ISC_LIST_APPEND(tempname->list, temprdataset, link);
+ dns_message_addname(message, tempname, DNS_SECTION_QUESTION);
+ tempname = NULL;
+ temprdataset = NULL;
+
+ if ((flags & DNS_NOTIFY_NOSOA) != 0)
+ goto done;
+
+ result = dns_message_gettempname(message, &tempname);
+ if (result != ISC_R_SUCCESS)
+ goto soa_cleanup;
+ result = dns_message_gettemprdata(message, &temprdata);
+ if (result != ISC_R_SUCCESS)
+ goto soa_cleanup;
+ result = dns_message_gettemprdataset(message, &temprdataset);
+ if (result != ISC_R_SUCCESS)
+ goto soa_cleanup;
+ result = dns_message_gettemprdatalist(message, &temprdatalist);
+ if (result != ISC_R_SUCCESS)
+ goto soa_cleanup;
+
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+ INSIST(zone->db != NULL); /* XXXJT: is this assumption correct? */
+ dns_db_attach(zone->db, &zonedb);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+
+ dns_name_init(tempname, NULL);
+ dns_name_clone(&zone->origin, tempname);
+ dns_db_currentversion(zonedb, &version);
+ result = dns_db_findnode(zonedb, tempname, ISC_FALSE, &node);
+ if (result != ISC_R_SUCCESS)
+ goto soa_cleanup;
+
+ dns_rdataset_init(&rdataset);
+ result = dns_db_findrdataset(zonedb, node, version,
+ dns_rdatatype_soa,
+ dns_rdatatype_none, 0, &rdataset,
+ NULL);
+ if (result != ISC_R_SUCCESS)
+ goto soa_cleanup;
+ result = dns_rdataset_first(&rdataset);
+ if (result != ISC_R_SUCCESS)
+ goto soa_cleanup;
+ dns_rdataset_current(&rdataset, &rdata);
+ dns_rdata_toregion(&rdata, &r);
+ result = isc_buffer_allocate(zone->mctx, &b, r.length);
+ if (result != ISC_R_SUCCESS)
+ goto soa_cleanup;
+ isc_buffer_putmem(b, r.base, r.length);
+ isc_buffer_usedregion(b, &r);
+ dns_rdata_init(temprdata);
+ dns_rdata_fromregion(temprdata, rdata.rdclass, rdata.type, &r);
+ dns_message_takebuffer(message, &b);
+ result = dns_rdataset_next(&rdataset);
+ dns_rdataset_disassociate(&rdataset);
+ if (result != ISC_R_NOMORE)
+ goto soa_cleanup;
+ temprdatalist->rdclass = rdata.rdclass;
+ temprdatalist->type = rdata.type;
+ temprdatalist->covers = 0;
+ temprdatalist->ttl = rdataset.ttl;
+ ISC_LIST_INIT(temprdatalist->rdata);
+ ISC_LIST_APPEND(temprdatalist->rdata, temprdata, link);
+
+ dns_rdataset_init(temprdataset);
+ result = dns_rdatalist_tordataset(temprdatalist, temprdataset);
+ if (result != ISC_R_SUCCESS)
+ goto soa_cleanup;
+
+ ISC_LIST_APPEND(tempname->list, temprdataset, link);
+ dns_message_addname(message, tempname, DNS_SECTION_ANSWER);
+ temprdatalist = NULL;
+ temprdataset = NULL;
+ temprdata = NULL;
+ tempname = NULL;
+
+ soa_cleanup:
+ if (node != NULL)
+ dns_db_detachnode(zonedb, &node);
+ if (version != NULL)
+ dns_db_closeversion(zonedb, &version, ISC_FALSE);
+ if (zonedb != NULL)
+ dns_db_detach(&zonedb);
+ if (tempname != NULL)
+ dns_message_puttempname(message, &tempname);
+ if (temprdata != NULL)
+ dns_message_puttemprdata(message, &temprdata);
+ if (temprdataset != NULL)
+ dns_message_puttemprdataset(message, &temprdataset);
+ if (temprdatalist != NULL)
+ dns_message_puttemprdatalist(message, &temprdatalist);
+
+ done:
+ *messagep = message;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (tempname != NULL)
+ dns_message_puttempname(message, &tempname);
+ if (temprdataset != NULL)
+ dns_message_puttemprdataset(message, &temprdataset);
+ dns_message_destroy(&message);
+ return (result);
+}
+
+isc_result_t
+dns_zone_notifyreceive(dns_zone_t *zone, isc_sockaddr_t *from,
+ dns_message_t *msg)
+{
+ unsigned int i;
+ dns_rdata_soa_t soa;
+ dns_rdataset_t *rdataset = NULL;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_result_t result;
+ char fromtext[ISC_SOCKADDR_FORMATSIZE];
+ int match = 0;
+ isc_netaddr_t netaddr;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ /*
+ * If type != T_SOA return DNS_R_REFUSED. We don't yet support
+ * ROLLOVER.
+ *
+ * SOA: RFC1996
+ * Check that 'from' is a valid notify source, (zone->masters).
+ * Return DNS_R_REFUSED if not.
+ *
+ * If the notify message contains a serial number check it
+ * against the zones serial and return if <= current serial
+ *
+ * If a refresh check is progress, if so just record the
+ * fact we received a NOTIFY and from where and return.
+ * We will perform a new refresh check when the current one
+ * completes. Return ISC_R_SUCCESS.
+ *
+ * Otherwise initiate a refresh check using 'from' as the
+ * first address to check. Return ISC_R_SUCCESS.
+ */
+
+ isc_sockaddr_format(from, fromtext, sizeof(fromtext));
+
+ /*
+ * We only handle NOTIFY (SOA) at the present.
+ */
+ LOCK_ZONE(zone);
+ if (isc_sockaddr_pf(from) == PF_INET)
+ inc_stats(zone, dns_zonestatscounter_notifyinv4);
+ else
+ inc_stats(zone, dns_zonestatscounter_notifyinv6);
+ if (msg->counts[DNS_SECTION_QUESTION] == 0 ||
+ dns_message_findname(msg, DNS_SECTION_QUESTION, &zone->origin,
+ dns_rdatatype_soa, dns_rdatatype_none,
+ NULL, NULL) != ISC_R_SUCCESS) {
+ UNLOCK_ZONE(zone);
+ if (msg->counts[DNS_SECTION_QUESTION] == 0) {
+ dns_zone_log(zone, ISC_LOG_NOTICE,
+ "NOTIFY with no "
+ "question section from: %s", fromtext);
+ return (DNS_R_FORMERR);
+ }
+ dns_zone_log(zone, ISC_LOG_NOTICE,
+ "NOTIFY zone does not match");
+ return (DNS_R_NOTIMP);
+ }
+
+ /*
+ * If we are a master zone just succeed.
+ */
+ if (zone->type == dns_zone_master) {
+ UNLOCK_ZONE(zone);
+ return (ISC_R_SUCCESS);
+ }
+
+ isc_netaddr_fromsockaddr(&netaddr, from);
+ for (i = 0; i < zone->masterscnt; i++) {
+ if (isc_sockaddr_eqaddr(from, &zone->masters[i]))
+ break;
+ if (zone->view->aclenv.match_mapped &&
+ IN6_IS_ADDR_V4MAPPED(&from->type.sin6.sin6_addr) &&
+ isc_sockaddr_pf(&zone->masters[i]) == AF_INET) {
+ isc_netaddr_t na1, na2;
+ isc_netaddr_fromv4mapped(&na1, &netaddr);
+ isc_netaddr_fromsockaddr(&na2, &zone->masters[i]);
+ if (isc_netaddr_equal(&na1, &na2))
+ break;
+ }
+ }
+
+ /*
+ * Accept notify requests from non masters if they are on
+ * 'zone->notify_acl'.
+ */
+ if (i >= zone->masterscnt && zone->notify_acl != NULL &&
+ dns_acl_match(&netaddr, NULL, zone->notify_acl,
+ &zone->view->aclenv,
+ &match, NULL) == ISC_R_SUCCESS &&
+ match > 0)
+ {
+ /* Accept notify. */
+ } else if (i >= zone->masterscnt) {
+ UNLOCK_ZONE(zone);
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "refused notify from non-master: %s", fromtext);
+ inc_stats(zone, dns_zonestatscounter_notifyrej);
+ return (DNS_R_REFUSED);
+ }
+
+ /*
+ * If the zone is loaded and there are answers check the serial
+ * to see if we need to do a refresh. Do not worry about this
+ * check if we are a dialup zone as we use the notify request
+ * to trigger a refresh check.
+ */
+ if (msg->counts[DNS_SECTION_ANSWER] > 0 &&
+ DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED) &&
+ !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NOREFRESH)) {
+ result = dns_message_findname(msg, DNS_SECTION_ANSWER,
+ &zone->origin,
+ dns_rdatatype_soa,
+ dns_rdatatype_none, NULL,
+ &rdataset);
+ if (result == ISC_R_SUCCESS)
+ result = dns_rdataset_first(rdataset);
+ if (result == ISC_R_SUCCESS) {
+ isc_uint32_t serial = 0;
+
+ dns_rdataset_current(rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &soa, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ serial = soa.serial;
+ if (isc_serial_le(serial, zone->serial)) {
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "notify from %s: "
+ "zone is up to date",
+ fromtext);
+ UNLOCK_ZONE(zone);
+ return (ISC_R_SUCCESS);
+ }
+ }
+ }
+
+ /*
+ * If we got this far and there was a refresh in progress just
+ * let it complete. Record where we got the notify from so we
+ * can perform a refresh check when the current one completes
+ */
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_REFRESH)) {
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDREFRESH);
+ zone->notifyfrom = *from;
+ UNLOCK_ZONE(zone);
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "notify from %s: refresh in progress, "
+ "refresh check queued",
+ fromtext);
+ return (ISC_R_SUCCESS);
+ }
+ zone->notifyfrom = *from;
+ UNLOCK_ZONE(zone);
+ dns_zone_refresh(zone);
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_zone_setnotifyacl(dns_zone_t *zone, dns_acl_t *acl) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (zone->notify_acl != NULL)
+ dns_acl_detach(&zone->notify_acl);
+ dns_acl_attach(acl, &zone->notify_acl);
+ UNLOCK_ZONE(zone);
+}
+
+void
+dns_zone_setqueryacl(dns_zone_t *zone, dns_acl_t *acl) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (zone->query_acl != NULL)
+ dns_acl_detach(&zone->query_acl);
+ dns_acl_attach(acl, &zone->query_acl);
+ UNLOCK_ZONE(zone);
+}
+
+void
+dns_zone_setqueryonacl(dns_zone_t *zone, dns_acl_t *acl) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (zone->queryon_acl != NULL)
+ dns_acl_detach(&zone->queryon_acl);
+ dns_acl_attach(acl, &zone->queryon_acl);
+ UNLOCK_ZONE(zone);
+}
+
+void
+dns_zone_setupdateacl(dns_zone_t *zone, dns_acl_t *acl) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (zone->update_acl != NULL)
+ dns_acl_detach(&zone->update_acl);
+ dns_acl_attach(acl, &zone->update_acl);
+ UNLOCK_ZONE(zone);
+}
+
+void
+dns_zone_setforwardacl(dns_zone_t *zone, dns_acl_t *acl) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (zone->forward_acl != NULL)
+ dns_acl_detach(&zone->forward_acl);
+ dns_acl_attach(acl, &zone->forward_acl);
+ UNLOCK_ZONE(zone);
+}
+
+void
+dns_zone_setxfracl(dns_zone_t *zone, dns_acl_t *acl) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (zone->xfr_acl != NULL)
+ dns_acl_detach(&zone->xfr_acl);
+ dns_acl_attach(acl, &zone->xfr_acl);
+ UNLOCK_ZONE(zone);
+}
+
+dns_acl_t *
+dns_zone_getnotifyacl(dns_zone_t *zone) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->notify_acl);
+}
+
+dns_acl_t *
+dns_zone_getqueryacl(dns_zone_t *zone) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->query_acl);
+}
+
+dns_acl_t *
+dns_zone_getqueryonacl(dns_zone_t *zone) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->queryon_acl);
+}
+
+dns_acl_t *
+dns_zone_getupdateacl(dns_zone_t *zone) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->update_acl);
+}
+
+dns_acl_t *
+dns_zone_getforwardacl(dns_zone_t *zone) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->forward_acl);
+}
+
+dns_acl_t *
+dns_zone_getxfracl(dns_zone_t *zone) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->xfr_acl);
+}
+
+void
+dns_zone_clearupdateacl(dns_zone_t *zone) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (zone->update_acl != NULL)
+ dns_acl_detach(&zone->update_acl);
+ UNLOCK_ZONE(zone);
+}
+
+void
+dns_zone_clearforwardacl(dns_zone_t *zone) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (zone->forward_acl != NULL)
+ dns_acl_detach(&zone->forward_acl);
+ UNLOCK_ZONE(zone);
+}
+
+void
+dns_zone_clearnotifyacl(dns_zone_t *zone) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (zone->notify_acl != NULL)
+ dns_acl_detach(&zone->notify_acl);
+ UNLOCK_ZONE(zone);
+}
+
+void
+dns_zone_clearqueryacl(dns_zone_t *zone) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (zone->query_acl != NULL)
+ dns_acl_detach(&zone->query_acl);
+ UNLOCK_ZONE(zone);
+}
+
+void
+dns_zone_clearqueryonacl(dns_zone_t *zone) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (zone->queryon_acl != NULL)
+ dns_acl_detach(&zone->queryon_acl);
+ UNLOCK_ZONE(zone);
+}
+
+void
+dns_zone_clearxfracl(dns_zone_t *zone) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (zone->xfr_acl != NULL)
+ dns_acl_detach(&zone->xfr_acl);
+ UNLOCK_ZONE(zone);
+}
+
+isc_boolean_t
+dns_zone_getupdatedisabled(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ return (zone->update_disabled);
+
+}
+
+void
+dns_zone_setupdatedisabled(dns_zone_t *zone, isc_boolean_t state) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ zone->update_disabled = state;
+}
+
+isc_boolean_t
+dns_zone_getzeronosoattl(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ return (zone->zero_no_soa_ttl);
+
+}
+
+void
+dns_zone_setzeronosoattl(dns_zone_t *zone, isc_boolean_t state) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ zone->zero_no_soa_ttl = state;
+}
+
+void
+dns_zone_setchecknames(dns_zone_t *zone, dns_severity_t severity) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ zone->check_names = severity;
+}
+
+dns_severity_t
+dns_zone_getchecknames(dns_zone_t *zone) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->check_names);
+}
+
+void
+dns_zone_setjournalsize(dns_zone_t *zone, isc_int32_t size) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ zone->journalsize = size;
+}
+
+isc_int32_t
+dns_zone_getjournalsize(dns_zone_t *zone) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->journalsize);
+}
+
+static void
+zone_namerd_tostr(dns_zone_t *zone, char *buf, size_t length) {
+ isc_result_t result = ISC_R_FAILURE;
+ isc_buffer_t buffer;
+
+ REQUIRE(buf != NULL);
+ REQUIRE(length > 1U);
+
+ /*
+ * Leave space for terminating '\0'.
+ */
+ isc_buffer_init(&buffer, buf, length - 1);
+ if (dns_name_dynamic(&zone->origin))
+ result = dns_name_totext(&zone->origin, ISC_TRUE, &buffer);
+ if (result != ISC_R_SUCCESS &&
+ isc_buffer_availablelength(&buffer) >= (sizeof("<UNKNOWN>") - 1))
+ isc_buffer_putstr(&buffer, "<UNKNOWN>");
+
+ if (isc_buffer_availablelength(&buffer) > 0)
+ isc_buffer_putstr(&buffer, "/");
+ (void)dns_rdataclass_totext(zone->rdclass, &buffer);
+
+ if (zone->view != NULL && strcmp(zone->view->name, "_bind") != 0 &&
+ strcmp(zone->view->name, "_default") != 0 &&
+ strlen(zone->view->name) < isc_buffer_availablelength(&buffer)) {
+ isc_buffer_putstr(&buffer, "/");
+ isc_buffer_putstr(&buffer, zone->view->name);
+ }
+
+ buf[isc_buffer_usedlength(&buffer)] = '\0';
+}
+
+static void
+zone_name_tostr(dns_zone_t *zone, char *buf, size_t length) {
+ isc_result_t result = ISC_R_FAILURE;
+ isc_buffer_t buffer;
+
+ REQUIRE(buf != NULL);
+ REQUIRE(length > 1U);
+
+ /*
+ * Leave space for terminating '\0'.
+ */
+ isc_buffer_init(&buffer, buf, length - 1);
+ if (dns_name_dynamic(&zone->origin))
+ result = dns_name_totext(&zone->origin, ISC_TRUE, &buffer);
+ if (result != ISC_R_SUCCESS &&
+ isc_buffer_availablelength(&buffer) >= (sizeof("<UNKNOWN>") - 1))
+ isc_buffer_putstr(&buffer, "<UNKNOWN>");
+
+ buf[isc_buffer_usedlength(&buffer)] = '\0';
+}
+
+static void
+zone_rdclass_tostr(dns_zone_t *zone, char *buf, size_t length) {
+ isc_buffer_t buffer;
+
+ REQUIRE(buf != NULL);
+ REQUIRE(length > 1U);
+
+ /*
+ * Leave space for terminating '\0'.
+ */
+ isc_buffer_init(&buffer, buf, length - 1);
+ (void)dns_rdataclass_totext(zone->rdclass, &buffer);
+
+ buf[isc_buffer_usedlength(&buffer)] = '\0';
+}
+
+static void
+zone_viewname_tostr(dns_zone_t *zone, char *buf, size_t length) {
+ isc_buffer_t buffer;
+
+ REQUIRE(buf != NULL);
+ REQUIRE(length > 1U);
+
+
+ /*
+ * Leave space for terminating '\0'.
+ */
+ isc_buffer_init(&buffer, buf, length - 1);
+
+ if (zone->view == NULL) {
+ isc_buffer_putstr(&buffer, "_none");
+ } else if (strlen(zone->view->name)
+ < isc_buffer_availablelength(&buffer)) {
+ isc_buffer_putstr(&buffer, zone->view->name);
+ } else {
+ isc_buffer_putstr(&buffer, "_toolong");
+ }
+
+ buf[isc_buffer_usedlength(&buffer)] = '\0';
+}
+
+void
+dns_zone_name(dns_zone_t *zone, char *buf, size_t length) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(buf != NULL);
+ zone_namerd_tostr(zone, buf, length);
+}
+
+static void
+notify_log(dns_zone_t *zone, int level, const char *fmt, ...) {
+ va_list ap;
+ char message[4096];
+
+ if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE)
+ return;
+
+ va_start(ap, fmt);
+ vsnprintf(message, sizeof(message), fmt, ap);
+ va_end(ap);
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_NOTIFY, DNS_LOGMODULE_ZONE,
+ level, "zone %s: %s", zone->strnamerd, message);
+}
+
+void
+dns_zone_logc(dns_zone_t *zone, isc_logcategory_t *category,
+ int level, const char *fmt, ...) {
+ va_list ap;
+ char message[4096];
+
+ if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE)
+ return;
+
+ va_start(ap, fmt);
+ vsnprintf(message, sizeof(message), fmt, ap);
+ va_end(ap);
+ isc_log_write(dns_lctx, category, DNS_LOGMODULE_ZONE,
+ level, "zone %s: %s", zone->strnamerd, message);
+}
+
+void
+dns_zone_log(dns_zone_t *zone, int level, const char *fmt, ...) {
+ va_list ap;
+ char message[4096];
+
+ if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE)
+ return;
+
+ va_start(ap, fmt);
+ vsnprintf(message, sizeof(message), fmt, ap);
+ va_end(ap);
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE,
+ level, "zone %s: %s", zone->strnamerd, message);
+}
+
+static void
+zone_debuglog(dns_zone_t *zone, const char *me, int debuglevel,
+ const char *fmt, ...)
+{
+ va_list ap;
+ char message[4096];
+ int level = ISC_LOG_DEBUG(debuglevel);
+
+ if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE)
+ return;
+
+ va_start(ap, fmt);
+ vsnprintf(message, sizeof(message), fmt, ap);
+ va_end(ap);
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE,
+ level, "%s: zone %s: %s", me, zone->strnamerd, message);
+}
+
+static int
+message_count(dns_message_t *msg, dns_section_t section, dns_rdatatype_t type)
+{
+ isc_result_t result;
+ dns_name_t *name;
+ dns_rdataset_t *curr;
+ int count = 0;
+
+ result = dns_message_firstname(msg, section);
+ while (result == ISC_R_SUCCESS) {
+ name = NULL;
+ dns_message_currentname(msg, section, &name);
+
+ for (curr = ISC_LIST_TAIL(name->list); curr != NULL;
+ curr = ISC_LIST_PREV(curr, link)) {
+ if (curr->type == type)
+ count++;
+ }
+ result = dns_message_nextname(msg, section);
+ }
+
+ return (count);
+}
+
+void
+dns_zone_setmaxxfrin(dns_zone_t *zone, isc_uint32_t maxxfrin) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ zone->maxxfrin = maxxfrin;
+}
+
+isc_uint32_t
+dns_zone_getmaxxfrin(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->maxxfrin);
+}
+
+void
+dns_zone_setmaxxfrout(dns_zone_t *zone, isc_uint32_t maxxfrout) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ zone->maxxfrout = maxxfrout;
+}
+
+isc_uint32_t
+dns_zone_getmaxxfrout(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->maxxfrout);
+}
+
+dns_zonetype_t
+dns_zone_gettype(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->type);
+}
+
+dns_name_t *
+dns_zone_getorigin(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (&zone->origin);
+}
+
+void
+dns_zone_settask(dns_zone_t *zone, isc_task_t *task) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (zone->task != NULL)
+ isc_task_detach(&zone->task);
+ isc_task_attach(task, &zone->task);
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+ if (zone->db != NULL)
+ dns_db_settask(zone->db, zone->task);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+ UNLOCK_ZONE(zone);
+}
+
+void
+dns_zone_gettask(dns_zone_t *zone, isc_task_t **target) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ isc_task_attach(zone->task, target);
+}
+
+void
+dns_zone_setidlein(dns_zone_t *zone, isc_uint32_t idlein) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ if (idlein == 0)
+ idlein = DNS_DEFAULT_IDLEIN;
+ zone->idlein = idlein;
+}
+
+isc_uint32_t
+dns_zone_getidlein(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->idlein);
+}
+
+void
+dns_zone_setidleout(dns_zone_t *zone, isc_uint32_t idleout) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ zone->idleout = idleout;
+}
+
+isc_uint32_t
+dns_zone_getidleout(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->idleout);
+}
+
+static void
+notify_done(isc_task_t *task, isc_event_t *event) {
+ dns_requestevent_t *revent = (dns_requestevent_t *)event;
+ dns_notify_t *notify;
+ isc_result_t result;
+ dns_message_t *message = NULL;
+ isc_buffer_t buf;
+ char rcode[128];
+ char addrbuf[ISC_SOCKADDR_FORMATSIZE];
+
+ UNUSED(task);
+
+ notify = event->ev_arg;
+ REQUIRE(DNS_NOTIFY_VALID(notify));
+ INSIST(task == notify->zone->task);
+
+ isc_buffer_init(&buf, rcode, sizeof(rcode));
+ isc_sockaddr_format(&notify->dst, addrbuf, sizeof(addrbuf));
+
+ result = revent->result;
+ if (result == ISC_R_SUCCESS)
+ result = dns_message_create(notify->zone->mctx,
+ DNS_MESSAGE_INTENTPARSE, &message);
+ if (result == ISC_R_SUCCESS)
+ result = dns_request_getresponse(revent->request, message,
+ DNS_MESSAGEPARSE_PRESERVEORDER);
+ if (result == ISC_R_SUCCESS)
+ result = dns_rcode_totext(message->rcode, &buf);
+ if (result == ISC_R_SUCCESS)
+ notify_log(notify->zone, ISC_LOG_DEBUG(3),
+ "notify response from %s: %.*s",
+ addrbuf, (int)buf.used, rcode);
+ else
+ notify_log(notify->zone, ISC_LOG_DEBUG(2),
+ "notify to %s failed: %s", addrbuf,
+ dns_result_totext(result));
+
+ /*
+ * Old bind's return formerr if they see a soa record. Retry w/o
+ * the soa if we see a formerr and had sent a SOA.
+ */
+ isc_event_free(&event);
+ if (message != NULL && message->rcode == dns_rcode_formerr &&
+ (notify->flags & DNS_NOTIFY_NOSOA) == 0) {
+ notify->flags |= DNS_NOTIFY_NOSOA;
+ dns_request_destroy(&notify->request);
+ result = notify_send_queue(notify);
+ if (result != ISC_R_SUCCESS)
+ notify_destroy(notify, ISC_FALSE);
+ } else {
+ if (result == ISC_R_TIMEDOUT)
+ notify_log(notify->zone, ISC_LOG_DEBUG(1),
+ "notify to %s: retries exceeded", addrbuf);
+ notify_destroy(notify, ISC_FALSE);
+ }
+ if (message != NULL)
+ dns_message_destroy(&message);
+}
+
+isc_result_t
+dns_zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) {
+ isc_result_t result;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ LOCK_ZONE(zone);
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_write);
+ result = zone_replacedb(zone, db, dump);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write);
+ UNLOCK_ZONE(zone);
+ return (result);
+}
+
+static isc_result_t
+zone_replacedb(dns_zone_t *zone, dns_db_t *db, isc_boolean_t dump) {
+ dns_dbversion_t *ver;
+ isc_result_t result;
+ unsigned int soacount = 0;
+ unsigned int nscount = 0;
+
+ /*
+ * 'zone' and 'zonedb' locked by caller.
+ */
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(LOCKED_ZONE(zone));
+
+ result = zone_get_from_db(zone, db, &nscount, &soacount,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if (result == ISC_R_SUCCESS) {
+ if (soacount != 1) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "has %d SOA records", soacount);
+ result = DNS_R_BADZONE;
+ }
+ if (nscount == 0) {
+ dns_zone_log(zone, ISC_LOG_ERROR, "has no NS records");
+ result = DNS_R_BADZONE;
+ }
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ } else {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "retrieving SOA and NS records failed: %s",
+ dns_result_totext(result));
+ return (result);
+ }
+
+ result = check_nsec3param(zone, db);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ ver = NULL;
+ dns_db_currentversion(db, &ver);
+
+ /*
+ * The initial version of a slave zone is always dumped;
+ * subsequent versions may be journalled instead if this
+ * is enabled in the configuration.
+ */
+ if (zone->db != NULL && zone->journal != NULL &&
+ DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IXFRFROMDIFFS) &&
+ !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FORCEXFER)) {
+ isc_uint32_t serial;
+
+ dns_zone_log(zone, ISC_LOG_DEBUG(3), "generating diffs");
+
+ result = dns_db_getsoaserial(db, ver, &serial);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "ixfr-from-differences: unable to get "
+ "new serial");
+ goto fail;
+ }
+
+ /*
+ * This is checked in zone_postload() for master zones.
+ */
+ if (zone->type == dns_zone_slave &&
+ !isc_serial_gt(serial, zone->serial)) {
+ isc_uint32_t serialmin, serialmax;
+ serialmin = (zone->serial + 1) & 0xffffffffU;
+ serialmax = (zone->serial + 0x7fffffffU) & 0xffffffffU;
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "ixfr-from-differences: failed: "
+ "new serial (%u) out of range [%u - %u]",
+ serial, serialmin, serialmax);
+ result = ISC_R_RANGE;
+ goto fail;
+ }
+
+ result = dns_db_diff(zone->mctx, db, ver, zone->db, NULL,
+ zone->journal);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ if (dump)
+ zone_needdump(zone, DNS_DUMP_DELAY);
+ else if (zone->journalsize != -1) {
+ result = dns_journal_compact(zone->mctx, zone->journal,
+ serial, zone->journalsize);
+ switch (result) {
+ case ISC_R_SUCCESS:
+ case ISC_R_NOSPACE:
+ case ISC_R_NOTFOUND:
+ dns_zone_log(zone, ISC_LOG_DEBUG(3),
+ "dns_journal_compact: %s",
+ dns_result_totext(result));
+ break;
+ default:
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "dns_journal_compact failed: %s",
+ dns_result_totext(result));
+ break;
+ }
+ }
+ } else {
+ if (dump && zone->masterfile != NULL) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_ZONE, ISC_LOG_DEBUG(3),
+ "dumping new zone version");
+ result = dns_db_dump2(db, ver, zone->masterfile,
+ zone->masterformat);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+
+ /*
+ * Update the time the zone was updated, so
+ * dns_zone_load can avoid loading it when
+ * the server is reloaded. If isc_time_now
+ * fails for some reason, all that happens is
+ * the timestamp is not updated.
+ */
+ TIME_NOW(&zone->loadtime);
+ }
+
+ if (dump && zone->journal != NULL) {
+ /*
+ * The in-memory database just changed, and
+ * because 'dump' is set, it didn't change by
+ * being loaded from disk. Also, we have not
+ * journalled diffs for this change.
+ * Therefore, the on-disk journal is missing
+ * the deltas for this change. Since it can
+ * no longer be used to bring the zone
+ * up-to-date, it is useless and should be
+ * removed.
+ */
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_ZONE, ISC_LOG_DEBUG(3),
+ "removing journal file");
+ (void)remove(zone->journal);
+ }
+ }
+
+ dns_db_closeversion(db, &ver, ISC_FALSE);
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_ZONE, ISC_LOG_DEBUG(3),
+ "replacing zone database");
+
+ if (zone->db != NULL)
+ zone_detachdb(zone);
+ zone_attachdb(zone, db);
+ dns_db_settask(zone->db, zone->task);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED|DNS_ZONEFLG_NEEDNOTIFY);
+ return (ISC_R_SUCCESS);
+
+ fail:
+ dns_db_closeversion(db, &ver, ISC_FALSE);
+ return (result);
+}
+
+/* The caller must hold the dblock as a writer. */
+static inline void
+zone_attachdb(dns_zone_t *zone, dns_db_t *db) {
+ REQUIRE(zone->db == NULL && db != NULL);
+
+ dns_db_attach(db, &zone->db);
+ if (zone->acache != NULL) {
+ isc_result_t result;
+ result = dns_acache_setdb(zone->acache, db);
+ if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "dns_acache_setdb() failed: %s",
+ isc_result_totext(result));
+ }
+ }
+}
+
+/* The caller must hold the dblock as a writer. */
+static inline void
+zone_detachdb(dns_zone_t *zone) {
+ REQUIRE(zone->db != NULL);
+
+ if (zone->acache != NULL)
+ (void)dns_acache_putdb(zone->acache, zone->db);
+ dns_db_detach(&zone->db);
+}
+
+static void
+zone_xfrdone(dns_zone_t *zone, isc_result_t result) {
+ isc_time_t now;
+ isc_boolean_t again = ISC_FALSE;
+ unsigned int soacount;
+ unsigned int nscount;
+ isc_uint32_t serial, refresh, retry, expire, minimum;
+ isc_result_t xfrresult = result;
+ isc_boolean_t free_needed;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "zone transfer finished: %s", dns_result_totext(result));
+
+ LOCK_ZONE(zone);
+ INSIST((zone->flags & DNS_ZONEFLG_REFRESH) != 0);
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_REFRESH);
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_SOABEFOREAXFR);
+
+ TIME_NOW(&now);
+ switch (result) {
+ case ISC_R_SUCCESS:
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY);
+ /*FALLTHROUGH*/
+ case DNS_R_UPTODATE:
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_FORCEXFER);
+ /*
+ * Has the zone expired underneath us?
+ */
+ ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
+ if (zone->db == NULL) {
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+ goto same_master;
+ }
+
+ /*
+ * Update the zone structure's data from the actual
+ * SOA received.
+ */
+ nscount = 0;
+ soacount = 0;
+ INSIST(zone->db != NULL);
+ result = zone_get_from_db(zone, zone->db, &nscount,
+ &soacount, &serial, &refresh,
+ &retry, &expire, &minimum, NULL);
+ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
+ if (result == ISC_R_SUCCESS) {
+ if (soacount != 1)
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "transferred zone "
+ "has %d SOA record%s", soacount,
+ (soacount != 0) ? "s" : "");
+ if (nscount == 0) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "transferred zone "
+ "has no NS records");
+ if (DNS_ZONE_FLAG(zone,
+ DNS_ZONEFLG_HAVETIMERS)) {
+ zone->refresh = DNS_ZONE_DEFAULTREFRESH;
+ zone->retry = DNS_ZONE_DEFAULTRETRY;
+ }
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_HAVETIMERS);
+ zone_unload(zone);
+ goto next_master;
+ }
+ zone->serial = serial;
+ zone->refresh = RANGE(refresh, zone->minrefresh,
+ zone->maxrefresh);
+ zone->retry = RANGE(retry, zone->minretry,
+ zone->maxretry);
+ zone->expire = RANGE(expire,
+ zone->refresh + zone->retry,
+ DNS_MAX_EXPIRE);
+ zone->minimum = minimum;
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_HAVETIMERS);
+ }
+
+ /*
+ * Set our next update/expire times.
+ */
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDREFRESH)) {
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NEEDREFRESH);
+ zone->refreshtime = now;
+ DNS_ZONE_TIME_ADD(&now, zone->expire,
+ &zone->expiretime);
+ } else {
+ DNS_ZONE_JITTER_ADD(&now, zone->refresh,
+ &zone->refreshtime);
+ DNS_ZONE_TIME_ADD(&now, zone->expire,
+ &zone->expiretime);
+ }
+ if (result == ISC_R_SUCCESS && xfrresult == ISC_R_SUCCESS) {
+ char buf[DNS_NAME_FORMATSIZE + sizeof(": TSIG ''")];
+ if (zone->tsigkey != NULL) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_name_format(&zone->tsigkey->name, namebuf,
+ sizeof(namebuf));
+ snprintf(buf, sizeof(buf), ": TSIG '%s'",
+ namebuf);
+ } else
+ buf[0] = '\0';
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "transferred serial %u%s",
+ zone->serial, buf);
+ }
+
+ /*
+ * This is not neccessary if we just performed a AXFR
+ * however it is necessary for an IXFR / UPTODATE and
+ * won't hurt with an AXFR.
+ */
+ if (zone->masterfile != NULL || zone->journal != NULL) {
+ result = ISC_R_FAILURE;
+ if (zone->journal != NULL)
+ result = isc_file_settime(zone->journal, &now);
+ if (result != ISC_R_SUCCESS &&
+ zone->masterfile != NULL)
+ result = isc_file_settime(zone->masterfile,
+ &now);
+ /* Someone removed the file from underneath us! */
+ if (result == ISC_R_FILENOTFOUND &&
+ zone->masterfile != NULL)
+ zone_needdump(zone, DNS_DUMP_DELAY);
+ else if (result != ISC_R_SUCCESS)
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "transfer: could not set file "
+ "modification time of '%s': %s",
+ zone->masterfile,
+ dns_result_totext(result));
+ }
+
+ inc_stats(zone, dns_zonestatscounter_xfrsuccess);
+ break;
+
+ case DNS_R_BADIXFR:
+ /* Force retry with AXFR. */
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLAG_NOIXFR);
+ goto same_master;
+
+ default:
+ next_master:
+ /*
+ * Skip to next failed / untried master.
+ */
+ do {
+ zone->curmaster++;
+ } while (zone->curmaster < zone->masterscnt &&
+ zone->mastersok[zone->curmaster]);
+ /* FALLTHROUGH */
+ same_master:
+ if (zone->curmaster >= zone->masterscnt) {
+ zone->curmaster = 0;
+ if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_USEALTXFRSRC) &&
+ !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_USEALTXFRSRC)) {
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_REFRESH);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_USEALTXFRSRC);
+ while (zone->curmaster < zone->masterscnt &&
+ zone->mastersok[zone->curmaster])
+ zone->curmaster++;
+ again = ISC_TRUE;
+ } else
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_USEALTXFRSRC);
+ } else {
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_REFRESH);
+ again = ISC_TRUE;
+ }
+ inc_stats(zone, dns_zonestatscounter_xfrfail);
+ break;
+ }
+ zone_settimer(zone, &now);
+
+ /*
+ * If creating the transfer object failed, zone->xfr is NULL.
+ * Otherwise, we are called as the done callback of a zone
+ * transfer object that just entered its shutting-down
+ * state. Since we are no longer responsible for shutting
+ * it down, we can detach our reference.
+ */
+ if (zone->xfr != NULL)
+ dns_xfrin_detach(&zone->xfr);
+
+ if (zone->tsigkey != NULL)
+ dns_tsigkey_detach(&zone->tsigkey);
+
+ /*
+ * Handle any deferred journal compaction.
+ */
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDCOMPACT)) {
+ result = dns_journal_compact(zone->mctx, zone->journal,
+ zone->compact_serial,
+ zone->journalsize);
+ switch (result) {
+ case ISC_R_SUCCESS:
+ case ISC_R_NOSPACE:
+ case ISC_R_NOTFOUND:
+ dns_zone_log(zone, ISC_LOG_DEBUG(3),
+ "dns_journal_compact: %s",
+ dns_result_totext(result));
+ break;
+ default:
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "dns_journal_compact failed: %s",
+ dns_result_totext(result));
+ break;
+ }
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NEEDCOMPACT);
+ }
+
+ /*
+ * This transfer finishing freed up a transfer quota slot.
+ * Let any other zones waiting for quota have it.
+ */
+ RWLOCK(&zone->zmgr->rwlock, isc_rwlocktype_write);
+ ISC_LIST_UNLINK(zone->zmgr->xfrin_in_progress, zone, statelink);
+ zone->statelist = NULL;
+ zmgr_resume_xfrs(zone->zmgr, ISC_FALSE);
+ RWUNLOCK(&zone->zmgr->rwlock, isc_rwlocktype_write);
+
+ /*
+ * Retry with a different server if necessary.
+ */
+ if (again && !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING))
+ queue_soa_query(zone);
+
+ INSIST(zone->irefs > 0);
+ zone->irefs--;
+ free_needed = exit_check(zone);
+ UNLOCK_ZONE(zone);
+ if (free_needed)
+ zone_free(zone);
+}
+
+static void
+zone_loaddone(void *arg, isc_result_t result) {
+ static char me[] = "zone_loaddone";
+ dns_load_t *load = arg;
+ dns_zone_t *zone;
+ isc_result_t tresult;
+
+ REQUIRE(DNS_LOAD_VALID(load));
+ zone = load->zone;
+
+ ENTER;
+
+ tresult = dns_db_endload(load->db, &load->callbacks.add_private);
+ if (tresult != ISC_R_SUCCESS &&
+ (result == ISC_R_SUCCESS || result == DNS_R_SEENINCLUDE))
+ result = tresult;
+
+ LOCK_ZONE(load->zone);
+ (void)zone_postload(load->zone, load->db, load->loadtime, result);
+ zonemgr_putio(&load->zone->readio);
+ DNS_ZONE_CLRFLAG(load->zone, DNS_ZONEFLG_LOADING);
+ UNLOCK_ZONE(load->zone);
+
+ load->magic = 0;
+ dns_db_detach(&load->db);
+ if (load->zone->lctx != NULL)
+ dns_loadctx_detach(&load->zone->lctx);
+ dns_zone_idetach(&load->zone);
+ isc_mem_putanddetach(&load->mctx, load, sizeof(*load));
+}
+
+void
+dns_zone_getssutable(dns_zone_t *zone, dns_ssutable_t **table) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(table != NULL);
+ REQUIRE(*table == NULL);
+
+ LOCK_ZONE(zone);
+ if (zone->ssutable != NULL)
+ dns_ssutable_attach(zone->ssutable, table);
+ UNLOCK_ZONE(zone);
+}
+
+void
+dns_zone_setssutable(dns_zone_t *zone, dns_ssutable_t *table) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (zone->ssutable != NULL)
+ dns_ssutable_detach(&zone->ssutable);
+ if (table != NULL)
+ dns_ssutable_attach(table, &zone->ssutable);
+ UNLOCK_ZONE(zone);
+}
+
+void
+dns_zone_setsigvalidityinterval(dns_zone_t *zone, isc_uint32_t interval) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ zone->sigvalidityinterval = interval;
+}
+
+isc_uint32_t
+dns_zone_getsigvalidityinterval(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->sigvalidityinterval);
+}
+
+void
+dns_zone_setsigresigninginterval(dns_zone_t *zone, isc_uint32_t interval) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ zone->sigresigninginterval = interval;
+}
+
+isc_uint32_t
+dns_zone_getsigresigninginterval(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->sigresigninginterval);
+}
+
+static void
+queue_xfrin(dns_zone_t *zone) {
+ const char me[] = "queue_xfrin";
+ isc_result_t result;
+ dns_zonemgr_t *zmgr = zone->zmgr;
+
+ ENTER;
+
+ INSIST(zone->statelist == NULL);
+
+ RWLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+ ISC_LIST_APPEND(zmgr->waiting_for_xfrin, zone, statelink);
+ LOCK_ZONE(zone);
+ zone->irefs++;
+ UNLOCK_ZONE(zone);
+ zone->statelist = &zmgr->waiting_for_xfrin;
+ result = zmgr_start_xfrin_ifquota(zmgr, zone);
+ RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+
+ if (result == ISC_R_QUOTA) {
+ dns_zone_logc(zone, DNS_LOGCATEGORY_XFER_IN, ISC_LOG_INFO,
+ "zone transfer deferred due to quota");
+ } else if (result != ISC_R_SUCCESS) {
+ dns_zone_logc(zone, DNS_LOGCATEGORY_XFER_IN, ISC_LOG_ERROR,
+ "starting zone transfer: %s",
+ isc_result_totext(result));
+ }
+}
+
+/*
+ * This event callback is called when a zone has received
+ * any necessary zone transfer quota. This is the time
+ * to go ahead and start the transfer.
+ */
+static void
+got_transfer_quota(isc_task_t *task, isc_event_t *event) {
+ isc_result_t result;
+ dns_peer_t *peer = NULL;
+ char master[ISC_SOCKADDR_FORMATSIZE];
+ char source[ISC_SOCKADDR_FORMATSIZE];
+ dns_rdatatype_t xfrtype;
+ dns_zone_t *zone = event->ev_arg;
+ isc_netaddr_t masterip;
+ isc_sockaddr_t sourceaddr;
+ isc_sockaddr_t masteraddr;
+ isc_time_t now;
+
+ UNUSED(task);
+
+ INSIST(task == zone->task);
+
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING)) {
+ result = ISC_R_CANCELED;
+ goto cleanup;
+ }
+
+ TIME_NOW(&now);
+
+ isc_sockaddr_format(&zone->masteraddr, master, sizeof(master));
+ if (dns_zonemgr_unreachable(zone->zmgr, &zone->masteraddr,
+ &zone->sourceaddr, &now)) {
+ isc_sockaddr_format(&zone->sourceaddr, source, sizeof(source));
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "got_transfer_quota: skipping zone transfer as "
+ "master %s (source %s) is unreachable (cached)",
+ master, source);
+ result = ISC_R_CANCELED;
+ goto cleanup;
+ }
+
+ isc_netaddr_fromsockaddr(&masterip, &zone->masteraddr);
+ (void)dns_peerlist_peerbyaddr(zone->view->peers, &masterip, &peer);
+
+ /*
+ * Decide whether we should request IXFR or AXFR.
+ */
+ if (zone->db == NULL) {
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "no database exists yet, requesting AXFR of "
+ "initial version from %s", master);
+ xfrtype = dns_rdatatype_axfr;
+ } else if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_IXFRFROMDIFFS)) {
+ dns_zone_log(zone, ISC_LOG_DEBUG(1), "ixfr-from-differences "
+ "set, requesting AXFR from %s", master);
+ xfrtype = dns_rdatatype_axfr;
+ } else if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FORCEXFER)) {
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "forced reload, requesting AXFR of "
+ "initial version from %s", master);
+ xfrtype = dns_rdatatype_axfr;
+ } else if (DNS_ZONE_FLAG(zone, DNS_ZONEFLAG_NOIXFR)) {
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "retrying with AXFR from %s due to "
+ "previous IXFR failure", master);
+ xfrtype = dns_rdatatype_axfr;
+ LOCK_ZONE(zone);
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLAG_NOIXFR);
+ UNLOCK_ZONE(zone);
+ } else {
+ isc_boolean_t use_ixfr = ISC_TRUE;
+ if (peer != NULL &&
+ dns_peer_getrequestixfr(peer, &use_ixfr) ==
+ ISC_R_SUCCESS) {
+ ; /* Using peer setting */
+ } else {
+ use_ixfr = zone->view->requestixfr;
+ }
+ if (use_ixfr == ISC_FALSE) {
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "IXFR disabled, requesting AXFR from %s",
+ master);
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_SOABEFOREAXFR))
+ xfrtype = dns_rdatatype_soa;
+ else
+ xfrtype = dns_rdatatype_axfr;
+ } else {
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "requesting IXFR from %s", master);
+ xfrtype = dns_rdatatype_ixfr;
+ }
+ }
+
+ /*
+ * Determine if we should attempt to sign the request with TSIG.
+ */
+ result = ISC_R_NOTFOUND;
+ /*
+ * First, look for a tsig key in the master statement, then
+ * try for a server key.
+ */
+ if ((zone->masterkeynames != NULL) &&
+ (zone->masterkeynames[zone->curmaster] != NULL)) {
+ dns_view_t *view = dns_zone_getview(zone);
+ dns_name_t *keyname = zone->masterkeynames[zone->curmaster];
+ result = dns_view_gettsig(view, keyname, &zone->tsigkey);
+ }
+ if (zone->tsigkey == NULL)
+ result = dns_view_getpeertsig(zone->view, &masterip,
+ &zone->tsigkey);
+
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) {
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "could not get TSIG key for zone transfer: %s",
+ isc_result_totext(result));
+ }
+
+ LOCK_ZONE(zone);
+ masteraddr = zone->masteraddr;
+ sourceaddr = zone->sourceaddr;
+ UNLOCK_ZONE(zone);
+ INSIST(isc_sockaddr_pf(&masteraddr) == isc_sockaddr_pf(&sourceaddr));
+ result = dns_xfrin_create2(zone, xfrtype, &masteraddr, &sourceaddr,
+ zone->tsigkey, zone->mctx,
+ zone->zmgr->timermgr, zone->zmgr->socketmgr,
+ zone->task, zone_xfrdone, &zone->xfr);
+ if (result == ISC_R_SUCCESS) {
+ LOCK_ZONE(zone);
+ if (xfrtype == dns_rdatatype_axfr) {
+ if (isc_sockaddr_pf(&masteraddr) == PF_INET)
+ inc_stats(zone, dns_zonestatscounter_axfrreqv4);
+ else
+ inc_stats(zone, dns_zonestatscounter_axfrreqv6);
+ } else if (xfrtype == dns_rdatatype_ixfr) {
+ if (isc_sockaddr_pf(&masteraddr) == PF_INET)
+ inc_stats(zone, dns_zonestatscounter_ixfrreqv4);
+ else
+ inc_stats(zone, dns_zonestatscounter_ixfrreqv6);
+ }
+ UNLOCK_ZONE(zone);
+ }
+ cleanup:
+ /*
+ * Any failure in this function is handled like a failed
+ * zone transfer. This ensures that we get removed from
+ * zmgr->xfrin_in_progress.
+ */
+ if (result != ISC_R_SUCCESS)
+ zone_xfrdone(zone, result);
+
+ isc_event_free(&event);
+}
+
+/*
+ * Update forwarding support.
+ */
+
+static void
+forward_destroy(dns_forward_t *forward) {
+
+ forward->magic = 0;
+ if (forward->request != NULL)
+ dns_request_destroy(&forward->request);
+ if (forward->msgbuf != NULL)
+ isc_buffer_free(&forward->msgbuf);
+ if (forward->zone != NULL)
+ dns_zone_idetach(&forward->zone);
+ isc_mem_putanddetach(&forward->mctx, forward, sizeof(*forward));
+}
+
+static isc_result_t
+sendtomaster(dns_forward_t *forward) {
+ isc_result_t result;
+ isc_sockaddr_t src;
+
+ LOCK_ZONE(forward->zone);
+ if (forward->which >= forward->zone->masterscnt) {
+ UNLOCK_ZONE(forward->zone);
+ return (ISC_R_NOMORE);
+ }
+
+ forward->addr = forward->zone->masters[forward->which];
+ /*
+ * Always use TCP regardless of whether the original update
+ * used TCP.
+ * XXX The timeout may but a bit small if we are far down a
+ * transfer graph and the master has to try several masters.
+ */
+ switch (isc_sockaddr_pf(&forward->addr)) {
+ case PF_INET:
+ src = forward->zone->xfrsource4;
+ break;
+ case PF_INET6:
+ src = forward->zone->xfrsource6;
+ break;
+ default:
+ result = ISC_R_NOTIMPLEMENTED;
+ goto unlock;
+ }
+ result = dns_request_createraw(forward->zone->view->requestmgr,
+ forward->msgbuf,
+ &src, &forward->addr,
+ DNS_REQUESTOPT_TCP, 15 /* XXX */,
+ forward->zone->task,
+ forward_callback, forward,
+ &forward->request);
+ unlock:
+ UNLOCK_ZONE(forward->zone);
+ return (result);
+}
+
+static void
+forward_callback(isc_task_t *task, isc_event_t *event) {
+ const char me[] = "forward_callback";
+ dns_requestevent_t *revent = (dns_requestevent_t *)event;
+ dns_message_t *msg = NULL;
+ char master[ISC_SOCKADDR_FORMATSIZE];
+ isc_result_t result;
+ dns_forward_t *forward;
+ dns_zone_t *zone;
+
+ UNUSED(task);
+
+ forward = revent->ev_arg;
+ INSIST(DNS_FORWARD_VALID(forward));
+ zone = forward->zone;
+ INSIST(DNS_ZONE_VALID(zone));
+
+ ENTER;
+
+ isc_sockaddr_format(&forward->addr, master, sizeof(master));
+
+ if (revent->result != ISC_R_SUCCESS) {
+ dns_zone_log(zone, ISC_LOG_INFO,
+ "could not forward dynamic update to %s: %s",
+ master, dns_result_totext(revent->result));
+ goto next_master;
+ }
+
+ result = dns_message_create(zone->mctx, DNS_MESSAGE_INTENTPARSE, &msg);
+ if (result != ISC_R_SUCCESS)
+ goto next_master;
+
+ result = dns_request_getresponse(revent->request, msg,
+ DNS_MESSAGEPARSE_PRESERVEORDER |
+ DNS_MESSAGEPARSE_CLONEBUFFER);
+ if (result != ISC_R_SUCCESS)
+ goto next_master;
+
+ switch (msg->rcode) {
+ /*
+ * Pass these rcodes back to client.
+ */
+ case dns_rcode_noerror:
+ case dns_rcode_yxdomain:
+ case dns_rcode_yxrrset:
+ case dns_rcode_nxrrset:
+ case dns_rcode_refused:
+ case dns_rcode_nxdomain:
+ break;
+
+ /* These should not occur if the masters/zone are valid. */
+ case dns_rcode_notzone:
+ case dns_rcode_notauth: {
+ char rcode[128];
+ isc_buffer_t rb;
+
+ isc_buffer_init(&rb, rcode, sizeof(rcode));
+ (void)dns_rcode_totext(msg->rcode, &rb);
+ dns_zone_log(zone, ISC_LOG_WARNING,
+ "forwarding dynamic update: "
+ "unexpected response: master %s returned: %.*s",
+ master, (int)rb.used, rcode);
+ goto next_master;
+ }
+
+ /* Try another server for these rcodes. */
+ case dns_rcode_formerr:
+ case dns_rcode_servfail:
+ case dns_rcode_notimp:
+ case dns_rcode_badvers:
+ default:
+ goto next_master;
+ }
+
+ /* call callback */
+ (forward->callback)(forward->callback_arg, ISC_R_SUCCESS, msg);
+ msg = NULL;
+ dns_request_destroy(&forward->request);
+ forward_destroy(forward);
+ isc_event_free(&event);
+ return;
+
+ next_master:
+ if (msg != NULL)
+ dns_message_destroy(&msg);
+ isc_event_free(&event);
+ forward->which++;
+ dns_request_destroy(&forward->request);
+ result = sendtomaster(forward);
+ if (result != ISC_R_SUCCESS) {
+ /* call callback */
+ dns_zone_log(zone, ISC_LOG_DEBUG(3),
+ "exhausted dynamic update forwarder list");
+ (forward->callback)(forward->callback_arg, result, NULL);
+ forward_destroy(forward);
+ }
+}
+
+isc_result_t
+dns_zone_forwardupdate(dns_zone_t *zone, dns_message_t *msg,
+ dns_updatecallback_t callback, void *callback_arg)
+{
+ dns_forward_t *forward;
+ isc_result_t result;
+ isc_region_t *mr;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(msg != NULL);
+ REQUIRE(callback != NULL);
+
+ forward = isc_mem_get(zone->mctx, sizeof(*forward));
+ if (forward == NULL)
+ return (ISC_R_NOMEMORY);
+
+ forward->request = NULL;
+ forward->zone = NULL;
+ forward->msgbuf = NULL;
+ forward->which = 0;
+ forward->mctx = 0;
+ forward->callback = callback;
+ forward->callback_arg = callback_arg;
+ forward->magic = FORWARD_MAGIC;
+
+ mr = dns_message_getrawmessage(msg);
+ if (mr == NULL) {
+ result = ISC_R_UNEXPECTEDEND;
+ goto cleanup;
+ }
+
+ result = isc_buffer_allocate(zone->mctx, &forward->msgbuf, mr->length);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = isc_buffer_copyregion(forward->msgbuf, mr);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ isc_mem_attach(zone->mctx, &forward->mctx);
+ dns_zone_iattach(zone, &forward->zone);
+ result = sendtomaster(forward);
+
+ cleanup:
+ if (result != ISC_R_SUCCESS) {
+ forward_destroy(forward);
+ }
+ return (result);
+}
+
+isc_result_t
+dns_zone_next(dns_zone_t *zone, dns_zone_t **next) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(next != NULL && *next == NULL);
+
+ *next = ISC_LIST_NEXT(zone, link);
+ if (*next == NULL)
+ return (ISC_R_NOMORE);
+ else
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+dns_zone_first(dns_zonemgr_t *zmgr, dns_zone_t **first) {
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+ REQUIRE(first != NULL && *first == NULL);
+
+ *first = ISC_LIST_HEAD(zmgr->zones);
+ if (*first == NULL)
+ return (ISC_R_NOMORE);
+ else
+ return (ISC_R_SUCCESS);
+}
+
+/***
+ *** Zone manager.
+ ***/
+
+isc_result_t
+dns_zonemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
+ isc_timermgr_t *timermgr, isc_socketmgr_t *socketmgr,
+ dns_zonemgr_t **zmgrp)
+{
+ dns_zonemgr_t *zmgr;
+ isc_result_t result;
+ isc_interval_t interval;
+
+ zmgr = isc_mem_get(mctx, sizeof(*zmgr));
+ if (zmgr == NULL)
+ return (ISC_R_NOMEMORY);
+ zmgr->mctx = NULL;
+ zmgr->refs = 1;
+ isc_mem_attach(mctx, &zmgr->mctx);
+ zmgr->taskmgr = taskmgr;
+ zmgr->timermgr = timermgr;
+ zmgr->socketmgr = socketmgr;
+ zmgr->zonetasks = NULL;
+ zmgr->task = NULL;
+ zmgr->rl = NULL;
+ ISC_LIST_INIT(zmgr->zones);
+ ISC_LIST_INIT(zmgr->waiting_for_xfrin);
+ ISC_LIST_INIT(zmgr->xfrin_in_progress);
+ memset(zmgr->unreachable, 0, sizeof(zmgr->unreachable));
+ result = isc_rwlock_init(&zmgr->rwlock, 0, 0);
+ if (result != ISC_R_SUCCESS)
+ goto free_mem;
+
+ zmgr->transfersin = 10;
+ zmgr->transfersperns = 2;
+
+ /* Create the zone task pool. */
+ result = isc_taskpool_create(taskmgr, mctx,
+ 8 /* XXX */, 2, &zmgr->zonetasks);
+ if (result != ISC_R_SUCCESS)
+ goto free_rwlock;
+
+ /* Create a single task for queueing of SOA queries. */
+ result = isc_task_create(taskmgr, 1, &zmgr->task);
+ if (result != ISC_R_SUCCESS)
+ goto free_taskpool;
+ isc_task_setname(zmgr->task, "zmgr", zmgr);
+ result = isc_ratelimiter_create(mctx, timermgr, zmgr->task,
+ &zmgr->rl);
+ if (result != ISC_R_SUCCESS)
+ goto free_task;
+ /* default to 20 refresh queries / notifies per second. */
+ isc_interval_set(&interval, 0, 1000000000/2);
+ result = isc_ratelimiter_setinterval(zmgr->rl, &interval);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ isc_ratelimiter_setpertic(zmgr->rl, 10);
+
+ zmgr->iolimit = 1;
+ zmgr->ioactive = 0;
+ ISC_LIST_INIT(zmgr->high);
+ ISC_LIST_INIT(zmgr->low);
+
+ result = isc_mutex_init(&zmgr->iolock);
+ if (result != ISC_R_SUCCESS)
+ goto free_rl;
+
+ zmgr->magic = ZONEMGR_MAGIC;
+
+ *zmgrp = zmgr;
+ return (ISC_R_SUCCESS);
+
+#if 0
+ free_iolock:
+ DESTROYLOCK(&zmgr->iolock);
+#endif
+ free_rl:
+ isc_ratelimiter_detach(&zmgr->rl);
+ free_task:
+ isc_task_detach(&zmgr->task);
+ free_taskpool:
+ isc_taskpool_destroy(&zmgr->zonetasks);
+ free_rwlock:
+ isc_rwlock_destroy(&zmgr->rwlock);
+ free_mem:
+ isc_mem_put(zmgr->mctx, zmgr, sizeof(*zmgr));
+ isc_mem_detach(&mctx);
+ return (result);
+}
+
+isc_result_t
+dns_zonemgr_managezone(dns_zonemgr_t *zmgr, dns_zone_t *zone) {
+ isc_result_t result;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ RWLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+ LOCK_ZONE(zone);
+ REQUIRE(zone->task == NULL);
+ REQUIRE(zone->timer == NULL);
+ REQUIRE(zone->zmgr == NULL);
+
+ isc_taskpool_gettask(zmgr->zonetasks,
+ dns_name_hash(dns_zone_getorigin(zone),
+ ISC_FALSE),
+ &zone->task);
+
+ /*
+ * Set the task name. The tag will arbitrarily point to one
+ * of the zones sharing the task (in practice, the one
+ * to be managed last).
+ */
+ isc_task_setname(zone->task, "zone", zone);
+
+ result = isc_timer_create(zmgr->timermgr, isc_timertype_inactive,
+ NULL, NULL,
+ zone->task, zone_timer, zone,
+ &zone->timer);
+
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_task;
+
+ /*
+ * The timer "holds" a iref.
+ */
+ zone->irefs++;
+ INSIST(zone->irefs != 0);
+
+ ISC_LIST_APPEND(zmgr->zones, zone, link);
+ zone->zmgr = zmgr;
+ zmgr->refs++;
+
+ goto unlock;
+
+ cleanup_task:
+ isc_task_detach(&zone->task);
+
+ unlock:
+ UNLOCK_ZONE(zone);
+ RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+ return (result);
+}
+
+void
+dns_zonemgr_releasezone(dns_zonemgr_t *zmgr, dns_zone_t *zone) {
+ isc_boolean_t free_now = ISC_FALSE;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+ REQUIRE(zone->zmgr == zmgr);
+
+ RWLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+ LOCK_ZONE(zone);
+
+ ISC_LIST_UNLINK(zmgr->zones, zone, link);
+ zone->zmgr = NULL;
+ zmgr->refs--;
+ if (zmgr->refs == 0)
+ free_now = ISC_TRUE;
+
+ UNLOCK_ZONE(zone);
+ RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+
+ if (free_now)
+ zonemgr_free(zmgr);
+ ENSURE(zone->zmgr == NULL);
+}
+
+void
+dns_zonemgr_attach(dns_zonemgr_t *source, dns_zonemgr_t **target) {
+ REQUIRE(DNS_ZONEMGR_VALID(source));
+ REQUIRE(target != NULL && *target == NULL);
+
+ RWLOCK(&source->rwlock, isc_rwlocktype_write);
+ REQUIRE(source->refs > 0);
+ source->refs++;
+ INSIST(source->refs > 0);
+ RWUNLOCK(&source->rwlock, isc_rwlocktype_write);
+ *target = source;
+}
+
+void
+dns_zonemgr_detach(dns_zonemgr_t **zmgrp) {
+ dns_zonemgr_t *zmgr;
+ isc_boolean_t free_now = ISC_FALSE;
+
+ REQUIRE(zmgrp != NULL);
+ zmgr = *zmgrp;
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ RWLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+ zmgr->refs--;
+ if (zmgr->refs == 0)
+ free_now = ISC_TRUE;
+ RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+
+ if (free_now)
+ zonemgr_free(zmgr);
+}
+
+isc_result_t
+dns_zonemgr_forcemaint(dns_zonemgr_t *zmgr) {
+ dns_zone_t *p;
+
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ RWLOCK(&zmgr->rwlock, isc_rwlocktype_read);
+ for (p = ISC_LIST_HEAD(zmgr->zones);
+ p != NULL;
+ p = ISC_LIST_NEXT(p, link))
+ {
+ dns_zone_maintenance(p);
+ }
+ RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_read);
+
+ /*
+ * Recent configuration changes may have increased the
+ * amount of available transfers quota. Make sure any
+ * transfers currently blocked on quota get started if
+ * possible.
+ */
+ RWLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+ zmgr_resume_xfrs(zmgr, ISC_TRUE);
+ RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_zonemgr_resumexfrs(dns_zonemgr_t *zmgr) {
+
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ RWLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+ zmgr_resume_xfrs(zmgr, ISC_TRUE);
+ RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+}
+
+void
+dns_zonemgr_shutdown(dns_zonemgr_t *zmgr) {
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ isc_ratelimiter_shutdown(zmgr->rl);
+
+ if (zmgr->task != NULL)
+ isc_task_destroy(&zmgr->task);
+ if (zmgr->zonetasks != NULL)
+ isc_taskpool_destroy(&zmgr->zonetasks);
+}
+
+static void
+zonemgr_free(dns_zonemgr_t *zmgr) {
+ isc_mem_t *mctx;
+
+ INSIST(zmgr->refs == 0);
+ INSIST(ISC_LIST_EMPTY(zmgr->zones));
+
+ zmgr->magic = 0;
+
+ DESTROYLOCK(&zmgr->iolock);
+ isc_ratelimiter_detach(&zmgr->rl);
+
+ isc_rwlock_destroy(&zmgr->rwlock);
+ mctx = zmgr->mctx;
+ isc_mem_put(zmgr->mctx, zmgr, sizeof(*zmgr));
+ isc_mem_detach(&mctx);
+}
+
+void
+dns_zonemgr_settransfersin(dns_zonemgr_t *zmgr, isc_uint32_t value) {
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ zmgr->transfersin = value;
+}
+
+isc_uint32_t
+dns_zonemgr_getttransfersin(dns_zonemgr_t *zmgr) {
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ return (zmgr->transfersin);
+}
+
+void
+dns_zonemgr_settransfersperns(dns_zonemgr_t *zmgr, isc_uint32_t value) {
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ zmgr->transfersperns = value;
+}
+
+isc_uint32_t
+dns_zonemgr_getttransfersperns(dns_zonemgr_t *zmgr) {
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ return (zmgr->transfersperns);
+}
+
+/*
+ * Try to start a new incoming zone transfer to fill a quota
+ * slot that was just vacated.
+ *
+ * Requires:
+ * The zone manager is locked by the caller.
+ */
+static void
+zmgr_resume_xfrs(dns_zonemgr_t *zmgr, isc_boolean_t multi) {
+ dns_zone_t *zone;
+ dns_zone_t *next;
+
+ for (zone = ISC_LIST_HEAD(zmgr->waiting_for_xfrin);
+ zone != NULL;
+ zone = next)
+ {
+ isc_result_t result;
+ next = ISC_LIST_NEXT(zone, statelink);
+ result = zmgr_start_xfrin_ifquota(zmgr, zone);
+ if (result == ISC_R_SUCCESS) {
+ if (multi)
+ continue;
+ /*
+ * We successfully filled the slot. We're done.
+ */
+ break;
+ } else if (result == ISC_R_QUOTA) {
+ /*
+ * Not enough quota. This is probably the per-server
+ * quota, because we usually get called when a unit of
+ * global quota has just been freed. Try the next
+ * zone, it may succeed if it uses another master.
+ */
+ continue;
+ } else {
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "starting zone transfer: %s",
+ isc_result_totext(result));
+ break;
+ }
+ }
+}
+
+/*
+ * Try to start an incoming zone transfer for 'zone', quota permitting.
+ *
+ * Requires:
+ * The zone manager is locked by the caller.
+ *
+ * Returns:
+ * ISC_R_SUCCESS There was enough quota and we attempted to
+ * start a transfer. zone_xfrdone() has been or will
+ * be called.
+ * ISC_R_QUOTA Not enough quota.
+ * Others Failure.
+ */
+static isc_result_t
+zmgr_start_xfrin_ifquota(dns_zonemgr_t *zmgr, dns_zone_t *zone) {
+ dns_peer_t *peer = NULL;
+ isc_netaddr_t masterip;
+ isc_uint32_t nxfrsin, nxfrsperns;
+ dns_zone_t *x;
+ isc_uint32_t maxtransfersin, maxtransfersperns;
+ isc_event_t *e;
+
+ /*
+ * Find any configured information about the server we'd
+ * like to transfer this zone from.
+ */
+ isc_netaddr_fromsockaddr(&masterip, &zone->masteraddr);
+ (void)dns_peerlist_peerbyaddr(zone->view->peers,
+ &masterip, &peer);
+
+ /*
+ * Determine the total maximum number of simultaneous
+ * transfers allowed, and the maximum for this specific
+ * master.
+ */
+ maxtransfersin = zmgr->transfersin;
+ maxtransfersperns = zmgr->transfersperns;
+ if (peer != NULL)
+ (void)dns_peer_gettransfers(peer, &maxtransfersperns);
+
+ /*
+ * Count the total number of transfers that are in progress,
+ * and the number of transfers in progress from this master.
+ * We linearly scan a list of all transfers; if this turns
+ * out to be too slow, we could hash on the master address.
+ */
+ nxfrsin = nxfrsperns = 0;
+ for (x = ISC_LIST_HEAD(zmgr->xfrin_in_progress);
+ x != NULL;
+ x = ISC_LIST_NEXT(x, statelink))
+ {
+ isc_netaddr_t xip;
+ isc_netaddr_fromsockaddr(&xip, &x->masteraddr);
+ nxfrsin++;
+ if (isc_netaddr_equal(&xip, &masterip))
+ nxfrsperns++;
+ }
+
+ /* Enforce quota. */
+ if (nxfrsin >= maxtransfersin)
+ return (ISC_R_QUOTA);
+
+ if (nxfrsperns >= maxtransfersperns)
+ return (ISC_R_QUOTA);
+
+ /*
+ * We have sufficient quota. Move the zone to the "xfrin_in_progress"
+ * list and send it an event to let it start the actual transfer in the
+ * context of its own task.
+ */
+ e = isc_event_allocate(zmgr->mctx, zmgr,
+ DNS_EVENT_ZONESTARTXFRIN,
+ got_transfer_quota, zone,
+ sizeof(isc_event_t));
+ if (e == NULL)
+ return (ISC_R_NOMEMORY);
+
+ LOCK_ZONE(zone);
+ INSIST(zone->statelist == &zmgr->waiting_for_xfrin);
+ ISC_LIST_UNLINK(zmgr->waiting_for_xfrin, zone, statelink);
+ ISC_LIST_APPEND(zmgr->xfrin_in_progress, zone, statelink);
+ zone->statelist = &zmgr->xfrin_in_progress;
+ isc_task_send(zone->task, &e);
+ dns_zone_log(zone, ISC_LOG_INFO, "Transfer started.");
+ UNLOCK_ZONE(zone);
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_zonemgr_setiolimit(dns_zonemgr_t *zmgr, isc_uint32_t iolimit) {
+
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+ REQUIRE(iolimit > 0);
+
+ zmgr->iolimit = iolimit;
+}
+
+isc_uint32_t
+dns_zonemgr_getiolimit(dns_zonemgr_t *zmgr) {
+
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ return (zmgr->iolimit);
+}
+
+/*
+ * Get permission to request a file handle from the OS.
+ * An event will be sent to action when one is available.
+ * There are two queues available (high and low), the high
+ * queue will be serviced before the low one.
+ *
+ * zonemgr_putio() must be called after the event is delivered to
+ * 'action'.
+ */
+
+static isc_result_t
+zonemgr_getio(dns_zonemgr_t *zmgr, isc_boolean_t high,
+ isc_task_t *task, isc_taskaction_t action, void *arg,
+ dns_io_t **iop)
+{
+ dns_io_t *io;
+ isc_boolean_t queue;
+
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+ REQUIRE(iop != NULL && *iop == NULL);
+
+ io = isc_mem_get(zmgr->mctx, sizeof(*io));
+ if (io == NULL)
+ return (ISC_R_NOMEMORY);
+ io->event = isc_event_allocate(zmgr->mctx, task, DNS_EVENT_IOREADY,
+ action, arg, sizeof(*io->event));
+ if (io->event == NULL) {
+ isc_mem_put(zmgr->mctx, io, sizeof(*io));
+ return (ISC_R_NOMEMORY);
+ }
+ io->zmgr = zmgr;
+ io->high = high;
+ io->task = NULL;
+ isc_task_attach(task, &io->task);
+ ISC_LINK_INIT(io, link);
+ io->magic = IO_MAGIC;
+
+ LOCK(&zmgr->iolock);
+ zmgr->ioactive++;
+ queue = ISC_TF(zmgr->ioactive > zmgr->iolimit);
+ if (queue) {
+ if (io->high)
+ ISC_LIST_APPEND(zmgr->high, io, link);
+ else
+ ISC_LIST_APPEND(zmgr->low, io, link);
+ }
+ UNLOCK(&zmgr->iolock);
+ *iop = io;
+
+ if (!queue) {
+ isc_task_send(io->task, &io->event);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static void
+zonemgr_putio(dns_io_t **iop) {
+ dns_io_t *io;
+ dns_io_t *next;
+ dns_zonemgr_t *zmgr;
+
+ REQUIRE(iop != NULL);
+ io = *iop;
+ REQUIRE(DNS_IO_VALID(io));
+
+ *iop = NULL;
+
+ INSIST(!ISC_LINK_LINKED(io, link));
+ INSIST(io->event == NULL);
+
+ zmgr = io->zmgr;
+ isc_task_detach(&io->task);
+ io->magic = 0;
+ isc_mem_put(zmgr->mctx, io, sizeof(*io));
+
+ LOCK(&zmgr->iolock);
+ INSIST(zmgr->ioactive > 0);
+ zmgr->ioactive--;
+ next = HEAD(zmgr->high);
+ if (next == NULL)
+ next = HEAD(zmgr->low);
+ if (next != NULL) {
+ if (next->high)
+ ISC_LIST_UNLINK(zmgr->high, next, link);
+ else
+ ISC_LIST_UNLINK(zmgr->low, next, link);
+ INSIST(next->event != NULL);
+ }
+ UNLOCK(&zmgr->iolock);
+ if (next != NULL)
+ isc_task_send(next->task, &next->event);
+}
+
+static void
+zonemgr_cancelio(dns_io_t *io) {
+ isc_boolean_t send_event = ISC_FALSE;
+
+ REQUIRE(DNS_IO_VALID(io));
+
+ /*
+ * If we are queued to be run then dequeue.
+ */
+ LOCK(&io->zmgr->iolock);
+ if (ISC_LINK_LINKED(io, link)) {
+ if (io->high)
+ ISC_LIST_UNLINK(io->zmgr->high, io, link);
+ else
+ ISC_LIST_UNLINK(io->zmgr->low, io, link);
+
+ send_event = ISC_TRUE;
+ INSIST(io->event != NULL);
+ }
+ UNLOCK(&io->zmgr->iolock);
+ if (send_event) {
+ io->event->ev_attributes |= ISC_EVENTATTR_CANCELED;
+ isc_task_send(io->task, &io->event);
+ }
+}
+
+static void
+zone_saveunique(dns_zone_t *zone, const char *path, const char *templat) {
+ char *buf;
+ int buflen;
+ isc_result_t result;
+
+ buflen = strlen(path) + strlen(templat) + 2;
+
+ buf = isc_mem_get(zone->mctx, buflen);
+ if (buf == NULL)
+ return;
+
+ result = isc_file_template(path, templat, buf, buflen);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = isc_file_renameunique(path, buf);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ dns_zone_log(zone, ISC_LOG_WARNING, "saved '%s' as '%s'",
+ path, buf);
+
+ cleanup:
+ isc_mem_put(zone->mctx, buf, buflen);
+}
+
+#if 0
+/* Hook for ondestroy notifcation from a database. */
+
+static void
+dns_zonemgr_dbdestroyed(isc_task_t *task, isc_event_t *event) {
+ dns_db_t *db = event->sender;
+ UNUSED(task);
+
+ isc_event_free(&event);
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ DNS_LOGMODULE_ZONE, ISC_LOG_DEBUG(3),
+ "database (%p) destroyed", (void*) db);
+}
+#endif
+
+void
+dns_zonemgr_setserialqueryrate(dns_zonemgr_t *zmgr, unsigned int value) {
+ isc_interval_t interval;
+ isc_uint32_t s, ns;
+ isc_uint32_t pertic;
+ isc_result_t result;
+
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ if (value == 0)
+ value = 1;
+
+ if (value == 1) {
+ s = 1;
+ ns = 0;
+ pertic = 1;
+ } else if (value <= 10) {
+ s = 0;
+ ns = 1000000000 / value;
+ pertic = 1;
+ } else {
+ s = 0;
+ ns = (1000000000 / value) * 10;
+ pertic = 10;
+ }
+
+ isc_interval_set(&interval, s, ns);
+ result = isc_ratelimiter_setinterval(zmgr->rl, &interval);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ isc_ratelimiter_setpertic(zmgr->rl, pertic);
+
+ zmgr->serialqueryrate = value;
+}
+
+unsigned int
+dns_zonemgr_getserialqueryrate(dns_zonemgr_t *zmgr) {
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ return (zmgr->serialqueryrate);
+}
+
+static isc_boolean_t
+dns_zonemgr_unreachable(dns_zonemgr_t *zmgr, isc_sockaddr_t *remote,
+ isc_sockaddr_t *local, isc_time_t *now)
+{
+ unsigned int i;
+ isc_rwlocktype_t locktype;
+ isc_result_t result;
+ isc_uint32_t seconds = isc_time_seconds(now);
+
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ locktype = isc_rwlocktype_read;
+ RWLOCK(&zmgr->rwlock, locktype);
+ for (i = 0; i < UNREACH_CHACHE_SIZE; i++) {
+ if (zmgr->unreachable[i].expire >= seconds &&
+ isc_sockaddr_equal(&zmgr->unreachable[i].remote, remote) &&
+ isc_sockaddr_equal(&zmgr->unreachable[i].local, local)) {
+ result = isc_rwlock_tryupgrade(&zmgr->rwlock);
+ if (result == ISC_R_SUCCESS) {
+ locktype = isc_rwlocktype_write;
+ zmgr->unreachable[i].last = seconds;
+ }
+ break;
+ }
+ }
+ RWUNLOCK(&zmgr->rwlock, locktype);
+ return (ISC_TF(i < UNREACH_CHACHE_SIZE));
+}
+
+void
+dns_zonemgr_unreachableadd(dns_zonemgr_t *zmgr, isc_sockaddr_t *remote,
+ isc_sockaddr_t *local, isc_time_t *now)
+{
+ isc_uint32_t seconds = isc_time_seconds(now);
+ isc_uint32_t last = seconds;
+ unsigned int i, slot = UNREACH_CHACHE_SIZE, oldest = 0;
+
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ RWLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+ for (i = 0; i < UNREACH_CHACHE_SIZE; i++) {
+ /* Existing entry? */
+ if (isc_sockaddr_equal(&zmgr->unreachable[i].remote, remote) &&
+ isc_sockaddr_equal(&zmgr->unreachable[i].local, local))
+ break;
+ /* Empty slot? */
+ if (zmgr->unreachable[i].expire < seconds)
+ slot = i;
+ /* Least recently used slot? */
+ if (zmgr->unreachable[i].last < last) {
+ last = zmgr->unreachable[i].last;
+ oldest = i;
+ }
+ }
+ if (i < UNREACH_CHACHE_SIZE) {
+ /*
+ * Found a existing entry. Update the expire timer and
+ * last usage timestamps.
+ */
+ zmgr->unreachable[i].expire = seconds + UNREACH_HOLD_TIME;
+ zmgr->unreachable[i].last = seconds;
+ } else if (slot != UNREACH_CHACHE_SIZE) {
+ /*
+ * Found a empty slot. Add a new entry to the cache.
+ */
+ zmgr->unreachable[slot].expire = seconds + UNREACH_HOLD_TIME;
+ zmgr->unreachable[slot].last = seconds;
+ zmgr->unreachable[slot].remote = *remote;
+ zmgr->unreachable[slot].local = *local;
+ } else {
+ /*
+ * Replace the least recently used entry in the cache.
+ */
+ zmgr->unreachable[oldest].expire = seconds + UNREACH_HOLD_TIME;
+ zmgr->unreachable[oldest].last = seconds;
+ zmgr->unreachable[oldest].remote = *remote;
+ zmgr->unreachable[oldest].local = *local;
+ }
+ RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_write);
+}
+
+void
+dns_zone_forcereload(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ if (zone->type == dns_zone_master)
+ return;
+
+ LOCK_ZONE(zone);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_FORCEXFER);
+ UNLOCK_ZONE(zone);
+ dns_zone_refresh(zone);
+}
+
+isc_boolean_t
+dns_zone_isforced(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FORCEXFER));
+}
+
+isc_result_t
+dns_zone_setstatistics(dns_zone_t *zone, isc_boolean_t on) {
+ /*
+ * This function is obsoleted.
+ */
+ UNUSED(zone);
+ UNUSED(on);
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+isc_uint64_t *
+dns_zone_getstatscounters(dns_zone_t *zone) {
+ /*
+ * This function is obsoleted.
+ */
+ UNUSED(zone);
+ return (NULL);
+}
+
+void
+dns_zone_setstats(dns_zone_t *zone, dns_stats_t *stats) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ REQUIRE(zone->stats == NULL);
+
+ LOCK_ZONE(zone);
+ zone->stats = NULL;
+ dns_stats_attach(stats, &zone->stats);
+ UNLOCK_ZONE(zone);
+}
+
+void
+dns_zone_setrequeststats(dns_zone_t *zone, dns_stats_t *stats) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ if (zone->requeststats_on && stats == NULL)
+ zone->requeststats_on = ISC_FALSE;
+ else if (!zone->requeststats_on && stats != NULL) {
+ if (zone->requeststats == NULL) {
+ dns_stats_attach(stats, &zone->requeststats);
+ zone->requeststats_on = ISC_TRUE;
+ }
+ }
+ UNLOCK_ZONE(zone);
+
+ return;
+}
+
+dns_stats_t *
+dns_zone_getrequeststats(dns_zone_t *zone) {
+ /*
+ * We don't lock zone for efficiency reason. This is not catastrophic
+ * because requeststats must always be valid when requeststats_on is
+ * true.
+ * Some counters may be incremented while requeststats_on is becoming
+ * false, or some cannot be incremented just after the statistics are
+ * installed, but it shouldn't matter much in practice.
+ */
+ if (zone->requeststats_on)
+ return (zone->requeststats);
+ else
+ return (NULL);
+}
+
+void
+dns_zone_dialup(dns_zone_t *zone) {
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ zone_debuglog(zone, "dns_zone_dialup", 3,
+ "notify = %d, refresh = %d",
+ DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALNOTIFY),
+ DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALREFRESH));
+
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALNOTIFY))
+ dns_zone_notify(zone);
+ if (zone->type != dns_zone_master &&
+ DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALREFRESH))
+ dns_zone_refresh(zone);
+}
+
+void
+dns_zone_setdialup(dns_zone_t *zone, dns_dialuptype_t dialup) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_DIALNOTIFY |
+ DNS_ZONEFLG_DIALREFRESH |
+ DNS_ZONEFLG_NOREFRESH);
+ switch (dialup) {
+ case dns_dialuptype_no:
+ break;
+ case dns_dialuptype_yes:
+ DNS_ZONE_SETFLAG(zone, (DNS_ZONEFLG_DIALNOTIFY |
+ DNS_ZONEFLG_DIALREFRESH |
+ DNS_ZONEFLG_NOREFRESH));
+ break;
+ case dns_dialuptype_notify:
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_DIALNOTIFY);
+ break;
+ case dns_dialuptype_notifypassive:
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_DIALNOTIFY);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOREFRESH);
+ break;
+ case dns_dialuptype_refresh:
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_DIALREFRESH);
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOREFRESH);
+ break;
+ case dns_dialuptype_passive:
+ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOREFRESH);
+ break;
+ default:
+ INSIST(0);
+ }
+ UNLOCK_ZONE(zone);
+}
+
+isc_result_t
+dns_zone_setkeydirectory(dns_zone_t *zone, const char *directory) {
+ isc_result_t result = ISC_R_SUCCESS;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ result = dns_zone_setstring(zone, &zone->keydirectory, directory);
+ UNLOCK_ZONE(zone);
+
+ return (result);
+}
+
+const char *
+dns_zone_getkeydirectory(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->keydirectory);
+}
+
+unsigned int
+dns_zonemgr_getcount(dns_zonemgr_t *zmgr, int state) {
+ dns_zone_t *zone;
+ unsigned int count = 0;
+
+ REQUIRE(DNS_ZONEMGR_VALID(zmgr));
+
+ RWLOCK(&zmgr->rwlock, isc_rwlocktype_read);
+ switch (state) {
+ case DNS_ZONESTATE_XFERRUNNING:
+ for (zone = ISC_LIST_HEAD(zmgr->xfrin_in_progress);
+ zone != NULL;
+ zone = ISC_LIST_NEXT(zone, statelink))
+ count++;
+ break;
+ case DNS_ZONESTATE_XFERDEFERRED:
+ for (zone = ISC_LIST_HEAD(zmgr->waiting_for_xfrin);
+ zone != NULL;
+ zone = ISC_LIST_NEXT(zone, statelink))
+ count++;
+ break;
+ case DNS_ZONESTATE_SOAQUERY:
+ for (zone = ISC_LIST_HEAD(zmgr->zones);
+ zone != NULL;
+ zone = ISC_LIST_NEXT(zone, link))
+ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_REFRESH))
+ count++;
+ break;
+ case DNS_ZONESTATE_ANY:
+ for (zone = ISC_LIST_HEAD(zmgr->zones);
+ zone != NULL;
+ zone = ISC_LIST_NEXT(zone, link)) {
+ dns_view_t *view = zone->view;
+ if (view != NULL && strcmp(view->name, "_bind") == 0)
+ continue;
+ count++;
+ }
+ break;
+ default:
+ INSIST(0);
+ }
+
+ RWUNLOCK(&zmgr->rwlock, isc_rwlocktype_read);
+
+ return (count);
+}
+
+isc_result_t
+dns_zone_checknames(dns_zone_t *zone, dns_name_t *name, dns_rdata_t *rdata) {
+ isc_boolean_t ok = ISC_TRUE;
+ isc_boolean_t fail = ISC_FALSE;
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char namebuf2[DNS_NAME_FORMATSIZE];
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
+ int level = ISC_LOG_WARNING;
+ dns_name_t bad;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ if (!DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKNAMES))
+ return (ISC_R_SUCCESS);
+
+ if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKNAMESFAIL)) {
+ level = ISC_LOG_ERROR;
+ fail = ISC_TRUE;
+ }
+
+ ok = dns_rdata_checkowner(name, rdata->rdclass, rdata->type, ISC_TRUE);
+ if (!ok) {
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ dns_rdatatype_format(rdata->type, typebuf, sizeof(typebuf));
+ dns_zone_log(zone, level, "%s/%s: %s", namebuf, typebuf,
+ dns_result_totext(DNS_R_BADOWNERNAME));
+ if (fail)
+ return (DNS_R_BADOWNERNAME);
+ }
+
+ dns_name_init(&bad, NULL);
+ ok = dns_rdata_checknames(rdata, name, &bad);
+ if (!ok) {
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ dns_name_format(&bad, namebuf2, sizeof(namebuf2));
+ dns_rdatatype_format(rdata->type, typebuf, sizeof(typebuf));
+ dns_zone_log(zone, level, "%s/%s: %s: %s ", namebuf, typebuf,
+ namebuf2, dns_result_totext(DNS_R_BADNAME));
+ if (fail)
+ return (DNS_R_BADNAME);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_zone_setcheckmx(dns_zone_t *zone, dns_checkmxfunc_t checkmx) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ zone->checkmx = checkmx;
+}
+
+void
+dns_zone_setchecksrv(dns_zone_t *zone, dns_checksrvfunc_t checksrv) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ zone->checksrv = checksrv;
+}
+
+void
+dns_zone_setcheckns(dns_zone_t *zone, dns_checknsfunc_t checkns) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ zone->checkns = checkns;
+}
+
+void
+dns_zone_setisself(dns_zone_t *zone, dns_isselffunc_t isself, void *arg) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ zone->isself = isself;
+ zone->isselfarg = arg;
+ UNLOCK_ZONE(zone);
+}
+
+void
+dns_zone_setnotifydelay(dns_zone_t *zone, isc_uint32_t delay) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ LOCK_ZONE(zone);
+ zone->notifydelay = delay;
+ UNLOCK_ZONE(zone);
+}
+
+isc_uint32_t
+dns_zone_getnotifydelay(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (zone->notifydelay);
+}
+
+isc_result_t
+dns_zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm,
+ isc_uint16_t keyid, isc_boolean_t delete)
+{
+ isc_result_t result;
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ dns_zone_log(zone, ISC_LOG_NOTICE,
+ "dns_zone_signwithkey(algorithm=%u, keyid=%u)",
+ algorithm, keyid);
+ LOCK_ZONE(zone);
+ result = zone_signwithkey(zone, algorithm, keyid, delete);
+ UNLOCK_ZONE(zone);
+
+ return (result);
+}
+
+static const char *hex = "0123456789ABCDEF";
+
+isc_result_t
+dns_zone_addnsec3chain(dns_zone_t *zone, dns_rdata_nsec3param_t *nsec3param) {
+ isc_result_t result;
+ char salt[255*2+1];
+ unsigned int i, j;
+
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ if (nsec3param->salt_length != 0) {
+ INSIST((nsec3param->salt_length * 2U) < sizeof(salt));
+ for (i = 0, j = 0; i < nsec3param->salt_length; i++) {
+ salt[j++] = hex[(nsec3param->salt[i] >> 4) & 0xf];
+ salt[j++] = hex[nsec3param->salt[i] & 0xf];
+ }
+ salt[j] = '\0';
+ } else
+ strcpy(salt, "-");
+ dns_zone_log(zone, ISC_LOG_NOTICE,
+ "dns_zone_addnsec3chain(hash=%u, iterations=%u, salt=%s)",
+ nsec3param->hash, nsec3param->iterations,
+ salt);
+ LOCK_ZONE(zone);
+ result = zone_addnsec3chain(zone, nsec3param);
+ UNLOCK_ZONE(zone);
+
+ return (result);
+}
+
+void
+dns_zone_setnodes(dns_zone_t *zone, isc_uint32_t nodes) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ if (nodes == 0)
+ nodes = 1;
+ zone->nodes = nodes;
+}
+
+void
+dns_zone_setsignatures(dns_zone_t *zone, isc_uint32_t signatures) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ /*
+ * We treat signatures as a signed value so explicitly
+ * limit its range here.
+ */
+ if (signatures > ISC_INT32_MAX)
+ signatures = ISC_INT32_MAX;
+ else if (signatures == 0)
+ signatures = 1;
+ zone->signatures = signatures;
+}
+
+void
+dns_zone_setprivatetype(dns_zone_t *zone, dns_rdatatype_t type) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ zone->privatetype = type;
+}
+
+dns_rdatatype_t
+dns_zone_getprivatetype(dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ return (zone->privatetype);
+}
+
+static isc_result_t
+zone_signwithkey(dns_zone_t *zone, dns_secalg_t algorithm, isc_uint16_t keyid,
+ isc_boolean_t delete)
+{
+ dns_signing_t *signing;
+ dns_signing_t *current;
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_time_t now;
+
+ signing = isc_mem_get(zone->mctx, sizeof *signing);
+ if (signing == NULL)
+ return (ISC_R_NOMEMORY);
+
+ signing->magic = 0;
+ signing->db = NULL;
+ signing->dbiterator = NULL;
+ signing->algorithm = algorithm;
+ signing->keyid = keyid;
+ signing->delete = delete;
+ signing->done = ISC_FALSE;
+
+ TIME_NOW(&now);
+
+ for (current = ISC_LIST_HEAD(zone->signing);
+ current != NULL;
+ current = ISC_LIST_NEXT(current, link)) {
+ if (current->db == zone->db &&
+ current->algorithm == signing->algorithm &&
+ current->keyid == signing->keyid) {
+ if (current->delete != signing->delete)
+ current->done = ISC_TRUE;
+ else
+ goto cleanup;
+ }
+ }
+
+ if (zone->db != NULL) {
+ dns_db_attach(zone->db, &signing->db);
+ result = dns_db_createiterator(signing->db, 0,
+ &signing->dbiterator);
+
+ if (result == ISC_R_SUCCESS)
+ result = dns_dbiterator_first(signing->dbiterator);
+ if (result == ISC_R_SUCCESS) {
+ dns_dbiterator_pause(signing->dbiterator);
+ ISC_LIST_INITANDAPPEND(zone->signing, signing, link);
+ signing = NULL;
+ if (isc_time_isepoch(&zone->signingtime)) {
+ zone->signingtime = now;
+ if (zone->task != NULL)
+ zone_settimer(zone, &now);
+ }
+ }
+ } else
+ result = ISC_R_NOTFOUND;
+
+ cleanup:
+ if (signing != NULL) {
+ dns_db_detach(&signing->db);
+ if (signing->dbiterator != NULL)
+ dns_dbiterator_destroy(&signing->dbiterator);
+ isc_mem_put(zone->mctx, signing, sizeof *signing);
+ }
+ return (result);
+}
diff --git a/lib/dns/zonekey.c b/lib/dns/zonekey.c
new file mode 100644
index 0000000..bf7474b
--- /dev/null
+++ b/lib/dns/zonekey.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2001, 2003 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: zonekey.c,v 1.9 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/result.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+#include <dns/keyvalues.h>
+#include <dns/rdata.h>
+#include <dns/rdatastruct.h>
+#include <dns/types.h>
+#include <dns/zonekey.h>
+
+isc_boolean_t
+dns_zonekey_iszonekey(dns_rdata_t *keyrdata) {
+ isc_result_t result;
+ dns_rdata_dnskey_t key;
+ isc_boolean_t iszonekey = ISC_TRUE;
+
+ REQUIRE(keyrdata != NULL);
+
+ result = dns_rdata_tostruct(keyrdata, &key, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_FALSE);
+
+ if ((key.flags & DNS_KEYTYPE_NOAUTH) != 0)
+ iszonekey = ISC_FALSE;
+ if ((key.flags & DNS_KEYFLAG_OWNERMASK) != DNS_KEYOWNER_ZONE)
+ iszonekey = ISC_FALSE;
+ if (key.protocol != DNS_KEYPROTO_DNSSEC &&
+ key.protocol != DNS_KEYPROTO_ANY)
+ iszonekey = ISC_FALSE;
+
+ return (iszonekey);
+}
diff --git a/lib/dns/zt.c b/lib/dns/zt.c
new file mode 100644
index 0000000..ed7f28a
--- /dev/null
+++ b/lib/dns/zt.c
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * 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 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.
+ */
+
+/* $Id: zt.c,v 1.47 2007/06/19 23:47:16 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/file.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/log.h>
+#include <dns/name.h>
+#include <dns/rbt.h>
+#include <dns/rdataclass.h>
+#include <dns/result.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+#include <dns/zt.h>
+
+struct dns_zt {
+ /* Unlocked. */
+ unsigned int magic;
+ isc_mem_t *mctx;
+ dns_rdataclass_t rdclass;
+ isc_rwlock_t rwlock;
+ /* Locked by lock. */
+ isc_uint32_t references;
+ dns_rbt_t *table;
+};
+
+#define ZTMAGIC ISC_MAGIC('Z', 'T', 'b', 'l')
+#define VALID_ZT(zt) ISC_MAGIC_VALID(zt, ZTMAGIC)
+
+static void
+auto_detach(void *, void *);
+
+static isc_result_t
+load(dns_zone_t *zone, void *uap);
+
+static isc_result_t
+loadnew(dns_zone_t *zone, void *uap);
+
+static isc_result_t
+freezezones(dns_zone_t *zone, void *uap);
+
+isc_result_t
+dns_zt_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, dns_zt_t **ztp)
+{
+ dns_zt_t *zt;
+ isc_result_t result;
+
+ REQUIRE(ztp != NULL && *ztp == NULL);
+
+ zt = isc_mem_get(mctx, sizeof(*zt));
+ if (zt == NULL)
+ return (ISC_R_NOMEMORY);
+
+ zt->table = NULL;
+ result = dns_rbt_create(mctx, auto_detach, zt, &zt->table);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_zt;
+
+ result = isc_rwlock_init(&zt->rwlock, 0, 0);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_rbt;
+
+ zt->mctx = mctx;
+ zt->references = 1;
+ zt->rdclass = rdclass;
+ zt->magic = ZTMAGIC;
+ *ztp = zt;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup_rbt:
+ dns_rbt_destroy(&zt->table);
+
+ cleanup_zt:
+ isc_mem_put(mctx, zt, sizeof(*zt));
+
+ return (result);
+}
+
+isc_result_t
+dns_zt_mount(dns_zt_t *zt, dns_zone_t *zone) {
+ isc_result_t result;
+ dns_zone_t *dummy = NULL;
+ dns_name_t *name;
+
+ REQUIRE(VALID_ZT(zt));
+
+ name = dns_zone_getorigin(zone);
+
+ RWLOCK(&zt->rwlock, isc_rwlocktype_write);
+
+ result = dns_rbt_addname(zt->table, name, zone);
+ if (result == ISC_R_SUCCESS)
+ dns_zone_attach(zone, &dummy);
+
+ RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
+
+ return (result);
+}
+
+isc_result_t
+dns_zt_unmount(dns_zt_t *zt, dns_zone_t *zone) {
+ isc_result_t result;
+ dns_name_t *name;
+
+ REQUIRE(VALID_ZT(zt));
+
+ name = dns_zone_getorigin(zone);
+
+ RWLOCK(&zt->rwlock, isc_rwlocktype_write);
+
+ result = dns_rbt_deletename(zt->table, name, ISC_FALSE);
+
+ RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
+
+ return (result);
+}
+
+isc_result_t
+dns_zt_find(dns_zt_t *zt, dns_name_t *name, unsigned int options,
+ dns_name_t *foundname, dns_zone_t **zonep)
+{
+ isc_result_t result;
+ dns_zone_t *dummy = NULL;
+ unsigned int rbtoptions = 0;
+
+ REQUIRE(VALID_ZT(zt));
+
+ if ((options & DNS_ZTFIND_NOEXACT) != 0)
+ rbtoptions |= DNS_RBTFIND_NOEXACT;
+
+ RWLOCK(&zt->rwlock, isc_rwlocktype_read);
+
+ result = dns_rbt_findname(zt->table, name, rbtoptions, foundname,
+ (void **) (void*)&dummy);
+ if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
+ dns_zone_attach(dummy, zonep);
+
+ RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
+
+ return (result);
+}
+
+void
+dns_zt_attach(dns_zt_t *zt, dns_zt_t **ztp) {
+
+ REQUIRE(VALID_ZT(zt));
+ REQUIRE(ztp != NULL && *ztp == NULL);
+
+ RWLOCK(&zt->rwlock, isc_rwlocktype_write);
+
+ INSIST(zt->references > 0);
+ zt->references++;
+ INSIST(zt->references != 0);
+
+ RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
+
+ *ztp = zt;
+}
+
+static isc_result_t
+flush(dns_zone_t *zone, void *uap) {
+ UNUSED(uap);
+ return (dns_zone_flush(zone));
+}
+
+static void
+zt_flushanddetach(dns_zt_t **ztp, isc_boolean_t need_flush) {
+ isc_boolean_t destroy = ISC_FALSE;
+ dns_zt_t *zt;
+
+ REQUIRE(ztp != NULL && VALID_ZT(*ztp));
+
+ zt = *ztp;
+
+ RWLOCK(&zt->rwlock, isc_rwlocktype_write);
+
+ INSIST(zt->references > 0);
+ zt->references--;
+ if (zt->references == 0)
+ destroy = ISC_TRUE;
+
+ RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
+
+ if (destroy) {
+ if (need_flush)
+ (void)dns_zt_apply(zt, ISC_FALSE, flush, NULL);
+ dns_rbt_destroy(&zt->table);
+ isc_rwlock_destroy(&zt->rwlock);
+ zt->magic = 0;
+ isc_mem_put(zt->mctx, zt, sizeof(*zt));
+ }
+
+ *ztp = NULL;
+}
+
+void
+dns_zt_flushanddetach(dns_zt_t **ztp) {
+ zt_flushanddetach(ztp, ISC_TRUE);
+}
+
+void
+dns_zt_detach(dns_zt_t **ztp) {
+ zt_flushanddetach(ztp, ISC_FALSE);
+}
+
+isc_result_t
+dns_zt_load(dns_zt_t *zt, isc_boolean_t stop) {
+ isc_result_t result;
+
+ REQUIRE(VALID_ZT(zt));
+
+ RWLOCK(&zt->rwlock, isc_rwlocktype_read);
+ result = dns_zt_apply(zt, stop, load, NULL);
+ RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
+ return (result);
+}
+
+static isc_result_t
+load(dns_zone_t *zone, void *uap) {
+ isc_result_t result;
+ UNUSED(uap);
+ result = dns_zone_load(zone);
+ if (result == DNS_R_CONTINUE || result == DNS_R_UPTODATE)
+ result = ISC_R_SUCCESS;
+ return (result);
+}
+
+isc_result_t
+dns_zt_loadnew(dns_zt_t *zt, isc_boolean_t stop) {
+ isc_result_t result;
+
+ REQUIRE(VALID_ZT(zt));
+
+ RWLOCK(&zt->rwlock, isc_rwlocktype_read);
+ result = dns_zt_apply(zt, stop, loadnew, NULL);
+ RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
+ return (result);
+}
+
+static isc_result_t
+loadnew(dns_zone_t *zone, void *uap) {
+ isc_result_t result;
+ UNUSED(uap);
+ result = dns_zone_loadnew(zone);
+ if (result == DNS_R_CONTINUE || result == DNS_R_UPTODATE ||
+ result == DNS_R_DYNAMIC)
+ result = ISC_R_SUCCESS;
+ return (result);
+}
+
+isc_result_t
+dns_zt_freezezones(dns_zt_t *zt, isc_boolean_t freeze) {
+ isc_result_t result, tresult;
+
+ REQUIRE(VALID_ZT(zt));
+
+ RWLOCK(&zt->rwlock, isc_rwlocktype_read);
+ result = dns_zt_apply2(zt, ISC_FALSE, &tresult, freezezones, &freeze);
+ RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
+ return ((result == ISC_R_SUCCESS) ? tresult : result);
+}
+
+static isc_result_t
+freezezones(dns_zone_t *zone, void *uap) {
+ isc_boolean_t freeze = *(isc_boolean_t *)uap;
+ isc_boolean_t frozen;
+ isc_result_t result = ISC_R_SUCCESS;
+ char classstr[DNS_RDATACLASS_FORMATSIZE];
+ char zonename[DNS_NAME_FORMATSIZE];
+ dns_view_t *view;
+ char *journal;
+ const char *vname;
+ const char *sep;
+ int level;
+
+ if (dns_zone_gettype(zone) != dns_zone_master)
+ return (ISC_R_SUCCESS);
+
+ frozen = dns_zone_getupdatedisabled(zone);
+ if (freeze) {
+ if (frozen)
+ result = DNS_R_FROZEN;
+ if (result == ISC_R_SUCCESS)
+ result = dns_zone_flush(zone);
+ if (result == ISC_R_SUCCESS) {
+ journal = dns_zone_getjournal(zone);
+ if (journal != NULL)
+ (void)isc_file_remove(journal);
+ }
+ } else {
+ if (frozen) {
+ result = dns_zone_load(zone);
+ if (result == DNS_R_CONTINUE ||
+ result == DNS_R_UPTODATE)
+ result = ISC_R_SUCCESS;
+ }
+ }
+ if (result == ISC_R_SUCCESS)
+ dns_zone_setupdatedisabled(zone, freeze);
+ view = dns_zone_getview(zone);
+ if (strcmp(view->name, "_bind") == 0 ||
+ strcmp(view->name, "_default") == 0)
+ {
+ vname = "";
+ sep = "";
+ } else {
+ vname = view->name;
+ sep = " ";
+ }
+ dns_rdataclass_format(dns_zone_getclass(zone), classstr,
+ sizeof(classstr));
+ dns_name_format(dns_zone_getorigin(zone), zonename, sizeof(zonename));
+ level = (result != ISC_R_SUCCESS) ? ISC_LOG_ERROR : ISC_LOG_DEBUG(1);
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE,
+ level, "%s zone '%s/%s'%s%s: %s",
+ freeze ? "freezing" : "thawing",
+ zonename, classstr, sep, vname,
+ isc_result_totext(result));
+ return (result);
+}
+
+isc_result_t
+dns_zt_apply(dns_zt_t *zt, isc_boolean_t stop,
+ isc_result_t (*action)(dns_zone_t *, void *), void *uap)
+{
+ return (dns_zt_apply2(zt, stop, NULL, action, uap));
+}
+
+isc_result_t
+dns_zt_apply2(dns_zt_t *zt, isc_boolean_t stop, isc_result_t *sub,
+ isc_result_t (*action)(dns_zone_t *, void *), void *uap)
+{
+ dns_rbtnode_t *node;
+ dns_rbtnodechain_t chain;
+ isc_result_t result, tresult = ISC_R_SUCCESS;
+ dns_zone_t *zone;
+
+ REQUIRE(VALID_ZT(zt));
+ REQUIRE(action != NULL);
+
+ dns_rbtnodechain_init(&chain, zt->mctx);
+ result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL);
+ if (result == ISC_R_NOTFOUND) {
+ /*
+ * The tree is empty.
+ */
+ result = ISC_R_NOMORE;
+ }
+ while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
+ result = dns_rbtnodechain_current(&chain, NULL, NULL,
+ &node);
+ if (result == ISC_R_SUCCESS) {
+ zone = node->data;
+ if (zone != NULL)
+ result = (action)(zone, uap);
+ if (result != ISC_R_SUCCESS && stop) {
+ tresult = result;
+ goto cleanup; /* don't break */
+ } else if (result != ISC_R_SUCCESS &&
+ tresult == ISC_R_SUCCESS)
+ tresult = result;
+ }
+ result = dns_rbtnodechain_next(&chain, NULL, NULL);
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ dns_rbtnodechain_invalidate(&chain);
+ if (sub != NULL)
+ *sub = tresult;
+
+ return (result);
+}
+
+/***
+ *** Private
+ ***/
+
+static void
+auto_detach(void *data, void *arg) {
+ dns_zone_t *zone = data;
+
+ UNUSED(arg);
+
+ dns_zone_detach(&zone);
+}